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