2 * AdPlug - Replayer for many OPL2/OPL3 audio file formats.
3 * Copyright (c) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * adplugdb.cpp - AdPlug database maintenance utility
20 * Copyright (c) 2002 Riven the Mage <riven@ok.ru>
21 * Copyright (c) 2002, 2003, 2006 Simon Peter <dn.tlp@gmx.net>
33 #if defined(HAVE_SYS_TYPES_H) && defined(HAVE_SYS_STAT_H)
35 # include <sys/types.h>
38 # include <sys/stat.h>
46 #include "../src/adplug.h"
47 #include "../src/silentopl.h"
48 #include "../src/database.h"
51 * Apple (OS X) and Sun systems declare getopt in unistd.h, other systems
52 * (Linux) use getopt.h.
54 #if defined (__APPLE__) || (defined(__SVR4) && defined(__sun))
60 # include "mygetopt.h"
66 // Default file name of AdPlug's database file
67 #define ADPLUGDB_FILE "adplug.db"
69 // Default AdPlug user's configuration subdirectory
70 #define ADPLUG_CONFDIR ".adplug"
72 // Default path to AdPlug's system-wide database file
73 #ifdef ADPLUG_DATA_DIR
74 # define ADPLUGDB_PATH ADPLUG_DATA_DIR "/" ADPLUGDB_FILE
76 # define ADPLUGDB_PATH ADPLUGDB_FILE
79 // Unknown filetype indicator
80 #define UNKNOWN_FILETYPE "*** Unknown ***"
82 // Message urgency levels
83 #define MSG_PANIC 0 // Unmaskable
89 /***** Global variables *****/
93 CAdPlugDatabase::CRecord::RecordType type;
95 { "plain", CAdPlugDatabase::CRecord::Plain },
96 { "songinfo", CAdPlugDatabase::CRecord::SongInfo },
97 { "clockspeed", CAdPlugDatabase::CRecord::ClockSpeed },
103 CAdPlugDatabase::CRecord::RecordType rtype;
105 bool usedefaultdb, usercomment, cmdkeys;
109 CAdPlugDatabase::CRecord::Plain,
115 static CAdPlugDatabase mydb;
116 static const char *program_name;
118 /***** Functions *****/
120 static void message(int level, const char *fmt, ...)
124 if(cfg.message_level < level) return;
126 fprintf(stderr, "%s: ", program_name);
127 va_start(argptr, fmt);
128 vfprintf(stderr, fmt, argptr);
130 fprintf(stderr, "\n");
133 static CAdPlugDatabase::CKey file2key(const char *filename)
135 if(cfg.cmdkeys) { // We got a key spec
136 CAdPlugDatabase::CKey key;
138 sscanf(filename, "%hx:%lx", &key.crc16, &key.crc32);
140 } else { // We got a real filename
141 binifstream f(filename);
144 message(MSG_ERROR, "can't open specified file -- %s", filename);
148 return CAdPlugDatabase::CKey(f);
154 printf("Usage: %s [options] <command> [arguments]\n\n"
156 " add <files> Add (and replace) files to database\n"
157 " list [files] List files (or everything) from database\n"
158 " remove <files> Remove files from database\n"
159 " merge <files> Merge other databases with the current one\n"
161 "Database options:\n"
162 " -d <file> Use different database file\n"
163 #ifdef ADPLUG_DATA_DIR
164 " -s Use system-wide database file (" ADPLUGDB_PATH ")\n"
166 " -t <type> Add as different record type (plain, songinfo, clockspeed)\n"
167 " -c Prompt for record comment\n"
168 " -k Specify keys instead of files on commandline\n"
171 " -q Be more quiet\n"
172 " -v Be more verbose\n"
173 " -h Display this help\n"
174 " -V Display version information\n",
178 static const std::string file2type(const char *filename)
180 CPlayers::const_iterator i;
184 for(i = CAdPlug::players.begin(); i != CAdPlug::players.end(); i++)
185 if((p = (*i)->factory(&opl)))
186 if(p->load(filename)) {
188 return (*i)->filetype;
192 message(MSG_WARN, "unknown filetype -- %s", filename);
193 return UNKNOWN_FILETYPE;
196 static void db_add(const char *filename)
198 CAdPlugDatabase::CRecord *record = CAdPlugDatabase::CRecord::factory(cfg.rtype);
201 message(MSG_ERROR, "adding records is only possible while specifying"
207 message(MSG_PANIC, "internal error (not enough memory?)");
211 record->key = file2key(filename);
212 record->filetype = file2type(filename);
214 std::cin >> record->comment;
216 record->comment = filename;
217 if(record->filetype == UNKNOWN_FILETYPE) { delete record; return; }
218 if(!record->user_read(std::cin, std::cout)) {
219 message(MSG_ERROR, "data entry error");
223 if(mydb.lookup(record->key)) {
224 message(MSG_NOTE, "replacing previous record -- %s", filename);
228 if(mydb.insert(record)) {
229 message(MSG_NOTE, "added record, file type \"%s\" -- %s",
230 record->filetype.c_str(), filename);
233 message(MSG_ERROR, "error adding record to database -- %s", filename);
238 static bool db_resolve(const char *filename)
239 /* Resolves and lists one entry from the database */
241 if(mydb.lookup(file2key(filename))) {
242 message(MSG_NOTE, "viewing entry -- %s", filename);
243 mydb.get_record()->user_write(std::cout);
246 message(MSG_WARN, "no entry in database -- %s", filename);
251 static void db_remove(const char *filename)
252 /* Removes one entry from the database */
254 if(mydb.lookup(file2key(filename))) {
256 message(MSG_NOTE, "deleted entry -- %s", filename);
258 message(MSG_WARN, "no entry in database, could not delete -- %s", filename);
261 static void copyright()
262 /* Print copyright notice and version information */
264 printf("AdPlug database maintenance utility %s\n", CAdPlug::get_version().c_str());
265 printf("Copyright (c) 2002 Riven the Mage <riven@ok.ru>\n"
266 "Copyright (c) 2002, 2003 Simon Peter <dn.tlp@gmx.net>\n");
269 static void db_error(bool dbokay)
270 /* Checks if database is open. Exits program otherwise */
272 if(!dbokay) { // Database could not be opened
273 message(MSG_ERROR, "database could not be opened -- %s", cfg.db_file);
278 static void db_save(void)
279 /* Saves database to file, making path if it doesn't exist yet. */
285 if(!mydb.save(cfg.db_file)) {
288 savedir = cfg.homedir; savedir += "/" ADPLUG_CONFDIR;
289 mkdir(savedir.c_str(), 0755);
290 if(mydb.save(cfg.db_file)) return;
293 message(MSG_ERROR, "could not save database -- %s", cfg.db_file);
297 static void shutdown(void)
299 // Free userdb variable, if applicable
300 if(cfg.homedir && !cfg.usedefaultdb) free(cfg.db_file);
303 /***** Main program *****/
305 int main(int argc, char *argv[])
312 program_name = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 :
313 (strrchr(argv[0], '\\') ? strrchr(argv[0], '\\') + 1 : argv[0]);
317 while((opt = getopt(argc, argv, "d:t:qvhVsck")) != -1)
319 case 'd': cfg.db_file = optarg; break; // Set database file
320 case 't': // Different record type
321 for(i = 0; rtypes[i].typestr; i++)
322 if(!strcmp(rtypes[i].typestr, optarg)) {
323 cfg.rtype = rtypes[i].type;
327 if(!rtypes[i].typestr) {
328 message(MSG_ERROR, "unknown record type -- %s", optarg);
332 case 'q': if(cfg.message_level) cfg.message_level--; break; // Be more quiet
333 case 'v': cfg.message_level++; break; // Be more verbose
334 case 'h': usage(); exit(EXIT_SUCCESS); break; // Display help
335 case 'V': copyright(); exit(EXIT_SUCCESS); break; // Display version
336 case 's': // Use system-wide database
337 #ifdef ADPLUG_DATA_DIR
338 cfg.usedefaultdb = true;
340 message(MSG_WARN, "option not supported on this system -- s");
343 case 'c': cfg.usercomment = true; break; // Prompt for comments
344 case 'k': cfg.cmdkeys = true; break; // Keys on commandline
345 case '?': exit(EXIT_FAILURE);
348 // Check for commands
350 fprintf(stderr, "%s: need a command\n", program_name);
351 fprintf(stderr, "Try '%s -h' for more information.\n", program_name);
355 // Try user's home directory first, before trying the default location.
356 cfg.homedir = getenv("HOME");
357 if(cfg.homedir && !cfg.usedefaultdb) {
358 cfg.db_file = (char *)malloc(strlen(cfg.homedir) + strlen(ADPLUG_CONFDIR) +
359 strlen(ADPLUGDB_FILE) + 3);
360 strcpy(cfg.db_file, cfg.homedir);
361 strcat(cfg.db_file, "/" ADPLUG_CONFDIR "/");
362 strcat(cfg.db_file, ADPLUGDB_FILE);
365 // Load database file
366 message(MSG_DEBUG, "using database -- %s", cfg.db_file);
367 dbokay = mydb.load(cfg.db_file);
370 if(!strcmp(argv[optind], "add")) { // Add file to database
371 if(++optind < argc) {
372 for(;optind < argc; optind++)
373 db_add(argv[optind]);
376 message(MSG_ERROR, "add -- missing file argument");
380 if(!strcmp(argv[optind], "list")) { // List (files from) database
382 if(++optind < argc) {
383 for(;optind < argc; optind++) {
384 db_resolve(argv[optind]);
390 mydb.get_record()->user_write(std::cout);
392 } while(mydb.go_forward());
395 if(!strcmp(argv[optind], "remove")) { // Remove files from database
397 if(++optind < argc) {
398 for(;optind < argc; optind++)
399 db_remove(argv[optind]);
402 message(MSG_ERROR, "remove -- missing file argument");
406 if(!strcmp(argv[optind], "merge")) { // Merge databases together
408 if(++optind < argc) {
409 for(;optind < argc; optind++)
410 if(mydb.load(argv[optind]))
411 message(MSG_NOTE, "merged database -- %s", argv[optind]);
413 message(MSG_WARN, "could not open database -- %s", argv[optind]);
416 message(MSG_ERROR, "merge -- missing file argument");
420 message(MSG_ERROR, "unknown command -- %s", argv[optind]);