]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/file.c
make bind(2) error handling consistent
[plan9front.git] / sys / src / cmd / file.c
index 2759cce8df922a375ba7ea25ce32a73a09ffcb7e..bd9d1afb5b492146277b6c53411a34177dbc2026 100644 (file)
@@ -150,6 +150,9 @@ int ishtml(void);
 int    isrfc822(void);
 int    ismbox(void);
 int    islimbo(void);
+int    istga(void);
+int    ismp3(void);
+int    ismp4(void);
 int    ismung(void);
 int    isp9bit(void);
 int    isp9font(void);
@@ -165,7 +168,8 @@ int longoff(void);
 int    istar(void);
 int    isface(void);
 int    isexec(void);
-int    p9bitnum(uchar*);
+int    isudiff(void);
+int    p9bitnum(char*, int*);
 int    p9subfont(uchar*);
 void   print_utf(void);
 void   type(char*, int);
@@ -181,11 +185,12 @@ int       (*call[])(void) =
        iff,            /* interchange file format (strings) */
        longoff,        /* recognizable by 4 bytes at some offset */
        isoffstr,       /* recognizable by string at some offset */
+       isudiff,        /* unified diff output */
        isrfc822,       /* email file */
        ismbox,         /* mail box */
        istar,          /* recognizable by tar checksum */
-       ishtml,         /* html keywords */
        iscint,         /* compiler/assembler intermediate */
+       ishtml,         /* html keywords */
        islimbo,        /* limbo source */
        isc,            /* c & alef compiler key words */
        isas,           /* assembler key words */
@@ -195,6 +200,9 @@ int (*call[])(void) =
        ismsdos,        /* msdos exe (virus file attachement) */
        isicocur,               /* windows icon or cursor file */
        isface,         /* ascii face file */
+       istga,
+       ismp4,
+       ismp3,
 
        /* last resorts */
        ismung,         /* entropy compressed/encrypted */
@@ -204,8 +212,8 @@ int (*call[])(void) =
 
 int mime;
 
-char OCTET[] = "application/octet-stream\n";
-char PLAIN[] = "text/plain\n";
+char OCTET[] = "application/octet-stream";
+char PLAIN[] = "text/plain";
 
 void
 main(int argc, char *argv[])
@@ -262,101 +270,113 @@ type(char *file, int nlen)
        }
        fname = file;
        if ((fd = open(file, OREAD)) < 0) {
-               print("cannot open: %r\n");
+               fprint(2, "cannot open: %r\n");
                return;
        }
        filetype(fd);
        close(fd);
 }
 
-/*
- * Unicode 4.0 4-byte runes.
- */
-typedef int Rune1;
-
-enum {
-       UTFmax1 = 4,
-};
-
-int
-fullrune1(char *p, int n)
-{
-       int c;
-
-       if(n >= 1) {
-               c = *(uchar*)p;
-               if(c < 0x80)
-                       return 1;
-               if(n >= 2 && c < 0xE0)
-                       return 1;
-               if(n >= 3 && c < 0xF0)
-                       return 1;
-               if(n >= 4)
-                       return 1;
-       }
-       return 0;
-}
-
-int
-chartorune1(Rune1 *rune, char *str)
+void
+utfconv(void)
 {
-       int c, c1, c2, c3, n;
        Rune r;
+       uchar *rb;
+       char *p, *e;
+       int i;
 
-       c = *(uchar*)str;
-       if(c < 0xF0){
-               r = 0;
-               n = chartorune(&r, str);
-               *rune = r;
-               return n;
-       }
-       c &= ~0xF0;
-       c1 = *(uchar*)(str+1) & ~0x80;
-       c2 = *(uchar*)(str+2) & ~0x80;
-       c3 = *(uchar*)(str+3) & ~0x80;
-       n = (c<<18) | (c1<<12) | (c2<<6) | c3;
-       if(n < 0x10000 || n > 0x10FFFF){
-               *rune = Runeerror;
-               return 1;
+       if(nbuf < 4)
+               return;
+
+       if(memcmp(buf, "\x00\x00\xFE\xFF", 4) == 0){
+               if(!mime)
+                       print("utf-32be ");
+               return;
+       } else
+       if(memcmp(buf, "\xFE\xFF\x00\x00", 4) == 0){
+               if(!mime)
+                       print("utf-32le ");
+               return;
+       } else
+       if(memcmp(buf, "\xEF\xBB\xBF", 3) == 0){
+               memmove(buf, buf+3, nbuf-3);
+               nbuf -= 3;
+               return;
+       } else
+       if(memcmp(buf, "\xFE\xFF", 2) == 0){
+               if(!mime)
+                       print("utf-16be ");
+
+               nbuf -= 2;
+               rb = malloc(nbuf+1);
+               memmove(rb, buf+2, nbuf);
+               p = (char*)buf;
+               e = p+sizeof(buf)-UTFmax-1;
+               for(i=0; i<nbuf && p < e; i+=2){
+                       r = rb[i+1] | rb[i]<<8;
+                       p += runetochar(p, &r);
+               }
+               *p = 0;
+               free(rb);
+               nbuf = p - (char*)buf;
+       } else
+       if(memcmp(buf, "\xFF\xFE", 2) == 0){
+               if(!mime)
+                       print("utf-16le ");
+
+               nbuf -= 2;
+               rb = malloc(nbuf+1);
+               memmove(rb, buf+2, nbuf);
+               p = (char*)buf;
+               e = p+sizeof(buf)-UTFmax-1;
+               for(i=0; i<nbuf && p < e; i+=2){
+                       r = rb[i] | rb[i+1]<<8;
+                       p += runetochar(p, &r);
+               }
+               *p = 0;
+               free(rb);
+               nbuf = p - (char*)buf;
        }
-       *rune = n;
-       return 4;
 }
 
 void
 filetype(int fd)
 {
-       Rune1 r;
+       Rune r;
        int i, f, n;
        char *p, *eob;
 
        free(mbuf);
        mbuf = dirfstat(fd);
        if(mbuf == nil){
-               print("cannot stat: %r\n");
+               fprint(2, "cannot stat: %r\n");
                return;
        }
        if(mbuf->mode & DMDIR) {
-               print(mime ? OCTET : "directory\n");
+               print("%s\n", mime ? OCTET : "directory");
                return;
        }
        if(mbuf->type != 'M' && mbuf->type != '|') {
-               print(mime ? OCTET : "special file #%C/%s\n",
-                       mbuf->type, mbuf->name);
+               if(mime)
+                       print("%s\n", OCTET);
+               else
+                       print("special file #%C/%s\n", mbuf->type, mbuf->name);
                return;
        }
        /* may be reading a pipe on standard input */
        nbuf = readn(fd, buf, sizeof(buf)-1);
        if(nbuf < 0) {
-               print("cannot read: %r\n");
+               fprint(2, "cannot read: %r\n");
                return;
        }
        if(nbuf == 0) {
-               print(mime ? PLAIN : "empty file\n");
+               print("%s\n", mime ? PLAIN : "empty file");
                return;
        }
        buf[nbuf] = 0;
 
+       utfconv();
+
        /*
         * build histogram table
         */
@@ -365,9 +385,9 @@ filetype(int fd)
                language[i].count = 0;
        eob = (char *)buf+nbuf;
        for(n = 0, p = (char *)buf; p < eob; n++) {
-               if (!fullrune1(p, eob-p) && eob-p < UTFmax1)
+               if (!fullrune(p, eob-p) && eob-p < UTFmax)
                        break;
-               p += chartorune1(&r, p);
+               p += chartorune(&r, p);
                if (r == 0)
                        f = Cnull;
                else if (r <= 0x7f) {
@@ -422,14 +442,14 @@ filetype(int fd)
        if (nbuf < 100 && !mime)
                print(mime ? PLAIN : "short ");
        if (guess == Fascii)
-               print(mime ? PLAIN : "Ascii\n");
+               print("%s\n", mime ? PLAIN : "Ascii");
        else if (guess == Feascii)
-               print(mime ? PLAIN : "extended ascii\n");
+               print("%s\n", mime ? PLAIN : "extended ascii");
        else if (guess == Flatin)
-               print(mime ? PLAIN : "latin ascii\n");
+               print("%s\n", mime ? PLAIN : "latin ascii");
        else if (guess == Futf && utf_count() < 4)
                print_utf();
-       else print(mime ? OCTET : "binary\n");
+       else print("%s\n", mime ? OCTET : "binary");
 }
 
 void
@@ -500,7 +520,7 @@ print_utf(void)
        int i, printed, j;
 
        if(mime){
-               print(PLAIN);
+               print("%s\n", PLAIN);
                return;
        }
        if (chkascii()) {
@@ -582,37 +602,41 @@ struct Filemagic {
  * when read from a file.
  */
 Filemagic long0tab[] = {
-       0xF16DF16D,     0xFFFFFFFF,     "pac1 audio file\n",    OCTET,
+       0xF16DF16D,     0xFFFFFFFF,     "pac1 audio file",      OCTET,
        /* "pac1" */
-       0x31636170,     0xFFFFFFFF,     "pac3 audio file\n",    OCTET,
+       0x31636170,     0xFFFFFFFF,     "pac3 audio file",      OCTET,
        /* "pXc2 */
-       0x32630070,     0xFFFF00FF,     "pac4 audio file\n",    OCTET,
-       0xBA010000,     0xFFFFFFFF,     "mpeg system stream\n", OCTET,
-       0x43614c66,     0xFFFFFFFF,     "FLAC audio file\n",    "audio/flac",
-       0x30800CC0,     0xFFFFFFFF,     "inferno .dis executable\n", OCTET,
-       0x04034B50,     0xFFFFFFFF,     "zip archive\n", "application/zip",
-       070707,         0xFFFF,         "cpio archive\n", "application/x-cpio",
-       0x2F7,          0xFFFF,         "tex dvi\n", "application/dvi",
-       0xfaff,         0xfeff,         "mp3 audio\n",  "audio/mpeg",
-       0xfeff0000,     0xffffffff,     "utf-32be\n",   "text/plain charset=utf-32be",
-       0xfffe,         0xffffffff,     "utf-32le\n",   "text/plain charset=utf-32le",
-       0xfeff,         0xffff,         "utf-16be\n",   "text/plain charset=utf-16be",
-       0xfffe,         0xffff,         "utf-16le\n",   "text/plain charset=utf-16le",
+       0x32630070,     0xFFFF00FF,     "pac4 audio file",      OCTET,
+       0xBA010000,     0xFFFFFFFF,     "mpeg system stream",   OCTET,
+       0x43614c66,     0xFFFFFFFF,     "FLAC audio file",      "audio/flac",
+       0x30800CC0,     0xFFFFFFFF,     "inferno .dis executable", OCTET,
+       0x04034B50,     0xFFFFFFFF,     "zip archive", "application/zip",
+       070707,         0xFFFF,         "cpio archive", "application/x-cpio",
+       0x2F7,          0xFFFF,         "tex dvi", "application/dvi",
+       0xfaff,         0xfeff,         "mp3 audio",    "audio/mpeg",
+       0xf0ff,         0xf6ff,         "aac audio",    "audio/mpeg",
        /* 0xfeedface: this could alternately be a Next Plan 9 boot image */
-       0xcefaedfe,     0xFFFFFFFF,     "32-bit power Mach-O executable\n", OCTET,
+       0xcefaedfe,     0xFFFFFFFF,     "32-bit power Mach-O executable", OCTET,
        /* 0xfeedfacf */
-       0xcffaedfe,     0xFFFFFFFF,     "64-bit power Mach-O executable\n", OCTET,
+       0xcffaedfe,     0xFFFFFFFF,     "64-bit power Mach-O executable", OCTET,
        /* 0xcefaedfe */
-       0xfeedface,     0xFFFFFFFF,     "386 Mach-O executable\n", OCTET,
+       0xfeedface,     0xFFFFFFFF,     "386 Mach-O executable", OCTET,
        /* 0xcffaedfe */
-       0xfeedfacf,     0xFFFFFFFF,     "amd64 Mach-O executable\n", OCTET,
+       0xfeedfacf,     0xFFFFFFFF,     "amd64 Mach-O executable", OCTET,
        /* 0xcafebabe */
-       0xbebafeca,     0xFFFFFFFF,     "Mach-O universal executable\n", OCTET,
+       0xbebafeca,     0xFFFFFFFF,     "Mach-O universal executable", OCTET,
        /*
         * venti & fossil magic numbers are stored big-endian on disk,
         * thus the numbers appear reversed in this table.
         */
-       0xad4e5cd1,     0xFFFFFFFF,     "venti arena\n", OCTET,
+       0xad4e5cd1,     0xFFFFFFFF,     "venti arena", OCTET,
+       0x2bb19a52,     0xFFFFFFFF,     "paq archive", OCTET,
+       0x1a53454e,     0xFFFFFFFF,     "NES ROM", OCTET,
+       /* tcpdump pcap file */
+       0xa1b2c3d4,     0xFFFFFFFF,     "pcap file",    "application/vnd.tcpdump.pcap",
+       0xd4c3b2a1,     0xFFFFFFFF,     "pcap file",    "application/vnd.tcpdump.pcap",
+       0xa1b23c4d,     0xFFFFFFFF,     "pcap file",    "application/vnd.tcpdump.pcap",
+       0x4d3cb2a1,     0xFFFFFFFF,     "pcap file",    "application/vnd.tcpdump.pcap",
 };
 
 int
@@ -622,7 +646,7 @@ filemagic(Filemagic *tab, int ntab, ulong x)
 
        for(i=0; i<ntab; i++)
                if((x&tab[i].mask) == tab[i].x){
-                       print(mime ? tab[i].mime : tab[i].desc);
+                       print("%s\n", mime ? tab[i].mime : tab[i].desc);
                        return 1;
                }
        return 0;
@@ -649,10 +673,12 @@ Fileoffmag longofftab[] = {
         * venti & fossil magic numbers are stored big-endian on disk,
         * thus the numbers appear reversed in this table.
         */
-       256*1024, 0xe7a5e4a9, 0xFFFFFFFF, "venti arenas partition\n", OCTET,
-       256*1024, 0xc75e5cd1, 0xFFFFFFFF, "venti index section\n", OCTET,
-       128*1024, 0x89ae7637, 0xFFFFFFFF, "fossil write buffer\n", OCTET,
-       4,        0x31647542, 0xFFFFFFFF, "OS X finder properties\n", OCTET,
+       256*1024, 0xe7a5e4a9, 0xFFFFFFFF, "venti arenas partition", OCTET,
+       256*1024, 0xc75e5cd1, 0xFFFFFFFF, "venti index section", OCTET,
+       128*1024, 0x89ae7637, 0xFFFFFFFF, "fossil write buffer", OCTET,
+       4,        0x31647542, 0xFFFFFFFF, "OS X finder properties", OCTET,
+       0x100,    0x41474553, 0xFFFFFFFF, "SEGA ROM", OCTET,
+       0x1fc,    0xAA550000, 0xFFFF0000, "bootable disk image", OCTET,
 };
 
 int
@@ -670,7 +696,7 @@ fileoffmagic(Fileoffmag *tab, int ntab)
                        continue;
                x = LENDIAN(buf);
                if((x&tp->mask) == tp->x){
-                       print(mime? tp->mime: tp->desc);
+                       print("%s\n", mime ? tp->mime : tp->desc);
                        return 1;
                }
        }
@@ -690,7 +716,7 @@ isexec(void)
 
        seek(fd, 0, 0);         /* reposition to start of file */
        if(crackhdr(fd, &f)) {
-               print(mime ? OCTET : "%s\n", f.name);
+               print("%s\n", mime ? OCTET : f.name);
                return 1;
        }
        return 0;
@@ -776,11 +802,11 @@ struct    FILE_STRING
        "\x1f\x9d",             "compressed",                   2,      "application/x-compress",
        "\x1f\x8b",             "gzip compressed",              2,      "application/x-gzip",
        "BZh",                  "bzip2 compressed",             3,      "application/x-bzip2",
-       "!<arch>\n__.SYMDEF",   "archive random library",       16,     "application/octet-stream",
-       "!<arch>\n",            "archive",                      8,      "application/octet-stream",
-       "070707",               "cpio archive - ascii header",  6,      "application/octet-stream",
-       "#!/bin/rc",            "rc executable file",           9,      "text/plain",
-       "#!/bin/sh",            "sh executable file",           9,      "text/plain",
+       "!<arch>\n__.SYMDEF",   "archive random library",       16,     OCTET,
+       "!<arch>\n",            "archive",                      8,      OCTET,
+       "070707",               "cpio archive - ascii header",  6,      OCTET,
+       "#!/bin/rc",            "rc executable file",           9,      PLAIN,
+       "#!/bin/sh",            "sh executable file",           9,      PLAIN,
        "%!",                   "postscript",                   2,      "application/postscript",
        "\004%!",               "postscript",                   3,      "application/postscript",
        "x T post",             "troff output for post",        8,      "application/troff",
@@ -788,6 +814,7 @@ struct      FILE_STRING
        "x T utf",              "troff output for UTF",         7,      "application/troff",
        "x T 202",              "troff output for 202",         7,      "application/troff",
        "x T aps",              "troff output for aps",         7,      "application/troff",
+       "x T ",                 "troff output",                 4,      "application/troff",
        "GIF",                  "GIF image",                    3,      "image/gif",
        "\0PC Research, Inc\0", "ghostscript fax file",         18,     "application/ghostscript",
        "%PDF",                 "PDF",                          4,      "application/pdf",
@@ -806,13 +833,20 @@ struct    FILE_STRING
        "BM",                   "bmp",                          2,      "image/bmp", 
        "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1",     "microsoft office document",    8,      "application/doc",
        "<MakerFile ",          "FrameMaker file",              11,     "application/framemaker",
-       "\033E\033",    "HP PCL printer data",          3,      OCTET,
-       "\033%-12345X", "HPJCL file",           9,      "application/hpjcl",
+       "\033E\033",            "HP PCL printer data",          3,      OCTET,
+       "\033&",                "HP PCL printer data",          2,      OCTET,
+       "\033%-12345X",         "HPJCL file",           9,      "application/hpjcl",
+       "\033Lua",              "Lua bytecode",         4,      OCTET,
        "ID3",                  "mp3 audio with id3",   3,      "audio/mpeg",
        "OggS",                 "ogg audio",            4,      "audio/ogg",
+       ".snd",                 "sun audio",            4,      "audio/basic",
        "\211PNG",              "PNG image",            4,      "image/png",
-       "P3\n",                 "ppm",                          3,      "image/ppm",
-       "P6\n",                 "ppm",                          3,      "image/ppm",
+       "P1\n",                 "ppm",                  3,      "image/ppm",
+       "P2\n",                 "ppm",                  3,      "image/ppm",
+       "P3\n",                 "ppm",                  3,      "image/ppm",
+       "P4\n",                 "ppm",                  3,      "image/ppm",
+       "P5\n",                 "ppm",                  3,      "image/ppm",
+       "P6\n",                 "ppm",                  3,      "image/ppm",
        "/* XPM */\n",  "xbm",                          10,     "image/xbm",
        ".HTML ",               "troff -ms input",      6,      "text/troff",
        ".LP",                  "troff -ms input",      3,      "text/troff",
@@ -826,18 +860,32 @@ struct    FILE_STRING
        ".if",                  "troff input",          3,      "text/troff",
        ".nr",                  "troff input",          3,      "text/troff",
        ".tr",                  "troff input",          3,      "text/troff",
-       "vac:",                 "venti score",          4,      "text/plain",
+       "vac:",                 "venti score",          4,      PLAIN,
        "-----BEGIN CERTIFICATE-----\n",
-                               "pem certificate",      -1,     "text/plain",
+                               "pem certificate",      -1,     PLAIN,
        "-----BEGIN TRUSTED CERTIFICATE-----\n",
-                               "pem trusted certificate", -1,  "text/plain",
+                               "pem trusted certificate", -1,  PLAIN,
        "-----BEGIN X509 CERTIFICATE-----\n",
-                               "pem x.509 certificate", -1,    "text/plain",
-       "subject=/C=",          "pem certificate with header", -1, "text/plain",
+                               "pem x.509 certificate", -1,    PLAIN,
+       "subject=/C=",          "pem certificate with header", -1, PLAIN,
        "process snapshot ",    "process snapshot",     -1,     "application/snapfs",
        "d8:announce",          "torrent file",         11,     "application/x-bittorrent",
        "[playlist]",           "playlist",             10,     "application/x-scpls",
        "#EXTM3U",              "playlist",             7,      "audio/x-mpegurl",
+       "BEGIN:VCARD\r\n",      "vCard",                13,     "text/directory;profile=vcard",
+       "BEGIN:VCARD\n",        "vCard",                12,     "text/directory;profile=vcard",
+       "AT&T",                 "DjVu document",        4,      "image/vnd.djvu",
+       "Extended module: ",    "XM audio",             17,     "audio/xm",
+       "MThd",                 "midi audio",           4,      "audio/midi",
+       "MUS\x1a",              "mus audio",            4,      "audio/mus",
+       "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+       "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+       "\x00\x00\x00\xbb\x11\x22\x00\x44\xff\xff\xff\xff\xff\xff\xff\xff"
+       "\xaa\x99\x55\x66", "Xilinx bitstream (not byteswappped)", 52, OCTET,
+       "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+       "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+       "\xbb\x00\x00\x00\x44\x00\x22\x11\xff\xff\xff\xff\xff\xff\xff\xff"
+       "\x66\x55\x99\xaa", "Xilinx bitstream (byteswappped)", 52, OCTET,
        0,0,0,0
 };
 
@@ -852,10 +900,7 @@ istring(void)
                if(l == -1)
                        l = strlen(p->key);
                if(nbuf >= l && memcmp(buf, p->key, l) == 0) {
-                       if(mime)
-                               print("%s\n", p->mime);
-                       else
-                               print("%s\n", p->filetype);
+                       print("%s\n", mime ? p->mime : p->filetype);
                        return 1;
                }
        }
@@ -864,7 +909,7 @@ istring(void)
                        if(buf[i] == '\n')
                                break;
                if(mime)
-                       print(OCTET);
+                       print("%s\n", OCTET);
                else
                        print("%.*s picture\n", utfnlen((char*)buf+5, i-5), (char*)buf+5);
                return 1;
@@ -878,6 +923,7 @@ struct offstr
        struct FILE_STRING;
 } offstrs[] = {
        32*1024, "\001CD001\001",       "ISO9660 CD image",     7,      "application/x-iso9660-image",
+       32*4, "DICM",   "DICOM medical imaging data",   4,      "application/dicom",
        0, 0, 0, 0, 0
 };
 
@@ -896,10 +942,7 @@ isoffstr(void)
                if (readn(fd, buf, n) != n)
                        continue;
                if(memcmp(buf, p->key, n) == 0) {
-                       if(mime)
-                               print("%s\n", p->mime);
-                       else
-                               print("%s\n", p->filetype);
+                       print("%s\n", mime ? p->mime : p->filetype);
                        return 1;
                }
        }
@@ -920,8 +963,7 @@ iff(void)
                else if (strncmp((char*)buf+8, "AVI ", 4) == 0)
                        print("%s\n", mime? "video/avi": "avi video");
                else
-                       print("%s\n", mime? "application/octet-stream":
-                               "riff file");
+                       print("%s\n", mime? OCTET : "riff file");
                return 1;
        }
        return 0;
@@ -944,6 +986,25 @@ char*      html_string[] = {
        0,
 };
 
+int
+isudiff(void)
+{
+       char *p;
+
+       p = (char*)buf;
+       if((p = strstr(p, "diff")) != nil)
+       if((p = strchr(p, '\n')) != nil)
+       if(strncmp(++p, "--- ", 4) == 0)
+       if((p = strchr(p, '\n')) != nil)
+       if(strncmp(++p, "+++ ", 4) == 0)
+       if((p = strchr(p, '\n')) != nil)
+       if(strncmp(++p, "@@ ", 3) == 0){
+               print("%s\n", mime ? "text/plain" : "unified diff output");
+               return 1;
+       }
+       return 0;
+}
+
 int
 ishtml(void)
 {
@@ -970,7 +1031,7 @@ ishtml(void)
                                p += n;
                                if(p < buf+nbuf && strchr("\t\r\n />", *p)){
                                        if(++count > 2) {
-                                               print(mime ? "text/html\n" : "HTML file\n");
+                                               print("%s\n", mime ? "text/html" : "HTML file");
                                                return 1;
                                        }
                                }
@@ -1028,7 +1089,7 @@ isrfc822(void)
                p = q+1;
        }
        if(count >= 3){
-               print(mime ? "message/rfc822\n" : "email file\n");
+               print("%s\n", mime ? "message/rfc822" : "email file");
                return 1;
        }
        return 0;
@@ -1045,7 +1106,7 @@ ismbox(void)
                return 0;
        *q = 0;
        if(strncmp(p, "From ", 5) == 0 && strstr(p, " remote from ") == nil){
-               print(mime ? "text/plain\n" : "mail box\n");
+               print("%s\n", mime ? PLAIN : "mail box");
                return 1;
        }
        *q = '\n';
@@ -1066,7 +1127,7 @@ iscint(void)
        if(type < 0)
                return 0;
        if(mime)
-               print(OCTET);
+               print("%s\n", OCTET);
        else
                print("%s intermediate\n", name);
        return 1;
@@ -1099,7 +1160,7 @@ isc(void)
 
 yes:
        if(mime){
-               print(PLAIN);
+               print("%s\n", PLAIN);
                return 1;
        }
        if(wfreq[Alword] > 0)
@@ -1112,29 +1173,95 @@ yes:
 int
 islimbo(void)
 {
-
        /*
         * includes
         */
        if(wfreq[Lword] < 4)
                return 0;
-       print(mime ? PLAIN : "limbo program\n");
+       print("%s\n", mime ? PLAIN : "limbo program");
        return 1;
 }
 
 int
 isas(void)
 {
-
        /*
         * includes
         */
        if(wfreq[Aword] < 2)
                return 0;
-       print(mime ? PLAIN : "as program\n");
+       print("%s\n", mime ? PLAIN : "as program");
        return 1;
 }
 
+int
+istga(void)
+{
+       uchar *p;
+
+       p = buf;
+       if(nbuf < 18)
+               return 0;
+       if((p[12] | p[13]<<8) == 0)     /* width */
+               return 0;
+       if((p[14] | p[15]<<8) == 0)     /* height */
+               return 0;
+       if(p[16] != 8 && p[16] != 15 && p[16] != 16 && p[16] != 24 && p[16] != 32)      /* bpp */
+               return 0;
+       if(((p[2]|(1<<3)) & (~3)) != (1<<3))    /* rle flag */
+               return 0;
+       if(p[1] == 0){  /* non color-mapped */
+               if((p[2]&3) != 2 && (p[2]&3) != 3)      
+                       return 0;
+               if((p[5] | p[6]<<8) != 0)       /* palette length */
+                       return 0;
+       } else
+       if(p[1] == 1){  /* color-mapped */
+               if((p[2]&3) != 1 || p[7] == 0)  
+                       return 0;
+               if((p[5] | p[6]<<8) == 0)       /* palette length */
+                       return 0;
+       } else
+               return 0;
+       print("%s\n", mime ? "image/tga" : "targa image");
+       return 1;
+}
+
+int
+ismp3(void)
+{
+       uchar *p, *e;
+
+       p = buf;
+       e = p + nbuf-1;
+       while((p < e) && (p = memchr(p, 0xFF, e - p))){
+               if((p[1] & 0xFE) == 0xFA){
+                       print("%s\n", mime ? "audio/mpeg" : "mp3 audio");
+                       return 1;
+               }
+               p++;
+       }
+       return 0;
+}
+
+int
+ismp4(void)
+{
+       if(nbuf <= 12)
+               return 0;
+       if(memcmp(&buf[4], "ftyp", 4) != 0)
+               return 0;
+       if(memcmp(&buf[8], "isom", 4) == 0){
+               print("%s\n", mime ? "video/mp4" : "mp4 video");
+               return 1;
+       }
+       if(memcmp(&buf[8], "M4A ", 4) == 0){
+               print("%s\n", mime ? "audio/m4a" : "m4a audio");
+               return 1;
+       }
+       return 0;
+}
+
 /*
  * low entropy means encrypted
  */
@@ -1156,18 +1283,18 @@ ismung(void)
        cs /= 8.;
        if(cs <= 24.322) {
                if(buf[0]==0x1f && buf[1]==0x9d)
-                       print(mime ? "application/x-compress" : "compressed\n");
+                       print("%s\n", mime ? "application/x-compress" : "compressed");
                else
                if(buf[0]==0x1f && buf[1]==0x8b)
-                       print(mime ? "application/x-gzip" : "gzip compressed\n");
+                       print("%s\n", mime ? "application/x-gzip" : "gzip compressed");
                else
                if(buf[0]=='B' && buf[1]=='Z' && buf[2]=='h')
-                       print(mime ? "application/x-bzip2" : "bzip2 compressed\n");
+                       print("%s\n", mime ? "application/x-bzip2" : "bzip2 compressed");
                else
                if(buf[0]==0x78 && buf[1]==0x9c)
-                       print(mime ? "application/x-deflate" : "zlib compressed\n");
+                       print("%s\n", mime ? "application/x-deflate" : "zlib compressed");
                else
-                       print(mime ? OCTET : "encrypted\n");
+                       print("%s\n", mime ? OCTET : "encrypted");
                return 1;
        }
        return 0;
@@ -1222,7 +1349,7 @@ isenglish(void)
                rare += cfreq[tolower(*p)];
        }
        if(vow*5 >= nbuf-cfreq[' '] && comm >= 10*rare) {
-               print(mime ? PLAIN : "English text\n");
+               print("%s\n", mime ? PLAIN : "English text");
                return 1;
        }
        return 0;
@@ -1234,28 +1361,18 @@ isenglish(void)
  */
 #define        P9BITLEN        12
 int
-p9bitnum(uchar *bp)
+p9bitnum(char *s, int *v)
 {
-       int n, c, len;
+       char *es;
 
-       len = P9BITLEN;
-       while(*bp == ' ') {
-               bp++;
-               len--;
-               if(len <= 0)
-                       return -1;
-       }
-       n = 0;
-       while(len > 1) {
-               c = *bp++;
-               if(!isdigit(c))
-                       return -1;
-               n = n*10 + c-'0';
-               len--;
-       }
-       if(*bp != ' ')
+       if(s[P9BITLEN-1] != ' ')
                return -1;
-       return n;
+       s[P9BITLEN-1] = '\0';
+       *v = strtol(s, &es, 10);
+       s[P9BITLEN-1] = ' ';
+       if(es != &s[P9BITLEN-1])
+               return -1;
+       return 0;
 }
 
 int
@@ -1271,13 +1388,18 @@ depthof(char *s, int *newp)
        if(s == es)
                return -1;
        if('0'<=*s && *s<='9')
-               return 1<<strtol(s, 0, 0);
+               return 1<<strtol(s, nil, 0);
 
        *newp = 1;
        d = 0;
        while(s<es && *s!=' '){
-               s++;                    /* skip letter */
-               d += strtoul(s, &s, 10);
+               if(strchr("rgbkamx", *s) == nil)
+                       return -1;
+               s++;
+               if('0'<=*s && *s<='9')
+                       d += strtoul(s, &s, 10);
+               else
+                       return -1;
        }
 
        if(d % 8 == 0 || 8 % d == 0)
@@ -1290,43 +1412,41 @@ int
 isp9bit(void)
 {
        int dep, lox, loy, hix, hiy, px, new, cmpr;
-       ulong t;
        long len;
        char *newlabel;
        uchar *cp;
 
        cp = buf;
        cmpr = 0;
-       newlabel = "old ";
-
        if(memcmp(cp, "compressed\n", 11) == 0) {
                cmpr = 1;
                cp = buf + 11;
        }
 
-       dep = depthof((char*)cp + 0*P9BITLEN, &new);
-       if(new)
-               newlabel = "";
-       lox = p9bitnum(cp + 1*P9BITLEN);
-       loy = p9bitnum(cp + 2*P9BITLEN);
-       hix = p9bitnum(cp + 3*P9BITLEN);
-       hiy = p9bitnum(cp + 4*P9BITLEN);
-       if(dep < 0 || lox < 0 || loy < 0 || hix < 0 || hiy < 0)
+       if((dep = depthof((char*)cp + 0*P9BITLEN, &new)) < 0)
+               return 0;
+       newlabel = new ? "" : "old ";
+       if(p9bitnum((char*)cp + 1*P9BITLEN, &lox) < 0)
+               return 0;
+       if(p9bitnum((char*)cp + 2*P9BITLEN, &loy) < 0)
+               return 0;
+       if(p9bitnum((char*)cp + 3*P9BITLEN, &hix) < 0)
+               return 0;
+       if(p9bitnum((char*)cp + 4*P9BITLEN, &hiy) < 0)
+               return 0;
+
+       hix -= lox;
+       hiy -= loy;
+       if(hix <= 0 || hiy <= 0)
                return 0;
 
        if(dep < 8){
                px = 8/dep;             /* pixels per byte */
                /* set l to number of bytes of data per scan line */
-               if(lox >= 0)
-                       len = (hix+px-1)/px - lox/px;
-               else{                   /* make positive before divide */
-                       t = (-lox)+px-1;
-                       t = (t/px)*px;
-                       len = (t+hix+px-1)/px;
-               }
+               len = (hix+px-1)/px;
        }else
-               len = (hix-lox)*dep/8;
-       len *= hiy - loy;               /* col length */
+               len = hix*dep/8;
+       len *= hiy;                     /* col length */
        len += 5 * P9BITLEN;            /* size of initial ascii */
 
        /*
@@ -1336,8 +1456,8 @@ isp9bit(void)
         * for subfont, the subfont header should follow immediately.
         */
        if (cmpr) {
-               print(mime ? "image/p9bit\n" : "Compressed %splan 9 image or subfont, depth %d\n",
-                       newlabel, dep);
+               print(mime ? "image/p9bit\n" : "Compressed %splan 9 image or subfont, depth %d, size %dx%d\n",
+                       newlabel, dep, hix, hiy);
                return 1;
        }
        /*
@@ -1346,11 +1466,13 @@ isp9bit(void)
         */
        if (len != 0 && (mbuf->length == 0 || mbuf->length == len ||
            mbuf->length > len && mbuf->length < len+P9BITLEN)) {
-               print(mime ? "image/p9bit\n" : "%splan 9 image, depth %d\n", newlabel, dep);
+               print(mime ? "image/p9bit\n" : "%splan 9 image, depth %d, size %dx%d\n",
+                       newlabel, dep, hix, hiy);
                return 1;
        }
        if (p9subfont(buf+len)) {
-               print(mime ? "image/p9bit\n" : "%ssubfont file, depth %d\n", newlabel, dep);
+               print(mime ? "image/p9bit\n" : "%ssubfont file, depth %d, size %dx%d\n",
+                       newlabel, dep, hix, hiy);
                return 1;
        }
        return 0;
@@ -1365,16 +1487,15 @@ p9subfont(uchar *p)
        if (p+3*P9BITLEN > buf+sizeof(buf))
                return 1;
 
-       n = p9bitnum(p + 0*P9BITLEN);   /* char count */
-       if (n < 0)
+       if (p9bitnum((char*)p + 0*P9BITLEN, &n) < 0)    /* char count */
                return 0;
-       h = p9bitnum(p + 1*P9BITLEN);   /* height */
-       if (h < 0)
+       if (p9bitnum((char*)p + 1*P9BITLEN, &h) < 0)    /* height */
                return 0;
-       a = p9bitnum(p + 2*P9BITLEN);   /* ascent */
-       if (a < 0)
+       if (p9bitnum((char*)p + 2*P9BITLEN, &a) < 0)    /* ascent */
                return 0;
-       return 1;
+       if(n > 0 && h > 0 && a >= 0)
+               return 1;
+       return 0;
 }
 
 #define        WHITESPACE(c)           ((c) == ' ' || (c) == '\t' || (c) == '\n')
@@ -1421,7 +1542,7 @@ isp9font(void)
                }
        }
        if (i) {
-               print(mime ? "text/plain\n" : "font file\n");
+               print("%s\n", mime ? PLAIN : "font file");
                return 1;
        }
        return 0;
@@ -1539,7 +1660,7 @@ iself(void)
                        print("%s ELF %s\n", p, t);
                }
                else
-                       print("application/x-elf-executable");
+                       print("application/x-elf-executable\n");
                return 1;
        }
 
@@ -1583,4 +1704,3 @@ isface(void)
                print("face image depth %d\n", ldepth);
        return 1;
 }
-