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