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