1 /* This file is part of TCD 2.0.
2    cddb.c - CDDB remote and local functions.
3 
4    Copyright (C) 1997-98 Tim P. Gerla <timg@rrv.net>
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20    Tim P. Gerla
21    RR 1, Box 40
22    Climax, MN  56523
23    timg@rrv.net
24 */
25 
26 #include <pwd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <sys/param.h>
32 #include <unistd.h>
33 
34 #include <SDL/SDL.h>
35 
36 #include "cd-utils.h"
37 #include "cddb.h"
38 
append_data(char * dest,const char * data,size_t maxlen)39 static void append_data(char *dest, const char *data, size_t maxlen)
40 {
41     const size_t destlen = strlen(dest);
42     dest += destlen;
43     if (maxlen <= destlen) {
44         return;
45     }
46     maxlen -= destlen;
47 
48     while (maxlen > 1 && *data != '\0') {
49         if (*data == '\\') {
50             data++;
51             if (*data == 'n') {
52                 *dest++ = '\n';
53                 maxlen--;
54                 data++;
55             } else if (*data == 't') {
56                 *dest++ = '\t';
57                 maxlen--;
58                 data++;
59             } else {
60                 *dest++ = *data++;
61                 maxlen--;
62             }
63         } else {
64             *dest++ = *data++;
65             maxlen--;
66         }
67     }
68     *dest = '\0';
69 }
70 
write_data(FILE * fp,const char * key,int num,const char * data)71 static void write_data(FILE * fp, const char *key, int num,
72                        const char *data)
73 {
74     size_t nchars = 0;
75 
76     fputs(key, fp);
77     if (num != -1) {
78         fprintf(fp, "%d", num);
79     }
80     putc('=', fp);
81     for (; *data != '\0'; data++) {
82         if (*data == '\n') {
83             fputs("\\n", fp);
84             nchars += 2;
85         } else if (data[0] == '\t') {
86             fputs("\\t", fp);
87             nchars += 2;
88         } else if (data[0] == '\\') {
89             fputs("\\\\", fp);
90             nchars += 2;
91         } else {
92             putc(*data, fp);
93             nchars += 1;
94         }
95         if (nchars > 60) {
96             putc('\n', fp);
97             fputs(key, fp);
98             if (num != -1) {
99                 fprintf(fp, "%d", num);
100             }
101             putc('=', fp);
102             nchars = 0;
103         }
104     }
105     putc('\n', fp);
106 }
107 
tcd_readcddb(struct cd_info * cd,SDL_CD * cdrom,const char * filename)108 static int tcd_readcddb(struct cd_info *cd, SDL_CD * cdrom, const char *filename)
109 {
110     FILE *fp;
111     char string[256];
112     size_t i;
113 
114     /* zero out the extended data sections ... */
115     cd->disc_title[0] = '\0';
116     cd->extra_data[0] = '\0';
117     for (i = 0; i < (size_t) cdrom->numtracks; i++) {
118         cd->trk[i].name[0] = '\0';
119         cd->trk[i].extra_data[0] = '\0';
120     }
121 
122     fp = fopen(filename, "r");
123     if (fp == NULL) {
124         return -1;
125     }
126 
127     /* AC: dont feof.. feof is only true _after_ eof is read */
128     while (fgets(string, 255, fp) != NULL) {
129         if (strlen(string) > 0) {
130             string[strlen(string) - 1] = 0;
131         }
132 
133         /* If it's a comment, ignore. */
134         if (string[0] == '#')
135             continue;
136 
137         if (strncmp(string, "DTITLE=", 7) == 0) {
138             append_data(cd->disc_title, string + 7,
139                         sizeof(cd->disc_title));
140             continue;
141         } else if (strncmp(string, "EXTD=", 5) == 0) {
142             append_data(cd->extra_data, string + 5,
143                         sizeof(cd->extra_data));
144         } else if (strncmp(string, "TTITLE", 6) == 0) {
145             char *equals;
146             unsigned long trk = strtoul(string + 6, &equals, 10);
147             if (*equals == '=' && trk < lengthof(cd->trk)) {
148                 append_data(cd->trk[trk].name, equals + 1,
149                             sizeof(cd->trk[trk].name));
150             }
151         } else if (strncmp(string, "EXTT", 4) == 0) {
152             char *equals;
153             unsigned long trk = strtoul(string + 4, &equals, 10);
154             if (*equals == '=' && trk < lengthof(cd->trk)) {
155                 append_data(cd->trk[trk].extra_data, equals + 1,
156                             sizeof(cd->trk[trk].extra_data));
157             }
158         } else {
159             /* ignore everything that's unknown. */
160         }
161     }
162     fclose(fp);
163     return 0;
164 }
165 
tcd_writecddb(struct cd_info * cd,SDL_CD * cdrom,const char * filename)166 static int tcd_writecddb(struct cd_info *cd, SDL_CD * cdrom, const char *filename)
167 {
168     FILE *fp;
169     size_t i;
170 
171     if ((fp = fopen(filename, "w")) == NULL) {
172         return -1;
173     }
174     fputs("# xmcd CD Database Entry\n", fp);
175     fputs("#\n", fp);
176     fputs("# Track frame offsets:\n", fp);
177 
178     /* Print the frame offsets */
179     for (i = 0; i < (size_t) cdrom->numtracks; i++) {
180         fprintf(fp, "# %u\n", cdrom->track[i].offset);
181     }
182 
183     fputs("#\n", fp);
184     fprintf(fp, "# Disc length: %i seconds\n", cd_length(cdrom) / CD_FPS);
185     fprintf(fp, "# Submitted via: %s\n", PACKAGE_STRING);
186     fputs("#\n", fp);
187 
188     fprintf(fp, "DISCID=%08lx\n", cddb_discid(cdrom));
189     write_data(fp, "DTITLE", -1, cd->disc_title);
190     for (i = 0; i < (size_t) cdrom->numtracks; i++) {
191         write_data(fp, "TTITLE", i, cd->trk[i].name);
192     }
193 
194     write_data(fp, "EXTD", -1, cd->extra_data);
195     for (i = 0; i < (size_t) cdrom->numtracks; i++) {
196         write_data(fp, "EXTT", i, cd->trk[i].extra_data);
197     }
198     fprintf(fp, "PLAYORDER=\n");
199     fclose(fp);
200     return 0;
201 }
202 
get_home_dir(void)203 static const char *get_home_dir(void)
204 {
205     char *result;
206     struct passwd *passwd;
207 
208     if ((result = getenv("HOME")) != NULL) {
209         return result;
210     }
211     if ((passwd = getpwuid(getuid())) != NULL) {
212         if (passwd->pw_dir != NULL) {
213             return passwd->pw_dir;
214         }
215     }
216     (void) fprintf(stderr, "$HOME not set. giving up.\n");
217     exit(EXIT_FAILURE);
218 }
219 
cddb_filename(unsigned long discid)220 static char *cddb_filename(unsigned long discid)
221 {
222     static char filename[MAXPATHLEN];
223     snprintf(filename, MAXPATHLEN-1, "%s/.tcd/%08lx", get_home_dir(), discid);
224     return filename;
225 }
226 
tcd_readdiskinfo(struct tcd_state * cds,SDL_CD * cdrom)227 extern int tcd_readdiskinfo(struct tcd_state *cds, SDL_CD * cdrom)
228 {
229     int result;
230     char *filename;
231     struct cd_info *cd = &cds->cd_info;
232 
233     result = 0;
234 
235     if ((filename = cddb_filename(cddb_discid(cdrom))) != NULL)
236         result = tcd_readcddb(cd, cdrom, filename);
237 
238     return result;
239 }
240 
mkparentdir(char * filename)241 static void mkparentdir(char *filename)
242 {
243     char *slash;
244     for (slash = filename + 1; *slash; slash++) {
245         if (*slash == '/') {
246             *slash = '\0';
247             (void)mkdir(filename, 0777);
248             *slash = '/';
249         }
250     }
251 }
252 
tcd_writediskinfo(struct cd_info * cd,SDL_CD * cdrom)253 void tcd_writediskinfo(struct cd_info *cd, SDL_CD * cdrom)
254 {
255     char *filename;
256 
257     if ((filename = cddb_filename(cddb_discid(cdrom))) != NULL) {
258         mkparentdir(filename);
259         if (tcd_writecddb(cd, cdrom, filename) == 0) {
260             cd->modified = 0;
261         }
262     }
263 }
264