diff options
Diffstat (limited to 'appl/cmd/cddb.b')
-rw-r--r-- | appl/cmd/cddb.b | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/appl/cmd/cddb.b b/appl/cmd/cddb.b new file mode 100644 index 00000000..6265ba24 --- /dev/null +++ b/appl/cmd/cddb.b @@ -0,0 +1,257 @@ +implement Cddb; + +# this is a near transliteration of Plan 9 source, and subject to the Lucent Public License 1.02 + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "string.m"; + str: String; + +include "arg.m"; + +Cddb: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +server := "freedb.freedb.org"; +debug := 0; +tflag := 0; +Tflag := 0; + +Track: adt { + n: int; + title: string; +}; + +Toc: adt { + diskid: int; + ntrack: int; + title: string; + track: array of Track; +}; + +DPRINT(fd: int, s: string) +{ + if(debug) + sys->fprint(sys->fildes(fd), "%s", s); +} + +dumpcddb(t: ref Toc) +{ + sys->print("title %s\n", t.title); + for(i:=0; i<t.ntrack; i++){ + if(tflag){ + n := t.track[i+1].n; + if(i == t.ntrack-1) + n *= 75; + s := (n - t.track[i].n)/75; + sys->print("%d\t%s\t%d:%2.2d\n", i+1, t.track[i].title, s/60, s%60); + } + else + sys->print("%d\t%s\n", i+1, t.track[i].title); + } + if(Tflag){ + s := t.track[i].n; + sys->print("Total time: %d:%2.2d\n", s/60, s%60); + } +} + +cddbfilltoc(t: ref Toc): int +{ + (ok, conn) := sys->dial(netmkaddr(server, "tcp", "888"), nil); + if(ok < 0) { + sys->fprint(sys->fildes(2), "cddb: cannot dial %s: %r\n", server); + return -1; + } + bin := bufio->fopen(conn.dfd, Bufio->OREAD); + + if((p:=getline(bin)) == nil || atoi(p)/100 != 2) + return died(p); + + sys->fprint(conn.dfd, "cddb hello gre plan9 9cd 1.0\r\n"); + if((p = getline(bin)) == nil || atoi(p)/100 != 2) + return died(p); + + # + # Protocol level 6 is the same as level 5 except that + # the character set is now UTF-8 instead of ISO-8859-1. + # + sys->fprint(conn.dfd, "proto 6\r\n"); + if((p = getline(bin)) == nil || atoi(p)/100 != 2) + return died(p); + DPRINT(2, sys->sprint("%s\n", p)); + + sys->fprint(conn.dfd, "cddb query %8.8ux %d", t.diskid, t.ntrack); + DPRINT(2, sys->sprint("cddb query %8.8ux %d", t.diskid, t.ntrack)); + for(i:=0; i<t.ntrack; i++) { + sys->fprint(conn.dfd, " %d", t.track[i].n); + DPRINT(2, sys->sprint(" %d", t.track[i].n)); + } + sys->fprint(conn.dfd, " %d\r\n", t.track[t.ntrack].n); + DPRINT(2, sys->sprint(" %d\r\n", t.track[t.ntrack].n)); + + if((p = getline(bin)) == nil || atoi(p)/100 != 2) + return died(p); + DPRINT(2, sys->sprint("cddb: %s\n", p)); + (nf, fl) := sys->tokenize(p, " \t\n\r"); + if(nf < 1) + return died(p); + + categ, id: string; + case atoi(hd fl) { + 200 => # exact match + if(nf < 3) + return died(p); + categ = hd tl fl; + id = hd tl tl fl; + 211 => # close matches + if((p = getline(bin)) == nil) + return died(nil); + if(p[0] == '.') # no close matches? + return died(nil); + + # accept first match + (nsf, f) := sys->tokenize(p, " \t\n\r"); + if(nsf < 2) + return died(p); + categ = hd f; + id = hd tl f; + + # snarf rest of buffer + while(p[0] != '.') { + if((p = getline(bin)) == nil) + return died(p); + DPRINT(2, sys->sprint("cddb: %s\n", p)); + } + 202 or # no match + * => + return died(p); + } + + t.title = ""; + for(i=0; i<t.ntrack; i++) + t.track[i].title = ""; + + # fetch results for this cd + sys->fprint(conn.dfd, "cddb read %s %s\r\n", categ, id); + do { + if((p = getline(bin)) == nil) + return died(nil); +DPRINT(2, sys->sprint("cddb %s\n", p)); + if(len p >= 7 && p[0:7] == "DTITLE=") + t.title += p[7:]; + else if(len p >= 6 && p[0:6] == "TTITLE"&& isdigit(p[6])) { + i = atoi(p[6:]); + if(i < t.ntrack) { + p = p[6:]; + while(p != nil && isdigit(p[0])) + p = p[1:]; + if(p != nil && p[0] == '=') + p = p[1:]; + t.track[i].title += p; + } + } + } while(p[0] != '.'); + + sys->fprint(conn.dfd, "quit\r\n"); + + return 0; +} + +getline(f: ref Iobuf): string +{ + p := f.gets('\n'); + while(p != nil && isspace(p[len p-1])) + p = p[0: len p-1]; + return p; +} + +isdigit(c: int): int +{ + return c>='0' && c <= '9'; +} + +isspace(c: int): int +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; +} + +died(p: string): int +{ + sys->fprint(sys->fildes(2), "cddb: error talking to server\n"); + if(p != nil){ + p = p[0:len p-1]; + sys->fprint(sys->fildes(2), "cddb: server says: %s\n", p); + } + return -1; +} + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + str = load String String->PATH; + + arg := load Arg Arg->PATH; + arg->init(args); + arg->setusage("cddb [-DTt] [-s server] query diskid n ..."); + while((o := arg->opt()) != 0) + case o { + 'D' => debug = 1; + 's' => server = arg->earg(); + 'T' => Tflag = 1; tflag = 1; + 't' => tflag = 1; + * => arg->usage(); + } + args = arg->argv(); + argc := len args; + if(argc < 3 || hd args != "query") + arg->usage(); + arg = nil; + + ntrack := atoi(hd tl tl args); + toc := ref Toc(str->toint(hd tl args, 16).t0, ntrack, nil, array[ntrack+1] of Track); + if(argc != 3+toc.ntrack+1){ + sys->fprint(sys->fildes(2), "cddb: argument count does not match given ntrack"); + raise "fail:error"; + } + args = tl tl tl args; + + for(i:=0; i<=toc.ntrack; i++){ # <=? + toc.track[i].n = atoi(hd args); + args = tl args; + } + + if(cddbfilltoc(toc) < 0) + raise "fail:whoops"; + + dumpcddb(toc); +} + +netmkaddr(addr, net, svc: string): string +{ + if(net == nil) + net = "net"; + (n, nil) := sys->tokenize(addr, "!"); + if(n <= 1){ + if(svc== nil) + return sys->sprint("%s!%s", net, addr); + return sys->sprint("%s!%s!%s", net, addr, svc); + } + if(svc == nil || n > 2) + return addr; + return sys->sprint("%s!%s", addr, svc); +} + +atoi(s: string): int +{ + return int s; +} |