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