1 /* discdb.c
2 *
3 * Based on code from libcdaudio 0.5.0 (Copyright (C)1998 Tony Arcieri)
4 *
5 * All changes Copyright (c) 1998-2004 Mike Oliphant <grip@nostatic.org>
6 *
7 * http://www.nostatic.org/grip
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 * USA
23 *
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #if defined(__sun__)
30 #include <strings.h>
31 #endif
32 #include <ctype.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <string.h>
36 #include <pwd.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <curl/curl.h>
40 #include <curl/easy.h>
41 #include "cddev.h"
42 #include "discdb.h"
43 #include "grip_id3.h"
44 #include "common.h"
45 #include "config.h"
46
47 extern char *Program;
48 static char *StrConvertEncoding(char *str,const char *from,
49 const char *to,int max_len);
50 gboolean DiscDBUTF8Validate(const DiscInfo *disc,const DiscData *data);
51 static void DiscDBConvertEncoding(DiscInfo *disc,DiscData *data,
52 const char *from,const char *to);
53 static int DiscDBSum(int val);
54 static char *DiscDBReadLine(char **dataptr);
55 static GString *DiscDBMakeURI(DiscDBServer *server,DiscDBHello *hello,
56 char *cmd);
57 static char *DiscDBMakeRequest(DiscDBServer *server,DiscDBHello *hello,
58 char *cmd);
59 static void DiscDBProcessLine(char *inbuffer,DiscData *data,
60 int numtracks);
61 static void DiscDBWriteLine(char *header,int num,char *data,FILE *outfile,
62 char *encoding);
63
64 static char *discdb_genres[]={"unknown","blues","classical","country",
65 "data","folk","jazz","misc","newage",
66 "reggae","rock","soundtrack"};
67
68 /* DiscDB sum function */
69
DiscDBSum(int val)70 static int DiscDBSum(int val)
71 {
72 char *bufptr, buf[16];
73 int ret = 0;
74
75 g_snprintf(buf,16,"%lu",(unsigned long int)val);
76
77 for(bufptr = buf; *bufptr != '\0'; bufptr++)
78 ret += (*bufptr - '0');
79
80 return ret;
81 }
82
83 /* Produce DiscDB ID for CD currently in CD-ROM */
84
DiscDBDiscid(DiscInfo * disc)85 unsigned int DiscDBDiscid(DiscInfo *disc)
86 {
87 int index, tracksum = 0, discid;
88
89 if(!disc->have_info) CDStat(disc,TRUE);
90
91 for(index = 0; index < disc->num_tracks; index++)
92 tracksum += DiscDBSum(disc->track[index].start_pos.mins * 60 +
93 disc->track[index].start_pos.secs);
94
95 discid = (disc->length.mins * 60 + disc->length.secs) -
96 (disc->track[0].start_pos.mins * 60 + disc->track[0].start_pos.secs);
97
98 return (tracksum % 0xFF) << 24 | discid << 8 | disc->num_tracks;
99 }
100
101 /* Convert numerical genre to text */
102
DiscDBGenre(int genre)103 char *DiscDBGenre(int genre)
104 {
105 if(genre>11) return("unknown");
106
107 return discdb_genres[genre];
108 }
109
110 /* Convert genre from text form into an integer value */
111
DiscDBGenreValue(char * genre)112 int DiscDBGenreValue(char *genre)
113 {
114 int pos;
115
116 for(pos=0;pos<12;pos++)
117 if(!strcmp(genre,discdb_genres[pos])) return pos;
118
119 return 0;
120 }
121
122 /* Read a single line from the buffer and move the pointer along */
123
DiscDBReadLine(char ** dataptr)124 static char *DiscDBReadLine(char **dataptr)
125 {
126 char *data=*dataptr;
127 char *pos;
128
129 if(!data || !*data || *data=='.') {
130 *dataptr=NULL;
131
132 return NULL;
133 }
134
135 for(pos=data;*pos;pos++) {
136 if(*pos=='\n') {
137 *pos='\0';
138
139 Debug("[%s]\n",data);
140
141 *dataptr=pos+1;
142
143 return data;
144 }
145 }
146
147 Debug("[%s]\n",data);
148
149 *dataptr=NULL;
150
151 return data;
152 }
153
DiscDBMakeURI(DiscDBServer * server,DiscDBHello * hello,char * cmd)154 static GString *DiscDBMakeURI(DiscDBServer *server,DiscDBHello *hello,
155 char *cmd)
156 {
157 GString *uri;
158
159 uri=g_string_new(NULL);
160
161 g_string_printf(uri,"http://%s/%s?cmd=%s&hello=private+free.the.cddb+%s+%s"
162 "&proto=%d",
163 server->name,server->cgi_prog,cmd,
164 hello->hello_program,hello->hello_version,
165 hello->proto_version);
166
167 return uri;
168 }
169
DiscDBMakeRequest(DiscDBServer * server,DiscDBHello * hello,char * cmd)170 static char *DiscDBMakeRequest(DiscDBServer *server,DiscDBHello *hello,
171 char *cmd)
172 {
173 GString *uri;
174 GString *proxy=NULL;
175 GString *user=NULL;
176 char user_agent[256];
177 struct curl_slist *headers=NULL;
178 FILE *outfile;
179 char *data=NULL;
180 int success;
181 CURL *curl_handle;
182 long filesize;
183
184 curl_global_init(CURL_GLOBAL_ALL);
185 curl_handle=curl_easy_init();
186
187 if(curl_handle) {
188 if(server->use_proxy) {
189 proxy=g_string_new(NULL);
190
191 g_string_printf(proxy,"%s:%d",server->proxy->name,
192 server->proxy->port);
193
194 curl_easy_setopt(curl_handle,CURLOPT_PROXY,proxy->str);
195
196 if(*server->proxy->username) {
197
198 user=g_string_new(NULL);
199
200 g_string_printf(user,"%s:%s",server->proxy->username,
201 server->proxy->pswd);
202
203 curl_easy_setopt(curl_handle,CURLOPT_PROXYUSERPWD,user->str);
204 }
205 }
206
207 uri=DiscDBMakeURI(server,hello,cmd);
208
209 Debug(_("URI is %s\n"),uri->str);
210
211 curl_easy_setopt(curl_handle,CURLOPT_URL,uri->str);
212
213 g_snprintf(user_agent,256,"User-Agent: %s %s",
214 hello->hello_program,hello->hello_version);
215
216 headers=curl_slist_append(headers,user_agent);
217
218 curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER,headers);
219
220 outfile=tmpfile();
221
222 if(outfile) {
223 curl_easy_setopt(curl_handle,CURLOPT_FILE,outfile);
224
225 success=curl_easy_perform(curl_handle);
226
227 if(success==0) {
228 filesize=ftell(outfile);
229
230 rewind(outfile);
231
232 data=(char *)malloc(filesize+1);
233
234 if(data) {
235 if (fread(data,filesize,1,outfile) > 0)
236 data[filesize]='\0';
237 }
238 }
239
240 fclose(outfile);
241 }
242
243 curl_slist_free_all(headers);
244
245 curl_easy_cleanup(curl_handle);
246
247 g_string_free(uri,TRUE);
248
249 if(server->use_proxy) {
250 if (proxy)
251 g_string_free(proxy,TRUE);
252
253 if(*server->proxy->username) {
254 if (user)
255 g_string_free(user,TRUE);
256 }
257 }
258 }
259
260 curl_global_cleanup();
261
262 return data;
263 }
264
265
266 /* Query the DiscDB for the CD currently in the CD-ROM */
267
DiscDBDoQuery(DiscInfo * disc,DiscDBServer * server,DiscDBHello * hello,DiscDBQuery * query)268 gboolean DiscDBDoQuery(DiscInfo *disc,DiscDBServer *server,
269 DiscDBHello *hello,DiscDBQuery *query)
270 {
271 int index;
272 GString *cmd;
273 char *result,*inbuffer;
274 char *dataptr;
275
276 query->query_matches=0;
277
278 if(!disc->have_info) CDStat(disc,TRUE);
279
280 cmd=g_string_new(NULL);
281
282 g_string_printf(cmd,"cddb+query+%08x+%d",DiscDBDiscid(disc),
283 disc->num_tracks);
284
285 for(index=0;index<disc->num_tracks;index++)
286 g_string_append_printf(cmd,"+%d",disc->track[index].start_frame);
287
288 g_string_append_printf(cmd,"+%d",disc->length.mins*60 + disc->length.secs);
289
290 Debug(_("Query is [%s]\n"),cmd->str);
291
292 result=DiscDBMakeRequest(server,hello,cmd->str);
293
294 g_string_free(cmd,TRUE);
295
296 if(!result) {
297 return FALSE;
298 }
299
300 dataptr=result;
301
302 inbuffer=DiscDBReadLine(&dataptr);
303
304 switch(strtol(strtok(inbuffer," "),NULL,10)) {
305 /* 200 - exact match */
306 case 200:
307 query->query_match=MATCH_EXACT;
308 query->query_matches=1;
309
310 query->query_list[0].list_genre=
311 DiscDBGenreValue(g_strstrip(strtok(NULL," ")));
312
313 sscanf(g_strstrip(strtok(NULL," ")),"%xd",
314 &query->query_list[0].list_id);
315
316 DiscDBParseTitle(g_strstrip(strtok(NULL,"")),
317 query->query_list[0].list_title,
318 query->query_list[0].list_artist,"/");
319
320 break;
321 /* 210 - multiple exact matches */
322 case 210:
323 query->query_match=MATCH_EXACT;
324 query->query_matches=0;
325
326
327 while(query->query_matches < MAX_INEXACT_MATCHES &&
328 (inbuffer=DiscDBReadLine(&dataptr))) {
329 query->query_list[query->query_matches].list_genre=
330 DiscDBGenreValue(g_strstrip(strtok(inbuffer," ")));
331
332 sscanf(g_strstrip(strtok(NULL," ")),"%xd",
333 &query->query_list[query->query_matches].list_id);
334
335 DiscDBParseTitle(g_strstrip(strtok(NULL,"")),
336 query->query_list[query->query_matches].list_title,
337 query->query_list[query->query_matches].list_artist,
338 "/");
339
340 query->query_matches++;
341 }
342 break;
343 /* 211 - inexact match */
344 case 211:
345 query->query_match=MATCH_INEXACT;
346 query->query_matches=0;
347
348 while(query->query_matches < MAX_INEXACT_MATCHES &&
349 (inbuffer=DiscDBReadLine(&dataptr))) {
350 query->query_list[query->query_matches].list_genre=
351 DiscDBGenreValue(g_strstrip(strtok(inbuffer," ")));
352
353 sscanf(g_strstrip(strtok(NULL," ")),"%xd",
354 &query->query_list[query->query_matches].list_id);
355
356 DiscDBParseTitle(g_strstrip(strtok(NULL,"")),
357 query->query_list[query->query_matches].list_title,
358 query->query_list[query->query_matches].list_artist,
359 "/");
360
361 query->query_matches++;
362 }
363
364 break;
365 /* No match */
366 default:
367 query->query_match=MATCH_NOMATCH;
368
369 free(result);
370
371 return FALSE;
372 }
373
374 free(result);
375
376 return TRUE;
377 }
378
379 /* Split string into title/artist */
380
DiscDBParseTitle(char * buf,char * title,char * artist,char * sep)381 void DiscDBParseTitle(char *buf,char *title,char *artist,char *sep)
382 {
383 char *tmp;
384
385 tmp=strtok(buf,sep);
386
387 if(!tmp) return;
388
389 g_snprintf(artist,256,"%s",g_strstrip(tmp));
390
391 tmp=strtok(NULL,"");
392
393 if(tmp)
394 g_snprintf(title,256,"%s",g_strstrip(tmp));
395 else strcpy(title,artist);
396 }
397
398 /* Process a line of input data */
399
DiscDBProcessLine(char * inbuffer,DiscData * data,int numtracks)400 static void DiscDBProcessLine(char *inbuffer,DiscData *data,
401 int numtracks)
402 {
403 int track;
404 int len=0;
405 char *st;
406
407 strtok(inbuffer,"\n\r");
408
409 if(!strncasecmp(inbuffer,"# Revision: ",12)) {
410 data->revision=atoi(inbuffer+12);
411 }
412 else if(!strncasecmp(inbuffer,"DTITLE",6)) {
413 len=strlen(data->data_title);
414
415 g_snprintf(data->data_title+len,256-len,"%s",inbuffer+7);
416 }
417 else if(!strncasecmp(inbuffer,"DYEAR",5)) {
418 strtok(inbuffer,"=");
419
420 st = strtok(NULL, "");
421 if(st == NULL)
422 return;
423
424 data->data_year=atoi(g_strstrip(st));
425 }
426 else if(!strncasecmp(inbuffer,"DGENRE",6)) {
427 strtok(inbuffer,"=");
428
429 st = strtok(NULL, "");
430 if(st == NULL)
431 return;
432
433 st=g_strstrip(st);
434
435 if(*st) {
436 data->data_genre=DiscDBGenreValue(st);
437 data->data_id3genre=ID3GenreValue(st);
438 }
439 }
440 else if(!strncasecmp(inbuffer,"DID3",4)) {
441 strtok(inbuffer,"=");
442
443 st = strtok(NULL, "");
444 if(st == NULL)
445 return;
446
447 data->data_id3genre=atoi(g_strstrip(st));
448 }
449 else if(!strncasecmp(inbuffer,"TTITLE",6)) {
450 track=atoi(strtok(inbuffer+6,"="));
451
452 if(track<numtracks)
453 len=strlen(data->data_track[track].track_name);
454
455 st = strtok(NULL, "");
456 if(st == NULL)
457 return;
458
459 g_snprintf(data->data_track[track].track_name+len,256-len,"%s",
460 st);
461 }
462 else if(!strncasecmp(inbuffer,"TARTIST",7)) {
463 data->data_multi_artist=TRUE;
464
465 track=atoi(strtok(inbuffer+7,"="));
466
467 if(track<numtracks)
468 len=strlen(data->data_track[track].track_artist);
469
470 st = strtok(NULL, "");
471 if(st == NULL)
472 return;
473
474 g_snprintf(data->data_track[track].track_artist+len,256-len,"%s",
475 st);
476 }
477 else if(!strncasecmp(inbuffer,"EXTD",4)) {
478 len=strlen(data->data_extended);
479
480 g_snprintf(data->data_extended+len,4096-len,"%s",inbuffer+5);
481 }
482 else if(!strncasecmp(inbuffer,"EXTT",4)) {
483 track=atoi(strtok(inbuffer+4,"="));
484
485 if(track<numtracks)
486 len=strlen(data->data_track[track].track_extended);
487
488 st = strtok(NULL, "");
489 if(st == NULL)
490 return;
491
492 g_snprintf(data->data_track[track].track_extended+len,4096-len,"%s",
493 st);
494 }
495 else if(!strncasecmp(inbuffer,"PLAYORDER",5)) {
496 len=strlen(data->data_playlist);
497
498 g_snprintf(data->data_playlist+len,256-len,"%s",inbuffer+10);
499 }
500 }
501
StrConvertEncoding(char * str,const char * from,const char * to,int max_len)502 static char *StrConvertEncoding(char *str,const char *from,
503 const char *to,int max_len)
504 {
505 char *conv_str;
506 gsize rb,wb;
507
508 if(!str) return NULL;
509
510 conv_str=g_convert_with_fallback(str,strlen(str),to,from,NULL,&rb,&wb,NULL);
511
512 if(!conv_str) return str;
513
514 g_snprintf(str,max_len,"%s",conv_str);
515
516 g_free(conv_str);
517
518 return str;
519 }
520
DiscDBUTF8Validate(const DiscInfo * disc,const DiscData * data)521 gboolean DiscDBUTF8Validate(const DiscInfo *disc,const DiscData *data)
522 {
523 int track;
524
525 if(data->data_title && !g_utf8_validate(data->data_title,-1,NULL))
526 return FALSE;
527 if(data->data_artist && !g_utf8_validate(data->data_artist,-1,NULL))
528 return FALSE;
529 if(data->data_extended && !g_utf8_validate(data->data_extended,-1,NULL))
530 return FALSE;
531
532 for(track=0;track<disc->num_tracks;track++) {
533 if(data->data_track[track].track_name
534 && !g_utf8_validate(data->data_track[track].track_name,-1,NULL))
535 return FALSE;
536 if(data->data_track[track].track_artist
537 && !g_utf8_validate(data->data_track[track].track_artist,-1,NULL))
538 return FALSE;
539 if(data->data_track[track].track_extended
540 && !g_utf8_validate(data->data_track[track].track_extended,-1,NULL))
541 return FALSE;
542 }
543 return TRUE;
544 }
545
546
DiscDBConvertEncoding(DiscInfo * disc,DiscData * data,const char * from,const char * to)547 static void DiscDBConvertEncoding(DiscInfo *disc,DiscData *data,
548 const char *from,const char *to)
549 {
550 int track;
551
552 StrConvertEncoding(data->data_title,from,to,256);
553 StrConvertEncoding(data->data_artist,from,to,256);
554 StrConvertEncoding(data->data_extended,from,to,4096);
555
556 for(track=0;track<disc->num_tracks;track++) {
557 StrConvertEncoding(data->data_track[track].track_name,from,to,256);
558 StrConvertEncoding(data->data_track[track].track_artist,from,to,256);
559 StrConvertEncoding(data->data_track[track].track_extended,from,to,4096);
560 }
561 }
562
563 /* Read the actual DiscDB entry */
564
DiscDBRead(DiscInfo * disc,DiscDBServer * server,DiscDBHello * hello,DiscDBEntry * entry,DiscData * data,char * encoding)565 gboolean DiscDBRead(DiscInfo *disc,DiscDBServer *server,
566 DiscDBHello *hello,DiscDBEntry *entry,
567 DiscData *data,char *encoding)
568 {
569 int index;
570 GString *cmd;
571 char *result,*inbuffer,*dataptr;
572
573 if(!disc->have_info) CDStat(disc,TRUE);
574
575 data->data_genre=entry->entry_genre;
576 data->data_id=DiscDBDiscid(disc);
577 *(data->data_extended)='\0';
578 *(data->data_title)='\0';
579 *(data->data_artist)='\0';
580 *(data->data_playlist)='\0';
581 data->data_multi_artist=FALSE;
582 data->data_year=0;
583 data->data_id3genre=-1;
584 data->revision=-1;
585
586 for(index=0;index<MAX_TRACKS;index++) {
587 *(data->data_track[index].track_name)='\0';
588 *(data->data_track[index].track_artist)='\0';
589 *(data->data_track[index].track_extended)='\0';
590 }
591
592 cmd=g_string_new(NULL);
593
594 g_string_printf(cmd,"cddb+read+%s+%08x",DiscDBGenre(entry->entry_genre),
595 entry->entry_id);
596
597 result=DiscDBMakeRequest(server,hello,cmd->str);
598
599 g_string_free(cmd,TRUE);
600
601 if(!result) {
602 return FALSE;
603 }
604
605 dataptr=result;
606
607 inbuffer=DiscDBReadLine(&dataptr);
608
609 while((inbuffer=DiscDBReadLine(&dataptr)))
610 DiscDBProcessLine(inbuffer,data,disc->num_tracks);
611
612 /* Both disc title and artist have been stuffed in the title field, so the
613 need to be separated */
614
615 DiscDBParseTitle(data->data_title,data->data_title,data->data_artist,"/");
616
617 free(result);
618
619 /* Don't allow the genre to be overwritten */
620 data->data_genre=entry->entry_genre;
621
622 if(strcasecmp(encoding,"utf-8")) {
623 DiscDBConvertEncoding(disc,data,encoding,"utf-8");
624 }
625
626 return TRUE;
627 }
628
629 /* See if a disc is in the local database */
630
DiscDBStatDiscData(DiscInfo * disc)631 gboolean DiscDBStatDiscData(DiscInfo *disc)
632 {
633 int index,id;
634 struct stat st;
635 char root_dir[256],file[256];
636
637 if(!disc->have_info) CDStat(disc,TRUE);
638
639 id=DiscDBDiscid(disc);
640
641 g_snprintf(root_dir, 256, "%s/.cddb", g_get_home_dir());
642
643 if(stat(root_dir, &st) < 0)
644 return FALSE;
645 else {
646 if(!S_ISDIR(st.st_mode))
647 return FALSE;
648 }
649
650 g_snprintf(file,256,"%s/%08x",root_dir,id);
651 if(stat(file,&st)==0) return TRUE;
652
653 for(index=0;index<12;index++) {
654 g_snprintf(file,256,"%s/%s/%08x",root_dir,DiscDBGenre(index),id);
655
656 if(stat(file,&st) == 0)
657 return TRUE;
658 }
659
660 return FALSE;
661 }
662
663 /* Read from the local database */
664
DiscDBReadDiscData(DiscInfo * disc,DiscData * ddata,const char * encoding)665 int DiscDBReadDiscData(DiscInfo *disc,DiscData *ddata, const char *encoding)
666 {
667 FILE *discdb_data=NULL;
668 int index,genre;
669 char root_dir[256],file[256],inbuf[512];
670 struct stat st;
671
672 g_snprintf(root_dir, 256, "%s/.cddb", g_get_home_dir());
673
674 if(stat(root_dir, &st) < 0) {
675 return -1;
676 } else {
677 if(!S_ISDIR(st.st_mode)) {
678 errno = ENOTDIR;
679 return -1;
680 }
681 }
682
683 if(!disc->have_info) CDStat(disc,TRUE);
684
685 ddata->data_id=DiscDBDiscid(disc);
686 *(ddata->data_extended)='\0';
687 *(ddata->data_title)='\0';
688 *(ddata->data_artist)='\0';
689 *(ddata->data_playlist)='\0';
690 ddata->data_multi_artist=FALSE;
691 ddata->data_year=0;
692 ddata->data_genre=7;
693 ddata->data_id3genre=-1;
694 ddata->revision=-1;
695
696 for(index=0;index<MAX_TRACKS;index++) {
697 *(ddata->data_track[index].track_name)='\0';
698 *(ddata->data_track[index].track_artist)='\0';
699 *(ddata->data_track[index].track_extended)='\0';
700 }
701
702 g_snprintf(file,256,"%s/%08x",root_dir,ddata->data_id);
703 if(stat(file,&st)==0) {
704 discdb_data=fopen(file, "r");
705 }
706 else {
707 for(genre=0;genre<12;genre++) {
708 g_snprintf(file,256,"%s/%s/%08x",root_dir,DiscDBGenre(genre),
709 ddata->data_id);
710
711 if(stat(file,&st)==0) {
712 discdb_data=fopen(file, "r");
713
714 ddata->data_genre=genre;
715 break;
716 }
717 }
718
719 if(genre==12) return -1;
720 }
721
722 while(fgets(inbuf,512,discdb_data))
723 DiscDBProcessLine(inbuf,ddata,disc->num_tracks);
724
725 /* Both disc title and artist have been stuffed in the title field, so the
726 need to be separated */
727
728 DiscDBParseTitle(ddata->data_title,ddata->data_title,ddata->data_artist,"/");
729
730 if(!DiscDBUTF8Validate(disc,ddata)) {
731 DiscDBConvertEncoding(disc,ddata,strcasecmp(encoding,"UTF-8")?
732 encoding:"ISO-8859-1","UTF-8");
733 }
734
735 fclose(discdb_data);
736
737 return 0;
738 }
739
DiscDBWriteLine(char * header,int num,char * data,FILE * outfile,char * encoding)740 static void DiscDBWriteLine(char *header,int num,char *data,FILE *outfile,
741 char *encoding)
742 {
743 char *offset, *next, *chunk;
744
745 if(strcasecmp(encoding,"utf-8")) {
746 StrConvertEncoding(data,"utf-8",encoding,512);
747 }
748
749 offset=data;
750
751 do {
752 for(next=offset; next-offset<65&&*next; ) {
753 if (*next=='\\'&&*(next+1)) {
754 next+=2;
755 }
756 else if(!strcasecmp(encoding,"utf-8")) {
757 next=g_utf8_find_next_char(next,NULL);
758 }
759 else {
760 next++;
761 }
762 }
763 chunk=g_strndup(offset,(gsize)(next-offset));
764 if(num==-1)
765 fprintf(outfile,"%s=%s\n",header,chunk);
766 else
767 fprintf(outfile,"%s%d=%s\n",header,num,chunk);
768 g_free(chunk);
769 offset=next;
770 } while (*offset);
771 }
772
773
774 /* Write to the local cache */
775
DiscDBWriteDiscData(DiscInfo * disc,DiscData * ddata,FILE * outfile,gboolean gripext,gboolean freedbext,char * encoding)776 int DiscDBWriteDiscData(DiscInfo *disc,DiscData *ddata,FILE *outfile,
777 gboolean gripext,gboolean freedbext,char *encoding)
778 {
779 FILE *discdb_data;
780 int track;
781 char root_dir[256],file[256],tmp[512];
782 struct stat st;
783
784 if(!disc->have_info) CDStat(disc,TRUE);
785
786 if(!outfile) {
787 g_snprintf(root_dir, 256, "%s/.cddb", g_get_home_dir());
788 g_snprintf(file,256,"%s/%08x",root_dir,ddata->data_id);
789
790 if(stat(root_dir,&st)<0) {
791 if(errno != ENOENT) {
792 Debug(_("Stat error %d on %s\n"),errno,root_dir);
793 return -1;
794 }
795 else {
796 Debug(_("Creating directory %s\n"),root_dir);
797 mkdir(root_dir,0777);
798 }
799 } else {
800 if(!S_ISDIR(st.st_mode)) {
801 Debug(_("Error: %s exists, but is a file\n"),root_dir);
802 errno=ENOTDIR;
803 return -1;
804 }
805 }
806
807 if((discdb_data=fopen(file,"w"))==NULL) {
808 Debug(_("Error: Unable to open %s for writing\n"),file);
809 return -1;
810 }
811 }
812 else discdb_data=outfile;
813
814 fprintf(discdb_data,"# xmcd CD database file generated by Grip %s\n",
815 VERSION);
816 fputs("# \n",discdb_data);
817 fputs("# Track frame offsets:\n",discdb_data);
818
819 for(track=0;track<disc->num_tracks;track++)
820 fprintf(discdb_data, "# %d\n",disc->track[track].start_frame);
821
822 fputs("# \n",discdb_data);
823 fprintf(discdb_data,"# Disc length: %d seconds\n",disc->length.mins *
824 60 + disc->length.secs);
825 fputs("# \n",discdb_data);
826
827 if(gripext) fprintf(discdb_data,"# Revision: %d\n",ddata->revision);
828 else fprintf(discdb_data,"# Revision: %d\n",ddata->revision+1);
829
830 fprintf(discdb_data,"# Submitted via: Grip %s\n",VERSION);
831 fputs("# \n",discdb_data);
832 fprintf(discdb_data,"DISCID=%08x\n",ddata->data_id);
833
834 g_snprintf(tmp,512,"%s / %s",ddata->data_artist,ddata->data_title);
835
836 DiscDBWriteLine("DTITLE",-1,tmp,discdb_data,encoding);
837
838 if(gripext||freedbext) {
839 if(ddata->data_year)
840 fprintf(discdb_data,"DYEAR=%d\n",ddata->data_year);
841 else fprintf(discdb_data,"DYEAR=\n");
842 }
843
844 if(gripext) {
845 fprintf(discdb_data,"DGENRE=%s\n",DiscDBGenre(ddata->data_genre));
846 fprintf(discdb_data,"DID3=%d\n",ddata->data_id3genre);
847 }
848 else if(freedbext) {
849 fprintf(discdb_data,"DGENRE=%s\n",ID3GenreString(ddata->data_id3genre));
850 }
851
852 for(track=0;track<disc->num_tracks;track++) {
853 if(gripext||!*(ddata->data_track[track].track_artist)) {
854 DiscDBWriteLine("TTITLE",track,ddata->data_track[track].track_name,
855 discdb_data,encoding);
856 }
857 else {
858 g_snprintf(tmp,512,"%s / %s",ddata->data_track[track].track_artist,
859 ddata->data_track[track].track_name);
860 DiscDBWriteLine("TTITLE",track,tmp,discdb_data,encoding);
861 }
862
863 if(gripext&&*(ddata->data_track[track].track_artist))
864 DiscDBWriteLine("TARTIST",track,ddata->data_track[track].track_artist,
865 discdb_data,encoding);
866 }
867
868 DiscDBWriteLine("EXTD",-1,ddata->data_extended,discdb_data,encoding);
869
870 for(track=0;track<disc->num_tracks;track++)
871 DiscDBWriteLine("EXTT",track,
872 ddata->data_track[track].track_extended,discdb_data,
873 encoding);
874
875 if(outfile)
876 fprintf(discdb_data,"PLAYORDER=\n");
877 else {
878 fprintf(discdb_data,"PLAYORDER=%s\n",ddata->data_playlist);
879 fclose(discdb_data);
880 }
881
882 return 0;
883 }
884