#include "tag.h" char *filename; void warn(char *msg, ...) { char buf[1024], *p; va_list args; p = seprint(buf, buf+sizeof(buf), "%s %s: ", argv0, filename); va_start(args, msg); p = vseprint(p, buf+sizeof(buf), msg, args); p = seprint(p, buf+sizeof(buf), "\n"); va_end(args); write(2, buf, p-buf); } void usage(void) { fprint(2, "usage: %s [file]\n", argv0); exits("usage"); } void main(int argc, char **argv) { int fd; Biobuf bin, bout; uchar buf[1024], *p; Header hdr; Rechdr rhdr; int rtype; char err[ERRMAX]; ARGBEGIN { default: usage(); } ARGEND switch(argc) { case 0: fd = 0; filename = ""; break; case 1: if((fd = open(argv[0], OREAD)) < 0) sysfatal("open %s: %r", argv[0]); filename = argv[0]; break; default: usage(); SET(fd); } quotefmtinstall(); Binit(&bin, fd, OREAD); Binit(&bout, 1, OWRITE); readrec(&bin, buf, sizeof buf, HeaderSize, "header"); p = buf; gheader(&p, &hdr); if(strcmp(hdr.magic, "ID3") != 0) sysfatal("bad magic: %s", hdr.magic); if(hdr.version < 2 || hdr.version > 4) sysfatal("unrecognized version: %d", hdr.version); if(hdr.flags) { if(hdr.flags == 1<<7) warn("unsynchronized tag"); // FIXME -- actually implement this else sysfatal("unknown header flag: %.8b", hdr.flags); } while(Boffset(&bin) < HeaderSize + hdr.length) { readrec(&bin, buf, sizeof buf, hdr.version == 2 ? 6 : RecHdrSize, "rhdr"); // FIXME p = buf; grechdr(&p, &rhdr, &hdr); if(strcmp(rhdr.id, "") == 0) /* detect padding */ exits(nil); if(rhdr.length == 0) { // FIXME -- record parsers should handle this case warn("%s: discarding: zero-length record", rhdr.id); continue; } if((rtype = rectype(rhdr.id)) < 0) { warn("%s: discarding: unknown record type", rhdr.id); if(Bseek(&bin, rhdr.length, 1) < 0) sysfatal("seek: %r"); continue; } if(rhdr.length > sizeof buf) { warn("%s: discarding: too big for buffer: %ld/%ld", rhdr.id, rhdr.length, sizeof buf); if(Bseek(&bin, rhdr.length, 1) < 0) sysfatal("seek: %r"); continue; } readrec(&bin, buf, sizeof buf, rhdr.length, "record"); p = buf; switch(rtype) { case Rcomment: { Comment cmnt; if(gcomment(&p, &cmnt, rhdr.length) < 0) { rerrstr(err, sizeof err); if(strncmp(err, "discarding", 10) == 0) { warn("%s: %r", rhdr.id); break; } sysfatal("%s: %r", rhdr.id); } Bprint(&bout, "%s %q %q %q\n", rhdr.id, cmnt.language, cmnt.description, cmnt.comment); free(cmnt.description); free(cmnt.comment); } break; case Rtext: { Text txt; char **s; if(gtext(&p, &txt, rhdr.length) < 0) { rerrstr(err, sizeof err); if(strncmp(err, "discarding", 10) == 0) { warn("%s: %r", rhdr.id); break; } sysfatal("%s: %r", rhdr.id); } Bprint(&bout, "%s", rhdr.id); for(s = txt.strings; *s != nil; s++) { Bprint(&bout, " %q", *s); free(*s); } Bprint(&bout, "\n"); } break; case Runimplemented: warn("%s: discarding: umimplemented", rhdr.id); break; default: abort(); } } exits(nil); } // FIXME -- put filename in sysfatal