#include #include #include #include #include "blocker.h" #include "stringgetter.h" typedef enum { TGT_LEAF = 1, TGT_GROUP, } TGT; typedef struct tinfo TINFO; typedef struct tg TG; struct tinfo { unsigned int num; unsigned int start; unsigned int length; } ; struct tg { char *text; TGT type; union { int leaf; struct { int n; int a; TG **v; } group; } ; } ; static const char *cddir; static int ti_n; static int ti_a = 0; static TINFO *ti_v = 0; static TG *tgroot; static FILE *open_file(const char *cd, const char *fn, FILE *errf) { static char *p = 0; FILE *f; free(p); asprintf(&p,"%s/%s/%s",cddir,cd,fn); f = fopen(p,"r"); if (f == 0) { if (errf) fprintf(errf,"%s %s: %s\n",cd,fn,strerror(errno)); return(0); } return(f); } static int load_toc(FILE *f, FILE *errf) { STRINGGETTER *g; STRINGGOT line; unsigned int intracks; int n; int len; int beg; int ch; char junk; ti_n = 0; g = sg_init_pull(&sg_get_stdio,&sg_term_nl,f); intracks = 0; while (1) { line = sg_get(g); if (line.len < 0) break; if (!strncmp(line.str,"========",8)) { intracks |= 1; } else if (!strncmp(line.str,"TOTAL",5)) { intracks |= 2; } else if (intracks == 1) { if (sscanf(line.str,"%d.%d [%*d:%*d.%*d] %d [%*d:%*d.%*d] %*s %*s%d %c",&n,&len,&beg,&ch,&junk) == 4) { if (ti_n >= ti_a) ti_v = realloc(ti_v,(ti_a=ti_n+8)*sizeof(TINFO)); ti_v[ti_n++] = (TINFO){.num=n,.start=beg,.length=len}; } else { fprintf(errf,"bad toc line: %s\n",line.str); sg_done(g); return(-1); } } } sg_done(g); return(0); } static void tg_free(TG *tg) { int i; if (! tg) return; free(tg->text); switch (tg->type) { case TGT_LEAF: break; case TGT_GROUP: for (i=tg->group.n-1;i>=0;i--) tg_free(tg->group.v[i]); free(tg->group.v); break; default: abort(); break; } free(tg); } static int load_tracks(FILE *f, FILE *errf) { STRINGGETTER *g; STRINGGOT line; unsigned long int uli; char *ep; char *lp; int cp_n; static int cp_a = 0; static int *cp_v = 0; int rp_n; static int rp_a = 0; static int *rp_v = 0; TINFO *leaf; int i; TG *tgp; TG *tgn; g = sg_init_pull(&sg_get_stdio,&sg_term_nl,f); tg_free(tgroot); tgroot = malloc(sizeof(TG)); tgroot->text = 0; tgroot->type = TGT_GROUP; tgroot->group.n = 0; tgroot->group.a = 0; tgroot->group.v = 0; rp_n = 0; do <"fail"> { while (1) { line = sg_get(g); if (line.len < 0) break; uli = strtoul(line.str,&ep,0); if (ep != line.str) { int trk; trk = uli; if ((trk < 0) || (trk != uli)) { fprintf(errf,"track number out of range: %s\n",line.str); break <"fail">; } for (i=ti_n-1;i>=0;i--) if (trk == ti_v[i].num) break; if (i < 0) { fprintf(errf,"track number not in toc: %s\n",line.str); break <"fail">; } leaf = &ti_v[i]; } else { ep = line.str; leaf = 0; } cp_n = 0; while <"numbers"> (1) { switch (*ep) { case ' ': break <"numbers">; case '.': if (cp_n == 0) { fprintf(errf,"misplaced . at %d: %s\n",(int)(ep-line.str),line.str); break <"fail">; } break; case ':': if (cp_n) { fprintf(errf,"misplaced : at %d: %s\n",(int)(ep-line.str),line.str); break <"fail">; } break; default: fprintf(errf,"bad delimiter at %d: %s\n",(int)(ep-line.str),line.str); break <"fail">; } lp = ep + 1; uli = strtoul(lp,&ep,0); if (ep == lp) { fprintf(errf,"bad number at %d: %s\n",(int)(lp-line.str),line.str); break <"fail">; } i = uli; if ((i < 0) || (i != uli)) { fprintf(errf,"number out of range at %d: %s\n",(int)(lp-line.str),line.str); break <"fail">; } if (cp_n >= cp_a) cp_v = realloc(cp_v,(cp_a=cp_n+8)*sizeof(int)); cp_v[cp_n++] = i; } if (cp_n > rp_n+1) { fprintf(errf,"skipping nesting: %s\n",line.str); break <"fail">; } for (i=cp_n-2;i>=0;i--) { if (cp_v[i] != rp_v[i]) { fprintf(errf,"jumping nesting: %s\n",line.str); break <"fail">; } } tgn = malloc(sizeof(TG)); tgn->text = strdup(ep+1); if (leaf) { tgn->type = TGT_LEAF; tgn->leaf = leaf->num; } else { tgn->type = TGT_GROUP; tgn->group.n = 0; tgn->group.a = 0; tgn->group.v = 0; } tgp = tgroot; for (i=cp_n-2;i>=0;i--) { if ((tgp->type != TGT_GROUP) || (tgp->group.n < 1)) abort(); tgp = tgp->group.v[tgp->group.n-1]; } if (tgp->type != TGT_GROUP) abort(); if (tgp->group.n >= tgp->group.a) tgp->group.v = realloc(tgp->group.v,(tgp->group.a=tgp->group.n+8)*sizeof(*tgp->group.v)); tgp->group.v[tgp->group.n++] = tgn; if (cp_n >= rp_a) { free(rp_v); rp_v = malloc((rp_a=cp_n)*sizeof(int)); } rp_n = cp_n; for (i=cp_n-1;i>=0;i--) rp_v[i] = cp_v[i]; } sg_done(g); return(0); } while (0); sg_done(g); tg_free(tgroot); tgroot = 0; return(-1); } static int load_files(const char *cd, FILE *errf) { FILE *f; f = open_file(cd,"toc",errf); if (! f) return(-1); if (load_toc(f,errf) < 0) { fclose(f); return(-1); } fclose(f); f = open_file(cd,"tracks",0); if (f) { if (load_tracks(f,errf) < 0) { fclose(f); return(-1); } fclose(f); } return(0); } static void dump_toc(FILE *f) { int i; for (i=ti_n-1;i>=0;i--) { fprintf(f,"%u %u %u%c",ti_v[i].num,ti_v[i].start,ti_v[i].length,'\0'); } } static void dump_info(FILE *f) { void dump(TG *tg) { int i; switch (tg->type) { case TGT_LEAF: fprintf(f,"%d %s%c",tg->leaf,tg->text,'\0'); break; case TGT_GROUP: if (tg != tgroot) fprintf(f,"( %s%c",tg->text,'\0'); for (i=0;igroup.n;i++) dump(tg->group.v[i]); if (tg != tgroot) fprintf(f,")%c",'\0'); break; default: abort(); break; } } if (tgroot) dump(tgroot); } static void purge_info(void) { ti_n = 0; tg_free(tgroot); tgroot = 0; } int main(int, char **); int main(int ac, char **av) { STRINGGETTER *g; STRINGGOT cdname; FILE *f; if (ac != 2) exit(1); cddir = av[1]; g = sg_init_pull(&sg_get_stdio,&sg_term_nul,stdin); while (1) { cdname = sg_get(g); if (cdname.len < 0) break; f = fwrap_w_blocked(stdout); if (load_files(cdname.str,f) < 0) { fprintf(f,"CD load failed.\n"); fclose(f); continue; } fclose(f); f = fwrap_w_blocked(stdout); dump_toc(f); fclose(f); f = fwrap_w_blocked(stdout); dump_info(f); fclose(f); fflush(stdout); purge_info(); } exit(0); }