diff options
Diffstat (limited to 'kscd/libwm/database.c')
-rw-r--r-- | kscd/libwm/database.c | 1543 |
1 files changed, 1543 insertions, 0 deletions
diff --git a/kscd/libwm/database.c b/kscd/libwm/database.c new file mode 100644 index 00000000..f7f4e7cc --- /dev/null +++ b/kscd/libwm/database.c @@ -0,0 +1,1543 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Försterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Manage the CD database. All these routines assume that the "cd" global + * structure contains current information (as far as the outside world knows; + * obviously it won't contain track titles just after a CD is inserted.) + */ + +#define RCFILE "/.workmanrc" +#define DBFILE "/.workmandb" +#define FUZZFRAMES 75 + +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <time.h> +#include "include/wm_config.h" +#include "include/wm_helpers.h" +#include "include/wm_struct.h" +#include "include/wm_cdinfo.h" +#include "include/wm_cddb.h" +#include "include/wm_index.h" +#include "include/wm_database.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_DB + +#define SWALLOW_LINE(fp) { int _c; while ((_c = getc(fp)) != '\n' && _c != EOF); } + +/* local prototypes */ +char *print_cdinfo(struct wm_cdinfo *pcd, int prefs); +FILE *open_rcfile(const char *name, const char *mode); +int *reset_tracks(void); +int search_db( FILE *fp, int prefs, int scan, int holesize_wanted ); +void spinwheels(int secs); +int lockit(int fd, int type); +void save_globals(FILE *fp); +int save_entry(char *filename, int pref); +/* local prototypes END */ + +static int suppress_locking = 0; /* Turn off locking of datafile (dangerous) */ + +static char *rcfile = NULL; /* Personal rcfile */ +static char *dbfiles = NULL; /* Colon-separated list of databases */ +static char **databases = NULL; /* NULL-terminated list of databases */ + +static char *otherrc = NULL; /* Unrecognized cruft from start of rcfile */ + +static long rcpos, rclen; /* XXX */ + +static int found_in_db, found_in_rc; +static long holepos, firstpos; + +static int fuzz_frames = FUZZFRAMES; + +static int wm_db_save_disabled = FALSE; + +static int cur_playnew = -1; + +extern int cur_ntracks, cur_nsections; + +int mark_a = 0; +int mark_b = 0; + + +/* + * + */ +int wm_db_get_playnew( void ) +{ + return 0; +} + +/* + * split_workmandb() + * + * Split the WORKMANDB environment variable, if any, into a list of database + * files in the global "databases". If WORKMANDB is not available, make + * a single entry with $HOME/DBFILE. + * + * Also, fill the "rcfile" global with the personal preferences filename. + * + * The environment variables should have already been read and placed in the + * "rcfile" and "dbfiles" globals, respectively. + */ +void +split_workmandb( void ) +{ + int ndbs, i; + char *home, *wmdb; + int no_rc = 0, no_db = 0; + + if (rcfile == NULL) + { + if ((home = getenv("HOME")) != NULL) + { + rcfile = malloc(strlen(home) + sizeof(RCFILE)); + if (rcfile == NULL) + { + +nomem: + perror("split_workmandb()"); + exit(1); + } + + strcpy(rcfile, home); + strcat(rcfile, RCFILE); + } + else + no_rc = 1; + + } + + if ((wmdb = dbfiles) == NULL) + { + if ((home = getenv("HOME")) != NULL) + { + wmdb = malloc(strlen(home) + sizeof(DBFILE)); + if (wmdb == NULL) + goto nomem; + + databases = malloc(2 * sizeof (databases[0])); + if (databases == NULL) + goto nomem; + + strcpy(wmdb, home); + strcat(wmdb, DBFILE); + databases[0] = wmdb; + databases[1] = NULL; + } + else + { + static char *emptydb = NULL; + + databases = &emptydb; + no_db = 1; + } + } + else + { + ndbs = 1; + for (home = wmdb; *home; home++) + if (*home == ':') + { + *home = '\0'; + ndbs++; + } + + databases = malloc((ndbs + 1) * sizeof(databases[0])); + if (databases == NULL) + goto nomem; + + for (i = 0; i < ndbs; i++) + { + databases[i] = wmdb; + wmdb += strlen(wmdb) + 1; + } + + databases[i] = NULL; + } + + if (no_db || no_rc) + { +#ifndef NDEBUG + fprintf(stderr, +"WorkMan was run without a home directory, probably by a system daemon.\n"); + fprintf(stderr, "It doesn't know where to find "); + if (no_rc) + { + fprintf(stderr, "your personal preferences file "); + if (no_db) + fprintf(stderr, "or the\ndatabase of CD descriptions"); + } + else + fprintf(stderr, "the database of CD descriptions"); + + fprintf(stderr, +".\nYou can use the X resources \"workman.db.shared\" and \"workman.db.personal\"\nto tell WorkMan where to look.\n"); +#endif + wm_db_save_disabled = TRUE; + } +} + +/* + * print_cdinfo(cd, prefs) + * + * cd A pointer to a cdinfo struct. + * prefs Flag: write personal preferences? + * + * Print a CD's information (in more or less readable form) to a buffer. + * Returns a pointer to the buffer. + * + * XXX - could be more efficient about calling wm_strmcat() and strlen(). + */ +char * +print_cdinfo(struct wm_cdinfo *cd, int prefs) +{ + int i; + char tempbuf[2000]; /* XXX - is this always big enough? */ + static char *cdibuf = NULL; + struct wm_playlist *l; + + sprintf(tempbuf, "\ntracks %d", cd->ntracks); + for (i = 0; i < cur_ntracks; i++) + if (cd->trk[i].section < 2) + sprintf(tempbuf + strlen(tempbuf), " %d", + cd->trk[i].start); + sprintf(tempbuf + strlen(tempbuf), " %d\n", cd->length); + + wm_strmcpy(&cdibuf, tempbuf); + + if (cur_nsections) + { + sprintf(tempbuf, "sections %d", cur_nsections); + /* fixed a bug here */ + for (i = 0; i < cur_ntracks; i++) + if (cd->trk[i].section > 1) + sprintf(tempbuf + strlen(tempbuf), " %d", + cd->trk[i].start); + sprintf(tempbuf + strlen(tempbuf), "\n"); + + wm_strmcat(&cdibuf, tempbuf); + } + + if (prefs) + { + if (cd->autoplay) + wm_strmcat(&cdibuf, "autoplay\n"); + for (l = cd->lists; l != NULL && l->name != NULL; l++) + { + wm_strmcat(&cdibuf, "playlist "); + + i = strlen(cdibuf) - 1; + wm_strmcat(&cdibuf, l->name); + while (cdibuf[++i]) + if (cdibuf[i] == ' ' || cdibuf[i] == '\t') + cdibuf[i] = '_'; + + if (l->list != NULL) + { + for (i = 0; l->list[i]; i++) + ; + sprintf(tempbuf, " %d", i); + wm_strmcat(&cdibuf, tempbuf); + for (i = 0; l->list[i]; i++) + { + sprintf(tempbuf, " %d", l->list[i]); + wm_strmcat(&cdibuf, tempbuf); + } + wm_strmcat(&cdibuf, "\n"); + } + else + wm_strmcat(&cdibuf, " 0\n"); + } + + if (cd->volume) + { + /* + * Have to maintain compatibility with old versions, + * where volume was 0-32. + */ + sprintf(tempbuf, "cdvolume %d\n", (cd->volume * 32) / 100); + wm_strmcat(&cdibuf, tempbuf); + } + + if (cd->playmode) + { + sprintf(tempbuf, "playmode %d\n", cd->playmode); + wm_strmcat(&cdibuf, tempbuf); + } + + if (mark_a) + { + sprintf(tempbuf, "mark %d START\n", mark_a); + wm_strmcat(&cdibuf, tempbuf); + } + if (mark_b) + { + sprintf(tempbuf, "mark %d END\n", mark_b); + wm_strmcat(&cdibuf, tempbuf); + } + + if (cd->otherrc) + wm_strmcat(&cdibuf, cd->otherrc); + + for (i = 0; i < cur_ntracks; i++) + { + if (cd->trk[i].avoid) + { + sprintf(tempbuf, "dontplay %d\n", i + 1); + wm_strmcat(&cdibuf, tempbuf); + } + if (cd->trk[i].volume) + { + sprintf(tempbuf, "volume %d %d\n", i + 1, + (cd->trk[i].volume * 32) / 100); + wm_strmcat(&cdibuf, tempbuf); + } + if (cd->trk[i].otherrc) + wm_strmcat(&cdibuf, cd->trk[i].otherrc); + } + } + else + { + if (cd->cdname[0]) + { + wm_strmcat(&cdibuf, "cdname "); + wm_strmcat(&cdibuf, cd->cdname); + wm_strmcat(&cdibuf, "\n"); + } + + if (cd->artist[0]) + { + wm_strmcat(&cdibuf, "artist "); + wm_strmcat(&cdibuf, cd->artist); + wm_strmcat(&cdibuf, "\n"); + } + + if (cd->otherdb) + wm_strmcat(&cdibuf, cd->otherdb); + + for (i = 0; i < cur_ntracks; i++) + { + if (cd->trk[i].section > 1) + wm_strmcat(&cdibuf, "s-"); + wm_strmcat(&cdibuf, "track "); + if (cd->trk[i].songname != NULL) + wm_strmcat(&cdibuf, cd->trk[i].songname); + wm_strmcat(&cdibuf, "\n"); + if (cd->trk[i].contd) + { + if (cd->trk[i].section > 1) + wm_strmcat(&cdibuf, "s-"); + wm_strmcat(&cdibuf, "continue\n"); + } + if (cd->trk[i].otherdb) + wm_strmcat(&cdibuf, cd->trk[i].otherdb); + } + } + + return (cdibuf); +} /* print_cdinfo() */ + +/* + * Open the rcfile for reading or writing. + * + * name Filename + * mode "r" or "w" + */ +FILE * +open_rcfile(const char *name, const char *mode) +{ + FILE *fp; + struct stat st; + + fp = fopen(name, mode); + if (fp == NULL) + { + if (errno != ENOENT || mode[0] == 'w') + perror(name); + } + else + { + /* Don't let people open directories or devices */ + if (fstat(fileno(fp), &st) < 0) + { + perror(name); + fclose(fp); + return (NULL); + } + +#ifdef S_ISREG + if (! S_ISREG(st.st_mode)) +#else + if ((st.st_mode & S_IFMT) != S_IFREG) +#endif + { + errno = EISDIR; + perror(name); + fclose(fp); + return (NULL); + } + + if (mode[0] == 'w') /* create -- put data in so locks work */ + { + fputs("# WorkMan database file\n", fp); + fclose(fp); + fp = fopen(name, "r+"); + if (fp == NULL) + if (errno != ENOENT) + perror(name); + } + } + + return (fp); +} + +/* + * + * allocate and clear "trackmap". + * + */ +int *reset_tracks(void) +{ + int i, j; + int *trackmap; + /* + * Since we access track numbers indirectly (to handle sections + * with at least a little elegance), the track mapping needs to be + * set up before we read anything. Initially it must assume that + * no sections will appear in this datafile. + */ + trackmap = malloc(sizeof(int) * cur_ntracks); + if (trackmap == NULL) + { + perror("trackmap"); + exit(1); + } + j = 0; + for (i = 0; i < cd->ntracks; i++) + { + trackmap[i] = j; + while (cd->trk[++j].section > 1) + ; + } + return trackmap; +} /* reset_tracks() */ + +/* + * Load a new-format database file, searching for a match with the currently + * inserted CD. Modify the in-core copy of the CD info based on what's found + * in the database. + * + * Returns 1 if there was a match or 0 if not. + * + * fp FILE* of database or rcfile. + * prefs 1 if we're searching .workmanrc, 0 for .workmandb, + * 2 if just reading settings + * scan Scan for "tracks" location and entry size only + * holesize_wanted How big a hole we're looking for, if any. + * + * If a hole was found along the way, update the global "holepos" with its + * starting offset in the file. A hole is defined as a bunch of blank lines + * preceding a "tracks" line. Holepos will contain the best match. + * + * In addition, "firstpos" will be filled with the position of the first + * "tracks" keyword, so we know how much room is available for global + * settings at the rcfile's start. + */ +int +search_db( FILE *fp, int prefs, int scan, int holesize_wanted ) + +{ + char keyword[64], listname[64], *c; + int b, i, j, track = 0, listsize, ntracks, scratch, searching = 1; + int *trackmap = 0, gotsections = 0; + int fudge, maxfudge, sizediff, bestfudge = 0; + long pos = 0, thisholepos = -1, holesize = 99991239; + struct wm_playlist *l; + + rclen = 0; + + wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: Reached search_db()\n" ); + + /* We may not find any holes at all! */ + if (holesize_wanted) + holepos = -1; + + if( prefs != 2 ) + trackmap = reset_tracks(); + + if (prefs) + freeup(&otherrc); + firstpos = -1; + while (! feof(fp)) + { + pos = ftell(fp); + keyword[0] = '\0'; + do + b = getc(fp); + while (b != EOF && b != '\n' && isspace(b)); + + if (b == EOF || feof(fp)) + break; + + if (b != '\n') + { + keyword[0] = b; + fscanf(fp, "%62s", &keyword[1]); + } + if (keyword[0] == '\0') /* Blank line. */ + { + if (thisholepos < 0) + thisholepos = pos; + continue; + } + + /* Strip off "s-" if we've seen a "sections" keyword */ + if (gotsections && keyword[0] == 's' && keyword[1] == '-') + { + for (c = &keyword[2]; (c[-2] = *c) != '\0'; c++) + ; + wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: stripped off the 's-'. Result is %s\n", keyword); + } + + /* If this is the start of a CD entry, see if it matches. */ + if (! strcmp(keyword, "tracks")) + { + if (prefs == 2) + break; + + /* Is this the end of a hole? */ + if (holesize_wanted && (thisholepos >= 0)) + { + /* Yep. Is it better than the last one? */ + if (pos - thisholepos < holesize && pos - + thisholepos >= holesize_wanted) + { + holepos = thisholepos; + holesize = pos - thisholepos; + } + thisholepos = -1; + } + + /* Is it the start of the CD entries? */ + if (firstpos == -1) + firstpos = pos; + + /* Is this the end of the entry we really wanted? */ + if (! searching) + { + rclen = pos - rcpos; + break; + } + + /* If we have a near match, indicate that we + should stop reading tracks, etc now */ + if (searching == 2) + { + searching = 3; + continue; + } + + fscanf(fp, "%d", &ntracks); + + if (ntracks != cd->ntracks) + { +chomp: + SWALLOW_LINE(fp); + continue; + } + + fudge = 0; + maxfudge = (ntracks * fuzz_frames) >> 1; + track = 0; + for (i = 0; i < ntracks; i++) + { + fscanf(fp, "%d", &scratch); + if (scratch != cd->trk[track].start) + { + sizediff = abs(scratch - cd->trk[track].start); + if (sizediff > fuzz_frames || + (sizediff && scan)) + break; + fudge += sizediff; + } + while (cd->trk[++track].section > 1) + ; + } + if (i != ntracks) + goto chomp; + + if (fudge > 0) /* best near match? */ + { + if (fudge > maxfudge) + goto chomp; + if (bestfudge == 0 || fudge < bestfudge) + bestfudge = fudge; + else + goto chomp; + rcpos = pos; + track = 0; + searching = 2; + } + else /* probably exact match */ + { + fscanf(fp, "%d", &scratch); + + if (scratch != -1 && scratch != cd->length) + goto chomp; + + /* Found it! */ + rcpos = pos; + track = 0; + searching = 0; + } + + SWALLOW_LINE(fp); /* Get rid of newline */ + } + + /* Global mode stuff goes here */ + else if (! strcmp(keyword, "cddbprotocol")) + { + getc(fp); + i = getc(fp); /* only first letter is used */ + cddb.protocol = i == 'c' ? 1 : + i == 'h' ? 2 : 3 ; + do + i = getc(fp); + while (i != '\n' && i != EOF); + } + + else if (! strcmp(keyword, "cddbserver")) + { + getc(fp); /* lose the space */ + if (cddb.cddb_server[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cddb.cddb_server, + sizeof(cddb.cddb_server), fp); + if ((i = strlen(cddb.cddb_server))) + cddb.cddb_server[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "cddbmailadress")) + { + getc(fp); /* lose the space */ + if (cddb.mail_adress[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cddb.mail_adress, + sizeof(cddb.mail_adress), fp); + if ((i = strlen(cddb.mail_adress))) + cddb.mail_adress[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "cddbpathtocgi")) + { + getc(fp); /* lose the space */ + if (cddb.path_to_cgi[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cddb.path_to_cgi, + sizeof(cddb.path_to_cgi), fp); + if ((i = strlen(cddb.path_to_cgi))) + cddb.path_to_cgi[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "cddbproxy")) + { + getc(fp); /* lose the space */ + if (cddb.proxy_server[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cddb.proxy_server, + sizeof(cddb.proxy_server), fp); + if ((i = strlen(cddb.proxy_server))) + cddb.proxy_server[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "whendone")) + { + getc(fp); + i = getc(fp); /* only first letter is used */ + if (cur_stopmode == -1) /* user's setting preferred */ + cur_stopmode = i == 's' ? 0 : i == 'r' ? 1 : 2; + do + i = getc(fp); + while (i != '\n' && i != EOF); + } + + else if (! strcmp(keyword, "playnew")) + { + if (cur_playnew == -1) + cur_playnew = 1; + SWALLOW_LINE(fp); + } + + /* If we're searching, skip to the next "tracks" line. */ + else if (((searching & 1)|| scan) + && !(prefs && firstpos == -1)) + SWALLOW_LINE(fp) + + else if (! strcmp(keyword, "sections")) + { + gotsections = 1; + fscanf(fp, "%d", &ntracks); + + free(trackmap); + trackmap = (int *) malloc(sizeof(int) * + (cur_ntracks + ntracks)); + if (trackmap == NULL) + { + perror("section mapping"); + exit(1); + } + + /* + * If sections are already defined, use these as a + * reference, mapping this CD entry's section numbers + * to the ones in core. + * + * Otherwise, split the CD up according to the sections + * listed here. + */ + if (cur_nsections) + { + track = 0; + i = 0; + while (ntracks) + { + ntracks--; + fscanf(fp, "%d", &scratch); + while (scratch > cd->trk[track].start) + { + if (cd->trk[track].section < 2) + trackmap[i++] = track; + ++track; + + if (track == cur_ntracks) + break; + } + + /* rc has later sections than db... */ + if (track == cur_ntracks) + break; + + /* Matches can be approximate */ + if (scratch+75 > cd->trk[track].start && + scratch-75 < cd->trk[track].start) + trackmap[i++] = track++; + else + trackmap[i++] = -1; + + if (track == cur_ntracks) + break; + } + + /* This only happens if track == cur_ntracks */ + while (ntracks--) + trackmap[i++] = -1; + + while (track < cur_ntracks) + { + if (cd->trk[track].section < 2) + trackmap[i++] = track; + track++; + } + + track = 0; + SWALLOW_LINE(fp); + } + else + { + while (ntracks--) + { + fscanf(fp, "%d", &scratch); + split_trackinfo(scratch); + } + + for (i = 0; i < cur_ntracks; i++) + { + trackmap[i] = i; + /* split_trackinfo() sets this */ + cd->trk[i].contd = 0; + } + + SWALLOW_LINE(fp); + } + } + + else if (! strcmp(keyword, "track")) + { + char buf[502]; + + getc(fp); /* lose the space */ + + /* don't overwrite existing track names. */ + /* However, overwrite them if there was a "bad" fuzzy match before */ + if ((trackmap[track] == -1 || track > (cd->ntracks + cur_nsections)) && (searching == 2)) + SWALLOW_LINE(fp) + else if (cd->trk[trackmap[track]].songname && + cd->trk[trackmap[track]].songname[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(buf, sizeof(buf), fp); + wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG, "found track %s\n", buf); + if( (i = strlen(buf)) ) + buf[i - 1] = '\0'; + wm_strmcpy(&cd->trk[trackmap[track]].songname, buf); + } + track++; + } + + else if (! strcmp(keyword, "playmode")) + fscanf(fp, "%d", &cd->playmode); + + else if (! strcmp(keyword, "autoplay")) + cd->autoplay = 1; + + else if (! strcmp(keyword, "cdname")) + { + /* because of fuzzy matching that may change + the disk contents, we reset everything when + we find the name, in hopes that we will recover + most, if not all, of the information from the + file. */ +/* + * nasty bug was here. Was it? BUGBUGBUG + * + * wipe_cdinfo(); + * trackmap = reset_tracks(); + */ + + getc(fp); /* lose the space */ + /* don't overwrite existing cd name. */ + if (cd->cdname[0] && (searching == 2)) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + if (searching > 1) + { + strcpy(cd->cdname, "Probably://"); + fgets(cd->cdname + strlen(cd->cdname), sizeof(cd->cdname), fp); + } + else + { + fgets(cd->cdname, sizeof(cd->cdname), fp); + } + if ( (i = strlen(cd->cdname)) ) + cd->cdname[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "artist")) + { + getc(fp); /* lose the space */ + /* don't overwrite existing artist names. */ + if (cd->artist[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cd->artist, sizeof(cd->artist), fp); + if( (i = strlen(cd->artist)) ) + cd->artist[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "cdvolume")) + { + fscanf(fp, "%d", &cd->volume); + cd->volume = (cd->volume * 100) / 32; + } + + else if (! strcmp(keyword, "dontplay")) + { + fscanf(fp, "%d", &i); + if (trackmap[i - 1] != -1) + cd->trk[trackmap[i - 1]].avoid = 1; + } + + else if (! strcmp(keyword, "continue")) + { + if (trackmap[track - 1] != -1) + cd->trk[trackmap[track - 1]].contd = 1; + } + + else if (! strcmp(keyword, "volume")) + { + fscanf(fp, "%d", &i); + if (trackmap[i - 1] == -1) + SWALLOW_LINE(fp) + else + { + i = trackmap[i - 1]; + fscanf(fp, "%d", &cd->trk[i].volume); + cd->trk[i].volume = (cd->trk[i].volume*100)/32; + if (cd->trk[i].volume > 32) + cd->trk[i].volume = 0; + } + } + + else if (! strcmp(keyword, "playlist")) + { + getc(fp); + fscanf(fp, "%63s", listname); + +/* XXX take this out at some point */ + if (! strcmp(listname, "Default")) + strcpy(listname, "List A"); + + for (i = 0; listname[i]; i++) + if (listname[i] == '_') + listname[i] = ' '; + + l = new_playlist(cd, listname); + if (l == NULL) + { +plnomem: + perror("playlist read"); + exit(1); + } + + fscanf(fp, "%d", &listsize); + + l->list = malloc(sizeof(int) * (listsize + 1)); + if (l->list == NULL) + goto plnomem; + + /* Leave out tracks that weren't in .workmandb. */ + j = 0; + for (i = 0; i < listsize; i++) + { + fscanf(fp, "%d", &scratch); + scratch = trackmap[scratch - 1]; + if (scratch != -1) + l->list[j++] = scratch + 1; + } + + l->list[j] = 0; + } + + else if (! strcmp(keyword, "mark")) + { + int mark_val = -1, mark_namelen; + char mark_name[32]; + + fscanf(fp, "%d", &mark_val); + if (mark_val == -1) + goto chomp; + + if (getc(fp) != ' ') + continue; + + fgets(mark_name, sizeof(mark_name), fp); + if( ( mark_namelen = strlen(mark_name)) ) + mark_name[mark_namelen - 1] = '\0'; + +/* + if (! strcmp(mark_name, "START")) + set_abtimer(0, mark_val); + else if (! strcmp(mark_name, "END")) + set_abtimer(1, mark_val); + +*/ + } + + /* Unrecognized keyword. Put it in the right place. */ + else + { + char **buf, input[BUFSIZ]; + + if (track && trackmap[track - 1] == -1) + { + SWALLOW_LINE(fp); + continue; + } + + i = track ? trackmap[track - 1] : 0; + buf = prefs ? i ? &cd->trk[i].otherrc : &cd->otherrc : + i ? &cd->trk[i].otherdb : &cd->otherdb; + if (firstpos == -1) { + if (prefs) { + buf = &otherrc; + } else { + goto chomp; + } /* if() else */ + } /* if() */ + wm_strmcat(buf, keyword); + do { + input[sizeof(input) - 1] = 'x'; + fgets(input, sizeof(input), fp); + wm_strmcat(buf, input); + } while (input[sizeof(input) - 1] != 'x'); + } + } + + if (rclen == 0 && !searching) + rclen = pos - rcpos; + + if (searching > 1) /* A near match has been found. Good enough. */ + searching = 0; + + cddb_struct2cur(); + return (! searching); + +} /* search_db() */ + +/* + * Delay some amount of time without using interval timers. + */ +void +spinwheels(int secs) { + struct timeval tv; + + tv.tv_usec = 0; + tv.tv_sec = secs; + select(0, NULL, NULL, NULL, &tv); +} /* spinwheels() */ + +/* + * lockit(fd, type) + * + * fd file descriptor + * type lock type + * + * Lock a file. Time out after a little while if we can't get a lock; + * this usually means the locking system is broken. + * + * Unfortunately, if there are lots of people contending for a lock, + * this can result in the file not getting locked when it probably should. + */ +int +lockit(int fd, int type) +{ + struct flock fl; + int result, timer = 0; + + if (suppress_locking) + return (0); + + fl.l_type = type; + fl.l_whence = 0; + fl.l_start = 0; + fl.l_len = 0; + + while ((result = fcntl(fd, F_SETLK, &fl)) < 0) + { + if (errno != EACCES || errno != EAGAIN) + break; + if (timer++ == 30) + { + errno = ETIMEDOUT; + break; + } + + spinwheels(1); + } + + return (result); +} /* lockit() */ + +/* + * Search all the database files and our personal preference file for + * more information about the current CD. + */ +void +load( void ) +{ + FILE *fp; + char **dbfile; + int locked = 0; + int dbfound = 0, *trklist, i; + unsigned long dbpos; + +/* This is some kind of profiling code. I don't change it + to wm_lib_message() for now... */ +#ifndef NDEBUG + long t1, t2; + if( getenv( "WORKMAN_DEBUG" ) != NULL ) + { + time(&t1); + printf("%s (%d): search start = %ld\n", __FILE__, __LINE__, t1); + fflush(stdout); + } +#endif + + dbfile = databases; + + found_in_db = 0; + + /* Turn the cd->trk array into a simple array of ints. */ + trklist = (int *)malloc(sizeof(int) * cd->ntracks); + for (i = 0; i < cd->ntracks; i++) + trklist[i] = cd->trk[i].start; + + do { + if (*dbfile && idx_find_entry(*dbfile, cd->ntracks, trklist, + cd->length * 75, 0, &dbpos) == 0) + dbfound = 1; + + fp = *dbfile ? open_rcfile(*dbfile, "r") : NULL; + if (fp != NULL) + { + if (lockit(fileno(fp), F_RDLCK)) + perror("Couldn't get read (db) lock"); + else + locked = 1; + + if (dbfound) + fseek(fp, dbpos, 0); + + if (search_db(fp, 0, 0, 0)) + { + found_in_db = 1; + cd->whichdb = *dbfile; + } + + if (locked && lockit(fileno(fp), F_UNLCK)) + perror("Couldn't relinquish (db) lock"); + + fclose(fp); + } + } while (*++dbfile != NULL && cd->whichdb == NULL); + +#ifndef NDEBUG + if( getenv( "WORKMAN_DEBUG" ) != NULL ) + { + time(&t2); + printf("%s (%d): db search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1); + fflush(stdout); + } +#endif + + fp = rcfile ? open_rcfile(rcfile, "r") : NULL; + if (fp != NULL) + { + locked = 0; + if (lockit(fileno(fp), F_RDLCK)) + perror("Couldn't get read (rc) lock"); + else + locked = 1; + + rcpos = 0; + found_in_rc = search_db(fp, 1, 0, 0); + if (! found_in_rc) + cd->autoplay = wm_db_get_playnew(); + + if (locked && lockit(fileno(fp), F_UNLCK)) + perror("Couldn't relinquish (rc) lock"); + + fclose(fp); + } + + free(trklist); + + if (cur_playnew == -1) + cur_playnew = 0; + +#ifndef NDEBUG + if( getenv( "WORKMAN_DEBUG" ) != NULL ) + { + time(&t2); + printf("%s (%d): search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1); + fflush(stdout); + } +#endif +} /* load() */ + +/* + * Load program settings from the rcfile. + */ +void +load_settings( void ) +{ + FILE *fp; + int locked; + + fp = rcfile ? open_rcfile(rcfile, "r") : NULL; + if (fp != NULL) + { + locked = 0; + if (lockit(fileno(fp), F_RDLCK)) + perror("Couldn't get read (rc) lock"); + else + locked = 1; + + rcpos = 0; + found_in_rc = search_db(fp, 2, 0, 0); + if (! found_in_rc) + cd->autoplay = wm_db_get_playnew(); + + if (locked && lockit(fileno(fp), F_UNLCK)) + perror("Couldn't relinquish (rc) lock"); + + fclose(fp); + } +} /* load_settings() */ + +/* + * save_globals() + * + * Save the global preferences, scooting CD entries to the end if needed. + * The assumption here is that the rcfile is locked, and that firstpos has + * been set by a previous scan. + */ +void +save_globals(FILE *fp) +{ + char *globes = NULL, *cdentry = NULL, temp[100]; + long curpos; + int globesize, hit_cdent = 0, c = 0; + + if (otherrc) + wm_strmcpy(&globes, otherrc); + + if (cddb.protocol) + { + sprintf(temp, "cddbprotocol "); + switch(cddb.protocol) + { + case 1: /* cddbp */ + sprintf(temp + strlen(temp), "cddbp\n"); + break; + case 2: /* http */ + sprintf(temp + strlen(temp), "http\n"); + break; + case 3: /* proxy */ + sprintf(temp + strlen(temp), "proxy\n"); + break; + default: + break; + } + wm_strmcat(&globes, temp); + + if(cddb.mail_adress[0]) + { + sprintf(temp,"cddbmailadress %s\n", + cddb.mail_adress); + wm_strmcat(&globes, temp); + } + + if(cddb.cddb_server[0]) + { + sprintf(temp,"cddbserver %s\n", + cddb.cddb_server); + wm_strmcat(&globes, temp); + } + + if(cddb.path_to_cgi[0]) + { + sprintf(temp,"cddbpathtocgi %s\n", + cddb.mail_adress); + wm_strmcat(&globes, temp); + } + + if(cddb.proxy_server[0]) + { + sprintf(temp,"cddbproxy %s\n", + cddb.mail_adress); + wm_strmcat(&globes, temp); + } + } + + if (cur_stopmode == 1 || cur_stopmode == 2) + { + sprintf(temp, "whendone %s\n", cur_stopmode == 1 ? "repeat" : + "eject"); + wm_strmcat(&globes, temp); + } + + if (cur_playnew == 1) + wm_strmcat(&globes, "playnew\n"); + + curpos = firstpos; + if (curpos < 0) + curpos = 0; + + fseek(fp, curpos, SEEK_SET); + + if (firstpos < (globesize = globes != NULL ? strlen(globes) : 0)) + { + while (1) + { + temp[sizeof(temp)-1] = 'x'; + + if (fgets(temp, sizeof(temp), fp) == NULL) + { + fseek(fp, 0, SEEK_SET); + if (globes != NULL) + { + fwrite(globes, globesize, 1, fp); + free(globes); + } + if (cdentry != NULL) + { + fwrite(cdentry, strlen(cdentry), 1, fp); + free(cdentry); + } + return; + } + + if (! strncmp(temp, "tracks ", 7)) + { + hit_cdent = 1; + if (curpos >= globesize) + break; + } + + if (! hit_cdent) + { + curpos += strlen(temp); + if (temp[sizeof(temp)-1] == '\0') + while ((c = getc(fp)) != '\n' && + c != EOF) + curpos++; + if (c == '\n') + curpos++; + + continue; + } + + wm_strmcat(&cdentry, temp); + curpos += strlen(temp); + while (temp[sizeof(temp)-1] == '\0') + { + temp[sizeof(temp)-1] = 'x'; + if (fgets(temp, sizeof(temp), fp) == NULL) + break; + wm_strmcat(&cdentry, temp); + curpos += strlen(temp); + } + } + + if (cdentry != NULL) + { + fseek(fp, 0, SEEK_END); + fwrite(cdentry, strlen(cdentry), 1, fp); + free(cdentry); + } + } + + if (globes != NULL) + { + fseek(fp, 0, SEEK_SET); + fwrite(globes, globesize, 1, fp); + free(globes); + } + + while (globesize++ < curpos) + putc('\n', fp); +} /* save_globals() */ + +/* + * save_entry() + * + * Save the CD information to one database. + * + * filename Database to save to. + * pref 0 for hard data, 1 for preferences. + * + * If an entry for this CD exists already, overwrite it with the new entry + * if the new entry is the same size or smaller, or with newlines if the new + * entry is larger (in which case the new entry is appended to the file.) + * + * Also, if the preference information is being updated, save it to the + * file while we've got it locked. Scoot stuff from the beginning of + * the file to the end as needed to facilitate this. + * + * XXX Preference-saving should probably be done elsewhere, like in an + * Apply button on the Goodies popup, and in any case needs to go to a + * different file (.Xdefaults?) + * + * Returns 0 on success. + */ +int +save_entry(char *filename, int pref) +{ + FILE *fp; + char *buf; + int len, i, locked = 0; + + + if( filename == NULL ) + return (-1); + + fp = open_rcfile(filename, "r+"); + if (fp == NULL) + { + if (errno == ENOENT) /* doesn't exist already */ + fp = open_rcfile(filename, "w"); + if (fp == NULL) + return (-1); + } + + if (lockit(fileno(fp), F_WRLCK)) + perror("Warning: Couldn't get write lock"); + else + locked = 1; + + buf = print_cdinfo(cd, pref); + len = strlen(buf); /* doesn't return if there's an error */ + + rcpos = -1; + search_db(fp, pref, 1, len); + if (rcpos != -1) /* XXX */ + { + /* + * Jump to the entry's position in the database file, if + * it was found. + */ + fseek(fp, rcpos, SEEK_SET); + + if (rclen >= len && holepos == -1) + { + /* + * If the new entry will fit in the space occupied by + * the old one, overwrite the old one and make a hole + * of the appropriate size at its end. + * + * No need to update the index file in this case, as + * the entry's position hasn't changed. + */ + fputs(buf, fp); + for (i = len; i < rclen; i++) + fputc('\n', fp); + } + else + { + /* + * Overwrite the old entry with a hole and delete + * its pointer in the index file. + */ + for (i = 0; i < rclen; i++) + fputc('\n', fp); + idx_delete_entry(filename, cd->trk[cd->ntracks-1].start, + 0, rcpos); + + rcpos = -1; + } + } + + /* + * Old entry wasn't found, or its new version wouldn't fit where + * the old one was. + */ + if (rcpos == -1) + { + /* + * Write the new entry in a hole, if there is one, + * or at the end of the file. + */ + if (holepos >= 0) + { + fseek(fp, holepos, SEEK_SET); + if (holepos < firstpos) + firstpos = holepos; + } + else + { + fseek(fp, 0, SEEK_END); + holepos = ftell(fp); + } + fputs(buf, fp); + + /* + * Write a new index entry for this CD. + */ + idx_write_entry(filename, cd->trk[cd->ntracks - 1].start, + holepos); + } + + if (pref) + save_globals(fp); + + fflush(fp); + + if (locked && lockit(fileno(fp), F_UNLCK)) + perror("Warning: Couldn't relinquish write lock"); + + fclose(fp); + + return (0); +} /* save_entry() */ + +/* + * save() + * + * Save CD information to the appropriate datafile (the first file in the + * list, unless the entry came from another database file) and to the + * personal prefs file. + */ +int +save( void ) +{ + + if( wm_db_save_disabled == FALSE ) + { + if (save_entry(rcfile, 1)) + return (0); + + if (cd->whichdb == NULL || access(cd->whichdb, W_OK)) + cd->whichdb = databases[0]; + + if (save_entry(cd->whichdb, 0)) + return (0); + + return( WM_DB_SAVE_ERROR ); + } else { + return( WM_DB_SAVE_DISABLED ); + } +} /* save() */ |