summaryrefslogtreecommitdiff
path: root/appl/cmd/cddb.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/cddb.b')
-rw-r--r--appl/cmd/cddb.b257
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;
+}