8 * file - determine type of file
10 #define LENDIAN(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
84 /* codes for 'mode' field in language structure */
87 First, /* first entry for language spanning several ranges */
88 Multi, /* later entries " " " ... */
89 Shared, /* codes used in several languages */
94 int mode; /* see enum above */
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 */
132 Fascii, /* printable ascii */
134 Futf, /* UTF character set */
135 Fbinary, /* binary */
136 Feascii, /* ASCII with control chars */
137 Fnull, /* NULL in file */
140 void bump_utf_count(Rune);
141 int cistrncmp(char*, char*, int);
143 int getfontnum(uchar*, uchar**);
167 int p9bitnum(uchar*);
168 int p9subfont(uchar*);
169 void print_utf(void);
170 void type(char*, int);
174 int (*call[])(void) =
176 long0, /* recognizable by first 4 bytes */
177 istring, /* recognizable by first string */
178 iself, /* ELF (foreign) executable */
179 isexec, /* native executables */
180 iff, /* interchange file format (strings) */
181 longoff, /* recognizable by 4 bytes at some offset */
182 isoffstr, /* recognizable by string at some offset */
183 isrfc822, /* email file */
184 ismbox, /* mail box */
185 istar, /* recognizable by tar checksum */
186 ishtml, /* html keywords */
187 iscint, /* compiler/assembler intermediate */
188 islimbo, /* limbo source */
189 isc, /* c & alef compiler key words */
190 isas, /* assembler key words */
191 isp9font, /* plan 9 font */
192 isp9bit, /* plan 9 image (as from /dev/window) */
193 isrtf, /* rich text format */
194 ismsdos, /* msdos exe (virus file attachement) */
195 isface, /* ascii face file */
198 ismung, /* entropy compressed/encrypted */
199 isenglish, /* char frequency English */
205 char OCTET[] = "application/octet-stream\n";
206 char PLAIN[] = "text/plain\n";
209 main(int argc, char *argv[])
220 fprint(2, "usage: file [-m] [file...]\n");
225 if(mime == 0 || argc > 1){
226 for(i = 0; i < argc; i++) {
227 for (j = 0, cp = argv[i]; *cp; j++, cp += chartorune(&r, cp))
239 for(i = 0; i < argc; i++)
240 type(argv[i], maxlen);
246 type(char *file, int nlen)
254 for (i = 0, p = file; *p; i++) {
255 if (*p == '/') /* find rightmost slash */
257 p += chartorune(&r, p); /* count runes */
259 print("%s:%*s",file, nlen-i+1, "");
262 if ((fd = open(file, OREAD)) < 0) {
263 print("cannot open: %r\n");
271 * Unicode 4.0 4-byte runes.
280 fullrune1(char *p, int n)
288 if(n >= 2 && c < 0xE0)
290 if(n >= 3 && c < 0xF0)
299 chartorune1(Rune1 *rune, char *str)
301 int c, c1, c2, c3, n;
307 n = chartorune(&r, str);
312 c1 = *(uchar*)(str+1) & ~0x80;
313 c2 = *(uchar*)(str+2) & ~0x80;
314 c3 = *(uchar*)(str+3) & ~0x80;
315 n = (c<<18) | (c1<<12) | (c2<<6) | c3;
316 if(n < 0x10000 || n > 0x10FFFF){
334 print("cannot stat: %r\n");
337 if(mbuf->mode & DMDIR) {
338 print(mime ? OCTET : "directory\n");
341 if(mbuf->type != 'M' && mbuf->type != '|') {
342 print(mime ? OCTET : "special file #%C/%s\n",
343 mbuf->type, mbuf->name);
346 /* may be reading a pipe on standard input */
347 nbuf = readn(fd, buf, sizeof(buf)-1);
349 print("cannot read: %r\n");
353 print(mime ? PLAIN : "empty file\n");
359 * build histogram table
361 memset(cfreq, 0, sizeof(cfreq));
362 for (i = 0; language[i].name; i++)
363 language[i].count = 0;
364 eob = (char *)buf+nbuf;
365 for(n = 0, p = (char *)buf; p < eob; n++) {
366 if (!fullrune1(p, eob-p) && eob-p < UTFmax1)
368 p += chartorune1(&r, p);
371 else if (r <= 0x7f) {
372 if (!isprint(r) && !isspace(r))
373 f = Ceascii; /* ASCII control char */
375 } else if (r == 0x80) {
379 f = Cbinary; /* Invalid Runes */
381 f = Clatin; /* Latin 1 */
384 f = Cutf; /* UTF extension */
386 cfreq[f]++; /* ASCII chars peg directly */
393 else if (cfreq[Cutf])
395 else if (cfreq[Clatin])
397 else if (cfreq[Ceascii])
399 else if (cfreq[Cnull])
404 * lookup dictionary words
406 memset(wfreq, 0, sizeof(wfreq));
407 if(guess == Fascii || guess == Flatin || guess == Futf)
410 * call individual classify routines
412 for(i=0; call[i]; i++)
418 * print out gross classification
420 if (nbuf < 100 && !mime)
421 print(mime ? PLAIN : "short ");
423 print(mime ? PLAIN : "Ascii\n");
424 else if (guess == Feascii)
425 print(mime ? PLAIN : "extended ascii\n");
426 else if (guess == Flatin)
427 print(mime ? PLAIN : "latin ascii\n");
428 else if (guess == Futf && utf_count() < 4)
430 else print(mime ? OCTET : "binary\n");
434 bump_utf_count(Rune r)
438 high = sizeof(language)/sizeof(language[0])-1;
439 for (low = 0; low < high;) {
441 if (r >= language[mid].low) {
442 if (r <= language[mid].high) {
443 language[mid].count++;
456 for (i = 0; language[i].name; i++)
457 if (language[i].count > 0)
458 switch (language[i].mode) {
474 for (i = 'a'; i < 'z'; i++)
477 for (i = 'A'; i < 'Z'; i++)
484 find_first(char *name)
488 for (i = 0; language[i].name != 0; i++)
489 if (language[i].mode == First
490 && strcmp(language[i].name, name) == 0)
509 for (i = 0; language[i].name; i++)
510 if (language[i].count) {
511 switch(language[i].mode) {
513 j = find_first(language[i].name);
516 if (language[j].count > 0)
524 print("%s", language[i].name);
539 int low, high, mid, r;
544 while (p < buf+nbuf && !isalpha(*p))
549 while(p < buf+nbuf && isalpha(*p))
553 high = sizeof(dict)/sizeof(dict[0]);
554 for(low = 0;low < high;) {
556 r = strcmp(dict[mid].word, (char*)p2);
558 wfreq[dict[mid].class]++;
570 typedef struct Filemagic Filemagic;
579 * integers in this table must be as seen on a little-endian machine
580 * when read from a file.
582 Filemagic long0tab[] = {
583 0xF16DF16D, 0xFFFFFFFF, "pac1 audio file\n", OCTET,
585 0x31636170, 0xFFFFFFFF, "pac3 audio file\n", OCTET,
587 0x32630070, 0xFFFF00FF, "pac4 audio file\n", OCTET,
588 0xBA010000, 0xFFFFFFFF, "mpeg system stream\n", OCTET,
589 0x43614c66, 0xFFFFFFFF, "FLAC audio file\n", OCTET,
590 0x30800CC0, 0xFFFFFFFF, "inferno .dis executable\n", OCTET,
591 0x04034B50, 0xFFFFFFFF, "zip archive\n", "application/zip",
592 070707, 0xFFFF, "cpio archive\n", "application/x-cpio",
593 0x2F7, 0xFFFF, "tex dvi\n", "application/dvi",
594 0xfaff, 0xfeff, "mp3 audio\n", "audio/mpeg",
595 0xfeff0000, 0xffffffff, "utf-32be\n", "text/plain charset=utf-32be",
596 0xfffe, 0xffffffff, "utf-32le\n", "text/plain charset=utf-32le",
597 0xfeff, 0xffff, "utf-16be\n", "text/plain charset=utf-16be",
598 0xfffe, 0xffff, "utf-16le\n", "text/plain charset=utf-16le",
599 /* 0xfeedface: this could alternately be a Next Plan 9 boot image */
600 0xcefaedfe, 0xFFFFFFFF, "32-bit power Mach-O executable\n", OCTET,
602 0xcffaedfe, 0xFFFFFFFF, "64-bit power Mach-O executable\n", OCTET,
604 0xfeedface, 0xFFFFFFFF, "386 Mach-O executable\n", OCTET,
606 0xfeedfacf, 0xFFFFFFFF, "amd64 Mach-O executable\n", OCTET,
608 0xbebafeca, 0xFFFFFFFF, "Mach-O universal executable\n", OCTET,
610 * venti & fossil magic numbers are stored big-endian on disk,
611 * thus the numbers appear reversed in this table.
613 0xad4e5cd1, 0xFFFFFFFF, "venti arena\n", OCTET,
617 filemagic(Filemagic *tab, int ntab, ulong x)
621 for(i=0; i<ntab; i++)
622 if((x&tab[i].mask) == tab[i].x){
623 print(mime ? tab[i].mime : tab[i].desc);
632 return filemagic(long0tab, nelem(long0tab), LENDIAN(buf));
635 typedef struct Fileoffmag Fileoffmag;
642 * integers in this table must be as seen on a little-endian machine
643 * when read from a file.
645 Fileoffmag longofftab[] = {
647 * venti & fossil magic numbers are stored big-endian on disk,
648 * thus the numbers appear reversed in this table.
650 256*1024, 0xe7a5e4a9, 0xFFFFFFFF, "venti arenas partition\n", OCTET,
651 256*1024, 0xc75e5cd1, 0xFFFFFFFF, "venti index section\n", OCTET,
652 128*1024, 0x89ae7637, 0xFFFFFFFF, "fossil write buffer\n", OCTET,
653 4, 0x31647542, 0xFFFFFFFF, "OS X finder properties\n", OCTET,
657 fileoffmagic(Fileoffmag *tab, int ntab)
662 uchar buf[sizeof(long)];
664 for(i=0; i<ntab; i++) {
666 seek(fd, tp->off, 0);
667 if (readn(fd, buf, sizeof buf) != sizeof buf)
670 if((x&tp->mask) == tp->x){
671 print(mime? tp->mime: tp->desc);
681 return fileoffmagic(longofftab, nelem(longofftab));
689 seek(fd, 0, 0); /* reposition to start of file */
690 if(crackhdr(fd, &f)) {
691 print(mime ? OCTET : "%s\n", f.name);
699 enum { NAMSIZ = 100, TBLOCK = 512 };
714 char linkname[NAMSIZ];
715 /* rest are defined by POSIX's ustar format; see p1003.2b */
716 char magic[6]; /* "ustar" */
722 char prefix[155]; /* if non-null, path = prefix "/" name */
727 checksum(union hblock *hp)
731 struct header *hdr = &hp->dbuf;
733 for (cp = hdr->chksum; cp < &hdr->chksum[sizeof hdr->chksum]; cp++)
736 for (cp = hp->dummy; cp < &hp->dummy[TBLOCK]; cp++)
746 union hblock *hp = (union hblock *)tblock;
747 struct header *hdr = &hp->dbuf;
749 seek(fd, 0, 0); /* reposition to start of file */
750 if (readn(fd, tblock, sizeof tblock) != sizeof tblock)
752 chksum = strtol(hdr->chksum, 0, 8);
753 if (hdr->name[0] != '\0' && checksum(hp) == chksum) {
754 if (strcmp(hdr->magic, "ustar") == 0)
755 print(mime? "application/x-ustar\n": "posix tar archive\n");
757 print(mime? "application/x-tar\n": "tar archive\n");
764 * initial words to classify file
774 "\x1f\x9d", "compressed", 2, "application/x-compress",
775 "\x1f\x8b", "gzip compressed", 2, "application/x-gzip",
776 "BZh", "bzip2 compressed", 3, "application/x-bzip2",
777 "!<arch>\n__.SYMDEF", "archive random library", 16, "application/octet-stream",
778 "!<arch>\n", "archive", 8, "application/octet-stream",
779 "070707", "cpio archive - ascii header", 6, "application/octet-stream",
780 "#!/bin/rc", "rc executable file", 9, "text/plain",
781 "#!/bin/sh", "sh executable file", 9, "text/plain",
782 "%!", "postscript", 2, "application/postscript",
783 "\004%!", "postscript", 3, "application/postscript",
784 "x T post", "troff output for post", 8, "application/troff",
785 "x T Latin1", "troff output for Latin1", 10, "application/troff",
786 "x T utf", "troff output for UTF", 7, "application/troff",
787 "x T 202", "troff output for 202", 7, "application/troff",
788 "x T aps", "troff output for aps", 7, "application/troff",
789 "GIF", "GIF image", 3, "image/gif",
790 "\0PC Research, Inc\0", "ghostscript fax file", 18, "application/ghostscript",
791 "%PDF", "PDF", 4, "application/pdf",
792 "<!DOCTYPE", "HTML file", 9, "text/html",
793 "<!doctype", "HTML file", 9, "text/html",
794 "<!--", "HTML file", 4, "text/html",
795 "<html>", "HTML file", 6, "text/html",
796 "<HTML>", "HTML file", 6, "text/html",
797 "<?xml", "HTML file", 5, "text/html",
798 "\111\111\052\000", "tiff", 4, "image/tiff",
799 "\115\115\000\052", "tiff", 4, "image/tiff",
800 "\377\330\377\340", "jpeg", 4, "image/jpeg",
801 "\377\330\377\341", "jpeg", 4, "image/jpeg",
802 "\377\330\377\333", "jpeg", 4, "image/jpeg",
803 "BM", "bmp", 2, "image/bmp",
804 "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", "microsoft office document", 8, "application/doc",
805 "<MakerFile ", "FrameMaker file", 11, "application/framemaker",
806 "\033E\033", "HP PCL printer data", 3, OCTET,
807 "\033%-12345X", "HPJCL file", 9, "application/hpjcl",
808 "ID3", "mp3 audio with id3", 3, "audio/mpeg",
809 "\211PNG", "PNG image", 4, "image/png",
810 "P3\n", "ppm", 3, "image/ppm",
811 "P6\n", "ppm", 3, "image/ppm",
812 "/* XPM */\n", "xbm", 10, "image/xbm",
813 ".HTML ", "troff -ms input", 6, "text/troff",
814 ".LP", "troff -ms input", 3, "text/troff",
815 ".ND", "troff -ms input", 3, "text/troff",
816 ".PP", "troff -ms input", 3, "text/troff",
817 ".TL", "troff -ms input", 3, "text/troff",
818 ".TR", "troff -ms input", 3, "text/troff",
819 ".TH", "manual page", 3, "text/troff",
820 ".\\\"", "troff input", 3, "text/troff",
821 ".de", "troff input", 3, "text/troff",
822 ".if", "troff input", 3, "text/troff",
823 ".nr", "troff input", 3, "text/troff",
824 ".tr", "troff input", 3, "text/troff",
825 "vac:", "venti score", 4, "text/plain",
826 "-----BEGIN CERTIFICATE-----\n",
827 "pem certificate", -1, "text/plain",
828 "-----BEGIN TRUSTED CERTIFICATE-----\n",
829 "pem trusted certificate", -1, "text/plain",
830 "-----BEGIN X509 CERTIFICATE-----\n",
831 "pem x.509 certificate", -1, "text/plain",
832 "subject=/C=", "pem certificate with header", -1, "text/plain",
833 "process snapshot ", "process snapshot", -1, "application/snapfs",
834 "d8:announce", "torrent file", 11, "application/x-bittorrent",
842 struct FILE_STRING *p;
844 for(p = file_string; p->key; p++) {
848 if(nbuf >= l && memcmp(buf, p->key, l) == 0) {
850 print("%s\n", p->mime);
852 print("%s\n", p->filetype);
856 if(strncmp((char*)buf, "TYPE=", 5) == 0) { /* td */
857 for(i = 5; i < nbuf; i++)
863 print("%.*s picture\n", utfnlen((char*)buf+5, i-5), (char*)buf+5);
874 32*1024, "\001CD001\001", "ISO9660 CD image", 7, OCTET,
885 for(p = offstrs; p->key; p++) {
890 if (readn(fd, buf, n) != n)
892 if(memcmp(buf, p->key, n) == 0) {
894 print("%s\n", p->mime);
896 print("%s\n", p->filetype);
906 if (strncmp((char*)buf, "FORM", 4) == 0 &&
907 strncmp((char*)buf+8, "AIFF", 4) == 0) {
908 print("%s\n", mime? "audio/x-aiff": "aiff audio");
911 if (strncmp((char*)buf, "RIFF", 4) == 0) {
912 if (strncmp((char*)buf+8, "WAVE", 4) == 0)
913 print("%s\n", mime? "audio/wave": "wave audio");
914 else if (strncmp((char*)buf+8, "AVI ", 4) == 0)
915 print("%s\n", mime? "video/avi": "avi video");
917 print("%s\n", mime? "application/octet-stream":
924 char* html_string[] = {
926 "!DOCTYPE", "![CDATA[", "basefont", "frameset", "noframes", "textarea",
928 "button", "center", "iframe", "object", "option", "script",
930 "blink", "embed", "frame", "input", "label", "param", "small",
931 "style", "table", "tbody", "tfoot", "thead", "title",
932 "?xml", "body", "code", "font", "form", "head", "html",
933 "link", "menu", "meta", "span",
934 "!--", "big", "dir", "div", "img", "pre", "sub", "sup",
935 "br", "dd", "dl", "dt", "em", "h1", "h2", "h3", "h4", "h5",
936 "h6", "hr", "li", "ol", "td", "th", "tr", "tt", "ul",
937 "a", "b", "i", "p", "q", "u",
950 while(p < buf+nbuf && *p != '<')
959 for(i = 0; html_string[i]; i++){
960 n = strlen(html_string[i]);
963 if(cistrncmp(html_string[i], (char*)p, n) == 0) {
965 if(p < buf+nbuf && strchr("\t\r\n />", *p)){
967 print(mime ? "text/html\n" : "HTML file\n");
978 char* rfc822_string[] =
1000 q = strchr(p, '\n');
1004 if(p == (char*)buf && strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ")){
1011 if(*p != '\t' && *p != ' '){
1015 for(i = 0; rfc822_string[i]; i++) {
1016 if(cistrncmp(p, rfc822_string[i], strlen(rfc822_string[i])) == 0){
1025 print(mime ? "message/rfc822\n" : "email file\n");
1037 q = strchr(p, '\n');
1041 if(strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ") == nil){
1042 print(mime ? "text/plain\n" : "mail box\n");
1056 if(Binit(&b, fd, OREAD) == Beof)
1059 type = objtype(&b, &name);
1065 print("%s intermediate\n", name);
1078 if(n >= 2 && wfreq[I2] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1080 if(n >= 1 && wfreq[Alword] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1085 if(wfreq[Cword] >= 5 && cfreq[';'] >= 5)
1090 if(cfreq[';'] >= 10 && cfreq['='] >= 10 && wfreq[Cword] >= 1)
1099 if(wfreq[Alword] > 0)
1100 print("alef program\n");
1102 print("c program\n");
1113 if(wfreq[Lword] < 4)
1115 print(mime ? PLAIN : "limbo program\n");
1126 if(wfreq[Aword] < 2)
1128 print(mime ? PLAIN : "as program\n");
1133 * low entropy means encrypted
1143 memset(bucket, 0, sizeof(bucket));
1144 for(i=nbuf-64; i<nbuf; i++)
1145 bucket[(buf[i]>>5)&07] += 1;
1149 cs += (bucket[i]-8)*(bucket[i]-8);
1152 if(buf[0]==0x1f && buf[1]==0x9d)
1153 print(mime ? "application/x-compress" : "compressed\n");
1155 if(buf[0]==0x1f && buf[1]==0x8b)
1156 print(mime ? "application/x-gzip" : "gzip compressed\n");
1158 if(buf[0]=='B' && buf[1]=='Z' && buf[2]=='h')
1159 print(mime ? "application/x-bzip2" : "bzip2 compressed\n");
1161 if(buf[0]==0x78 && buf[1]==0x9c)
1162 print(mime ? "application/x-deflate" : "zlib compressed\n");
1164 print(mime ? OCTET : "encrypted\n");
1171 * english by punctuation and frequencies
1176 int vow, comm, rare, badpun, punct;
1179 if(guess != Fascii && guess != Feascii)
1183 for(p = (char *)buf; p < (char *)buf+nbuf-1; p++)
1193 if(p[1] != ' ' && p[1] != '\n')
1196 if(badpun*5 > punct)
1198 if(cfreq['>']+cfreq['<']+cfreq['/'] > cfreq['e']) /* shell file test */
1200 if(2*cfreq[';'] > cfreq['e'])
1204 for(p="AEIOU"; *p; p++) {
1206 vow += cfreq[tolower(*p)];
1209 for(p="ETAION"; *p; p++) {
1211 comm += cfreq[tolower(*p)];
1214 for(p="VJKQXZ"; *p; p++) {
1216 rare += cfreq[tolower(*p)];
1218 if(vow*5 >= nbuf-cfreq[' '] && comm >= 10*rare) {
1219 print(mime ? PLAIN : "English text\n");
1226 * pick up a number with
1256 depthof(char *s, int *newp)
1263 while(s<es && *s==' ')
1267 if('0'<=*s && *s<='9')
1268 return 1<<strtol(s, 0, 0);
1272 while(s<es && *s!=' '){
1273 s++; /* skip letter */
1274 d += strtoul(s, &s, 10);
1277 if(d % 8 == 0 || 8 % d == 0)
1286 int dep, lox, loy, hix, hiy, px, new, cmpr;
1296 if(memcmp(cp, "compressed\n", 11) == 0) {
1301 dep = depthof((char*)cp + 0*P9BITLEN, &new);
1304 lox = p9bitnum(cp + 1*P9BITLEN);
1305 loy = p9bitnum(cp + 2*P9BITLEN);
1306 hix = p9bitnum(cp + 3*P9BITLEN);
1307 hiy = p9bitnum(cp + 4*P9BITLEN);
1308 if(dep < 0 || lox < 0 || loy < 0 || hix < 0 || hiy < 0)
1312 px = 8/dep; /* pixels per byte */
1313 /* set l to number of bytes of data per scan line */
1315 len = (hix+px-1)/px - lox/px;
1316 else{ /* make positive before divide */
1319 len = (t+hix+px-1)/px;
1322 len = (hix-lox)*dep/8;
1323 len *= hiy - loy; /* col length */
1324 len += 5 * P9BITLEN; /* size of initial ascii */
1327 * for compressed images, don't look any further. otherwise:
1328 * for image file, length is non-zero and must match calculation above.
1329 * for /dev/window and /dev/screen the length is always zero.
1330 * for subfont, the subfont header should follow immediately.
1333 print(mime ? "image/p9bit\n" : "Compressed %splan 9 image or subfont, depth %d\n",
1338 * mbuf->length == 0 probably indicates reading a pipe.
1339 * Ghostscript sometimes produces a little extra on the end.
1341 if (len != 0 && (mbuf->length == 0 || mbuf->length == len ||
1342 mbuf->length > len && mbuf->length < len+P9BITLEN)) {
1343 print(mime ? "image/p9bit\n" : "%splan 9 image, depth %d\n", newlabel, dep);
1346 if (p9subfont(buf+len)) {
1347 print(mime ? "image/p9bit\n" : "%ssubfont file, depth %d\n", newlabel, dep);
1358 /* if image too big, assume it's a subfont */
1359 if (p+3*P9BITLEN > buf+sizeof(buf))
1362 n = p9bitnum(p + 0*P9BITLEN); /* char count */
1365 h = p9bitnum(p + 1*P9BITLEN); /* height */
1368 a = p9bitnum(p + 2*P9BITLEN); /* ascent */
1374 #define WHITESPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
1381 char pathname[1024];
1384 if (!getfontnum(cp, &cp)) /* height */
1386 if (!getfontnum(cp, &cp)) /* ascent */
1388 for (i = 0; cp=(uchar*)strchr((char*)cp, '\n'); i++) {
1389 if (!getfontnum(cp, &cp)) /* min */
1391 if (!getfontnum(cp, &cp)) /* max */
1393 getfontnum(cp, &cp); /* optional offset */
1394 while (WHITESPACE(*cp))
1396 for (p = cp; *cp && !WHITESPACE(*cp); cp++)
1398 /* construct a path name, if needed */
1400 if (*p != '/' && slash) {
1402 if (n < sizeof(pathname))
1403 memcpy(pathname, fname, n);
1406 if (n+cp-p+4 < sizeof(pathname)) {
1407 memcpy(pathname+n, p, cp-p);
1410 if (access(pathname, AEXIST) < 0) {
1411 strcpy(pathname+n, ".0");
1412 if (access(pathname, AEXIST) < 0)
1418 print(mime ? "text/plain\n" : "font file\n");
1425 getfontnum(uchar *cp, uchar **rp)
1427 while (WHITESPACE(*cp)) /* extract ulong delimited by whitespace */
1429 if (*cp < '0' || *cp > '9')
1431 strtoul((char *)cp, (char **)rp, 0);
1432 if (!WHITESPACE(**rp)) {
1442 if(strstr((char *)buf, "\\rtf1")){
1443 print(mime ? "application/rtf\n" : "rich text format\n");
1452 if (buf[0] == 0x4d && buf[1] == 0x5a){
1453 print(mime ? "application/x-msdownload\n" : "MSDOS executable\n");
1462 static char *cpu[] = { /* NB: incomplete and arbitary list */
1485 static char *type[] = {
1486 [1] "relocatable object",
1488 [3] "shared library",
1492 if (memcmp(buf, "\x7fELF", 4) == 0){
1495 int n = (buf[19] << 8) | buf[18];
1496 char *p = "unknown";
1497 char *t = "unknown";
1499 if (n > 0 && n < nelem(cpu) && cpu[n])
1502 /* try the other byte order */
1504 n = (buf[18] << 8) | buf[19];
1505 if (n > 0 && n < nelem(cpu) && cpu[n])
1509 n = (buf[16]<< 8) | buf[17];
1511 n = (buf[17]<< 8) | buf[16];
1513 if(n>0 && n < nelem(type) && type[n])
1515 print("%s ELF %s\n", p, t);
1518 print("application/x-elf-executable");
1528 int i, j, ldepth, l;
1532 for(j = 0; j < 3; j++){
1533 for(p = (char*)buf, i=0; i<3; i++){
1534 if(p[0] != '0' || p[1] != 'x')
1538 else if(buf[2+4] == ',')
1549 while(*p == ' ' || *p == '\t')
1557 print("application/x-face\n");
1559 print("face image depth %d\n", ldepth);