xref: /plan9/sys/src/cmd/aux/cddb.c (revision fd87a217ca5963330c6d5a07235e643d3509b52f)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ctype.h>
5 
6 char *server = "freedb.freedb.org";
7 
8 int debug;
9 #define DPRINT if(debug)fprint
10 int tflag;
11 int Tflag;
12 
13 typedef struct Track Track;
14 struct Track {
15 	int n;
16 	char *title;
17 };
18 
19 enum {
20 	MTRACK = 64,
21 };
22 
23 typedef struct Toc Toc;
24 struct Toc {
25 	ulong diskid;
26 	int ntrack;
27 	char *title;
28 	Track track[MTRACK];
29 };
30 
31 void*
emalloc(uint n)32 emalloc(uint n)
33 {
34 	void *p;
35 
36 	p = malloc(n);
37 	if(p == nil)
38 		sysfatal("can't malloc: %r");
39 	memset(p, 0, n);
40 	return p;
41 }
42 
43 char*
estrdup(char * s)44 estrdup(char *s)
45 {
46 	char *t;
47 
48 	t = emalloc(strlen(s)+1);
49 	strcpy(t, s);
50 	return t;
51 }
52 
53 static void
dumpcddb(Toc * t)54 dumpcddb(Toc *t)
55 {
56 	int i, n, s;
57 
58 	print("title	%s\n", t->title);
59 	for(i=0; i<t->ntrack; i++){
60 		if(tflag){
61 			n = t->track[i+1].n;
62 			if(i == t->ntrack-1)
63 				n *= 75;
64 			s = (n - t->track[i].n)/75;
65 			print("%d\t%s\t%d:%2.2d\n", i+1, t->track[i].title, s/60, s%60);
66 		}
67 		else
68 			print("%d\t%s\n", i+1, t->track[i].title);
69 	}
70 	if(Tflag){
71 		s = t->track[i].n;
72 		print("Total time: %d:%2.2d\n", s/60, s%60);
73 	}
74 }
75 
76 char*
append(char * a,char * b)77 append(char *a, char *b)
78 {
79 	char *c;
80 
81 	c = emalloc(strlen(a)+strlen(b)+1);
82 	strcpy(c, a);
83 	strcat(c, b);
84 	return c;
85 }
86 
87 static int
cddbfilltoc(Toc * t)88 cddbfilltoc(Toc *t)
89 {
90 	int fd;
91 	int i;
92 	char *p, *q;
93 	Biobuf bin;
94 	char *f[10];
95 	int nf;
96 	char *id, *categ;
97 
98 	fd = dial(netmkaddr(server, "tcp", "888"), 0, 0, 0);
99 	if(fd < 0) {
100 		fprint(2, "%s: %s: cannot dial: %r\n", argv0, server);
101 		return -1;
102 	}
103 	Binit(&bin, fd, OREAD);
104 
105 	if((p=Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2) {
106 	died:
107 		close(fd);
108 		Bterm(&bin);
109 		fprint(2, "%s: error talking to cddb server %s\n",
110 			argv0, server);
111 		if(p) {
112 			p[Blinelen(&bin)-1] = 0;
113 			fprint(2, "%s: server says: %s\n", argv0, p);
114 		}
115 		return -1;
116 	}
117 
118 	fprint(fd, "cddb hello gre plan9 9cd 1.0\r\n");
119 	if((p = Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2)
120 		goto died;
121 
122 	/*
123 	 *	Protocol level 6 is the same as level 5 except that
124 	 *	the character set is now UTF-8 instead of ISO-8859-1.
125  	 */
126 	fprint(fd, "proto 6\r\n");
127 	DPRINT(2, "proto 6\r\n");
128 	if((p = Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2)
129 		goto died;
130 	p[Blinelen(&bin)-1] = 0;
131 	DPRINT(2, "cddb: %s\n", p);
132 
133 	fprint(fd, "cddb query %8.8lux %d", t->diskid, t->ntrack);
134 	DPRINT(2, "cddb query %8.8lux %d", t->diskid, t->ntrack);
135 	for(i=0; i<t->ntrack; i++) {
136 		fprint(fd, " %d", t->track[i].n);
137 		DPRINT(2, " %d", t->track[i].n);
138 	}
139 	fprint(fd, " %d\r\n", t->track[t->ntrack].n);
140 	DPRINT(2, " %d\r\n", t->track[t->ntrack].n);
141 
142 	if((p = Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2)
143 		goto died;
144 	p[Blinelen(&bin)-1] = 0;
145 	DPRINT(2, "cddb: %s\n", p);
146 	nf = tokenize(p, f, nelem(f));
147 	if(nf < 1)
148 		goto died;
149 
150 	switch(atoi(f[0])) {
151 	case 200:	/* exact match */
152 		if(nf < 3)
153 			goto died;
154 		categ = f[1];
155 		id = f[2];
156 		break;
157 	case 210:	/* exact matches */
158 	case 211:	/* close matches */
159 		if((p = Brdline(&bin, '\n')) == nil)
160 			goto died;
161 		if(p[0] == '.')	/* no close matches? */
162 			goto died;
163 		p[Blinelen(&bin)-1] = '\0';
164 
165 		/* accept first match */
166 		nf = tokenize(p, f, nelem(f));
167 		if(nf < 2)
168 			goto died;
169 		categ = f[0];
170 		id = f[1];
171 
172 		/* snarf rest of buffer */
173 		while(p[0] != '.') {
174 			if((p = Brdline(&bin, '\n')) == nil)
175 				goto died;
176 			p[Blinelen(&bin)-1] = '\0';
177 			DPRINT(2, "cddb: %s\n", p);
178 		}
179 		break;
180 	case 202: /* no match */
181 	default:
182 		goto died;
183 	}
184 
185 	t->title = "";
186 	for(i=0; i<t->ntrack; i++)
187 		t->track[i].title = "";
188 
189 	/* fetch results for this cd */
190 	fprint(fd, "cddb read %s %s\r\n", categ, id);
191 	do {
192 		if((p = Brdline(&bin, '\n')) == nil)
193 			goto died;
194 		q = p+Blinelen(&bin)-1;
195 		while(isspace(*q))
196 			*q-- = 0;
197 DPRINT(2, "cddb %s\n", p);
198 		if(strncmp(p, "DTITLE=", 7) == 0)
199 			t->title = append(t->title, p+7);
200 		else if(strncmp(p, "TTITLE", 6) == 0 && isdigit(p[6])) {
201 			i = atoi(p+6);
202 			if(i < t->ntrack) {
203 				p += 6;
204 				while(isdigit(*p))
205 					p++;
206 				if(*p == '=')
207 					p++;
208 
209 				t->track[i].title = append(t->track[i].title, estrdup(p));
210 			}
211 		}
212 	} while(*p != '.');
213 
214 	fprint(fd, "quit\r\n");
215 	close(fd);
216 	Bterm(&bin);
217 
218 	return 0;
219 }
220 
221 void
usage(void)222 usage(void)
223 {
224 	fprint(2, "usage: aux/cddb [-DTt] [-s server] query diskid n ...\n");
225 	exits("usage");
226 }
227 
228 void
main(int argc,char ** argv)229 main(int argc, char **argv)
230 {
231 	int i;
232 	Toc toc;
233 
234 	ARGBEGIN{
235 	case 'D':
236 		debug = 1;
237 		break;
238 	case 's':
239 		server = EARGF(usage());
240 		break;
241 	case 'T':
242 		Tflag = 1;
243 		/*FALLTHROUGH*/
244 	case 't':
245 		tflag = 1;
246 		break;
247 	}ARGEND
248 
249 	if(argc < 3 || strcmp(argv[0], "query") != 0)
250 		usage();
251 
252 	toc.diskid = strtoul(argv[1], 0, 16);
253 	toc.ntrack = atoi(argv[2]);
254 	if(argc != 3+toc.ntrack+1)
255 		sysfatal("argument count does not match given ntrack");
256 
257 	for(i=0; i<=toc.ntrack; i++)
258 		toc.track[i].n = atoi(argv[3+i]);
259 
260 	if(cddbfilltoc(&toc) < 0)
261 		exits("whoops");
262 
263 	dumpcddb(&toc);
264 	exits(nil);
265 }
266