]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/file.c
Add Erik Quanstrom's smart tool for ATA SMART.
[plan9front.git] / sys / src / cmd / file.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ctype.h>
5 #include <mach.h>
6
7 /*
8  * file - determine type of file
9  */
10 #define LENDIAN(p)      ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
11
12 uchar   buf[6001];
13 short   cfreq[140];
14 short   wfreq[50];
15 int     nbuf;
16 Dir*    mbuf;
17 int     fd;
18 char    *fname;
19 char    *slash;
20
21 enum
22 {
23         Cword,
24         Fword,
25         Aword,
26         Alword,
27         Lword,
28         I1,
29         I2,
30         I3,
31         Clatin  = 128,
32         Cbinary,
33         Cnull,
34         Ceascii,
35         Cutf,
36 };
37 struct
38 {
39         char*   word;
40         int     class;
41 } dict[] =
42 {
43         "PATH",         Lword,
44         "TEXT",         Aword,
45         "adt",          Alword,
46         "aggr",         Alword,
47         "alef",         Alword,
48         "array",        Lword,
49         "block",        Fword,
50         "char",         Cword,
51         "common",       Fword,
52         "con",          Lword,
53         "data",         Fword,
54         "dimension",    Fword,
55         "double",       Cword,
56         "extern",       Cword,
57         "bio",          I2,
58         "float",        Cword,
59         "fn",           Lword,
60         "function",     Fword,
61         "h",            I3,
62         "implement",    Lword,
63         "import",       Lword,
64         "include",      I1,
65         "int",          Cword,
66         "integer",      Fword,
67         "iota",         Lword,
68         "libc",         I2,
69         "long",         Cword,
70         "module",       Lword,
71         "real",         Fword,
72         "ref",          Lword,
73         "register",     Cword,
74         "self",         Lword,
75         "short",        Cword,
76         "static",       Cword,
77         "stdio",        I2,
78         "struct",       Cword,
79         "subroutine",   Fword,
80         "u",            I2,
81         "void",         Cword,
82 };
83
84 /* codes for 'mode' field in language structure */
85 enum    {
86                 Normal  = 0,
87                 First,          /* first entry for language spanning several ranges */
88                 Multi,          /* later entries "   "       "  ... */
89                 Shared,         /* codes used in several languages */
90         };
91
92 struct
93 {
94         int     mode;           /* see enum above */
95         int     count;
96         int     low;
97         int     high;
98         char    *name;
99
100 } language[] =
101 {
102         Normal, 0,      0x0100, 0x01FF, "Extended Latin",
103         Normal, 0,      0x0370, 0x03FF, "Greek",
104         Normal, 0,      0x0400, 0x04FF, "Cyrillic",
105         Normal, 0,      0x0530, 0x058F, "Armenian",
106         Normal, 0,      0x0590, 0x05FF, "Hebrew",
107         Normal, 0,      0x0600, 0x06FF, "Arabic",
108         Normal, 0,      0x0900, 0x097F, "Devanagari",
109         Normal, 0,      0x0980, 0x09FF, "Bengali",
110         Normal, 0,      0x0A00, 0x0A7F, "Gurmukhi",
111         Normal, 0,      0x0A80, 0x0AFF, "Gujarati",
112         Normal, 0,      0x0B00, 0x0B7F, "Oriya",
113         Normal, 0,      0x0B80, 0x0BFF, "Tamil",
114         Normal, 0,      0x0C00, 0x0C7F, "Telugu",
115         Normal, 0,      0x0C80, 0x0CFF, "Kannada",
116         Normal, 0,      0x0D00, 0x0D7F, "Malayalam",
117         Normal, 0,      0x0E00, 0x0E7F, "Thai",
118         Normal, 0,      0x0E80, 0x0EFF, "Lao",
119         Normal, 0,      0x1000, 0x105F, "Tibetan",
120         Normal, 0,      0x10A0, 0x10FF, "Georgian",
121         Normal, 0,      0x3040, 0x30FF, "Japanese",
122         Normal, 0,      0x3100, 0x312F, "Chinese",
123         First,  0,      0x3130, 0x318F, "Korean",
124         Multi,  0,      0x3400, 0x3D2F, "Korean",
125         Shared, 0,      0x4e00, 0x9fff, "CJK",
126         Normal, 0,      0,      0,      0,              /* terminal entry */
127 };
128
129
130 enum
131 {
132         Fascii,         /* printable ascii */
133         Flatin,         /* latin 1*/
134         Futf,           /* UTF character set */
135         Fbinary,        /* binary */
136         Feascii,        /* ASCII with control chars */
137         Fnull,          /* NULL in file */
138 } guess;
139
140 void    bump_utf_count(Rune);
141 int     cistrncmp(char*, char*, int);
142 void    filetype(int);
143 int     getfontnum(uchar*, uchar**);
144 int     isas(void);
145 int     isc(void);
146 int     iscint(void);
147 int     isenglish(void);
148 int     ishp(void);
149 int     ishtml(void);
150 int     isrfc822(void);
151 int     ismbox(void);
152 int     islimbo(void);
153 int     ismp3(void);
154 int     ismung(void);
155 int     isp9bit(void);
156 int     isp9font(void);
157 int     isrtf(void);
158 int     ismsdos(void);
159 int     isicocur(void);
160 int     iself(void);
161 int     istring(void);
162 int     isoffstr(void);
163 int     iff(void);
164 int     long0(void);
165 int     longoff(void);
166 int     istar(void);
167 int     isface(void);
168 int     isexec(void);
169 int     p9bitnum(uchar*);
170 int     p9subfont(uchar*);
171 void    print_utf(void);
172 void    type(char*, int);
173 int     utf_count(void);
174 void    wordfreq(void);
175
176 int     (*call[])(void) =
177 {
178         long0,          /* recognizable by first 4 bytes */
179         istring,        /* recognizable by first string */
180         iself,          /* ELF (foreign) executable */
181         isexec,         /* native executables */
182         iff,            /* interchange file format (strings) */
183         longoff,        /* recognizable by 4 bytes at some offset */
184         isoffstr,       /* recognizable by string at some offset */
185         isrfc822,       /* email file */
186         ismbox,         /* mail box */
187         istar,          /* recognizable by tar checksum */
188         ishtml,         /* html keywords */
189         iscint,         /* compiler/assembler intermediate */
190         islimbo,        /* limbo source */
191         isc,            /* c & alef compiler key words */
192         isas,           /* assembler key words */
193         isp9font,       /* plan 9 font */
194         isp9bit,        /* plan 9 image (as from /dev/window) */
195         isrtf,          /* rich text format */
196         ismsdos,        /* msdos exe (virus file attachement) */
197         isicocur,               /* windows icon or cursor file */
198         isface,         /* ascii face file */
199         ismp3,
200
201         /* last resorts */
202         ismung,         /* entropy compressed/encrypted */
203         isenglish,      /* char frequency English */
204         0
205 };
206
207 int mime;
208
209 char OCTET[] =  "application/octet-stream\n";
210 char PLAIN[] =  "text/plain\n";
211
212 void
213 main(int argc, char *argv[])
214 {
215         int i, j, maxlen;
216         char *cp;
217         Rune r;
218
219         ARGBEGIN{
220         case 'm':
221                 mime = 1;
222                 break;
223         default:
224                 fprint(2, "usage: file [-m] [file...]\n");
225                 exits("usage");
226         }ARGEND;
227
228         maxlen = 0;
229         if(mime == 0 || argc > 1){
230                 for(i = 0; i < argc; i++) {
231                         for (j = 0, cp = argv[i]; *cp; j++, cp += chartorune(&r, cp))
232                                         ;
233                         if(j > maxlen)
234                                 maxlen = j;
235                 }
236         }
237         if (argc <= 0) {
238                 if(!mime)
239                         print ("stdin: ");
240                 filetype(0);
241         }
242         else {
243                 for(i = 0; i < argc; i++)
244                         type(argv[i], maxlen);
245         }
246         exits(0);
247 }
248
249 void
250 type(char *file, int nlen)
251 {
252         Rune r;
253         int i;
254         char *p;
255
256         if(nlen > 0){
257                 slash = 0;
258                 for (i = 0, p = file; *p; i++) {
259                         if (*p == '/')                  /* find rightmost slash */
260                                 slash = p;
261                         p += chartorune(&r, p);         /* count runes */
262                 }
263                 print("%s:%*s",file, nlen-i+1, "");
264         }
265         fname = file;
266         if ((fd = open(file, OREAD)) < 0) {
267                 print("cannot open: %r\n");
268                 return;
269         }
270         filetype(fd);
271         close(fd);
272 }
273
274 /*
275  * Unicode 4.0 4-byte runes.
276  */
277 typedef int Rune1;
278
279 enum {
280         UTFmax1 = 4,
281 };
282
283 int
284 fullrune1(char *p, int n)
285 {
286         int c;
287
288         if(n >= 1) {
289                 c = *(uchar*)p;
290                 if(c < 0x80)
291                         return 1;
292                 if(n >= 2 && c < 0xE0)
293                         return 1;
294                 if(n >= 3 && c < 0xF0)
295                         return 1;
296                 if(n >= 4)
297                         return 1;
298         }
299         return 0;
300 }
301
302 int
303 chartorune1(Rune1 *rune, char *str)
304 {
305         int c, c1, c2, c3, n;
306         Rune r;
307
308         c = *(uchar*)str;
309         if(c < 0xF0){
310                 r = 0;
311                 n = chartorune(&r, str);
312                 *rune = r;
313                 return n;
314         }
315         c &= ~0xF0;
316         c1 = *(uchar*)(str+1) & ~0x80;
317         c2 = *(uchar*)(str+2) & ~0x80;
318         c3 = *(uchar*)(str+3) & ~0x80;
319         n = (c<<18) | (c1<<12) | (c2<<6) | c3;
320         if(n < 0x10000 || n > 0x10FFFF){
321                 *rune = Runeerror;
322                 return 1;
323         }
324         *rune = n;
325         return 4;
326 }
327
328 void
329 filetype(int fd)
330 {
331         Rune1 r;
332         int i, f, n;
333         char *p, *eob;
334
335         free(mbuf);
336         mbuf = dirfstat(fd);
337         if(mbuf == nil){
338                 print("cannot stat: %r\n");
339                 return;
340         }
341         if(mbuf->mode & DMDIR) {
342                 print(mime ? OCTET : "directory\n");
343                 return;
344         }
345         if(mbuf->type != 'M' && mbuf->type != '|') {
346                 print(mime ? OCTET : "special file #%C/%s\n",
347                         mbuf->type, mbuf->name);
348                 return;
349         }
350         /* may be reading a pipe on standard input */
351         nbuf = readn(fd, buf, sizeof(buf)-1);
352         if(nbuf < 0) {
353                 print("cannot read: %r\n");
354                 return;
355         }
356         if(nbuf == 0) {
357                 print(mime ? PLAIN : "empty file\n");
358                 return;
359         }
360         buf[nbuf] = 0;
361
362         /*
363          * build histogram table
364          */
365         memset(cfreq, 0, sizeof(cfreq));
366         for (i = 0; language[i].name; i++)
367                 language[i].count = 0;
368         eob = (char *)buf+nbuf;
369         for(n = 0, p = (char *)buf; p < eob; n++) {
370                 if (!fullrune1(p, eob-p) && eob-p < UTFmax1)
371                         break;
372                 p += chartorune1(&r, p);
373                 if (r == 0)
374                         f = Cnull;
375                 else if (r <= 0x7f) {
376                         if (!isprint(r) && !isspace(r))
377                                 f = Ceascii;    /* ASCII control char */
378                         else f = r;
379                 } else if (r == 0x80) {
380                         bump_utf_count(r);
381                         f = Cutf;
382                 } else if (r < 0xA0)
383                         f = Cbinary;    /* Invalid Runes */
384                 else if (r <= 0xff)
385                         f = Clatin;     /* Latin 1 */
386                 else {
387                         bump_utf_count(r);
388                         f = Cutf;               /* UTF extension */
389                 }
390                 cfreq[f]++;                     /* ASCII chars peg directly */
391         }
392         /*
393          * gross classify
394          */
395         if (cfreq[Cbinary])
396                 guess = Fbinary;
397         else if (cfreq[Cutf])
398                 guess = Futf;
399         else if (cfreq[Clatin])
400                 guess = Flatin;
401         else if (cfreq[Ceascii])
402                 guess = Feascii;
403         else if (cfreq[Cnull])
404                 guess = Fbinary;
405         else
406                 guess = Fascii;
407         /*
408          * lookup dictionary words
409          */
410         memset(wfreq, 0, sizeof(wfreq));
411         if(guess == Fascii || guess == Flatin || guess == Futf)
412                 wordfreq();
413         /*
414          * call individual classify routines
415          */
416         for(i=0; call[i]; i++)
417                 if((*call[i])())
418                         return;
419
420         /*
421          * if all else fails,
422          * print out gross classification
423          */
424         if (nbuf < 100 && !mime)
425                 print(mime ? PLAIN : "short ");
426         if (guess == Fascii)
427                 print(mime ? PLAIN : "Ascii\n");
428         else if (guess == Feascii)
429                 print(mime ? PLAIN : "extended ascii\n");
430         else if (guess == Flatin)
431                 print(mime ? PLAIN : "latin ascii\n");
432         else if (guess == Futf && utf_count() < 4)
433                 print_utf();
434         else print(mime ? OCTET : "binary\n");
435 }
436
437 void
438 bump_utf_count(Rune r)
439 {
440         int low, high, mid;
441
442         high = sizeof(language)/sizeof(language[0])-1;
443         for (low = 0; low < high;) {
444                 mid = (low+high)/2;
445                 if (r >= language[mid].low) {
446                         if (r <= language[mid].high) {
447                                 language[mid].count++;
448                                 break;
449                         } else low = mid+1;
450                 } else high = mid;
451         }
452 }
453
454 int
455 utf_count(void)
456 {
457         int i, count;
458
459         count = 0;
460         for (i = 0; language[i].name; i++)
461                 if (language[i].count > 0)
462                         switch (language[i].mode) {
463                         case Normal:
464                         case First:
465                                 count++;
466                                 break;
467                         default:
468                                 break;
469                         }
470         return count;
471 }
472
473 int
474 chkascii(void)
475 {
476         int i;
477
478         for (i = 'a'; i < 'z'; i++)
479                 if (cfreq[i])
480                         return 1;
481         for (i = 'A'; i < 'Z'; i++)
482                 if (cfreq[i])
483                         return 1;
484         return 0;
485 }
486
487 int
488 find_first(char *name)
489 {
490         int i;
491
492         for (i = 0; language[i].name != 0; i++)
493                 if (language[i].mode == First
494                         && strcmp(language[i].name, name) == 0)
495                         return i;
496         return -1;
497 }
498
499 void
500 print_utf(void)
501 {
502         int i, printed, j;
503
504         if(mime){
505                 print(PLAIN);
506                 return;
507         }
508         if (chkascii()) {
509                 printed = 1;
510                 print("Ascii");
511         } else
512                 printed = 0;
513         for (i = 0; language[i].name; i++)
514                 if (language[i].count) {
515                         switch(language[i].mode) {
516                         case Multi:
517                                 j = find_first(language[i].name);
518                                 if (j < 0)
519                                         break;
520                                 if (language[j].count > 0)
521                                         break;
522                                 /* Fall through */
523                         case Normal:
524                         case First:
525                                 if (printed)
526                                         print(" & ");
527                                 else printed = 1;
528                                 print("%s", language[i].name);
529                                 break;
530                         case Shared:
531                         default:
532                                 break;
533                         }
534                 }
535         if(!printed)
536                 print("UTF");
537         print(" text\n");
538 }
539
540 void
541 wordfreq(void)
542 {
543         int low, high, mid, r;
544         uchar *p, *p2, c;
545
546         p = buf;
547         for(;;) {
548                 while (p < buf+nbuf && !isalpha(*p))
549                         p++;
550                 if (p >= buf+nbuf)
551                         return;
552                 p2 = p;
553                 while(p < buf+nbuf && isalpha(*p))
554                         p++;
555                 c = *p;
556                 *p = 0;
557                 high = sizeof(dict)/sizeof(dict[0]);
558                 for(low = 0;low < high;) {
559                         mid = (low+high)/2;
560                         r = strcmp(dict[mid].word, (char*)p2);
561                         if(r == 0) {
562                                 wfreq[dict[mid].class]++;
563                                 break;
564                         }
565                         if(r < 0)
566                                 low = mid+1;
567                         else
568                                 high = mid;
569                 }
570                 *p++ = c;
571         }
572 }
573
574 typedef struct Filemagic Filemagic;
575 struct Filemagic {
576         ulong x;
577         ulong mask;
578         char *desc;
579         char *mime;
580 };
581
582 /*
583  * integers in this table must be as seen on a little-endian machine
584  * when read from a file.
585  */
586 Filemagic long0tab[] = {
587         0xF16DF16D,     0xFFFFFFFF,     "pac1 audio file\n",    OCTET,
588         /* "pac1" */
589         0x31636170,     0xFFFFFFFF,     "pac3 audio file\n",    OCTET,
590         /* "pXc2 */
591         0x32630070,     0xFFFF00FF,     "pac4 audio file\n",    OCTET,
592         0xBA010000,     0xFFFFFFFF,     "mpeg system stream\n", OCTET,
593         0x43614c66,     0xFFFFFFFF,     "FLAC audio file\n",    "audio/flac",
594         0x30800CC0,     0xFFFFFFFF,     "inferno .dis executable\n", OCTET,
595         0x04034B50,     0xFFFFFFFF,     "zip archive\n", "application/zip",
596         070707,         0xFFFF,         "cpio archive\n", "application/x-cpio",
597         0x2F7,          0xFFFF,         "tex dvi\n", "application/dvi",
598         0xfaff,         0xfeff,         "mp3 audio\n",  "audio/mpeg",
599         0xfeff0000,     0xffffffff,     "utf-32be\n",   "text/plain charset=utf-32be",
600         0xfffe,         0xffffffff,     "utf-32le\n",   "text/plain charset=utf-32le",
601         0xfeff,         0xffff,         "utf-16be\n",   "text/plain charset=utf-16be",
602         0xfffe,         0xffff,         "utf-16le\n",   "text/plain charset=utf-16le",
603         /* 0xfeedface: this could alternately be a Next Plan 9 boot image */
604         0xcefaedfe,     0xFFFFFFFF,     "32-bit power Mach-O executable\n", OCTET,
605         /* 0xfeedfacf */
606         0xcffaedfe,     0xFFFFFFFF,     "64-bit power Mach-O executable\n", OCTET,
607         /* 0xcefaedfe */
608         0xfeedface,     0xFFFFFFFF,     "386 Mach-O executable\n", OCTET,
609         /* 0xcffaedfe */
610         0xfeedfacf,     0xFFFFFFFF,     "amd64 Mach-O executable\n", OCTET,
611         /* 0xcafebabe */
612         0xbebafeca,     0xFFFFFFFF,     "Mach-O universal executable\n", OCTET,
613         /*
614          * venti & fossil magic numbers are stored big-endian on disk,
615          * thus the numbers appear reversed in this table.
616          */
617         0xad4e5cd1,     0xFFFFFFFF,     "venti arena\n", OCTET,
618 };
619
620 int
621 filemagic(Filemagic *tab, int ntab, ulong x)
622 {
623         int i;
624
625         for(i=0; i<ntab; i++)
626                 if((x&tab[i].mask) == tab[i].x){
627                         print(mime ? tab[i].mime : tab[i].desc);
628                         return 1;
629                 }
630         return 0;
631 }
632
633 int
634 long0(void)
635 {
636         return filemagic(long0tab, nelem(long0tab), LENDIAN(buf));
637 }
638
639 typedef struct Fileoffmag Fileoffmag;
640 struct Fileoffmag {
641         ulong   off;
642         Filemagic;
643 };
644
645 /*
646  * integers in this table must be as seen on a little-endian machine
647  * when read from a file.
648  */
649 Fileoffmag longofftab[] = {
650         /*
651          * venti & fossil magic numbers are stored big-endian on disk,
652          * thus the numbers appear reversed in this table.
653          */
654         256*1024, 0xe7a5e4a9, 0xFFFFFFFF, "venti arenas partition\n", OCTET,
655         256*1024, 0xc75e5cd1, 0xFFFFFFFF, "venti index section\n", OCTET,
656         128*1024, 0x89ae7637, 0xFFFFFFFF, "fossil write buffer\n", OCTET,
657         4,        0x31647542, 0xFFFFFFFF, "OS X finder properties\n", OCTET,
658 };
659
660 int
661 fileoffmagic(Fileoffmag *tab, int ntab)
662 {
663         int i;
664         ulong x;
665         Fileoffmag *tp;
666         uchar buf[sizeof(long)];
667
668         for(i=0; i<ntab; i++) {
669                 tp = tab + i;
670                 seek(fd, tp->off, 0);
671                 if (readn(fd, buf, sizeof buf) != sizeof buf)
672                         continue;
673                 x = LENDIAN(buf);
674                 if((x&tp->mask) == tp->x){
675                         print(mime? tp->mime: tp->desc);
676                         return 1;
677                 }
678         }
679         return 0;
680 }
681
682 int
683 longoff(void)
684 {
685         return fileoffmagic(longofftab, nelem(longofftab));
686 }
687
688 int
689 isexec(void)
690 {
691         Fhdr f;
692
693         seek(fd, 0, 0);         /* reposition to start of file */
694         if(crackhdr(fd, &f)) {
695                 print(mime ? OCTET : "%s\n", f.name);
696                 return 1;
697         }
698         return 0;
699 }
700
701
702 /* from tar.c */
703 enum { NAMSIZ = 100, TBLOCK = 512 };
704
705 union   hblock
706 {
707         char    dummy[TBLOCK];
708         struct  header
709         {
710                 char    name[NAMSIZ];
711                 char    mode[8];
712                 char    uid[8];
713                 char    gid[8];
714                 char    size[12];
715                 char    mtime[12];
716                 char    chksum[8];
717                 char    linkflag;
718                 char    linkname[NAMSIZ];
719                 /* rest are defined by POSIX's ustar format; see p1003.2b */
720                 char    magic[6];       /* "ustar" */
721                 char    version[2];
722                 char    uname[32];
723                 char    gname[32];
724                 char    devmajor[8];
725                 char    devminor[8];
726                 char    prefix[155];  /* if non-null, path = prefix "/" name */
727         } dbuf;
728 };
729
730 int
731 checksum(union hblock *hp)
732 {
733         int i;
734         char *cp;
735         struct header *hdr = &hp->dbuf;
736
737         for (cp = hdr->chksum; cp < &hdr->chksum[sizeof hdr->chksum]; cp++)
738                 *cp = ' ';
739         i = 0;
740         for (cp = hp->dummy; cp < &hp->dummy[TBLOCK]; cp++)
741                 i += *cp & 0xff;
742         return i;
743 }
744
745 int
746 istar(void)
747 {
748         int chksum;
749         char tblock[TBLOCK];
750         union hblock *hp = (union hblock *)tblock;
751         struct header *hdr = &hp->dbuf;
752
753         seek(fd, 0, 0);         /* reposition to start of file */
754         if (readn(fd, tblock, sizeof tblock) != sizeof tblock)
755                 return 0;
756         chksum = strtol(hdr->chksum, 0, 8);
757         if (hdr->name[0] != '\0' && checksum(hp) == chksum) {
758                 if (strcmp(hdr->magic, "ustar") == 0)
759                         print(mime? "application/x-ustar\n": "posix tar archive\n");
760                 else
761                         print(mime? "application/x-tar\n": "tar archive\n");
762                 return 1;
763         }
764         return 0;
765 }
766
767 /*
768  * initial words to classify file
769  */
770 struct  FILE_STRING
771 {
772         char    *key;
773         char    *filetype;
774         int     length;
775         char    *mime;
776 } file_string[] =
777 {
778         "\x1f\x9d",             "compressed",                   2,      "application/x-compress",
779         "\x1f\x8b",             "gzip compressed",              2,      "application/x-gzip",
780         "BZh",                  "bzip2 compressed",             3,      "application/x-bzip2",
781         "!<arch>\n__.SYMDEF",   "archive random library",       16,     "application/octet-stream",
782         "!<arch>\n",            "archive",                      8,      "application/octet-stream",
783         "070707",               "cpio archive - ascii header",  6,      "application/octet-stream",
784         "#!/bin/rc",            "rc executable file",           9,      "text/plain",
785         "#!/bin/sh",            "sh executable file",           9,      "text/plain",
786         "%!",                   "postscript",                   2,      "application/postscript",
787         "\004%!",               "postscript",                   3,      "application/postscript",
788         "x T post",             "troff output for post",        8,      "application/troff",
789         "x T Latin1",           "troff output for Latin1",      10,     "application/troff",
790         "x T utf",              "troff output for UTF",         7,      "application/troff",
791         "x T 202",              "troff output for 202",         7,      "application/troff",
792         "x T aps",              "troff output for aps",         7,      "application/troff",
793         "GIF",                  "GIF image",                    3,      "image/gif",
794         "\0PC Research, Inc\0", "ghostscript fax file",         18,     "application/ghostscript",
795         "%PDF",                 "PDF",                          4,      "application/pdf",
796         "<!DOCTYPE",            "HTML file",                    9,      "text/html",
797         "<!doctype",            "HTML file",                    9,      "text/html",
798         "<!--",                 "HTML file",                    4,      "text/html",
799         "<html>",               "HTML file",                    6,      "text/html",
800         "<HTML>",               "HTML file",                    6,      "text/html",
801         "<?xml",                "HTML file",                    5,      "text/html",
802         "\111\111\052\000",     "tiff",                         4,      "image/tiff",
803         "\115\115\000\052",     "tiff",                         4,      "image/tiff",
804         "\377\330\377\340",     "jpeg",                         4,      "image/jpeg",
805         "\377\330\377\341",     "jpeg",                         4,      "image/jpeg",
806         "\377\330\377\333",     "jpeg",                         4,      "image/jpeg",
807         "\xff\xd8",             "jpeg",                         2,      "image/jpeg",
808         "BM",                   "bmp",                          2,      "image/bmp", 
809         "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1",     "microsoft office document",    8,      "application/doc",
810         "<MakerFile ",          "FrameMaker file",              11,     "application/framemaker",
811         "\033E\033",    "HP PCL printer data",          3,      OCTET,
812         "\033%-12345X", "HPJCL file",           9,      "application/hpjcl",
813         "ID3",                  "mp3 audio with id3",   3,      "audio/mpeg",
814         "OggS",                 "ogg audio",            4,      "audio/ogg",
815         "\211PNG",              "PNG image",            4,      "image/png",
816         "P3\n",                 "ppm",                          3,      "image/ppm",
817         "P6\n",                 "ppm",                          3,      "image/ppm",
818         "/* XPM */\n",  "xbm",                          10,     "image/xbm",
819         ".HTML ",               "troff -ms input",      6,      "text/troff",
820         ".LP",                  "troff -ms input",      3,      "text/troff",
821         ".ND",                  "troff -ms input",      3,      "text/troff",
822         ".PP",                  "troff -ms input",      3,      "text/troff",
823         ".TL",                  "troff -ms input",      3,      "text/troff",
824         ".TR",                  "troff -ms input",      3,      "text/troff",
825         ".TH",                  "manual page",          3,      "text/troff",
826         ".\\\"",                "troff input",          3,      "text/troff",
827         ".de",                  "troff input",          3,      "text/troff",
828         ".if",                  "troff input",          3,      "text/troff",
829         ".nr",                  "troff input",          3,      "text/troff",
830         ".tr",                  "troff input",          3,      "text/troff",
831         "vac:",                 "venti score",          4,      "text/plain",
832         "-----BEGIN CERTIFICATE-----\n",
833                                 "pem certificate",      -1,     "text/plain",
834         "-----BEGIN TRUSTED CERTIFICATE-----\n",
835                                 "pem trusted certificate", -1,  "text/plain",
836         "-----BEGIN X509 CERTIFICATE-----\n",
837                                 "pem x.509 certificate", -1,    "text/plain",
838         "subject=/C=",          "pem certificate with header", -1, "text/plain",
839         "process snapshot ",    "process snapshot",     -1,     "application/snapfs",
840         "d8:announce",          "torrent file",         11,     "application/x-bittorrent",
841         "[playlist]",           "playlist",             10,     "application/x-scpls",
842         "#EXTM3U",              "playlist",             7,      "audio/x-mpegurl",
843         0,0,0,0
844 };
845
846 int
847 istring(void)
848 {
849         int i, l;
850         struct FILE_STRING *p;
851
852         for(p = file_string; p->key; p++) {
853                 l = p->length;
854                 if(l == -1)
855                         l = strlen(p->key);
856                 if(nbuf >= l && memcmp(buf, p->key, l) == 0) {
857                         if(mime)
858                                 print("%s\n", p->mime);
859                         else
860                                 print("%s\n", p->filetype);
861                         return 1;
862                 }
863         }
864         if(strncmp((char*)buf, "TYPE=", 5) == 0) {      /* td */
865                 for(i = 5; i < nbuf; i++)
866                         if(buf[i] == '\n')
867                                 break;
868                 if(mime)
869                         print(OCTET);
870                 else
871                         print("%.*s picture\n", utfnlen((char*)buf+5, i-5), (char*)buf+5);
872                 return 1;
873         }
874         return 0;
875 }
876
877 struct offstr
878 {
879         ulong   off;
880         struct FILE_STRING;
881 } offstrs[] = {
882         32*1024, "\001CD001\001",       "ISO9660 CD image",     7,      "application/x-iso9660-image",
883         0, 0, 0, 0, 0
884 };
885
886 int
887 isoffstr(void)
888 {
889         int n;
890         char buf[256];
891         struct offstr *p;
892
893         for(p = offstrs; p->key; p++) {
894                 seek(fd, p->off, 0);
895                 n = p->length;
896                 if (n > sizeof buf)
897                         n = sizeof buf;
898                 if (readn(fd, buf, n) != n)
899                         continue;
900                 if(memcmp(buf, p->key, n) == 0) {
901                         if(mime)
902                                 print("%s\n", p->mime);
903                         else
904                                 print("%s\n", p->filetype);
905                         return 1;
906                 }
907         }
908         return 0;
909 }
910
911 int
912 iff(void)
913 {
914         if (strncmp((char*)buf, "FORM", 4) == 0 &&
915             strncmp((char*)buf+8, "AIFF", 4) == 0) {
916                 print("%s\n", mime? "audio/x-aiff": "aiff audio");
917                 return 1;
918         }
919         if (strncmp((char*)buf, "RIFF", 4) == 0) {
920                 if (strncmp((char*)buf+8, "WAVE", 4) == 0)
921                         print("%s\n", mime? "audio/wave": "wave audio");
922                 else if (strncmp((char*)buf+8, "AVI ", 4) == 0)
923                         print("%s\n", mime? "video/avi": "avi video");
924                 else
925                         print("%s\n", mime? "application/octet-stream":
926                                 "riff file");
927                 return 1;
928         }
929         return 0;
930 }
931
932 char*   html_string[] = {
933         "blockquote",
934         "!DOCTYPE", "![CDATA[", "basefont", "frameset", "noframes", "textarea",
935         "caption",
936         "button", "center", "iframe", "object", "option", "script",
937         "select", "strong",
938         "blink", "embed", "frame", "input", "label", "param", "small",
939         "style", "table", "tbody", "tfoot", "thead", "title",
940         "?xml", "body", "code", "font", "form", "head", "html",
941         "link", "menu", "meta", "span",
942         "!--", "big", "dir", "div", "img", "pre", "sub", "sup",
943         "br", "dd", "dl", "dt", "em", "h1", "h2", "h3", "h4", "h5",
944         "h6", "hr", "li", "ol", "td", "th", "tr", "tt", "ul",
945         "a", "b", "i", "p", "q", "u",
946         0,
947 };
948
949 int
950 ishtml(void)
951 {
952         int i, n, count;
953         uchar *p;
954
955         count = 0;
956         p = buf;
957         for(;;) {
958                 while(p < buf+nbuf && *p != '<')
959                         p++;
960                 p++;
961                 if (p >= buf+nbuf)
962                         break;
963                 if(*p == '/')
964                         p++;
965                 if(p >= buf+nbuf)
966                         break;
967                 for(i = 0; html_string[i]; i++){
968                         n = strlen(html_string[i]);
969                         if(p + n > buf+nbuf)
970                                 continue;
971                         if(cistrncmp(html_string[i], (char*)p, n) == 0) {
972                                 p += n;
973                                 if(p < buf+nbuf && strchr("\t\r\n />", *p)){
974                                         if(++count > 2) {
975                                                 print(mime ? "text/html\n" : "HTML file\n");
976                                                 return 1;
977                                         }
978                                 }
979                                 break;
980                         }
981                 }
982         }
983         return 0;
984 }
985
986 char*   rfc822_string[] =
987 {
988         "from:",
989         "date:",
990         "to:",
991         "subject:",
992         "received:",
993         "reply to:",
994         "sender:",
995         0,
996 };
997
998 int
999 isrfc822(void)
1000 {
1001
1002         char *p, *q, *r;
1003         int i, count;
1004
1005         count = 0;
1006         p = (char*)buf;
1007         for(;;) {
1008                 q = strchr(p, '\n');
1009                 if(q == nil)
1010                         break;
1011                 *q = 0;
1012                 if(p == (char*)buf && strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ")){
1013                         count++;
1014                         *q = '\n';
1015                         p = q+1;
1016                         continue;
1017                 }
1018                 *q = '\n';
1019                 if(*p != '\t' && *p != ' '){
1020                         r = strchr(p, ':');
1021                         if(r == 0 || r > q)
1022                                 break;
1023                         for(i = 0; rfc822_string[i]; i++) {
1024                                 if(cistrncmp(p, rfc822_string[i], strlen(rfc822_string[i])) == 0){
1025                                         count++;
1026                                         break;
1027                                 }
1028                         }
1029                 }
1030                 p = q+1;
1031         }
1032         if(count >= 3){
1033                 print(mime ? "message/rfc822\n" : "email file\n");
1034                 return 1;
1035         }
1036         return 0;
1037 }
1038
1039 int
1040 ismbox(void)
1041 {
1042         char *p, *q;
1043
1044         p = (char*)buf;
1045         q = strchr(p, '\n');
1046         if(q == nil)
1047                 return 0;
1048         *q = 0;
1049         if(strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ") == nil){
1050                 print(mime ? "text/plain\n" : "mail box\n");
1051                 return 1;
1052         }
1053         *q = '\n';
1054         return 0;
1055 }
1056
1057 int
1058 iscint(void)
1059 {
1060         int type;
1061         char *name;
1062         Biobuf b;
1063
1064         if(Binit(&b, fd, OREAD) == Beof)
1065                 return 0;
1066         seek(fd, 0, 0);
1067         type = objtype(&b, &name);
1068         if(type < 0)
1069                 return 0;
1070         if(mime)
1071                 print(OCTET);
1072         else
1073                 print("%s intermediate\n", name);
1074         return 1;
1075 }
1076
1077 int
1078 isc(void)
1079 {
1080         int n;
1081
1082         n = wfreq[I1];
1083         /*
1084          * includes
1085          */
1086         if(n >= 2 && wfreq[I2] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1087                 goto yes;
1088         if(n >= 1 && wfreq[Alword] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1089                 goto yes;
1090         /*
1091          * declarations
1092          */
1093         if(wfreq[Cword] >= 5 && cfreq[';'] >= 5)
1094                 goto yes;
1095         /*
1096          * assignments
1097          */
1098         if(cfreq[';'] >= 10 && cfreq['='] >= 10 && wfreq[Cword] >= 1)
1099                 goto yes;
1100         return 0;
1101
1102 yes:
1103         if(mime){
1104                 print(PLAIN);
1105                 return 1;
1106         }
1107         if(wfreq[Alword] > 0)
1108                 print("alef program\n");
1109         else
1110                 print("c program\n");
1111         return 1;
1112 }
1113
1114 int
1115 islimbo(void)
1116 {
1117
1118         /*
1119          * includes
1120          */
1121         if(wfreq[Lword] < 4)
1122                 return 0;
1123         print(mime ? PLAIN : "limbo program\n");
1124         return 1;
1125 }
1126
1127 int
1128 isas(void)
1129 {
1130
1131         /*
1132          * includes
1133          */
1134         if(wfreq[Aword] < 2)
1135                 return 0;
1136         print(mime ? PLAIN : "as program\n");
1137         return 1;
1138 }
1139
1140 int
1141 ismp3(void)
1142 {
1143         uchar *p, *e;
1144
1145         p = buf;
1146         e = p + nbuf-1;
1147         while((p < e) && (p = memchr(p, 0xFF, e - p))){
1148                 if((p[1] & 0xFE) == 0xFA){
1149                         print(mime ? "audio/mpeg\n" : "mp3 audio\n");
1150                         return 1;
1151                 }
1152                 p++;
1153         }
1154         return 0;
1155 }
1156
1157 /*
1158  * low entropy means encrypted
1159  */
1160 int
1161 ismung(void)
1162 {
1163         int i, bucket[8];
1164         float cs;
1165
1166         if(nbuf < 64)
1167                 return 0;
1168         memset(bucket, 0, sizeof(bucket));
1169         for(i=nbuf-64; i<nbuf; i++)
1170                 bucket[(buf[i]>>5)&07] += 1;
1171
1172         cs = 0.;
1173         for(i=0; i<8; i++)
1174                 cs += (bucket[i]-8)*(bucket[i]-8);
1175         cs /= 8.;
1176         if(cs <= 24.322) {
1177                 if(buf[0]==0x1f && buf[1]==0x9d)
1178                         print(mime ? "application/x-compress" : "compressed\n");
1179                 else
1180                 if(buf[0]==0x1f && buf[1]==0x8b)
1181                         print(mime ? "application/x-gzip" : "gzip compressed\n");
1182                 else
1183                 if(buf[0]=='B' && buf[1]=='Z' && buf[2]=='h')
1184                         print(mime ? "application/x-bzip2" : "bzip2 compressed\n");
1185                 else
1186                 if(buf[0]==0x78 && buf[1]==0x9c)
1187                         print(mime ? "application/x-deflate" : "zlib compressed\n");
1188                 else
1189                         print(mime ? OCTET : "encrypted\n");
1190                 return 1;
1191         }
1192         return 0;
1193 }
1194
1195 /*
1196  * english by punctuation and frequencies
1197  */
1198 int
1199 isenglish(void)
1200 {
1201         int vow, comm, rare, badpun, punct;
1202         char *p;
1203
1204         if(guess != Fascii && guess != Feascii)
1205                 return 0;
1206         badpun = 0;
1207         punct = 0;
1208         for(p = (char *)buf; p < (char *)buf+nbuf-1; p++)
1209                 switch(*p) {
1210                 case '.':
1211                 case ',':
1212                 case ')':
1213                 case '%':
1214                 case ';':
1215                 case ':':
1216                 case '?':
1217                         punct++;
1218                         if(p[1] != ' ' && p[1] != '\n')
1219                                 badpun++;
1220                 }
1221         if(badpun*5 > punct)
1222                 return 0;
1223         if(cfreq['>']+cfreq['<']+cfreq['/'] > cfreq['e'])       /* shell file test */
1224                 return 0;
1225         if(2*cfreq[';'] > cfreq['e'])
1226                 return 0;
1227
1228         vow = 0;
1229         for(p="AEIOU"; *p; p++) {
1230                 vow += cfreq[*p];
1231                 vow += cfreq[tolower(*p)];
1232         }
1233         comm = 0;
1234         for(p="ETAION"; *p; p++) {
1235                 comm += cfreq[*p];
1236                 comm += cfreq[tolower(*p)];
1237         }
1238         rare = 0;
1239         for(p="VJKQXZ"; *p; p++) {
1240                 rare += cfreq[*p];
1241                 rare += cfreq[tolower(*p)];
1242         }
1243         if(vow*5 >= nbuf-cfreq[' '] && comm >= 10*rare) {
1244                 print(mime ? PLAIN : "English text\n");
1245                 return 1;
1246         }
1247         return 0;
1248 }
1249
1250 /*
1251  * pick up a number with
1252  * syntax _*[0-9]+_
1253  */
1254 #define P9BITLEN        12
1255 int
1256 p9bitnum(uchar *bp)
1257 {
1258         int n, c, len;
1259
1260         len = P9BITLEN;
1261         while(*bp == ' ') {
1262                 bp++;
1263                 len--;
1264                 if(len <= 0)
1265                         return -1;
1266         }
1267         n = 0;
1268         while(len > 1) {
1269                 c = *bp++;
1270                 if(!isdigit(c))
1271                         return -1;
1272                 n = n*10 + c-'0';
1273                 len--;
1274         }
1275         if(*bp != ' ')
1276                 return -1;
1277         return n;
1278 }
1279
1280 int
1281 depthof(char *s, int *newp)
1282 {
1283         char *es;
1284         int d;
1285
1286         *newp = 0;
1287         es = s+12;
1288         while(s<es && *s==' ')
1289                 s++;
1290         if(s == es)
1291                 return -1;
1292         if('0'<=*s && *s<='9')
1293                 return 1<<strtol(s, 0, 0);
1294
1295         *newp = 1;
1296         d = 0;
1297         while(s<es && *s!=' '){
1298                 s++;                    /* skip letter */
1299                 d += strtoul(s, &s, 10);
1300         }
1301
1302         if(d % 8 == 0 || 8 % d == 0)
1303                 return d;
1304         else
1305                 return -1;
1306 }
1307
1308 int
1309 isp9bit(void)
1310 {
1311         int dep, lox, loy, hix, hiy, px, new, cmpr;
1312         ulong t;
1313         long len;
1314         char *newlabel;
1315         uchar *cp;
1316
1317         cp = buf;
1318         cmpr = 0;
1319         newlabel = "old ";
1320
1321         if(memcmp(cp, "compressed\n", 11) == 0) {
1322                 cmpr = 1;
1323                 cp = buf + 11;
1324         }
1325
1326         dep = depthof((char*)cp + 0*P9BITLEN, &new);
1327         if(new)
1328                 newlabel = "";
1329         lox = p9bitnum(cp + 1*P9BITLEN);
1330         loy = p9bitnum(cp + 2*P9BITLEN);
1331         hix = p9bitnum(cp + 3*P9BITLEN);
1332         hiy = p9bitnum(cp + 4*P9BITLEN);
1333         if(dep < 0 || lox < 0 || loy < 0 || hix < 0 || hiy < 0)
1334                 return 0;
1335
1336         if(dep < 8){
1337                 px = 8/dep;             /* pixels per byte */
1338                 /* set l to number of bytes of data per scan line */
1339                 if(lox >= 0)
1340                         len = (hix+px-1)/px - lox/px;
1341                 else{                   /* make positive before divide */
1342                         t = (-lox)+px-1;
1343                         t = (t/px)*px;
1344                         len = (t+hix+px-1)/px;
1345                 }
1346         }else
1347                 len = (hix-lox)*dep/8;
1348         len *= hiy - loy;               /* col length */
1349         len += 5 * P9BITLEN;            /* size of initial ascii */
1350
1351         /*
1352          * for compressed images, don't look any further. otherwise:
1353          * for image file, length is non-zero and must match calculation above.
1354          * for /dev/window and /dev/screen the length is always zero.
1355          * for subfont, the subfont header should follow immediately.
1356          */
1357         if (cmpr) {
1358                 print(mime ? "image/p9bit\n" : "Compressed %splan 9 image or subfont, depth %d\n",
1359                         newlabel, dep);
1360                 return 1;
1361         }
1362         /*
1363          * mbuf->length == 0 probably indicates reading a pipe.
1364          * Ghostscript sometimes produces a little extra on the end.
1365          */
1366         if (len != 0 && (mbuf->length == 0 || mbuf->length == len ||
1367             mbuf->length > len && mbuf->length < len+P9BITLEN)) {
1368                 print(mime ? "image/p9bit\n" : "%splan 9 image, depth %d\n", newlabel, dep);
1369                 return 1;
1370         }
1371         if (p9subfont(buf+len)) {
1372                 print(mime ? "image/p9bit\n" : "%ssubfont file, depth %d\n", newlabel, dep);
1373                 return 1;
1374         }
1375         return 0;
1376 }
1377
1378 int
1379 p9subfont(uchar *p)
1380 {
1381         int n, h, a;
1382
1383         /* if image too big, assume it's a subfont */
1384         if (p+3*P9BITLEN > buf+sizeof(buf))
1385                 return 1;
1386
1387         n = p9bitnum(p + 0*P9BITLEN);   /* char count */
1388         if (n < 0)
1389                 return 0;
1390         h = p9bitnum(p + 1*P9BITLEN);   /* height */
1391         if (h < 0)
1392                 return 0;
1393         a = p9bitnum(p + 2*P9BITLEN);   /* ascent */
1394         if (a < 0)
1395                 return 0;
1396         return 1;
1397 }
1398
1399 #define WHITESPACE(c)           ((c) == ' ' || (c) == '\t' || (c) == '\n')
1400
1401 int
1402 isp9font(void)
1403 {
1404         uchar *cp, *p;
1405         int i, n;
1406         char pathname[1024];
1407
1408         cp = buf;
1409         if (!getfontnum(cp, &cp))       /* height */
1410                 return 0;
1411         if (!getfontnum(cp, &cp))       /* ascent */
1412                 return 0;
1413         for (i = 0; cp=(uchar*)strchr((char*)cp, '\n'); i++) {
1414                 if (!getfontnum(cp, &cp))       /* min */
1415                         break;
1416                 if (!getfontnum(cp, &cp))       /* max */
1417                         return 0;
1418                 getfontnum(cp, &cp);    /* optional offset */
1419                 while (WHITESPACE(*cp))
1420                         cp++;
1421                 for (p = cp; *cp && !WHITESPACE(*cp); cp++)
1422                                 ;
1423                         /* construct a path name, if needed */
1424                 n = 0;
1425                 if (*p != '/' && slash) {
1426                         n = slash-fname+1;
1427                         if (n < sizeof(pathname))
1428                                 memcpy(pathname, fname, n);
1429                         else n = 0;
1430                 }
1431                 if (n+cp-p+4 < sizeof(pathname)) {
1432                         memcpy(pathname+n, p, cp-p);
1433                         n += cp-p;
1434                         pathname[n] = 0;
1435                         if (access(pathname, AEXIST) < 0) {
1436                                 strcpy(pathname+n, ".0");
1437                                 if (access(pathname, AEXIST) < 0)
1438                                         return 0;
1439                         }
1440                 }
1441         }
1442         if (i) {
1443                 print(mime ? "text/plain\n" : "font file\n");
1444                 return 1;
1445         }
1446         return 0;
1447 }
1448
1449 int
1450 getfontnum(uchar *cp, uchar **rp)
1451 {
1452         while (WHITESPACE(*cp))         /* extract ulong delimited by whitespace */
1453                 cp++;
1454         if (*cp < '0' || *cp > '9')
1455                 return 0;
1456         strtoul((char *)cp, (char **)rp, 0);
1457         if (!WHITESPACE(**rp)) {
1458                 *rp = cp;
1459                 return 0;
1460         }
1461         return 1;
1462 }
1463
1464 int
1465 isrtf(void)
1466 {
1467         if(strstr((char *)buf, "\\rtf1")){
1468                 print(mime ? "application/rtf\n" : "rich text format\n");
1469                 return 1;
1470         }
1471         return 0;
1472 }
1473
1474 int
1475 ismsdos(void)
1476 {
1477         if (buf[0] == 0x4d && buf[1] == 0x5a){
1478                 print(mime ? "application/x-msdownload\n" : "MSDOS executable\n");
1479                 return 1;
1480         }
1481         return 0;
1482 }
1483
1484 int
1485 isicocur(void)
1486 {
1487         if(buf[0] || buf[1] || buf[3] || buf[9])
1488                 return 0;
1489         if(buf[4] == 0x00 && buf[5] == 0x00)
1490                 return 0;
1491         switch(buf[2]){
1492         case 1:
1493                 print(mime ? "image/x-icon\n" : "Microsoft icon file\n");
1494                 return 1;
1495         case 2:
1496                 print(mime ? "image/x-icon\n" : "Microsoft cursor file\n");
1497                 return 1;
1498         }
1499         return 0;
1500 }
1501
1502 int
1503 iself(void)
1504 {
1505         static char *cpu[] = {          /* NB: incomplete and arbitary list */
1506         [1]     "WE32100",
1507         [2]     "SPARC",
1508         [3]     "i386",
1509         [4]     "M68000",
1510         [5]     "M88000",
1511         [6]     "i486",
1512         [7]     "i860",
1513         [8]     "R3000",
1514         [9]     "S370",
1515         [10]    "R4000",
1516         [15]    "HP-PA",
1517         [18]    "sparc v8+",
1518         [19]    "i960",
1519         [20]    "PPC-32",
1520         [21]    "PPC-64",
1521         [40]    "ARM",
1522         [41]    "Alpha",
1523         [43]    "sparc v9",
1524         [50]    "IA-64",
1525         [62]    "AMD64",
1526         [75]    "VAX",
1527         };
1528         static char *type[] = {
1529         [1]     "relocatable object",
1530         [2]     "executable",
1531         [3]     "shared library",
1532         [4]     "core dump",
1533         };
1534
1535         if (memcmp(buf, "\x7fELF", 4) == 0){
1536                 if (!mime){
1537                         int isdifend = 0;
1538                         int n = (buf[19] << 8) | buf[18];
1539                         char *p = "unknown";
1540                         char *t = "unknown";
1541
1542                         if (n > 0 && n < nelem(cpu) && cpu[n])
1543                                 p = cpu[n];
1544                         else {
1545                                 /* try the other byte order */
1546                                 isdifend = 1;
1547                                 n = (buf[18] << 8) | buf[19];
1548                                 if (n > 0 && n < nelem(cpu) && cpu[n])
1549                                         p = cpu[n];
1550                         }
1551                         if(isdifend)
1552                                 n = (buf[16]<< 8) | buf[17];
1553                         else
1554                                 n = (buf[17]<< 8) | buf[16];
1555
1556                         if(n>0 && n < nelem(type) && type[n])
1557                                 t = type[n];
1558                         print("%s ELF %s\n", p, t);
1559                 }
1560                 else
1561                         print("application/x-elf-executable");
1562                 return 1;
1563         }
1564
1565         return 0;
1566 }
1567
1568 int
1569 isface(void)
1570 {
1571         int i, j, ldepth, l;
1572         char *p;
1573
1574         ldepth = -1;
1575         for(j = 0; j < 3; j++){
1576                 for(p = (char*)buf, i=0; i<3; i++){
1577                         if(p[0] != '0' || p[1] != 'x')
1578                                 return 0;
1579                         if(buf[2+8] == ',')
1580                                 l = 2;
1581                         else if(buf[2+4] == ',')
1582                                 l = 1;
1583                         else
1584                                 return 0;
1585                         if(ldepth == -1)
1586                                 ldepth = l;
1587                         if(l != ldepth)
1588                                 return 0;
1589                         strtoul(p, &p, 16);
1590                         if(*p++ != ',')
1591                                 return 0;
1592                         while(*p == ' ' || *p == '\t')
1593                                 p++;
1594                 }
1595                 if (*p++ != '\n')
1596                         return 0;
1597         }
1598
1599         if(mime)
1600                 print("application/x-face\n");
1601         else
1602                 print("face image depth %d\n", ldepth);
1603         return 1;
1604 }
1605