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,
621 filemagic(Filemagic *tab, int ntab, ulong x)
625 for(i=0; i<ntab; i++)
626 if((x&tab[i].mask) == tab[i].x){
627 print(mime ? tab[i].mime : tab[i].desc);
636 return filemagic(long0tab, nelem(long0tab), LENDIAN(buf));
639 typedef struct Fileoffmag Fileoffmag;
646 * integers in this table must be as seen on a little-endian machine
647 * when read from a file.
649 Fileoffmag longofftab[] = {
651 * venti & fossil magic numbers are stored big-endian on disk,
652 * thus the numbers appear reversed in this table.
654 256*1024, 0xe7a5e4a9, 0xFFFFFFFF, "venti arenas partition\n", OCTET,
655 256*1024, 0xc75e5cd1, 0xFFFFFFFF, "venti index section\n", OCTET,
656 128*1024, 0x89ae7637, 0xFFFFFFFF, "fossil write buffer\n", OCTET,
657 4, 0x31647542, 0xFFFFFFFF, "OS X finder properties\n", OCTET,
661 fileoffmagic(Fileoffmag *tab, int ntab)
666 uchar buf[sizeof(long)];
668 for(i=0; i<ntab; i++) {
670 seek(fd, tp->off, 0);
671 if (readn(fd, buf, sizeof buf) != sizeof buf)
674 if((x&tp->mask) == tp->x){
675 print(mime? tp->mime: tp->desc);
685 return fileoffmagic(longofftab, nelem(longofftab));
693 seek(fd, 0, 0); /* reposition to start of file */
694 if(crackhdr(fd, &f)) {
695 print(mime ? OCTET : "%s\n", f.name);
703 enum { NAMSIZ = 100, TBLOCK = 512 };
718 char linkname[NAMSIZ];
719 /* rest are defined by POSIX's ustar format; see p1003.2b */
720 char magic[6]; /* "ustar" */
726 char prefix[155]; /* if non-null, path = prefix "/" name */
731 checksum(union hblock *hp)
735 struct header *hdr = &hp->dbuf;
737 for (cp = hdr->chksum; cp < &hdr->chksum[sizeof hdr->chksum]; cp++)
740 for (cp = hp->dummy; cp < &hp->dummy[TBLOCK]; cp++)
750 union hblock *hp = (union hblock *)tblock;
751 struct header *hdr = &hp->dbuf;
753 seek(fd, 0, 0); /* reposition to start of file */
754 if (readn(fd, tblock, sizeof tblock) != sizeof tblock)
756 chksum = strtol(hdr->chksum, 0, 8);
757 if (hdr->name[0] != '\0' && checksum(hp) == chksum) {
758 if (strcmp(hdr->magic, "ustar") == 0)
759 print(mime? "application/x-ustar\n": "posix tar archive\n");
761 print(mime? "application/x-tar\n": "tar archive\n");
768 * initial words to classify file
778 "\x1f\x9d", "compressed", 2, "application/x-compress",
779 "\x1f\x8b", "gzip compressed", 2, "application/x-gzip",
780 "BZh", "bzip2 compressed", 3, "application/x-bzip2",
781 "!<arch>\n__.SYMDEF", "archive random library", 16, "application/octet-stream",
782 "!<arch>\n", "archive", 8, "application/octet-stream",
783 "070707", "cpio archive - ascii header", 6, "application/octet-stream",
784 "#!/bin/rc", "rc executable file", 9, "text/plain",
785 "#!/bin/sh", "sh executable file", 9, "text/plain",
786 "%!", "postscript", 2, "application/postscript",
787 "\004%!", "postscript", 3, "application/postscript",
788 "x T post", "troff output for post", 8, "application/troff",
789 "x T Latin1", "troff output for Latin1", 10, "application/troff",
790 "x T utf", "troff output for UTF", 7, "application/troff",
791 "x T 202", "troff output for 202", 7, "application/troff",
792 "x T aps", "troff output for aps", 7, "application/troff",
793 "GIF", "GIF image", 3, "image/gif",
794 "\0PC Research, Inc\0", "ghostscript fax file", 18, "application/ghostscript",
795 "%PDF", "PDF", 4, "application/pdf",
796 "<!DOCTYPE", "HTML file", 9, "text/html",
797 "<!doctype", "HTML file", 9, "text/html",
798 "<!--", "HTML file", 4, "text/html",
799 "<html>", "HTML file", 6, "text/html",
800 "<HTML>", "HTML file", 6, "text/html",
801 "<?xml", "HTML file", 5, "text/html",
802 "\111\111\052\000", "tiff", 4, "image/tiff",
803 "\115\115\000\052", "tiff", 4, "image/tiff",
804 "\377\330\377\340", "jpeg", 4, "image/jpeg",
805 "\377\330\377\341", "jpeg", 4, "image/jpeg",
806 "\377\330\377\333", "jpeg", 4, "image/jpeg",
807 "\xff\xd8", "jpeg", 2, "image/jpeg",
808 "BM", "bmp", 2, "image/bmp",
809 "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", "microsoft office document", 8, "application/doc",
810 "<MakerFile ", "FrameMaker file", 11, "application/framemaker",
811 "\033E\033", "HP PCL printer data", 3, OCTET,
812 "\033%-12345X", "HPJCL file", 9, "application/hpjcl",
813 "ID3", "mp3 audio with id3", 3, "audio/mpeg",
814 "OggS", "ogg audio", 4, "audio/ogg",
815 "\211PNG", "PNG image", 4, "image/png",
816 "P3\n", "ppm", 3, "image/ppm",
817 "P6\n", "ppm", 3, "image/ppm",
818 "/* XPM */\n", "xbm", 10, "image/xbm",
819 ".HTML ", "troff -ms input", 6, "text/troff",
820 ".LP", "troff -ms input", 3, "text/troff",
821 ".ND", "troff -ms input", 3, "text/troff",
822 ".PP", "troff -ms input", 3, "text/troff",
823 ".TL", "troff -ms input", 3, "text/troff",
824 ".TR", "troff -ms input", 3, "text/troff",
825 ".TH", "manual page", 3, "text/troff",
826 ".\\\"", "troff input", 3, "text/troff",
827 ".de", "troff input", 3, "text/troff",
828 ".if", "troff input", 3, "text/troff",
829 ".nr", "troff input", 3, "text/troff",
830 ".tr", "troff input", 3, "text/troff",
831 "vac:", "venti score", 4, "text/plain",
832 "-----BEGIN CERTIFICATE-----\n",
833 "pem certificate", -1, "text/plain",
834 "-----BEGIN TRUSTED CERTIFICATE-----\n",
835 "pem trusted certificate", -1, "text/plain",
836 "-----BEGIN X509 CERTIFICATE-----\n",
837 "pem x.509 certificate", -1, "text/plain",
838 "subject=/C=", "pem certificate with header", -1, "text/plain",
839 "process snapshot ", "process snapshot", -1, "application/snapfs",
840 "d8:announce", "torrent file", 11, "application/x-bittorrent",
841 "[playlist]", "playlist", 10, "application/x-scpls",
842 "#EXTM3U", "playlist", 7, "audio/x-mpegurl",
850 struct FILE_STRING *p;
852 for(p = file_string; p->key; p++) {
856 if(nbuf >= l && memcmp(buf, p->key, l) == 0) {
858 print("%s\n", p->mime);
860 print("%s\n", p->filetype);
864 if(strncmp((char*)buf, "TYPE=", 5) == 0) { /* td */
865 for(i = 5; i < nbuf; i++)
871 print("%.*s picture\n", utfnlen((char*)buf+5, i-5), (char*)buf+5);
882 32*1024, "\001CD001\001", "ISO9660 CD image", 7, "application/x-iso9660-image",
893 for(p = offstrs; p->key; p++) {
898 if (readn(fd, buf, n) != n)
900 if(memcmp(buf, p->key, n) == 0) {
902 print("%s\n", p->mime);
904 print("%s\n", p->filetype);
914 if (strncmp((char*)buf, "FORM", 4) == 0 &&
915 strncmp((char*)buf+8, "AIFF", 4) == 0) {
916 print("%s\n", mime? "audio/x-aiff": "aiff audio");
919 if (strncmp((char*)buf, "RIFF", 4) == 0) {
920 if (strncmp((char*)buf+8, "WAVE", 4) == 0)
921 print("%s\n", mime? "audio/wave": "wave audio");
922 else if (strncmp((char*)buf+8, "AVI ", 4) == 0)
923 print("%s\n", mime? "video/avi": "avi video");
925 print("%s\n", mime? "application/octet-stream":
932 char* html_string[] = {
934 "!DOCTYPE", "![CDATA[", "basefont", "frameset", "noframes", "textarea",
936 "button", "center", "iframe", "object", "option", "script",
938 "blink", "embed", "frame", "input", "label", "param", "small",
939 "style", "table", "tbody", "tfoot", "thead", "title",
940 "?xml", "body", "code", "font", "form", "head", "html",
941 "link", "menu", "meta", "span",
942 "!--", "big", "dir", "div", "img", "pre", "sub", "sup",
943 "br", "dd", "dl", "dt", "em", "h1", "h2", "h3", "h4", "h5",
944 "h6", "hr", "li", "ol", "td", "th", "tr", "tt", "ul",
945 "a", "b", "i", "p", "q", "u",
958 while(p < buf+nbuf && *p != '<')
967 for(i = 0; html_string[i]; i++){
968 n = strlen(html_string[i]);
971 if(cistrncmp(html_string[i], (char*)p, n) == 0) {
973 if(p < buf+nbuf && strchr("\t\r\n />", *p)){
975 print(mime ? "text/html\n" : "HTML file\n");
986 char* rfc822_string[] =
1008 q = strchr(p, '\n');
1012 if(p == (char*)buf && strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ")){
1019 if(*p != '\t' && *p != ' '){
1023 for(i = 0; rfc822_string[i]; i++) {
1024 if(cistrncmp(p, rfc822_string[i], strlen(rfc822_string[i])) == 0){
1033 print(mime ? "message/rfc822\n" : "email file\n");
1045 q = strchr(p, '\n');
1049 if(strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ") == nil){
1050 print(mime ? "text/plain\n" : "mail box\n");
1064 if(Binit(&b, fd, OREAD) == Beof)
1067 type = objtype(&b, &name);
1073 print("%s intermediate\n", name);
1086 if(n >= 2 && wfreq[I2] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1088 if(n >= 1 && wfreq[Alword] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1093 if(wfreq[Cword] >= 5 && cfreq[';'] >= 5)
1098 if(cfreq[';'] >= 10 && cfreq['='] >= 10 && wfreq[Cword] >= 1)
1107 if(wfreq[Alword] > 0)
1108 print("alef program\n");
1110 print("c program\n");
1121 if(wfreq[Lword] < 4)
1123 print(mime ? PLAIN : "limbo program\n");
1134 if(wfreq[Aword] < 2)
1136 print(mime ? PLAIN : "as program\n");
1147 while((p < e) && (p = memchr(p, 0xFF, e - p))){
1148 if((p[1] & 0xFE) == 0xFA){
1149 print(mime ? "audio/mpeg\n" : "mp3 audio\n");
1158 * low entropy means encrypted
1168 memset(bucket, 0, sizeof(bucket));
1169 for(i=nbuf-64; i<nbuf; i++)
1170 bucket[(buf[i]>>5)&07] += 1;
1174 cs += (bucket[i]-8)*(bucket[i]-8);
1177 if(buf[0]==0x1f && buf[1]==0x9d)
1178 print(mime ? "application/x-compress" : "compressed\n");
1180 if(buf[0]==0x1f && buf[1]==0x8b)
1181 print(mime ? "application/x-gzip" : "gzip compressed\n");
1183 if(buf[0]=='B' && buf[1]=='Z' && buf[2]=='h')
1184 print(mime ? "application/x-bzip2" : "bzip2 compressed\n");
1186 if(buf[0]==0x78 && buf[1]==0x9c)
1187 print(mime ? "application/x-deflate" : "zlib compressed\n");
1189 print(mime ? OCTET : "encrypted\n");
1196 * english by punctuation and frequencies
1201 int vow, comm, rare, badpun, punct;
1204 if(guess != Fascii && guess != Feascii)
1208 for(p = (char *)buf; p < (char *)buf+nbuf-1; p++)
1218 if(p[1] != ' ' && p[1] != '\n')
1221 if(badpun*5 > punct)
1223 if(cfreq['>']+cfreq['<']+cfreq['/'] > cfreq['e']) /* shell file test */
1225 if(2*cfreq[';'] > cfreq['e'])
1229 for(p="AEIOU"; *p; p++) {
1231 vow += cfreq[tolower(*p)];
1234 for(p="ETAION"; *p; p++) {
1236 comm += cfreq[tolower(*p)];
1239 for(p="VJKQXZ"; *p; p++) {
1241 rare += cfreq[tolower(*p)];
1243 if(vow*5 >= nbuf-cfreq[' '] && comm >= 10*rare) {
1244 print(mime ? PLAIN : "English text\n");
1251 * pick up a number with
1281 depthof(char *s, int *newp)
1288 while(s<es && *s==' ')
1292 if('0'<=*s && *s<='9')
1293 return 1<<strtol(s, 0, 0);
1297 while(s<es && *s!=' '){
1298 s++; /* skip letter */
1299 d += strtoul(s, &s, 10);
1302 if(d % 8 == 0 || 8 % d == 0)
1311 int dep, lox, loy, hix, hiy, px, new, cmpr;
1321 if(memcmp(cp, "compressed\n", 11) == 0) {
1326 dep = depthof((char*)cp + 0*P9BITLEN, &new);
1329 lox = p9bitnum(cp + 1*P9BITLEN);
1330 loy = p9bitnum(cp + 2*P9BITLEN);
1331 hix = p9bitnum(cp + 3*P9BITLEN);
1332 hiy = p9bitnum(cp + 4*P9BITLEN);
1333 if(dep < 0 || lox < 0 || loy < 0 || hix < 0 || hiy < 0)
1337 px = 8/dep; /* pixels per byte */
1338 /* set l to number of bytes of data per scan line */
1340 len = (hix+px-1)/px - lox/px;
1341 else{ /* make positive before divide */
1344 len = (t+hix+px-1)/px;
1347 len = (hix-lox)*dep/8;
1348 len *= hiy - loy; /* col length */
1349 len += 5 * P9BITLEN; /* size of initial ascii */
1352 * for compressed images, don't look any further. otherwise:
1353 * for image file, length is non-zero and must match calculation above.
1354 * for /dev/window and /dev/screen the length is always zero.
1355 * for subfont, the subfont header should follow immediately.
1358 print(mime ? "image/p9bit\n" : "Compressed %splan 9 image or subfont, depth %d\n",
1363 * mbuf->length == 0 probably indicates reading a pipe.
1364 * Ghostscript sometimes produces a little extra on the end.
1366 if (len != 0 && (mbuf->length == 0 || mbuf->length == len ||
1367 mbuf->length > len && mbuf->length < len+P9BITLEN)) {
1368 print(mime ? "image/p9bit\n" : "%splan 9 image, depth %d\n", newlabel, dep);
1371 if (p9subfont(buf+len)) {
1372 print(mime ? "image/p9bit\n" : "%ssubfont file, depth %d\n", newlabel, dep);
1383 /* if image too big, assume it's a subfont */
1384 if (p+3*P9BITLEN > buf+sizeof(buf))
1387 n = p9bitnum(p + 0*P9BITLEN); /* char count */
1390 h = p9bitnum(p + 1*P9BITLEN); /* height */
1393 a = p9bitnum(p + 2*P9BITLEN); /* ascent */
1399 #define WHITESPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
1406 char pathname[1024];
1409 if (!getfontnum(cp, &cp)) /* height */
1411 if (!getfontnum(cp, &cp)) /* ascent */
1413 for (i = 0; cp=(uchar*)strchr((char*)cp, '\n'); i++) {
1414 if (!getfontnum(cp, &cp)) /* min */
1416 if (!getfontnum(cp, &cp)) /* max */
1418 getfontnum(cp, &cp); /* optional offset */
1419 while (WHITESPACE(*cp))
1421 for (p = cp; *cp && !WHITESPACE(*cp); cp++)
1423 /* construct a path name, if needed */
1425 if (*p != '/' && slash) {
1427 if (n < sizeof(pathname))
1428 memcpy(pathname, fname, n);
1431 if (n+cp-p+4 < sizeof(pathname)) {
1432 memcpy(pathname+n, p, cp-p);
1435 if (access(pathname, AEXIST) < 0) {
1436 strcpy(pathname+n, ".0");
1437 if (access(pathname, AEXIST) < 0)
1443 print(mime ? "text/plain\n" : "font file\n");
1450 getfontnum(uchar *cp, uchar **rp)
1452 while (WHITESPACE(*cp)) /* extract ulong delimited by whitespace */
1454 if (*cp < '0' || *cp > '9')
1456 strtoul((char *)cp, (char **)rp, 0);
1457 if (!WHITESPACE(**rp)) {
1467 if(strstr((char *)buf, "\\rtf1")){
1468 print(mime ? "application/rtf\n" : "rich text format\n");
1477 if (buf[0] == 0x4d && buf[1] == 0x5a){
1478 print(mime ? "application/x-msdownload\n" : "MSDOS executable\n");
1487 if(buf[0] || buf[1] || buf[3] || buf[9])
1489 if(buf[4] == 0x00 && buf[5] == 0x00)
1493 print(mime ? "image/x-icon\n" : "Microsoft icon file\n");
1496 print(mime ? "image/x-icon\n" : "Microsoft cursor file\n");
1505 static char *cpu[] = { /* NB: incomplete and arbitary list */
1528 static char *type[] = {
1529 [1] "relocatable object",
1531 [3] "shared library",
1535 if (memcmp(buf, "\x7fELF", 4) == 0){
1538 int n = (buf[19] << 8) | buf[18];
1539 char *p = "unknown";
1540 char *t = "unknown";
1542 if (n > 0 && n < nelem(cpu) && cpu[n])
1545 /* try the other byte order */
1547 n = (buf[18] << 8) | buf[19];
1548 if (n > 0 && n < nelem(cpu) && cpu[n])
1552 n = (buf[16]<< 8) | buf[17];
1554 n = (buf[17]<< 8) | buf[16];
1556 if(n>0 && n < nelem(type) && type[n])
1558 print("%s ELF %s\n", p, t);
1561 print("application/x-elf-executable");
1571 int i, j, ldepth, l;
1575 for(j = 0; j < 3; j++){
1576 for(p = (char*)buf, i=0; i<3; i++){
1577 if(p[0] != '0' || p[1] != 'x')
1581 else if(buf[2+4] == ',')
1592 while(*p == ' ' || *p == '\t')
1600 print("application/x-face\n");
1602 print("face image depth %d\n", ldepth);