1 /*
2 ** Copyright (C) 2005-2011 Erik de Castro Lopo
3 **
4 ** This program is free software; you can redistribute it and/or modify
5 ** it under the terms of the GNU General Public License as published by
6 ** the Free Software Foundation; either version 2 of the License, or
7 ** (at your option) any later version.
8 **
9 ** This program is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ** GNU General Public License for more details.
13 **
14 ** You should have received a copy of the GNU General Public License
15 ** along with this program; if not, write to the Free Software
16 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include "config.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #if HAVE_UNISTD_H
24 #include <unistd.h>
25 #else
26 #include "sf_unistd.h"
27 #endif
28 #include <string.h>
29 #include <fcntl.h>
30 #ifdef HAVE_DIRECT_H
31 #include <direct.h>
32 #endif
33 #include <sys/stat.h>
34
35 #include <sndfile.h>
36
37 #include "regtest.h"
38
39 #if HAVE_SQLITE3
40
41 #include <ctype.h>
42 #include <sqlite3.h>
43
44 typedef struct
45 { sqlite3 *sql ;
46
47 int count ;
48 int ekey_max ;
49
50 /* Filename and pathname for file. */
51 char filename [256] ;
52 char pathname [512] ;
53
54 /* Storage for createding SQL commands. Must be larger than logbuf below. */
55 char cmdbuf [1 << 15] ;
56
57 /* Storage for log buffer retrieved from SNDFILE* .*/
58 char logbuf [1 << 14] ;
59
60 } REGTEST_DB ;
61
62 /* In checksum.c */
63 int calc_checksum (SNDFILE * file, const SF_INFO * info) ;
64
65 static void get_filename_pathname (REGTEST_DB * db, const char *filepath) ;
66 static void single_quote_replace (char * buf) ;
67
68 static int get_ekey_from_filename (REGTEST_DB * db, const char *filepath) ;
69 static int get_filename_pathname_by_ekey (REGTEST_DB * db, int ekey) ;
70 static int check_file_by_ekey (REGTEST_DB * db, int ekey) ;
71
72 static int count_callback (REGTEST_DB * db, int argc, char **argv, char **colname) ;
73 static int ekey_max_callback (REGTEST_DB * db, int argc, char **argv, char **colname) ;
74 static int callback (void *unused, int argc, char **argv, char **colname) ;
75 static const char *db_basename (const char *fname);
76
77 /* Windows accepts both '\\' and '/' in paths */
78 #ifdef _WIN32
79 #define IS_SLASH(c) ((c) == '\\' || (c) == '/')
80 #define HAS_DRIVELETTER(path) (isalpha ((int)(path[0])) && path[1] == ':' && IS_SLASH(path[2]))
81 #else
82 #define IS_SLASH(c) ((c) == '/')
83 #define HAS_DRIVELETTER(path) 0
84 #endif
85
86 REG_DB *
db_open(const char * db_name)87 db_open (const char * db_name)
88 { REGTEST_DB * db ;
89 int err ;
90
91 if ((db = malloc (sizeof (REGTEST_DB))) == NULL)
92 { perror ("malloc") ;
93 exit (1) ;
94 } ;
95
96 if ((err = sqlite3_open (db_name, &(db->sql))) != 0)
97 { printf ("Can't open database: %s\n", sqlite3_errmsg (db->sql)) ;
98 sqlite3_close (db->sql) ;
99 free (db) ;
100 exit (1) ;
101 } ;
102
103 return (REG_DB *) db ;
104 } /* db_open */
105
106 int
db_create(const char * db_name)107 db_create (const char * db_name)
108 { REGTEST_DB * db ;
109 const char *cmd ;
110 char * errmsg = NULL ;
111 int err ;
112
113 db = (REGTEST_DB *) db_open (db_name) ;
114
115 cmd = "create table sndfile (ekey INTEGER PRIMARY KEY,"
116 "fname VARCHAR(1),"
117 "fpath VARCHAR(1),"
118 "srate INTEGER,"
119 "frames VARCHAR(1),"
120 "channels INTEGER,"
121 "format VARCHAR(1),"
122 "checksum VARCHAR(1),"
123 "logbuf VARCHAR(1)"
124 ");" ;
125
126 err = sqlite3_exec (db->sql, cmd, callback, 0, &errmsg) ;
127 if (err != SQLITE_OK)
128 printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ;
129
130 sqlite3_close (db->sql) ;
131 free (db) ;
132
133 return 0 ;
134 } /* db_create */
135
136 int
db_close(REG_DB * db_handle)137 db_close (REG_DB * db_handle)
138 { REGTEST_DB * db ;
139
140 db = (REGTEST_DB *) db_handle ;
141
142 sqlite3_close (db->sql) ;
143 free (db) ;
144
145 return 0 ;
146 } /* db_close */
147
148 /*==============================================================================
149 */
150
151 int
db_file_exists(REG_DB * db_handle,const char * filename)152 db_file_exists (REG_DB * db_handle, const char * filename)
153 { REGTEST_DB * db ;
154 char * errmsg ;
155 int err ;
156
157 db = (REGTEST_DB *) db_handle ;
158
159 filename = db_basename (filename);
160
161 snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select fname from sndfile where fname='%s'", filename) ;
162
163 db->count = 0 ;
164 err = sqlite3_exec (db->sql, db->cmdbuf, (sqlite3_callback) count_callback, db, &errmsg) ;
165 if (err == 0 && db->count == 1)
166 return 1 ;
167
168 return 0 ;
169 } /* db_file_exists */
170
171 int
db_add_file(REG_DB * db_handle,const char * filepath)172 db_add_file (REG_DB * db_handle, const char * filepath)
173 { REGTEST_DB * db ;
174 SNDFILE * sndfile ;
175 SF_INFO info ;
176 char * errmsg ;
177 int err, checksum ;
178
179 db = (REGTEST_DB *) db_handle ;
180
181 get_filename_pathname (db, filepath) ;
182
183 if (db_file_exists (db_handle, filepath))
184 { printf (" %s : already in database\n", db->filename) ;
185 return 0 ;
186 } ;
187
188 memset (&info, 0, sizeof (info)) ;
189 sndfile = sf_open (db->pathname, SFM_READ, &info) ;
190 sf_command (sndfile, SFC_GET_LOG_INFO, db->logbuf, sizeof (db->logbuf)) ;
191 checksum = (sndfile == NULL) ? 0 : calc_checksum (sndfile, &info) ;
192 sf_close (sndfile) ;
193
194 if (sndfile == NULL)
195 { printf (" %s : could not open : %s, filepath: '%s'\n", db->filename, sf_strerror (NULL), filepath) ;
196 puts (db->logbuf) ;
197 return 1 ;
198 } ;
199
200 single_quote_replace (db->logbuf) ;
201
202 snprintf (db->cmdbuf, sizeof (db->cmdbuf), "insert into sndfile "
203 "(fname, fpath, srate, frames, channels, format, checksum, logbuf) values"
204 "('%s','%s',%d,'%ld', %d, '0x%08x', '0x%08x', '%s');",
205 db->filename, db->pathname, info.samplerate, (long) info.frames, info.channels, info.format, checksum, db->logbuf) ;
206
207 if (strlen (db->cmdbuf) >= sizeof (db->cmdbuf) - 1)
208 { printf ("strlen (db->cmdbuf) too long.\n") ;
209 exit (1) ;
210 } ;
211
212 err = sqlite3_exec (db->sql, db->cmdbuf, callback, 0, &errmsg) ;
213 if (err != SQLITE_OK)
214 { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ;
215 puts (db->cmdbuf) ;
216 } ;
217
218 return 0 ;
219 } /* db_add_file */
220
221 int
db_check_file(REG_DB * db_handle,const char * filepath)222 db_check_file (REG_DB * db_handle, const char * filepath)
223 { REGTEST_DB * db ;
224 int ekey ;
225
226 if (db_file_exists (db_handle, filepath) == 0)
227 { printf ("\nFile not in database.\n\n") ;
228 exit (0) ;
229 } ;
230
231 db = (REGTEST_DB *) db_handle ;
232
233 ekey = get_ekey_from_filename (db, filepath) ;
234
235 return check_file_by_ekey (db, ekey) ;
236 } /* db_check_file */
237
238 /*==============================================================================
239 */
240
241 int
db_check_all(REG_DB * db_handle)242 db_check_all (REG_DB * db_handle)
243 { REGTEST_DB * db ;
244 char * errmsg ;
245 int err, ekey ;
246
247 db = (REGTEST_DB *) db_handle ;
248
249 db->ekey_max = 0 ;
250
251 snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select ekey from sndfile") ;
252
253 err = sqlite3_exec (db->sql, db->cmdbuf, (sqlite3_callback) ekey_max_callback, db, &errmsg) ;
254 if (err != SQLITE_OK)
255 { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ;
256 puts (db->cmdbuf) ;
257 } ;
258
259 for (ekey = 1 ; ekey <= db->ekey_max ; ekey++)
260 if (get_filename_pathname_by_ekey (db, ekey) != 0)
261 check_file_by_ekey (db, ekey) ;
262
263 return 0 ;
264 } /* db_check_all */
265
266
267 int
db_list_all(REG_DB * db_handle)268 db_list_all (REG_DB * db_handle)
269 {
270 printf ("%s : %p\n", __func__, db_handle) ;
271 return 0 ;
272 } /* db_list_all */
273
274 int
db_del_entry(REG_DB * db_handle,const char * entry)275 db_del_entry (REG_DB * db_handle, const char * entry)
276 {
277 printf ("%s : %p %s\n", __func__, db_handle, entry) ;
278 return 0 ;
279 } /* db_del_entry */
280
281 /*==============================================================================
282 */
283
284 static int
get_ekey_from_filename(REGTEST_DB * db,const char * filepath)285 get_ekey_from_filename (REGTEST_DB * db, const char *filepath)
286 { char * errmsg, **result ;
287 int err, ekey = 0, rows, cols ;
288
289 get_filename_pathname (db, filepath) ;
290
291 snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select ekey from sndfile where fname='%s'", db->filename) ;
292
293 err = sqlite3_get_table (db->sql, db->cmdbuf, &result, &rows, &cols, &errmsg) ;
294 if (err != SQLITE_OK)
295 { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ;
296 puts (db->cmdbuf) ;
297 } ;
298
299 if (cols != 1 || rows != 1)
300 { printf ("Bad juju!! rows = %d cols = %d\n", rows, cols) ;
301 exit (1) ;
302 } ;
303
304 ekey = strtol (result [1], NULL, 10) ;
305
306 sqlite3_free_table (result) ;
307
308 return ekey ;
309 } /* get_ekey_from_filename */
310
311 static int
get_filename_pathname_by_ekey(REGTEST_DB * db,int ekey)312 get_filename_pathname_by_ekey (REGTEST_DB * db, int ekey)
313 { char *errmsg, **result ;
314 int err, rows, cols ;
315
316 snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select fname,fpath from sndfile where ekey='%d'", ekey) ;
317
318 err = sqlite3_get_table (db->sql, db->cmdbuf, &result, &rows, &cols, &errmsg) ;
319 if (err != SQLITE_OK)
320 { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ;
321 puts (db->cmdbuf) ;
322 return 0 ;
323 } ;
324
325 if (cols != 2 || rows != 1)
326 { printf ("\nError (%s %d) : rows = %d cols = %d\n", __func__, __LINE__, rows, cols) ;
327 exit (1) ;
328 } ;
329
330 snprintf (db->filename, sizeof (db->filename), "%s", result [2]) ;
331 snprintf (db->pathname, sizeof (db->pathname), "%s", result [3]) ;
332
333 sqlite3_free_table (result) ;
334
335 return 1 ;
336 } /* get_filename_pathname_by_ekey */
337
338 static int
check_file_by_ekey(REGTEST_DB * db,int ekey)339 check_file_by_ekey (REGTEST_DB * db, int ekey)
340 { SNDFILE * sndfile ;
341 SF_INFO info ;
342 char * errmsg, **result ;
343 int err, k, rows, cols, checksum ;
344
345 printf (" %s : ", db->filename) ;
346 fflush (stdout) ;
347
348 memset (&info, 0, sizeof (info)) ;
349 sndfile = sf_open (db->pathname, SFM_READ, &info) ;
350 sf_command (sndfile, SFC_GET_LOG_INFO, db->logbuf, sizeof (db->logbuf)) ;
351 checksum = (sndfile == NULL) ? 0 : calc_checksum (sndfile, &info) ;
352 sf_close (sndfile) ;
353
354 if (sndfile == NULL)
355 { printf ("\n\nError : Could not open '%s' : %s\n", db->pathname, sf_strerror (NULL)) ;
356 puts (db->logbuf) ;
357 exit (1) ;
358 } ;
359
360 single_quote_replace (db->logbuf) ;
361
362 snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select fname,srate,frames,channels,format,"
363 "checksum,logbuf from sndfile where ekey='%d'", ekey) ;
364
365 err = sqlite3_get_table (db->sql, db->cmdbuf, &result, &rows, &cols, &errmsg) ;
366 if (err != SQLITE_OK)
367 { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ;
368 puts (db->cmdbuf) ;
369 } ;
370
371 for (k = 0 ; k < cols ; k++)
372 { if (strcmp (result [k], "fname") == 0)
373 { if (strcmp (result [k + cols], db->filename) == 0)
374 continue ;
375 printf ("\n\nError : fname doesn't match : %s != %s\n", result [k + cols], db->filename) ;
376 } ;
377
378 if (strcmp (result [k], "srate") == 0)
379 { if (strtol (result [k + cols], NULL, 10) == info.samplerate)
380 continue ;
381 printf ("\n\nError : srate doesn't match : %s == %d\n", result [k + cols], info.samplerate) ;
382 } ;
383
384 if (strcmp (result [k], "frames") == 0)
385 { if (strtoll (result [k + cols], NULL, 10) == info.frames)
386 continue ;
387 printf ("\n\nError : frames doesn't match : %s == %ld\n", result [k + cols], (long) info.frames) ;
388 } ;
389
390 if (strcmp (result [k], "channels") == 0)
391 { if (strtol (result [k + cols], NULL, 10) == info.channels)
392 continue ;
393 printf ("\n\nError : channels doesn't match : %s == %d\n", result [k + cols], info.channels) ;
394 } ;
395
396 if (strcmp (result [k], "format") == 0)
397 { if (strtol (result [k + cols], NULL, 16) == info.format)
398 continue ;
399 printf ("\n\nError : format doesn't match : %s == 0x%08x\n", result [k + cols], info.format) ;
400 } ;
401
402 if (strcmp (result [k], "checksum") == 0)
403 { int db_val = (int) strtoll (result [k + cols], NULL, 16) ;
404
405 if (db_val == checksum)
406 continue ;
407 printf ("\n\nError : checksum doesn't match : 0x%08x == 0x%08x\n", db_val, checksum) ;
408 } ;
409
410 if (strcmp (result [k], "logbuf") == 0)
411 continue ;
412
413 printf ("\nHere is the old logubuffer :\n\n%s\n\nand the new :\n\n%s\n\n", result [2 * cols - 1], db->logbuf) ;
414 exit (1) ;
415 } ;
416
417 sqlite3_free_table (result) ;
418
419 puts ("ok") ;
420
421 return 0 ;
422 } /* check_file_by_ekey */
423
424 /*==============================================================================
425 */
426
427 static void
get_filename_pathname(REGTEST_DB * db,const char * filepath)428 get_filename_pathname (REGTEST_DB * db, const char *filepath)
429 {
430 const char * basename = db_basename (filepath) ;
431 int slen ;
432
433 /* Test for a relative path
434 */
435 if (!IS_SLASH(filepath [0]) && !HAS_DRIVELETTER(filepath))
436 { memset (db->pathname, 0, sizeof (db->pathname)) ;
437 if (getcwd (db->pathname, sizeof (db->pathname)) == NULL)
438 { perror ("\ngetcwd failed") ;
439 exit (1) ;
440 } ;
441
442 slen = strlen (db->pathname) ;
443 /* a '/' is fine for Windows too */
444 snprintf (db->pathname + slen, sizeof (db->pathname) - slen, "/%s", filepath) ;
445 }
446 else
447 snprintf (db->pathname, sizeof (db->pathname), "%s", filepath) ;
448
449 snprintf (db->filename, sizeof (db->filename), "%s", basename) ;
450
451 basename = db_basename (db->pathname) ;
452 if (basename == db->pathname)
453 { printf ("\nError : bad pathname %s\n", filepath) ;
454 exit (1) ;
455 } ;
456 } /* get filename_pathname */
457
458 static void
single_quote_replace(char * buf)459 single_quote_replace (char * buf)
460 { while ((buf = strchr (buf, '\'')) != 0)
461 buf [0] = '"' ;
462 } /* single_quote_replace */
463
464 static int
count_callback(REGTEST_DB * db,int argc,char ** argv,char ** colname)465 count_callback (REGTEST_DB * db, int argc, char **argv, char **colname)
466 { db->count ++ ;
467
468 (void) argc ;
469 (void) argv ;
470 (void) colname ;
471 return 0 ;
472 } /* count_callback */
473
474 static int
ekey_max_callback(REGTEST_DB * db,int argc,char ** argv,char ** unused)475 ekey_max_callback (REGTEST_DB * db, int argc, char **argv, char **unused)
476 { int ekey ;
477
478 (void) argc ;
479 (void) unused ;
480
481 ekey = strtol (argv [0], NULL, 10) ;
482 if (ekey > db->ekey_max)
483 db->ekey_max = ekey ;
484
485 return 0 ;
486 } /* ekey_max_callback */
487
488 static int
callback(void * unused,int argc,char ** argv,char ** colname)489 callback (void *unused, int argc, char **argv, char **colname)
490 { int k ;
491
492 (void) unused ;
493
494 for (k = 0 ; k < argc ; k++)
495 printf ("%s = %s\n", colname [k], argv [k] ? argv [k] : "NULL") ;
496
497 printf ("\n") ;
498
499 return 0 ;
500 } /* callback */
501
502 /*
503 * Win32: Strip drive-letter and directory from a filename.
504 * non-Win32: Strip directory from a filename.
505 */
db_basename(const char * fname)506 static const char *db_basename (const char *fname)
507 {
508 const char *base = fname;
509
510 #if !defined(_WIN32)
511 const char *slash = strrchr (base, '/');
512
513 if (slash)
514 base = slash + 1 ;
515 #else
516 if (fname[0] && fname[1] == ':') {
517 fname += 2;
518 base = fname;
519 }
520 while (*fname) {
521 if (IS_SLASH(*fname))
522 base = fname + 1;
523 fname++;
524 }
525 #endif
526 return base ;
527 }
528
529 #else
530
531 int dummy (void) ;
532
533 int
dummy(void)534 dummy (void)
535 { /*
536 ** Empty dummy fnction so tha compiler doesn't winge about an
537 ** empty file.
538 */
539 return 0 ;
540 } /* dummy */
541
542 #endif
543