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",
841 struct FILE_STRING *p;
843 for(p = file_string; p->key; p++) {
847 if(nbuf >= l && memcmp(buf, p->key, l) == 0) {
849 print("%s\n", p->mime);
851 print("%s\n", p->filetype);
855 if(strncmp((char*)buf, "TYPE=", 5) == 0) { /* td */
856 for(i = 5; i < nbuf; i++)
862 print("%.*s picture\n", utfnlen((char*)buf+5, i-5), (char*)buf+5);
873 32*1024, "\001CD001\001", "ISO9660 CD image", 7, OCTET,
884 for(p = offstrs; p->key; p++) {
889 if (readn(fd, buf, n) != n)
891 if(memcmp(buf, p->key, n) == 0) {
893 print("%s\n", p->mime);
895 print("%s\n", p->filetype);
905 if (strncmp((char*)buf, "FORM", 4) == 0 &&
906 strncmp((char*)buf+8, "AIFF", 4) == 0) {
907 print("%s\n", mime? "audio/x-aiff": "aiff audio");
910 if (strncmp((char*)buf, "RIFF", 4) == 0) {
911 if (strncmp((char*)buf+8, "WAVE", 4) == 0)
912 print("%s\n", mime? "audio/wave": "wave audio");
913 else if (strncmp((char*)buf+8, "AVI ", 4) == 0)
914 print("%s\n", mime? "video/avi": "avi video");
916 print("%s\n", mime? "application/octet-stream":
923 char* html_string[] =
969 /* compare strings between '<' and '>' to html table */
973 while (p < buf+nbuf && *p != '<')
981 while(p < buf+nbuf && isalpha(*p))
985 for(i = 0; html_string[i]; i++) {
986 if(cistrncmp(html_string[i], (char*)q, p-q) == 0) {
988 print(mime ? "text/html\n" : "HTML file\n");
999 char* rfc822_string[] =
1021 q = strchr(p, '\n');
1025 if(p == (char*)buf && strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ")){
1032 if(*p != '\t' && *p != ' '){
1036 for(i = 0; rfc822_string[i]; i++) {
1037 if(cistrncmp(p, rfc822_string[i], strlen(rfc822_string[i])) == 0){
1046 print(mime ? "message/rfc822\n" : "email file\n");
1058 q = strchr(p, '\n');
1062 if(strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ") == nil){
1063 print(mime ? "text/plain\n" : "mail box\n");
1077 if(Binit(&b, fd, OREAD) == Beof)
1080 type = objtype(&b, &name);
1086 print("%s intermediate\n", name);
1099 if(n >= 2 && wfreq[I2] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1101 if(n >= 1 && wfreq[Alword] >= n && wfreq[I3] >= n && cfreq['.'] >= n)
1106 if(wfreq[Cword] >= 5 && cfreq[';'] >= 5)
1111 if(cfreq[';'] >= 10 && cfreq['='] >= 10 && wfreq[Cword] >= 1)
1120 if(wfreq[Alword] > 0)
1121 print("alef program\n");
1123 print("c program\n");
1134 if(wfreq[Lword] < 4)
1136 print(mime ? PLAIN : "limbo program\n");
1147 if(wfreq[Aword] < 2)
1149 print(mime ? PLAIN : "as program\n");
1154 * low entropy means encrypted
1164 memset(bucket, 0, sizeof(bucket));
1165 for(i=nbuf-64; i<nbuf; i++)
1166 bucket[(buf[i]>>5)&07] += 1;
1170 cs += (bucket[i]-8)*(bucket[i]-8);
1173 if(buf[0]==0x1f && buf[1]==0x9d)
1174 print(mime ? "application/x-compress" : "compressed\n");
1176 if(buf[0]==0x1f && buf[1]==0x8b)
1177 print(mime ? "application/x-gzip" : "gzip compressed\n");
1179 if(buf[0]=='B' && buf[1]=='Z' && buf[2]=='h')
1180 print(mime ? "application/x-bzip2" : "bzip2 compressed\n");
1182 print(mime ? OCTET : "encrypted\n");
1189 * english by punctuation and frequencies
1194 int vow, comm, rare, badpun, punct;
1197 if(guess != Fascii && guess != Feascii)
1201 for(p = (char *)buf; p < (char *)buf+nbuf-1; p++)
1211 if(p[1] != ' ' && p[1] != '\n')
1214 if(badpun*5 > punct)
1216 if(cfreq['>']+cfreq['<']+cfreq['/'] > cfreq['e']) /* shell file test */
1218 if(2*cfreq[';'] > cfreq['e'])
1222 for(p="AEIOU"; *p; p++) {
1224 vow += cfreq[tolower(*p)];
1227 for(p="ETAION"; *p; p++) {
1229 comm += cfreq[tolower(*p)];
1232 for(p="VJKQXZ"; *p; p++) {
1234 rare += cfreq[tolower(*p)];
1236 if(vow*5 >= nbuf-cfreq[' '] && comm >= 10*rare) {
1237 print(mime ? PLAIN : "English text\n");
1244 * pick up a number with
1274 depthof(char *s, int *newp)
1281 while(s<es && *s==' ')
1285 if('0'<=*s && *s<='9')
1286 return 1<<strtol(s, 0, 0);
1290 while(s<es && *s!=' '){
1291 s++; /* skip letter */
1292 d += strtoul(s, &s, 10);
1295 if(d % 8 == 0 || 8 % d == 0)
1304 int dep, lox, loy, hix, hiy, px, new, cmpr;
1314 if(memcmp(cp, "compressed\n", 11) == 0) {
1319 dep = depthof((char*)cp + 0*P9BITLEN, &new);
1322 lox = p9bitnum(cp + 1*P9BITLEN);
1323 loy = p9bitnum(cp + 2*P9BITLEN);
1324 hix = p9bitnum(cp + 3*P9BITLEN);
1325 hiy = p9bitnum(cp + 4*P9BITLEN);
1326 if(dep < 0 || lox < 0 || loy < 0 || hix < 0 || hiy < 0)
1330 px = 8/dep; /* pixels per byte */
1331 /* set l to number of bytes of data per scan line */
1333 len = (hix+px-1)/px - lox/px;
1334 else{ /* make positive before divide */
1337 len = (t+hix+px-1)/px;
1340 len = (hix-lox)*dep/8;
1341 len *= hiy - loy; /* col length */
1342 len += 5 * P9BITLEN; /* size of initial ascii */
1345 * for compressed images, don't look any further. otherwise:
1346 * for image file, length is non-zero and must match calculation above.
1347 * for /dev/window and /dev/screen the length is always zero.
1348 * for subfont, the subfont header should follow immediately.
1351 print(mime ? OCTET : "Compressed %splan 9 image or subfont, depth %d\n",
1356 * mbuf->length == 0 probably indicates reading a pipe.
1357 * Ghostscript sometimes produces a little extra on the end.
1359 if (len != 0 && (mbuf->length == 0 || mbuf->length == len ||
1360 mbuf->length > len && mbuf->length < len+P9BITLEN)) {
1361 print(mime ? OCTET : "%splan 9 image, depth %d\n", newlabel, dep);
1364 if (p9subfont(buf+len)) {
1365 print(mime ? OCTET : "%ssubfont file, depth %d\n", newlabel, dep);
1376 /* if image too big, assume it's a subfont */
1377 if (p+3*P9BITLEN > buf+sizeof(buf))
1380 n = p9bitnum(p + 0*P9BITLEN); /* char count */
1383 h = p9bitnum(p + 1*P9BITLEN); /* height */
1386 a = p9bitnum(p + 2*P9BITLEN); /* ascent */
1392 #define WHITESPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
1399 char pathname[1024];
1402 if (!getfontnum(cp, &cp)) /* height */
1404 if (!getfontnum(cp, &cp)) /* ascent */
1406 for (i = 0; cp=(uchar*)strchr((char*)cp, '\n'); i++) {
1407 if (!getfontnum(cp, &cp)) /* min */
1409 if (!getfontnum(cp, &cp)) /* max */
1411 getfontnum(cp, &cp); /* optional offset */
1412 while (WHITESPACE(*cp))
1414 for (p = cp; *cp && !WHITESPACE(*cp); cp++)
1416 /* construct a path name, if needed */
1418 if (*p != '/' && slash) {
1420 if (n < sizeof(pathname))
1421 memcpy(pathname, fname, n);
1424 if (n+cp-p+4 < sizeof(pathname)) {
1425 memcpy(pathname+n, p, cp-p);
1428 if (access(pathname, AEXIST) < 0) {
1429 strcpy(pathname+n, ".0");
1430 if (access(pathname, AEXIST) < 0)
1436 print(mime ? "text/plain\n" : "font file\n");
1443 getfontnum(uchar *cp, uchar **rp)
1445 while (WHITESPACE(*cp)) /* extract ulong delimited by whitespace */
1447 if (*cp < '0' || *cp > '9')
1449 strtoul((char *)cp, (char **)rp, 0);
1450 if (!WHITESPACE(**rp)) {
1460 if(strstr((char *)buf, "\\rtf1")){
1461 print(mime ? "application/rtf\n" : "rich text format\n");
1470 if (buf[0] == 0x4d && buf[1] == 0x5a){
1471 print(mime ? "application/x-msdownload\n" : "MSDOS executable\n");
1480 static char *cpu[] = { /* NB: incomplete and arbitary list */
1503 static char *type[] = {
1504 [1] "relocatable object",
1506 [3] "shared library",
1510 if (memcmp(buf, "\x7fELF", 4) == 0){
1513 int n = (buf[19] << 8) | buf[18];
1514 char *p = "unknown";
1515 char *t = "unknown";
1517 if (n > 0 && n < nelem(cpu) && cpu[n])
1520 /* try the other byte order */
1522 n = (buf[18] << 8) | buf[19];
1523 if (n > 0 && n < nelem(cpu) && cpu[n])
1527 n = (buf[16]<< 8) | buf[17];
1529 n = (buf[17]<< 8) | buf[16];
1531 if(n>0 && n < nelem(type) && type[n])
1533 print("%s ELF %s\n", p, t);
1536 print("application/x-elf-executable");
1546 int i, j, ldepth, l;
1550 for(j = 0; j < 3; j++){
1551 for(p = (char*)buf, i=0; i<3; i++){
1552 if(p[0] != '0' || p[1] != 'x')
1556 else if(buf[2+4] == ',')
1567 while(*p == ' ' || *p == '\t')
1575 print("application/x-face\n");
1577 print("face image depth %d\n", ldepth);