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