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**);
169 int p9bitnum(uchar*);
170 int p9subfont(uchar*);
171 void print_utf(void);
172 void type(char*, int);
176 int (*call[])(void) =
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 */
202 ismung, /* entropy compressed/encrypted */
203 isenglish, /* char frequency English */
209 char OCTET[] = "application/octet-stream\n";
210 char PLAIN[] = "text/plain\n";
213 main(int argc, char *argv[])
224 fprint(2, "usage: file [-m] [file...]\n");
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))
243 for(i = 0; i < argc; i++)
244 type(argv[i], maxlen);
250 type(char *file, int nlen)
258 for (i = 0, p = file; *p; i++) {
259 if (*p == '/') /* find rightmost slash */
261 p += chartorune(&r, p); /* count runes */
263 print("%s:%*s",file, nlen-i+1, "");
266 if ((fd = open(file, OREAD)) < 0) {
267 print("cannot open: %r\n");
275 * Unicode 4.0 4-byte runes.
284 fullrune1(char *p, int n)
292 if(n >= 2 && c < 0xE0)
294 if(n >= 3 && c < 0xF0)
303 chartorune1(Rune1 *rune, char *str)
305 int c, c1, c2, c3, n;
311 n = chartorune(&r, str);
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){
338 print("cannot stat: %r\n");
341 if(mbuf->mode & DMDIR) {
342 print(mime ? OCTET : "directory\n");
345 if(mbuf->type != 'M' && mbuf->type != '|') {
346 print(mime ? OCTET : "special file #%C/%s\n",
347 mbuf->type, mbuf->name);
350 /* may be reading a pipe on standard input */
351 nbuf = readn(fd, buf, sizeof(buf)-1);
353 print("cannot read: %r\n");
357 print(mime ? PLAIN : "empty file\n");
363 * build histogram table
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)
372 p += chartorune1(&r, p);
375 else if (r <= 0x7f) {
376 if (!isprint(r) && !isspace(r))
377 f = Ceascii; /* ASCII control char */
379 } else if (r == 0x80) {
383 f = Cbinary; /* Invalid Runes */
385 f = Clatin; /* Latin 1 */
388 f = Cutf; /* UTF extension */
390 cfreq[f]++; /* ASCII chars peg directly */
397 else if (cfreq[Cutf])
399 else if (cfreq[Clatin])
401 else if (cfreq[Ceascii])
403 else if (cfreq[Cnull])
408 * lookup dictionary words
410 memset(wfreq, 0, sizeof(wfreq));
411 if(guess == Fascii || guess == Flatin || guess == Futf)
414 * call individual classify routines
416 for(i=0; call[i]; i++)
422 * print out gross classification
424 if (nbuf < 100 && !mime)
425 print(mime ? PLAIN : "short ");
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)
434 else print(mime ? OCTET : "binary\n");
438 bump_utf_count(Rune r)
442 high = sizeof(language)/sizeof(language[0])-1;
443 for (low = 0; low < high;) {
445 if (r >= language[mid].low) {
446 if (r <= language[mid].high) {
447 language[mid].count++;
460 for (i = 0; language[i].name; i++)
461 if (language[i].count > 0)
462 switch (language[i].mode) {
478 for (i = 'a'; i < 'z'; i++)
481 for (i = 'A'; i < 'Z'; i++)
488 find_first(char *name)
492 for (i = 0; language[i].name != 0; i++)
493 if (language[i].mode == First
494 && strcmp(language[i].name, name) == 0)
513 for (i = 0; language[i].name; i++)
514 if (language[i].count) {
515 switch(language[i].mode) {
517 j = find_first(language[i].name);
520 if (language[j].count > 0)
528 print("%s", language[i].name);
543 int low, high, mid, r;
548 while (p < buf+nbuf && !isalpha(*p))
553 while(p < buf+nbuf && isalpha(*p))
557 high = sizeof(dict)/sizeof(dict[0]);
558 for(low = 0;low < high;) {
560 r = strcmp(dict[mid].word, (char*)p2);
562 wfreq[dict[mid].class]++;
574 typedef struct Filemagic Filemagic;
583 * integers in this table must be as seen on a little-endian machine
584 * when read from a file.
586 Filemagic long0tab[] = {
587 0xF16DF16D, 0xFFFFFFFF, "pac1 audio file\n", OCTET,
589 0x31636170, 0xFFFFFFFF, "pac3 audio file\n", OCTET,
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,
606 0xcffaedfe, 0xFFFFFFFF, "64-bit power Mach-O executable\n", OCTET,
608 0xfeedface, 0xFFFFFFFF, "386 Mach-O executable\n", OCTET,
610 0xfeedfacf, 0xFFFFFFFF, "amd64 Mach-O executable\n", OCTET,
612 0xbebafeca, 0xFFFFFFFF, "Mach-O universal executable\n", OCTET,
614 * venti & fossil magic numbers are stored big-endian on disk,
615 * thus the numbers appear reversed in this table.
617 0xad4e5cd1, 0xFFFFFFFF, "venti arena\n", OCTET,
618 0x2bb19a52, 0xFFFFFFFF, "paq archive\n", OCTET,
622 filemagic(Filemagic *tab, int ntab, ulong x)
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);
637 return filemagic(long0tab, nelem(long0tab), LENDIAN(buf));
640 typedef struct Fileoffmag Fileoffmag;
647 * integers in this table must be as seen on a little-endian machine
648 * when read from a file.
650 Fileoffmag longofftab[] = {
652 * venti & fossil magic numbers are stored big-endian on disk,
653 * thus the numbers appear reversed in this table.
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,
662 fileoffmagic(Fileoffmag *tab, int ntab)
667 uchar buf[sizeof(long)];
669 for(i=0; i<ntab; i++) {
671 seek(fd, tp->off, 0);
672 if (readn(fd, buf, sizeof buf) != sizeof buf)
675 if((x&tp->mask) == tp->x){
676 print(mime? tp->mime: tp->desc);
686 return fileoffmagic(longofftab, nelem(longofftab));
694 seek(fd, 0, 0); /* reposition to start of file */
695 if(crackhdr(fd, &f)) {
696 print(mime ? OCTET : "%s\n", f.name);
704 enum { NAMSIZ = 100, TBLOCK = 512 };
719 char linkname[NAMSIZ];
720 /* rest are defined by POSIX's ustar format; see p1003.2b */
721 char magic[6]; /* "ustar" */
727 char prefix[155]; /* if non-null, path = prefix "/" name */
732 checksum(union hblock *hp)
736 struct header *hdr = &hp->dbuf;
738 for (cp = hdr->chksum; cp < &hdr->chksum[sizeof hdr->chksum]; cp++)
741 for (cp = hp->dummy; cp < &hp->dummy[TBLOCK]; cp++)
751 union hblock *hp = (union hblock *)tblock;
752 struct header *hdr = &hp->dbuf;
754 seek(fd, 0, 0); /* reposition to start of file */
755 if (readn(fd, tblock, sizeof tblock) != sizeof tblock)
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");
762 print(mime? "application/x-tar\n": "tar archive\n");
769 * initial words to classify file
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",
856 struct FILE_STRING *p;
858 for(p = file_string; p->key; p++) {
862 if(nbuf >= l && memcmp(buf, p->key, l) == 0) {
864 print("%s\n", p->mime);
866 print("%s\n", p->filetype);
870 if(strncmp((char*)buf, "TYPE=", 5) == 0) { /* td */
871 for(i = 5; i < nbuf; i++)
877 print("%.*s picture\n", utfnlen((char*)buf+5, i-5), (char*)buf+5);
888 32*1024, "\001CD001\001", "ISO9660 CD image", 7, "application/x-iso9660-image",
899 for(p = offstrs; p->key; p++) {
904 if (readn(fd, buf, n) != n)
906 if(memcmp(buf, p->key, n) == 0) {
908 print("%s\n", p->mime);
910 print("%s\n", p->filetype);
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");
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");
931 print("%s\n", mime? "application/octet-stream":
938 char* html_string[] = {
940 "!DOCTYPE", "![CDATA[", "basefont", "frameset", "noframes", "textarea",
942 "button", "center", "iframe", "object", "option", "script",
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",
964 while(p < buf+nbuf && *p != '<')
973 for(i = 0; html_string[i]; i++){
974 n = strlen(html_string[i]);
977 if(cistrncmp(html_string[i], (char*)p, n) == 0) {
979 if(p < buf+nbuf && strchr("\t\r\n />", *p)){
981 print(mime ? "text/html\n" : "HTML file\n");
992 char* rfc822_string[] =
1014 q = strchr(p, '\n');
1018 if(p == (char*)buf && strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ")){
1025 if(*p != '\t' && *p != ' '){
1029 for(i = 0; rfc822_string[i]; i++) {
1030 if(cistrncmp(p, rfc822_string[i], strlen(rfc822_string[i])) == 0){
1039 print(mime ? "message/rfc822\n" : "email file\n");
1051 q = strchr(p, '\n');
1055 if(strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ") == nil){
1056 print(mime ? "text/plain\n" : "mail box\n");
1070 if(Binit(&b, fd, OREAD) == Beof)
1073 type = objtype(&b, &name);
1079 print("%s intermediate\n", name);
1092 if(n >= 2 && wfreq[I2] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1094 if(n >= 1 && wfreq[Alword] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1099 if(wfreq[Cword] >= 5 && cfreq[';'] >= 5)
1104 if(cfreq[';'] >= 10 && cfreq['='] >= 10 && wfreq[Cword] >= 1)
1113 if(wfreq[Alword] > 0)
1114 print("alef program\n");
1116 print("c program\n");
1127 if(wfreq[Lword] < 4)
1129 print(mime ? PLAIN : "limbo program\n");
1140 if(wfreq[Aword] < 2)
1142 print(mime ? PLAIN : "as program\n");
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");
1164 * low entropy means encrypted
1174 memset(bucket, 0, sizeof(bucket));
1175 for(i=nbuf-64; i<nbuf; i++)
1176 bucket[(buf[i]>>5)&07] += 1;
1180 cs += (bucket[i]-8)*(bucket[i]-8);
1183 if(buf[0]==0x1f && buf[1]==0x9d)
1184 print(mime ? "application/x-compress" : "compressed\n");
1186 if(buf[0]==0x1f && buf[1]==0x8b)
1187 print(mime ? "application/x-gzip" : "gzip compressed\n");
1189 if(buf[0]=='B' && buf[1]=='Z' && buf[2]=='h')
1190 print(mime ? "application/x-bzip2" : "bzip2 compressed\n");
1192 if(buf[0]==0x78 && buf[1]==0x9c)
1193 print(mime ? "application/x-deflate" : "zlib compressed\n");
1195 print(mime ? OCTET : "encrypted\n");
1202 * english by punctuation and frequencies
1207 int vow, comm, rare, badpun, punct;
1210 if(guess != Fascii && guess != Feascii)
1214 for(p = (char *)buf; p < (char *)buf+nbuf-1; p++)
1224 if(p[1] != ' ' && p[1] != '\n')
1227 if(badpun*5 > punct)
1229 if(cfreq['>']+cfreq['<']+cfreq['/'] > cfreq['e']) /* shell file test */
1231 if(2*cfreq[';'] > cfreq['e'])
1235 for(p="AEIOU"; *p; p++) {
1237 vow += cfreq[tolower(*p)];
1240 for(p="ETAION"; *p; p++) {
1242 comm += cfreq[tolower(*p)];
1245 for(p="VJKQXZ"; *p; p++) {
1247 rare += cfreq[tolower(*p)];
1249 if(vow*5 >= nbuf-cfreq[' '] && comm >= 10*rare) {
1250 print(mime ? PLAIN : "English text\n");
1257 * pick up a number with
1287 depthof(char *s, int *newp)
1294 while(s<es && *s==' ')
1298 if('0'<=*s && *s<='9')
1299 return 1<<strtol(s, 0, 0);
1303 while(s<es && *s!=' '){
1304 s++; /* skip letter */
1305 d += strtoul(s, &s, 10);
1308 if(d % 8 == 0 || 8 % d == 0)
1317 int dep, lox, loy, hix, hiy, px, new, cmpr;
1327 if(memcmp(cp, "compressed\n", 11) == 0) {
1332 dep = depthof((char*)cp + 0*P9BITLEN, &new);
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)
1343 px = 8/dep; /* pixels per byte */
1344 /* set l to number of bytes of data per scan line */
1346 len = (hix+px-1)/px - lox/px;
1347 else{ /* make positive before divide */
1350 len = (t+hix+px-1)/px;
1353 len = (hix-lox)*dep/8;
1354 len *= hiy - loy; /* col length */
1355 len += 5 * P9BITLEN; /* size of initial ascii */
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.
1364 print(mime ? "image/p9bit\n" : "Compressed %splan 9 image or subfont, depth %d\n",
1369 * mbuf->length == 0 probably indicates reading a pipe.
1370 * Ghostscript sometimes produces a little extra on the end.
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);
1377 if (p9subfont(buf+len)) {
1378 print(mime ? "image/p9bit\n" : "%ssubfont file, depth %d\n", newlabel, dep);
1389 /* if image too big, assume it's a subfont */
1390 if (p+3*P9BITLEN > buf+sizeof(buf))
1393 n = p9bitnum(p + 0*P9BITLEN); /* char count */
1396 h = p9bitnum(p + 1*P9BITLEN); /* height */
1399 a = p9bitnum(p + 2*P9BITLEN); /* ascent */
1405 #define WHITESPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
1412 char pathname[1024];
1415 if (!getfontnum(cp, &cp)) /* height */
1417 if (!getfontnum(cp, &cp)) /* ascent */
1419 for (i = 0; cp=(uchar*)strchr((char*)cp, '\n'); i++) {
1420 if (!getfontnum(cp, &cp)) /* min */
1422 if (!getfontnum(cp, &cp)) /* max */
1424 getfontnum(cp, &cp); /* optional offset */
1425 while (WHITESPACE(*cp))
1427 for (p = cp; *cp && !WHITESPACE(*cp); cp++)
1429 /* construct a path name, if needed */
1431 if (*p != '/' && slash) {
1433 if (n < sizeof(pathname))
1434 memcpy(pathname, fname, n);
1437 if (n+cp-p+4 < sizeof(pathname)) {
1438 memcpy(pathname+n, p, cp-p);
1441 if (access(pathname, AEXIST) < 0) {
1442 strcpy(pathname+n, ".0");
1443 if (access(pathname, AEXIST) < 0)
1449 print(mime ? "text/plain\n" : "font file\n");
1456 getfontnum(uchar *cp, uchar **rp)
1458 while (WHITESPACE(*cp)) /* extract ulong delimited by whitespace */
1460 if (*cp < '0' || *cp > '9')
1462 strtoul((char *)cp, (char **)rp, 0);
1463 if (!WHITESPACE(**rp)) {
1473 if(strstr((char *)buf, "\\rtf1")){
1474 print(mime ? "application/rtf\n" : "rich text format\n");
1483 if (buf[0] == 0x4d && buf[1] == 0x5a){
1484 print(mime ? "application/x-msdownload\n" : "MSDOS executable\n");
1493 if(buf[0] || buf[1] || buf[3] || buf[9])
1495 if(buf[4] == 0x00 && buf[5] == 0x00)
1499 print(mime ? "image/x-icon\n" : "Microsoft icon file\n");
1502 print(mime ? "image/x-icon\n" : "Microsoft cursor file\n");
1511 static char *cpu[] = { /* NB: incomplete and arbitary list */
1534 static char *type[] = {
1535 [1] "relocatable object",
1537 [3] "shared library",
1541 if (memcmp(buf, "\x7fELF", 4) == 0){
1544 int n = (buf[19] << 8) | buf[18];
1545 char *p = "unknown";
1546 char *t = "unknown";
1548 if (n > 0 && n < nelem(cpu) && cpu[n])
1551 /* try the other byte order */
1553 n = (buf[18] << 8) | buf[19];
1554 if (n > 0 && n < nelem(cpu) && cpu[n])
1558 n = (buf[16]<< 8) | buf[17];
1560 n = (buf[17]<< 8) | buf[16];
1562 if(n>0 && n < nelem(type) && type[n])
1564 print("%s ELF %s\n", p, t);
1567 print("application/x-elf-executable");
1577 int i, j, ldepth, l;
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')
1587 else if(buf[2+4] == ',')
1598 while(*p == ' ' || *p == '\t')
1606 print("application/x-face\n");
1608 print("face image depth %d\n", ldepth);