]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/file.c
Add exponential function.
[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         0x2bb19a52,     0xFFFFFFFF,     "paq archive\n", OCTET,
619 };
620
621 int
622 filemagic(Filemagic *tab, int ntab, ulong x)
623 {
624         int i;
625
626         for(i=0; i<ntab; i++)
627                 if((x&tab[i].mask) == tab[i].x){
628                         print(mime ? tab[i].mime : tab[i].desc);
629                         return 1;
630                 }
631         return 0;
632 }
633
634 int
635 long0(void)
636 {
637         return filemagic(long0tab, nelem(long0tab), LENDIAN(buf));
638 }
639
640 typedef struct Fileoffmag Fileoffmag;
641 struct Fileoffmag {
642         ulong   off;
643         Filemagic;
644 };
645
646 /*
647  * integers in this table must be as seen on a little-endian machine
648  * when read from a file.
649  */
650 Fileoffmag longofftab[] = {
651         /*
652          * venti & fossil magic numbers are stored big-endian on disk,
653          * thus the numbers appear reversed in this table.
654          */
655         256*1024, 0xe7a5e4a9, 0xFFFFFFFF, "venti arenas partition\n", OCTET,
656         256*1024, 0xc75e5cd1, 0xFFFFFFFF, "venti index section\n", OCTET,
657         128*1024, 0x89ae7637, 0xFFFFFFFF, "fossil write buffer\n", OCTET,
658         4,        0x31647542, 0xFFFFFFFF, "OS X finder properties\n", OCTET,
659 };
660
661 int
662 fileoffmagic(Fileoffmag *tab, int ntab)
663 {
664         int i;
665         ulong x;
666         Fileoffmag *tp;
667         uchar buf[sizeof(long)];
668
669         for(i=0; i<ntab; i++) {
670                 tp = tab + i;
671                 seek(fd, tp->off, 0);
672                 if (readn(fd, buf, sizeof buf) != sizeof buf)
673                         continue;
674                 x = LENDIAN(buf);
675                 if((x&tp->mask) == tp->x){
676                         print(mime? tp->mime: tp->desc);
677                         return 1;
678                 }
679         }
680         return 0;
681 }
682
683 int
684 longoff(void)
685 {
686         return fileoffmagic(longofftab, nelem(longofftab));
687 }
688
689 int
690 isexec(void)
691 {
692         Fhdr f;
693
694         seek(fd, 0, 0);         /* reposition to start of file */
695         if(crackhdr(fd, &f)) {
696                 print(mime ? OCTET : "%s\n", f.name);
697                 return 1;
698         }
699         return 0;
700 }
701
702
703 /* from tar.c */
704 enum { NAMSIZ = 100, TBLOCK = 512 };
705
706 union   hblock
707 {
708         char    dummy[TBLOCK];
709         struct  header
710         {
711                 char    name[NAMSIZ];
712                 char    mode[8];
713                 char    uid[8];
714                 char    gid[8];
715                 char    size[12];
716                 char    mtime[12];
717                 char    chksum[8];
718                 char    linkflag;
719                 char    linkname[NAMSIZ];
720                 /* rest are defined by POSIX's ustar format; see p1003.2b */
721                 char    magic[6];       /* "ustar" */
722                 char    version[2];
723                 char    uname[32];
724                 char    gname[32];
725                 char    devmajor[8];
726                 char    devminor[8];
727                 char    prefix[155];  /* if non-null, path = prefix "/" name */
728         } dbuf;
729 };
730
731 int
732 checksum(union hblock *hp)
733 {
734         int i;
735         char *cp;
736         struct header *hdr = &hp->dbuf;
737
738         for (cp = hdr->chksum; cp < &hdr->chksum[sizeof hdr->chksum]; cp++)
739                 *cp = ' ';
740         i = 0;
741         for (cp = hp->dummy; cp < &hp->dummy[TBLOCK]; cp++)
742                 i += *cp & 0xff;
743         return i;
744 }
745
746 int
747 istar(void)
748 {
749         int chksum;
750         char tblock[TBLOCK];
751         union hblock *hp = (union hblock *)tblock;
752         struct header *hdr = &hp->dbuf;
753
754         seek(fd, 0, 0);         /* reposition to start of file */
755         if (readn(fd, tblock, sizeof tblock) != sizeof tblock)
756                 return 0;
757         chksum = strtol(hdr->chksum, 0, 8);
758         if (hdr->name[0] != '\0' && checksum(hp) == chksum) {
759                 if (strcmp(hdr->magic, "ustar") == 0)
760                         print(mime? "application/x-ustar\n": "posix tar archive\n");
761                 else
762                         print(mime? "application/x-tar\n": "tar archive\n");
763                 return 1;
764         }
765         return 0;
766 }
767
768 /*
769  * initial words to classify file
770  */
771 struct  FILE_STRING
772 {
773         char    *key;
774         char    *filetype;
775         int     length;
776         char    *mime;
777 } file_string[] =
778 {
779         "\x1f\x9d",             "compressed",                   2,      "application/x-compress",
780         "\x1f\x8b",             "gzip compressed",              2,      "application/x-gzip",
781         "BZh",                  "bzip2 compressed",             3,      "application/x-bzip2",
782         "!<arch>\n__.SYMDEF",   "archive random library",       16,     "application/octet-stream",
783         "!<arch>\n",            "archive",                      8,      "application/octet-stream",
784         "070707",               "cpio archive - ascii header",  6,      "application/octet-stream",
785         "#!/bin/rc",            "rc executable file",           9,      "text/plain",
786         "#!/bin/sh",            "sh executable file",           9,      "text/plain",
787         "%!",                   "postscript",                   2,      "application/postscript",
788         "\004%!",               "postscript",                   3,      "application/postscript",
789         "x T post",             "troff output for post",        8,      "application/troff",
790         "x T Latin1",           "troff output for Latin1",      10,     "application/troff",
791         "x T utf",              "troff output for UTF",         7,      "application/troff",
792         "x T 202",              "troff output for 202",         7,      "application/troff",
793         "x T aps",              "troff output for aps",         7,      "application/troff",
794         "x T ",                 "troff output",                 4,      "application/troff",
795         "GIF",                  "GIF image",                    3,      "image/gif",
796         "\0PC Research, Inc\0", "ghostscript fax file",         18,     "application/ghostscript",
797         "%PDF",                 "PDF",                          4,      "application/pdf",
798         "<!DOCTYPE",            "HTML file",                    9,      "text/html",
799         "<!doctype",            "HTML file",                    9,      "text/html",
800         "<!--",                 "HTML file",                    4,      "text/html",
801         "<html>",               "HTML file",                    6,      "text/html",
802         "<HTML>",               "HTML file",                    6,      "text/html",
803         "<?xml",                "HTML file",                    5,      "text/html",
804         "\111\111\052\000",     "tiff",                         4,      "image/tiff",
805         "\115\115\000\052",     "tiff",                         4,      "image/tiff",
806         "\377\330\377\340",     "jpeg",                         4,      "image/jpeg",
807         "\377\330\377\341",     "jpeg",                         4,      "image/jpeg",
808         "\377\330\377\333",     "jpeg",                         4,      "image/jpeg",
809         "\xff\xd8",             "jpeg",                         2,      "image/jpeg",
810         "BM",                   "bmp",                          2,      "image/bmp", 
811         "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1",     "microsoft office document",    8,      "application/doc",
812         "<MakerFile ",          "FrameMaker file",              11,     "application/framemaker",
813         "\033E\033",    "HP PCL printer data",          3,      OCTET,
814         "\033&",        "HP PCL printer data",          2,      OCTET,
815         "\033%-12345X", "HPJCL file",           9,      "application/hpjcl",
816         "\033Lua",              "Lua bytecode",         4,      OCTET,
817         "ID3",                  "mp3 audio with id3",   3,      "audio/mpeg",
818         "OggS",                 "ogg audio",            4,      "audio/ogg",
819         "\211PNG",              "PNG image",            4,      "image/png",
820         "P3\n",                 "ppm",                          3,      "image/ppm",
821         "P6\n",                 "ppm",                          3,      "image/ppm",
822         "/* XPM */\n",  "xbm",                          10,     "image/xbm",
823         ".HTML ",               "troff -ms input",      6,      "text/troff",
824         ".LP",                  "troff -ms input",      3,      "text/troff",
825         ".ND",                  "troff -ms input",      3,      "text/troff",
826         ".PP",                  "troff -ms input",      3,      "text/troff",
827         ".TL",                  "troff -ms input",      3,      "text/troff",
828         ".TR",                  "troff -ms input",      3,      "text/troff",
829         ".TH",                  "manual page",          3,      "text/troff",
830         ".\\\"",                "troff input",          3,      "text/troff",
831         ".de",                  "troff input",          3,      "text/troff",
832         ".if",                  "troff input",          3,      "text/troff",
833         ".nr",                  "troff input",          3,      "text/troff",
834         ".tr",                  "troff input",          3,      "text/troff",
835         "vac:",                 "venti score",          4,      "text/plain",
836         "-----BEGIN CERTIFICATE-----\n",
837                                 "pem certificate",      -1,     "text/plain",
838         "-----BEGIN TRUSTED CERTIFICATE-----\n",
839                                 "pem trusted certificate", -1,  "text/plain",
840         "-----BEGIN X509 CERTIFICATE-----\n",
841                                 "pem x.509 certificate", -1,    "text/plain",
842         "subject=/C=",          "pem certificate with header", -1, "text/plain",
843         "process snapshot ",    "process snapshot",     -1,     "application/snapfs",
844         "d8:announce",          "torrent file",         11,     "application/x-bittorrent",
845         "[playlist]",           "playlist",             10,     "application/x-scpls",
846         "#EXTM3U",              "playlist",             7,      "audio/x-mpegurl",
847         "BEGIN:VCARD\r\n",      "vCard",                13,     "text/directory;profile=vcard",
848         "BEGIN:VCARD\n",        "vCard",                12,     "text/directory;profile=vcard",
849         0,0,0,0
850 };
851
852 int
853 istring(void)
854 {
855         int i, l;
856         struct FILE_STRING *p;
857
858         for(p = file_string; p->key; p++) {
859                 l = p->length;
860                 if(l == -1)
861                         l = strlen(p->key);
862                 if(nbuf >= l && memcmp(buf, p->key, l) == 0) {
863                         if(mime)
864                                 print("%s\n", p->mime);
865                         else
866                                 print("%s\n", p->filetype);
867                         return 1;
868                 }
869         }
870         if(strncmp((char*)buf, "TYPE=", 5) == 0) {      /* td */
871                 for(i = 5; i < nbuf; i++)
872                         if(buf[i] == '\n')
873                                 break;
874                 if(mime)
875                         print(OCTET);
876                 else
877                         print("%.*s picture\n", utfnlen((char*)buf+5, i-5), (char*)buf+5);
878                 return 1;
879         }
880         return 0;
881 }
882
883 struct offstr
884 {
885         ulong   off;
886         struct FILE_STRING;
887 } offstrs[] = {
888         32*1024, "\001CD001\001",       "ISO9660 CD image",     7,      "application/x-iso9660-image",
889         0, 0, 0, 0, 0
890 };
891
892 int
893 isoffstr(void)
894 {
895         int n;
896         char buf[256];
897         struct offstr *p;
898
899         for(p = offstrs; p->key; p++) {
900                 seek(fd, p->off, 0);
901                 n = p->length;
902                 if (n > sizeof buf)
903                         n = sizeof buf;
904                 if (readn(fd, buf, n) != n)
905                         continue;
906                 if(memcmp(buf, p->key, n) == 0) {
907                         if(mime)
908                                 print("%s\n", p->mime);
909                         else
910                                 print("%s\n", p->filetype);
911                         return 1;
912                 }
913         }
914         return 0;
915 }
916
917 int
918 iff(void)
919 {
920         if (strncmp((char*)buf, "FORM", 4) == 0 &&
921             strncmp((char*)buf+8, "AIFF", 4) == 0) {
922                 print("%s\n", mime? "audio/x-aiff": "aiff audio");
923                 return 1;
924         }
925         if (strncmp((char*)buf, "RIFF", 4) == 0) {
926                 if (strncmp((char*)buf+8, "WAVE", 4) == 0)
927                         print("%s\n", mime? "audio/wave": "wave audio");
928                 else if (strncmp((char*)buf+8, "AVI ", 4) == 0)
929                         print("%s\n", mime? "video/avi": "avi video");
930                 else
931                         print("%s\n", mime? "application/octet-stream":
932                                 "riff file");
933                 return 1;
934         }
935         return 0;
936 }
937
938 char*   html_string[] = {
939         "blockquote",
940         "!DOCTYPE", "![CDATA[", "basefont", "frameset", "noframes", "textarea",
941         "caption",
942         "button", "center", "iframe", "object", "option", "script",
943         "select", "strong",
944         "blink", "embed", "frame", "input", "label", "param", "small",
945         "style", "table", "tbody", "tfoot", "thead", "title",
946         "?xml", "body", "code", "font", "form", "head", "html",
947         "link", "menu", "meta", "span",
948         "!--", "big", "dir", "div", "img", "pre", "sub", "sup",
949         "br", "dd", "dl", "dt", "em", "h1", "h2", "h3", "h4", "h5",
950         "h6", "hr", "li", "ol", "td", "th", "tr", "tt", "ul",
951         "a", "b", "i", "p", "q", "u",
952         0,
953 };
954
955 int
956 ishtml(void)
957 {
958         int i, n, count;
959         uchar *p;
960
961         count = 0;
962         p = buf;
963         for(;;) {
964                 while(p < buf+nbuf && *p != '<')
965                         p++;
966                 p++;
967                 if (p >= buf+nbuf)
968                         break;
969                 if(*p == '/')
970                         p++;
971                 if(p >= buf+nbuf)
972                         break;
973                 for(i = 0; html_string[i]; i++){
974                         n = strlen(html_string[i]);
975                         if(p + n > buf+nbuf)
976                                 continue;
977                         if(cistrncmp(html_string[i], (char*)p, n) == 0) {
978                                 p += n;
979                                 if(p < buf+nbuf && strchr("\t\r\n />", *p)){
980                                         if(++count > 2) {
981                                                 print(mime ? "text/html\n" : "HTML file\n");
982                                                 return 1;
983                                         }
984                                 }
985                                 break;
986                         }
987                 }
988         }
989         return 0;
990 }
991
992 char*   rfc822_string[] =
993 {
994         "from:",
995         "date:",
996         "to:",
997         "subject:",
998         "received:",
999         "reply to:",
1000         "sender:",
1001         0,
1002 };
1003
1004 int
1005 isrfc822(void)
1006 {
1007
1008         char *p, *q, *r;
1009         int i, count;
1010
1011         count = 0;
1012         p = (char*)buf;
1013         for(;;) {
1014                 q = strchr(p, '\n');
1015                 if(q == nil)
1016                         break;
1017                 *q = 0;
1018                 if(p == (char*)buf && strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ")){
1019                         count++;
1020                         *q = '\n';
1021                         p = q+1;
1022                         continue;
1023                 }
1024                 *q = '\n';
1025                 if(*p != '\t' && *p != ' '){
1026                         r = strchr(p, ':');
1027                         if(r == 0 || r > q)
1028                                 break;
1029                         for(i = 0; rfc822_string[i]; i++) {
1030                                 if(cistrncmp(p, rfc822_string[i], strlen(rfc822_string[i])) == 0){
1031                                         count++;
1032                                         break;
1033                                 }
1034                         }
1035                 }
1036                 p = q+1;
1037         }
1038         if(count >= 3){
1039                 print(mime ? "message/rfc822\n" : "email file\n");
1040                 return 1;
1041         }
1042         return 0;
1043 }
1044
1045 int
1046 ismbox(void)
1047 {
1048         char *p, *q;
1049
1050         p = (char*)buf;
1051         q = strchr(p, '\n');
1052         if(q == nil)
1053                 return 0;
1054         *q = 0;
1055         if(strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ") == nil){
1056                 print(mime ? "text/plain\n" : "mail box\n");
1057                 return 1;
1058         }
1059         *q = '\n';
1060         return 0;
1061 }
1062
1063 int
1064 iscint(void)
1065 {
1066         int type;
1067         char *name;
1068         Biobuf b;
1069
1070         if(Binit(&b, fd, OREAD) == Beof)
1071                 return 0;
1072         seek(fd, 0, 0);
1073         type = objtype(&b, &name);
1074         if(type < 0)
1075                 return 0;
1076         if(mime)
1077                 print(OCTET);
1078         else
1079                 print("%s intermediate\n", name);
1080         return 1;
1081 }
1082
1083 int
1084 isc(void)
1085 {
1086         int n;
1087
1088         n = wfreq[I1];
1089         /*
1090          * includes
1091          */
1092         if(n >= 2 && wfreq[I2] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1093                 goto yes;
1094         if(n >= 1 && wfreq[Alword] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1095                 goto yes;
1096         /*
1097          * declarations
1098          */
1099         if(wfreq[Cword] >= 5 && cfreq[';'] >= 5)
1100                 goto yes;
1101         /*
1102          * assignments
1103          */
1104         if(cfreq[';'] >= 10 && cfreq['='] >= 10 && wfreq[Cword] >= 1)
1105                 goto yes;
1106         return 0;
1107
1108 yes:
1109         if(mime){
1110                 print(PLAIN);
1111                 return 1;
1112         }
1113         if(wfreq[Alword] > 0)
1114                 print("alef program\n");
1115         else
1116                 print("c program\n");
1117         return 1;
1118 }
1119
1120 int
1121 islimbo(void)
1122 {
1123
1124         /*
1125          * includes
1126          */
1127         if(wfreq[Lword] < 4)
1128                 return 0;
1129         print(mime ? PLAIN : "limbo program\n");
1130         return 1;
1131 }
1132
1133 int
1134 isas(void)
1135 {
1136
1137         /*
1138          * includes
1139          */
1140         if(wfreq[Aword] < 2)
1141                 return 0;
1142         print(mime ? PLAIN : "as program\n");
1143         return 1;
1144 }
1145
1146 int
1147 ismp3(void)
1148 {
1149         uchar *p, *e;
1150
1151         p = buf;
1152         e = p + nbuf-1;
1153         while((p < e) && (p = memchr(p, 0xFF, e - p))){
1154                 if((p[1] & 0xFE) == 0xFA){
1155                         print(mime ? "audio/mpeg\n" : "mp3 audio\n");
1156                         return 1;
1157                 }
1158                 p++;
1159         }
1160         return 0;
1161 }
1162
1163 /*
1164  * low entropy means encrypted
1165  */
1166 int
1167 ismung(void)
1168 {
1169         int i, bucket[8];
1170         float cs;
1171
1172         if(nbuf < 64)
1173                 return 0;
1174         memset(bucket, 0, sizeof(bucket));
1175         for(i=nbuf-64; i<nbuf; i++)
1176                 bucket[(buf[i]>>5)&07] += 1;
1177
1178         cs = 0.;
1179         for(i=0; i<8; i++)
1180                 cs += (bucket[i]-8)*(bucket[i]-8);
1181         cs /= 8.;
1182         if(cs <= 24.322) {
1183                 if(buf[0]==0x1f && buf[1]==0x9d)
1184                         print(mime ? "application/x-compress" : "compressed\n");
1185                 else
1186                 if(buf[0]==0x1f && buf[1]==0x8b)
1187                         print(mime ? "application/x-gzip" : "gzip compressed\n");
1188                 else
1189                 if(buf[0]=='B' && buf[1]=='Z' && buf[2]=='h')
1190                         print(mime ? "application/x-bzip2" : "bzip2 compressed\n");
1191                 else
1192                 if(buf[0]==0x78 && buf[1]==0x9c)
1193                         print(mime ? "application/x-deflate" : "zlib compressed\n");
1194                 else
1195                         print(mime ? OCTET : "encrypted\n");
1196                 return 1;
1197         }
1198         return 0;
1199 }
1200
1201 /*
1202  * english by punctuation and frequencies
1203  */
1204 int
1205 isenglish(void)
1206 {
1207         int vow, comm, rare, badpun, punct;
1208         char *p;
1209
1210         if(guess != Fascii && guess != Feascii)
1211                 return 0;
1212         badpun = 0;
1213         punct = 0;
1214         for(p = (char *)buf; p < (char *)buf+nbuf-1; p++)
1215                 switch(*p) {
1216                 case '.':
1217                 case ',':
1218                 case ')':
1219                 case '%':
1220                 case ';':
1221                 case ':':
1222                 case '?':
1223                         punct++;
1224                         if(p[1] != ' ' && p[1] != '\n')
1225                                 badpun++;
1226                 }
1227         if(badpun*5 > punct)
1228                 return 0;
1229         if(cfreq['>']+cfreq['<']+cfreq['/'] > cfreq['e'])       /* shell file test */
1230                 return 0;
1231         if(2*cfreq[';'] > cfreq['e'])
1232                 return 0;
1233
1234         vow = 0;
1235         for(p="AEIOU"; *p; p++) {
1236                 vow += cfreq[*p];
1237                 vow += cfreq[tolower(*p)];
1238         }
1239         comm = 0;
1240         for(p="ETAION"; *p; p++) {
1241                 comm += cfreq[*p];
1242                 comm += cfreq[tolower(*p)];
1243         }
1244         rare = 0;
1245         for(p="VJKQXZ"; *p; p++) {
1246                 rare += cfreq[*p];
1247                 rare += cfreq[tolower(*p)];
1248         }
1249         if(vow*5 >= nbuf-cfreq[' '] && comm >= 10*rare) {
1250                 print(mime ? PLAIN : "English text\n");
1251                 return 1;
1252         }
1253         return 0;
1254 }
1255
1256 /*
1257  * pick up a number with
1258  * syntax _*[0-9]+_
1259  */
1260 #define P9BITLEN        12
1261 int
1262 p9bitnum(uchar *bp)
1263 {
1264         int n, c, len;
1265
1266         len = P9BITLEN;
1267         while(*bp == ' ') {
1268                 bp++;
1269                 len--;
1270                 if(len <= 0)
1271                         return -1;
1272         }
1273         n = 0;
1274         while(len > 1) {
1275                 c = *bp++;
1276                 if(!isdigit(c))
1277                         return -1;
1278                 n = n*10 + c-'0';
1279                 len--;
1280         }
1281         if(*bp != ' ')
1282                 return -1;
1283         return n;
1284 }
1285
1286 int
1287 depthof(char *s, int *newp)
1288 {
1289         char *es;
1290         int d;
1291
1292         *newp = 0;
1293         es = s+12;
1294         while(s<es && *s==' ')
1295                 s++;
1296         if(s == es)
1297                 return -1;
1298         if('0'<=*s && *s<='9')
1299                 return 1<<strtol(s, 0, 0);
1300
1301         *newp = 1;
1302         d = 0;
1303         while(s<es && *s!=' '){
1304                 s++;                    /* skip letter */
1305                 d += strtoul(s, &s, 10);
1306         }
1307
1308         if(d % 8 == 0 || 8 % d == 0)
1309                 return d;
1310         else
1311                 return -1;
1312 }
1313
1314 int
1315 isp9bit(void)
1316 {
1317         int dep, lox, loy, hix, hiy, px, new, cmpr;
1318         ulong t;
1319         long len;
1320         char *newlabel;
1321         uchar *cp;
1322
1323         cp = buf;
1324         cmpr = 0;
1325         newlabel = "old ";
1326
1327         if(memcmp(cp, "compressed\n", 11) == 0) {
1328                 cmpr = 1;
1329                 cp = buf + 11;
1330         }
1331
1332         dep = depthof((char*)cp + 0*P9BITLEN, &new);
1333         if(new)
1334                 newlabel = "";
1335         lox = p9bitnum(cp + 1*P9BITLEN);
1336         loy = p9bitnum(cp + 2*P9BITLEN);
1337         hix = p9bitnum(cp + 3*P9BITLEN);
1338         hiy = p9bitnum(cp + 4*P9BITLEN);
1339         if(dep < 0 || lox < 0 || loy < 0 || hix < 0 || hiy < 0)
1340                 return 0;
1341
1342         if(dep < 8){
1343                 px = 8/dep;             /* pixels per byte */
1344                 /* set l to number of bytes of data per scan line */
1345                 if(lox >= 0)
1346                         len = (hix+px-1)/px - lox/px;
1347                 else{                   /* make positive before divide */
1348                         t = (-lox)+px-1;
1349                         t = (t/px)*px;
1350                         len = (t+hix+px-1)/px;
1351                 }
1352         }else
1353                 len = (hix-lox)*dep/8;
1354         len *= hiy - loy;               /* col length */
1355         len += 5 * P9BITLEN;            /* size of initial ascii */
1356
1357         /*
1358          * for compressed images, don't look any further. otherwise:
1359          * for image file, length is non-zero and must match calculation above.
1360          * for /dev/window and /dev/screen the length is always zero.
1361          * for subfont, the subfont header should follow immediately.
1362          */
1363         if (cmpr) {
1364                 print(mime ? "image/p9bit\n" : "Compressed %splan 9 image or subfont, depth %d\n",
1365                         newlabel, dep);
1366                 return 1;
1367         }
1368         /*
1369          * mbuf->length == 0 probably indicates reading a pipe.
1370          * Ghostscript sometimes produces a little extra on the end.
1371          */
1372         if (len != 0 && (mbuf->length == 0 || mbuf->length == len ||
1373             mbuf->length > len && mbuf->length < len+P9BITLEN)) {
1374                 print(mime ? "image/p9bit\n" : "%splan 9 image, depth %d\n", newlabel, dep);
1375                 return 1;
1376         }
1377         if (p9subfont(buf+len)) {
1378                 print(mime ? "image/p9bit\n" : "%ssubfont file, depth %d\n", newlabel, dep);
1379                 return 1;
1380         }
1381         return 0;
1382 }
1383
1384 int
1385 p9subfont(uchar *p)
1386 {
1387         int n, h, a;
1388
1389         /* if image too big, assume it's a subfont */
1390         if (p+3*P9BITLEN > buf+sizeof(buf))
1391                 return 1;
1392
1393         n = p9bitnum(p + 0*P9BITLEN);   /* char count */
1394         if (n < 0)
1395                 return 0;
1396         h = p9bitnum(p + 1*P9BITLEN);   /* height */
1397         if (h < 0)
1398                 return 0;
1399         a = p9bitnum(p + 2*P9BITLEN);   /* ascent */
1400         if (a < 0)
1401                 return 0;
1402         return 1;
1403 }
1404
1405 #define WHITESPACE(c)           ((c) == ' ' || (c) == '\t' || (c) == '\n')
1406
1407 int
1408 isp9font(void)
1409 {
1410         uchar *cp, *p;
1411         int i, n;
1412         char pathname[1024];
1413
1414         cp = buf;
1415         if (!getfontnum(cp, &cp))       /* height */
1416                 return 0;
1417         if (!getfontnum(cp, &cp))       /* ascent */
1418                 return 0;
1419         for (i = 0; cp=(uchar*)strchr((char*)cp, '\n'); i++) {
1420                 if (!getfontnum(cp, &cp))       /* min */
1421                         break;
1422                 if (!getfontnum(cp, &cp))       /* max */
1423                         return 0;
1424                 getfontnum(cp, &cp);    /* optional offset */
1425                 while (WHITESPACE(*cp))
1426                         cp++;
1427                 for (p = cp; *cp && !WHITESPACE(*cp); cp++)
1428                                 ;
1429                         /* construct a path name, if needed */
1430                 n = 0;
1431                 if (*p != '/' && slash) {
1432                         n = slash-fname+1;
1433                         if (n < sizeof(pathname))
1434                                 memcpy(pathname, fname, n);
1435                         else n = 0;
1436                 }
1437                 if (n+cp-p+4 < sizeof(pathname)) {
1438                         memcpy(pathname+n, p, cp-p);
1439                         n += cp-p;
1440                         pathname[n] = 0;
1441                         if (access(pathname, AEXIST) < 0) {
1442                                 strcpy(pathname+n, ".0");
1443                                 if (access(pathname, AEXIST) < 0)
1444                                         return 0;
1445                         }
1446                 }
1447         }
1448         if (i) {
1449                 print(mime ? "text/plain\n" : "font file\n");
1450                 return 1;
1451         }
1452         return 0;
1453 }
1454
1455 int
1456 getfontnum(uchar *cp, uchar **rp)
1457 {
1458         while (WHITESPACE(*cp))         /* extract ulong delimited by whitespace */
1459                 cp++;
1460         if (*cp < '0' || *cp > '9')
1461                 return 0;
1462         strtoul((char *)cp, (char **)rp, 0);
1463         if (!WHITESPACE(**rp)) {
1464                 *rp = cp;
1465                 return 0;
1466         }
1467         return 1;
1468 }
1469
1470 int
1471 isrtf(void)
1472 {
1473         if(strstr((char *)buf, "\\rtf1")){
1474                 print(mime ? "application/rtf\n" : "rich text format\n");
1475                 return 1;
1476         }
1477         return 0;
1478 }
1479
1480 int
1481 ismsdos(void)
1482 {
1483         if (buf[0] == 0x4d && buf[1] == 0x5a){
1484                 print(mime ? "application/x-msdownload\n" : "MSDOS executable\n");
1485                 return 1;
1486         }
1487         return 0;
1488 }
1489
1490 int
1491 isicocur(void)
1492 {
1493         if(buf[0] || buf[1] || buf[3] || buf[9])
1494                 return 0;
1495         if(buf[4] == 0x00 && buf[5] == 0x00)
1496                 return 0;
1497         switch(buf[2]){
1498         case 1:
1499                 print(mime ? "image/x-icon\n" : "Microsoft icon file\n");
1500                 return 1;
1501         case 2:
1502                 print(mime ? "image/x-icon\n" : "Microsoft cursor file\n");
1503                 return 1;
1504         }
1505         return 0;
1506 }
1507
1508 int
1509 iself(void)
1510 {
1511         static char *cpu[] = {          /* NB: incomplete and arbitary list */
1512         [1]     "WE32100",
1513         [2]     "SPARC",
1514         [3]     "i386",
1515         [4]     "M68000",
1516         [5]     "M88000",
1517         [6]     "i486",
1518         [7]     "i860",
1519         [8]     "R3000",
1520         [9]     "S370",
1521         [10]    "R4000",
1522         [15]    "HP-PA",
1523         [18]    "sparc v8+",
1524         [19]    "i960",
1525         [20]    "PPC-32",
1526         [21]    "PPC-64",
1527         [40]    "ARM",
1528         [41]    "Alpha",
1529         [43]    "sparc v9",
1530         [50]    "IA-64",
1531         [62]    "AMD64",
1532         [75]    "VAX",
1533         };
1534         static char *type[] = {
1535         [1]     "relocatable object",
1536         [2]     "executable",
1537         [3]     "shared library",
1538         [4]     "core dump",
1539         };
1540
1541         if (memcmp(buf, "\x7fELF", 4) == 0){
1542                 if (!mime){
1543                         int isdifend = 0;
1544                         int n = (buf[19] << 8) | buf[18];
1545                         char *p = "unknown";
1546                         char *t = "unknown";
1547
1548                         if (n > 0 && n < nelem(cpu) && cpu[n])
1549                                 p = cpu[n];
1550                         else {
1551                                 /* try the other byte order */
1552                                 isdifend = 1;
1553                                 n = (buf[18] << 8) | buf[19];
1554                                 if (n > 0 && n < nelem(cpu) && cpu[n])
1555                                         p = cpu[n];
1556                         }
1557                         if(isdifend)
1558                                 n = (buf[16]<< 8) | buf[17];
1559                         else
1560                                 n = (buf[17]<< 8) | buf[16];
1561
1562                         if(n>0 && n < nelem(type) && type[n])
1563                                 t = type[n];
1564                         print("%s ELF %s\n", p, t);
1565                 }
1566                 else
1567                         print("application/x-elf-executable");
1568                 return 1;
1569         }
1570
1571         return 0;
1572 }
1573
1574 int
1575 isface(void)
1576 {
1577         int i, j, ldepth, l;
1578         char *p;
1579
1580         ldepth = -1;
1581         for(j = 0; j < 3; j++){
1582                 for(p = (char*)buf, i=0; i<3; i++){
1583                         if(p[0] != '0' || p[1] != 'x')
1584                                 return 0;
1585                         if(buf[2+8] == ',')
1586                                 l = 2;
1587                         else if(buf[2+4] == ',')
1588                                 l = 1;
1589                         else
1590                                 return 0;
1591                         if(ldepth == -1)
1592                                 ldepth = l;
1593                         if(l != ldepth)
1594                                 return 0;
1595                         strtoul(p, &p, 16);
1596                         if(*p++ != ',')
1597                                 return 0;
1598                         while(*p == ' ' || *p == '\t')
1599                                 p++;
1600                 }
1601                 if (*p++ != '\n')
1602                         return 0;
1603         }
1604
1605         if(mime)
1606                 print("application/x-face\n");
1607         else
1608                 print("face image depth %d\n", ldepth);
1609         return 1;
1610 }
1611