Add missing libraries to examples' Makefiles
[dockapps.git] / ascd / libworkman / database.c
blob927679a75db3adcaf9e7f7ee0b40ea9515aaa2a9
1 /*
2 * $Id: database.c,v 1.8 1999/05/28 03:35:54 dirk Exp $
4 * This file is part of WorkMan, the civilized CD player library
5 * (c) 1991-1997 by Steven Grimm (original author)
6 * (c) by Dirk Försterling (current 'author' = maintainer)
7 * The maintainer can be contacted by his e-mail address:
8 * milliByte@DeathsDoor.com
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the Free
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Manage the CD database. All these routines assume that the "cd" global
26 * structure contains current information (as far as the outside world knows;
27 * obviously it won't contain track titles just after a CD is inserted.)
30 static char database_id[] = "$Id: database.c,v 1.8 1999/05/28 03:35:54 dirk Exp $";
32 #define RCFILE "/.workmanrc"
33 #define DBFILE "/.workmandb"
34 #define FUZZFRAMES 75
36 #include <errno.h>
37 #include <stdio.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <ctype.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/time.h>
46 #include <sys/stat.h>
47 #include "include/wm_config.h"
48 #include "include/wm_helpers.h"
49 #include "include/wm_struct.h"
50 #include "include/wm_cdinfo.h"
51 #include "include/wm_cddb.h"
52 #include "include/wm_index.h"
53 #include "include/wm_database.h"
55 #define WM_MSG_CLASS WM_MSG_CLASS_DB
57 #define SWALLOW_LINE(fp) { int c; while ((c = getc(fp)) != '\n' && c != EOF); }
59 int suppress_locking = 0; /* Turn off locking of datafile (dangerous) */
61 char *rcfile = NULL; /* Personal rcfile */
62 char *dbfiles = NULL; /* Colon-separated list of databases */
63 char **databases = NULL; /* NULL-terminated list of databases */
65 char *otherrc = NULL; /* Unrecognized cruft from start of rcfile */
67 long rcpos, rclen; /* XXX */
69 int found_in_db, found_in_rc;
70 long holepos, firstpos;
72 int fuzz_frames = FUZZFRAMES;
74 int wm_db_save_disabled = FALSE;
76 int cur_playnew = -1;
78 extern int cur_ntracks, cur_nsections;
80 int mark_a = 0;
81 int mark_b = 0;
87 int wm_db_get_playnew( void )
89 return 0;
93 * split_workmandb()
95 * Split the WORKMANDB environment variable, if any, into a list of database
96 * files in the global "databases". If WORKMANDB is not available, make
97 * a single entry with $HOME/DBFILE.
99 * Also, fill the "rcfile" global with the personal preferences filename.
101 * The environment variables should have already been read and placed in the
102 * "rcfile" and "dbfiles" globals, respectively.
104 void
105 split_workmandb( void )
107 int ndbs, i;
108 char *home, *wmdb;
109 int no_rc = 0, no_db = 0;
111 if (rcfile == NULL)
113 if ((home = getenv("HOME")) != NULL)
115 rcfile = malloc(strlen(home) + sizeof(RCFILE));
116 if (rcfile == NULL)
119 nomem:
120 perror("split_workmandb()");
121 exit(1);
124 strcpy(rcfile, home);
125 strcat(rcfile, RCFILE);
127 else
128 no_rc = 1;
132 if ((wmdb = dbfiles) == NULL)
134 if ((home = getenv("HOME")) != NULL)
136 wmdb = malloc(strlen(home) + sizeof(DBFILE));
137 if (wmdb == NULL)
138 goto nomem;
140 databases = malloc(2 * sizeof (databases[0]));
141 if (databases == NULL)
142 goto nomem;
144 strcpy(wmdb, home);
145 strcat(wmdb, DBFILE);
146 databases[0] = wmdb;
147 databases[1] = NULL;
149 else
151 static char *emptydb = NULL;
153 databases = &emptydb;
154 no_db = 1;
157 else
159 ndbs = 1;
160 for (home = wmdb; *home; home++)
161 if (*home == ':')
163 *home = '\0';
164 ndbs++;
167 databases = malloc((ndbs + 1) * sizeof(databases[0]));
168 if (databases == NULL)
169 goto nomem;
171 for (i = 0; i < ndbs; i++)
173 databases[i] = wmdb;
174 wmdb += strlen(wmdb) + 1;
177 databases[i] = NULL;
180 if (no_db || no_rc)
182 fprintf(stderr,
183 "WorkMan was run without a home directory, probably by a system daemon.\n");
184 fprintf(stderr, "It doesn't know where to find ");
185 if (no_rc)
187 fprintf(stderr, "your personal preferences file ");
188 if (no_db)
189 fprintf(stderr, "or the\ndatabase of CD descriptions");
191 else
192 fprintf(stderr, "the database of CD descriptions");
194 fprintf(stderr,
195 ".\nYou can use the X resources \"workman.db.shared\" and \"workman.db.personal\"\nto tell WorkMan where to look.\n");
197 wm_db_save_disabled = TRUE;
202 * print_cdinfo(cd, prefs)
204 * cd A pointer to a cdinfo struct.
205 * prefs Flag: write personal preferences?
207 * Print a CD's information (in more or less readable form) to a buffer.
208 * Returns a pointer to the buffer.
210 * XXX - could be more efficient about calling wm_strmcat() and strlen().
212 char *
213 print_cdinfo(struct wm_cdinfo *cd, int prefs)
215 int i;
216 char tempbuf[2000]; /* XXX - is this always big enough? */
217 static char *cdibuf = NULL;
218 struct wm_playlist *l;
220 sprintf(tempbuf, "\ntracks %d", cd->ntracks);
221 for (i = 0; i < cur_ntracks; i++)
222 if (cd->trk[i].section < 2)
223 sprintf(tempbuf + strlen(tempbuf), " %d",
224 cd->trk[i].start);
225 sprintf(tempbuf + strlen(tempbuf), " %d\n", cd->length);
227 wm_strmcpy(&cdibuf, tempbuf);
229 if (cur_nsections)
231 sprintf(tempbuf, "sections %d", cur_nsections);
232 /* fixed a bug here */
233 for (i = 0; i < cur_ntracks; i++)
234 if (cd->trk[i].section > 1)
235 sprintf(tempbuf + strlen(tempbuf), " %d",
236 cd->trk[i].start);
237 sprintf(tempbuf + strlen(tempbuf), "\n");
239 wm_strmcat(&cdibuf, tempbuf);
242 if (prefs)
244 if (cd->autoplay)
245 wm_strmcat(&cdibuf, "autoplay\n");
246 for (l = cd->lists; l != NULL && l->name != NULL; l++)
248 wm_strmcat(&cdibuf, "playlist ");
250 i = strlen(cdibuf) - 1;
251 wm_strmcat(&cdibuf, l->name);
252 while (cdibuf[++i])
253 if (cdibuf[i] == ' ' || cdibuf[i] == '\t')
254 cdibuf[i] = '_';
256 if (l->list != NULL)
258 for (i = 0; l->list[i]; i++)
260 sprintf(tempbuf, " %d", i);
261 wm_strmcat(&cdibuf, tempbuf);
262 for (i = 0; l->list[i]; i++)
264 sprintf(tempbuf, " %d", l->list[i]);
265 wm_strmcat(&cdibuf, tempbuf);
267 wm_strmcat(&cdibuf, "\n");
269 else
270 wm_strmcat(&cdibuf, " 0\n");
273 if (cd->volume)
276 * Have to maintain compatibility with old versions,
277 * where volume was 0-32.
279 sprintf(tempbuf, "cdvolume %d\n", (cd->volume * 32) / 100);
280 wm_strmcat(&cdibuf, tempbuf);
283 if (cd->playmode)
285 sprintf(tempbuf, "playmode %d\n", cd->playmode);
286 wm_strmcat(&cdibuf, tempbuf);
289 if (mark_a)
291 sprintf(tempbuf, "mark %d START\n", mark_a);
292 wm_strmcat(&cdibuf, tempbuf);
294 if (mark_b)
296 sprintf(tempbuf, "mark %d END\n", mark_b);
297 wm_strmcat(&cdibuf, tempbuf);
300 if (cd->otherrc)
301 wm_strmcat(&cdibuf, cd->otherrc);
303 for (i = 0; i < cur_ntracks; i++)
305 if (cd->trk[i].avoid)
307 sprintf(tempbuf, "dontplay %d\n", i + 1);
308 wm_strmcat(&cdibuf, tempbuf);
310 if (cd->trk[i].volume)
312 sprintf(tempbuf, "volume %d %d\n", i + 1,
313 (cd->trk[i].volume * 32) / 100);
314 wm_strmcat(&cdibuf, tempbuf);
316 if (cd->trk[i].otherrc)
317 wm_strmcat(&cdibuf, cd->trk[i].otherrc);
320 else
322 if (cd->cdname[0])
324 wm_strmcat(&cdibuf, "cdname ");
325 wm_strmcat(&cdibuf, cd->cdname);
326 wm_strmcat(&cdibuf, "\n");
329 if (cd->artist[0])
331 wm_strmcat(&cdibuf, "artist ");
332 wm_strmcat(&cdibuf, cd->artist);
333 wm_strmcat(&cdibuf, "\n");
336 if (cd->otherdb)
337 wm_strmcat(&cdibuf, cd->otherdb);
339 for (i = 0; i < cur_ntracks; i++)
341 if (cd->trk[i].section > 1)
342 wm_strmcat(&cdibuf, "s-");
343 wm_strmcat(&cdibuf, "track ");
344 if (cd->trk[i].songname != NULL)
345 wm_strmcat(&cdibuf, cd->trk[i].songname);
346 wm_strmcat(&cdibuf, "\n");
347 if (cd->trk[i].contd)
349 if (cd->trk[i].section > 1)
350 wm_strmcat(&cdibuf, "s-");
351 wm_strmcat(&cdibuf, "continue\n");
353 if (cd->trk[i].otherdb)
354 wm_strmcat(&cdibuf, cd->trk[i].otherdb);
358 return (cdibuf);
359 } /* print_cdinfo() */
362 * Open the rcfile for reading or writing.
364 * name Filename
365 * mode "r" or "w"
367 FILE *
368 open_rcfile(char *name, char *mode)
370 FILE *fp;
371 struct stat st;
373 fp = fopen(name, mode);
374 if (fp == NULL)
376 if (errno != ENOENT || mode[0] == 'w')
377 perror(name);
379 else
381 /* Don't let people open directories or devices */
382 if (fstat(fileno(fp), &st) < 0)
384 perror(name);
385 fclose(fp);
386 return (NULL);
389 #ifdef S_ISREG
390 if (! S_ISREG(st.st_mode))
391 #else
392 if ((st.st_mode & S_IFMT) != S_IFREG)
393 #endif
395 errno = EISDIR;
396 perror(name);
397 fclose(fp);
398 return (NULL);
401 if (mode[0] == 'w') /* create -- put data in so locks work */
403 fputs("# WorkMan database file\n", fp);
404 fclose(fp);
405 fp = fopen(name, "r+");
406 if (fp == NULL)
407 if (errno != ENOENT)
408 perror(name);
412 return (fp);
417 * allocate and clear "trackmap".
420 int *reset_tracks(void)
422 int i, j;
423 int *trackmap;
425 * Since we access track numbers indirectly (to handle sections
426 * with at least a little elegance), the track mapping needs to be
427 * set up before we read anything. Initially it must assume that
428 * no sections will appear in this datafile.
430 trackmap = malloc(sizeof(int) * cur_ntracks);
431 if (trackmap == NULL)
433 perror("trackmap");
434 exit(1);
436 j = 0;
437 for (i = 0; i < cd->ntracks; i++)
439 trackmap[i] = j;
440 while (cd->trk[++j].section > 1)
443 return trackmap;
444 } /* reset_tracks() */
447 * Load a new-format database file, searching for a match with the currently
448 * inserted CD. Modify the in-core copy of the CD info based on what's found
449 * in the database.
451 * Returns 1 if there was a match or 0 if not.
453 * fp FILE* of database or rcfile.
454 * prefs 1 if we're searching .workmanrc, 0 for .workmandb,
455 * 2 if just reading settings
456 * scan Scan for "tracks" location and entry size only
457 * holesize_wanted How big a hole we're looking for, if any.
459 * If a hole was found along the way, update the global "holepos" with its
460 * starting offset in the file. A hole is defined as a bunch of blank lines
461 * preceding a "tracks" line. Holepos will contain the best match.
463 * In addition, "firstpos" will be filled with the position of the first
464 * "tracks" keyword, so we know how much room is available for global
465 * settings at the rcfile's start.
468 search_db( FILE *fp, int prefs, int scan, int holesize_wanted )
471 char keyword[64], listname[64], *c;
472 int b, i, j, track = 0, listsize, ntracks, scratch, searching = 1;
473 int *trackmap = 0, gotsections = 0;
474 int fudge, maxfudge, sizediff, bestfudge = 0;
475 long pos = 0, thisholepos = -1, holesize = 99991239;
476 struct wm_playlist *l;
478 rclen = 0;
480 /* We may not find any holes at all! */
481 if (holesize_wanted)
482 holepos = -1;
484 if( prefs != 2 )
485 trackmap = reset_tracks();
487 if (prefs)
488 freeup(&otherrc);
489 firstpos = -1;
490 while (! feof(fp))
492 pos = ftell(fp);
493 keyword[0] = '\0';
495 b = getc(fp);
496 while (b != EOF && b != '\n' && isspace(b));
498 if (b == EOF || feof(fp))
499 break;
501 if (b != '\n')
503 keyword[0] = b;
504 fscanf(fp, "%s", &keyword[1]);
506 if (keyword[0] == '\0') /* Blank line. */
508 if (thisholepos < 0)
509 thisholepos = pos;
510 continue;
513 /* Strip off "s-" if we've seen a "sections" keyword */
514 if (gotsections && keyword[0] == 's' && keyword[1] == '-')
515 for (c = &keyword[2]; (c[-2] = *c) != '\0'; c++)
518 /* If this is the start of a CD entry, see if it matches. */
519 if (! strcmp(keyword, "tracks"))
521 if (prefs == 2)
522 break;
524 /* Is this the end of a hole? */
525 if (holesize_wanted && (thisholepos >= 0))
527 /* Yep. Is it better than the last one? */
528 if (pos - thisholepos < holesize && pos -
529 thisholepos >= holesize_wanted)
531 holepos = thisholepos;
532 holesize = pos - thisholepos;
534 thisholepos = -1;
537 /* Is it the start of the CD entries? */
538 if (firstpos == -1)
539 firstpos = pos;
541 /* Is this the end of the entry we really wanted? */
542 if (! searching)
544 rclen = pos - rcpos;
545 break;
548 /* If we have a near match, indicate that we
549 should stop reading tracks, etc now */
550 if (searching == 2)
552 searching = 3;
553 continue;
556 fscanf(fp, "%d", &ntracks);
558 if (ntracks != cd->ntracks)
560 chomp:
561 SWALLOW_LINE(fp);
562 continue;
565 fudge = 0;
566 maxfudge = (ntracks * fuzz_frames) >> 1;
567 track = 0;
568 for (i = 0; i < ntracks; i++)
570 fscanf(fp, "%d", &scratch);
571 if (scratch != cd->trk[track].start)
573 sizediff = abs(scratch - cd->trk[track].start);
574 if (sizediff > fuzz_frames ||
575 (sizediff && scan))
576 break;
577 fudge += sizediff;
579 while (cd->trk[++track].section > 1)
582 if (i != ntracks)
583 goto chomp;
585 if (fudge > 0) /* best near match? */
587 if (fudge > maxfudge)
588 goto chomp;
589 if (bestfudge == 0 || fudge < bestfudge)
590 bestfudge = fudge;
591 else
592 goto chomp;
593 rcpos = pos;
594 track = 0;
595 searching = 2;
597 else /* probably exact match */
599 fscanf(fp, "%d", &scratch);
601 if (scratch != -1 && scratch != cd->length)
602 goto chomp;
604 /* Found it! */
605 rcpos = pos;
606 track = 0;
607 searching = 0;
610 SWALLOW_LINE(fp); /* Get rid of newline */
613 /* Global mode stuff goes here */
614 else if (! strcmp(keyword, "cddbprotocol"))
616 getc(fp);
617 i = getc(fp); /* only first letter is used */
618 cddb.protocol = i == 'c' ? 1 :
619 i == 'h' ? 2 : 3 ;
621 i = getc(fp);
622 while (i != '\n' && i != EOF);
625 else if (! strcmp(keyword, "cddbserver"))
627 getc(fp); /* lose the space */
628 if (cddb.cddb_server[0])
630 i = getc(fp);
631 while (i != '\n' && i != EOF);
632 else
634 fgets(cddb.cddb_server,
635 sizeof(cddb.cddb_server), fp);
636 if ((i = strlen(cddb.cddb_server)))
637 cddb.cddb_server[i - 1] = '\0';
641 else if (! strcmp(keyword, "cddbmailadress"))
643 getc(fp); /* lose the space */
644 if (cddb.mail_adress[0])
646 i = getc(fp);
647 while (i != '\n' && i != EOF);
648 else
650 fgets(cddb.mail_adress,
651 sizeof(cddb.mail_adress), fp);
652 if ((i = strlen(cddb.mail_adress)))
653 cddb.mail_adress[i - 1] = '\0';
657 else if (! strcmp(keyword, "cddbpathtocgi"))
659 getc(fp); /* lose the space */
660 if (cddb.path_to_cgi[0])
662 i = getc(fp);
663 while (i != '\n' && i != EOF);
664 else
666 fgets(cddb.path_to_cgi,
667 sizeof(cddb.path_to_cgi), fp);
668 if ((i = strlen(cddb.path_to_cgi)))
669 cddb.path_to_cgi[i - 1] = '\0';
673 else if (! strcmp(keyword, "cddbproxy"))
675 getc(fp); /* lose the space */
676 if (cddb.proxy_server[0])
678 i = getc(fp);
679 while (i != '\n' && i != EOF);
680 else
682 fgets(cddb.proxy_server,
683 sizeof(cddb.proxy_server), fp);
684 if ((i = strlen(cddb.proxy_server)))
685 cddb.proxy_server[i - 1] = '\0';
689 else if (! strcmp(keyword, "whendone"))
691 getc(fp);
692 i = getc(fp); /* only first letter is used */
693 if (cur_stopmode == -1) /* user's setting preferred */
694 cur_stopmode = i == 's' ? 0 : i == 'r' ? 1 : 2;
696 i = getc(fp);
697 while (i != '\n' && i != EOF);
700 else if (! strcmp(keyword, "playnew"))
702 if (cur_playnew == -1)
703 cur_playnew = 1;
704 SWALLOW_LINE(fp);
707 /* If we're searching, skip to the next "tracks" line. */
708 else if (((searching & 1)|| scan)
709 && !(prefs && firstpos == -1))
710 SWALLOW_LINE(fp)
712 else if (! strcmp(keyword, "sections"))
714 gotsections = 1;
715 fscanf(fp, "%d", &ntracks);
717 free(trackmap);
718 trackmap = (int *) malloc(sizeof(int) *
719 (cur_ntracks + ntracks));
720 if (trackmap == NULL)
722 perror("section mapping");
723 exit(1);
727 * If sections are already defined, use these as a
728 * reference, mapping this CD entry's section numbers
729 * to the ones in core.
731 * Otherwise, split the CD up according to the sections
732 * listed here.
734 if (cur_nsections)
736 track = 0;
737 i = 0;
738 while (ntracks)
740 ntracks--;
741 fscanf(fp, "%d", &scratch);
742 while (scratch > cd->trk[track].start)
744 if (cd->trk[track].section < 2)
745 trackmap[i++] = track;
746 ++track;
748 if (track == cur_ntracks)
749 break;
752 /* rc has later sections than db... */
753 if (track == cur_ntracks)
754 break;
756 /* Matches can be approximate */
757 if (scratch+75 > cd->trk[track].start &&
758 scratch-75 < cd->trk[track].start)
759 trackmap[i++] = track++;
760 else
761 trackmap[i++] = -1;
763 if (track == cur_ntracks)
764 break;
767 /* This only happens if track == cur_ntracks */
768 while (ntracks--)
769 trackmap[i++] = -1;
771 while (track < cur_ntracks)
773 if (cd->trk[track].section < 2)
774 trackmap[i++] = track;
775 track++;
778 track = 0;
779 SWALLOW_LINE(fp);
781 else
783 while (ntracks--)
785 fscanf(fp, "%d", &scratch);
786 split_trackinfo(scratch);
789 for (i = 0; i < cur_ntracks; i++)
791 trackmap[i] = i;
792 /* split_trackinfo() sets this */
793 cd->trk[i].contd = 0;
796 SWALLOW_LINE(fp);
800 else if (! strcmp(keyword, "track"))
802 char buf[502];
804 getc(fp); /* lose the space */
805 /* don't overwrite existing track names. */
806 /* However, overwrite them if there was a "bad" fuzzy match before */
807 if ((trackmap[track] == -1 || track > (cd->ntracks + cur_nsections)) && (searching == 2))
808 SWALLOW_LINE(fp)
809 else if (cd->trk[trackmap[track]].songname &&
810 cd->trk[trackmap[track]].songname[0])
812 i = getc(fp);
813 while (i != '\n' && i != EOF);
814 else
816 fgets(buf, sizeof(buf), fp);
817 if( (i = strlen(buf)) )
818 buf[i - 1] = '\0';
819 wm_strmcpy(&cd->trk[trackmap[track]].songname,
820 buf);
822 track++;
825 else if (! strcmp(keyword, "playmode"))
826 fscanf(fp, "%d", &cd->playmode);
828 else if (! strcmp(keyword, "autoplay"))
829 cd->autoplay = 1;
831 else if (! strcmp(keyword, "cdname"))
833 /* because of fuzzy matching that may change
834 the disk contents, we reset everything when
835 we find the name, in hopes that we will recover
836 most, if not all, of the information from the
837 file. */
839 * nasty bug was here. Was it? BUGBUGBUG
841 * wipe_cdinfo();
842 */ trackmap = reset_tracks();
844 getc(fp); /* lose the space */
845 /* don't overwrite existing cd name. */
846 if (cd->cdname[0] && (searching == 2))
848 i = getc(fp);
849 while (i != '\n' && i != EOF);
850 else
852 if (searching > 1)
854 strcpy(cd->cdname, "Probably://");
855 fgets(cd->cdname + strlen(cd->cdname), sizeof(cd->cdname), fp);
857 else
859 fgets(cd->cdname, sizeof(cd->cdname), fp);
861 if ( (i = strlen(cd->cdname)) )
862 cd->cdname[i - 1] = '\0';
866 else if (! strcmp(keyword, "artist"))
868 getc(fp); /* lose the space */
869 /* don't overwrite existing artist names. */
870 if (cd->artist[0])
872 i = getc(fp);
873 while (i != '\n' && i != EOF);
874 else
876 fgets(cd->artist, sizeof(cd->artist), fp);
877 if( (i = strlen(cd->artist)) )
878 cd->artist[i - 1] = '\0';
882 else if (! strcmp(keyword, "cdvolume"))
884 fscanf(fp, "%d", &cd->volume);
885 cd->volume = (cd->volume * 100) / 32;
888 else if (! strcmp(keyword, "dontplay"))
890 fscanf(fp, "%d", &i);
891 if (trackmap[i - 1] != -1)
892 cd->trk[trackmap[i - 1]].avoid = 1;
895 else if (! strcmp(keyword, "continue"))
897 if (trackmap[track - 1] != -1)
898 cd->trk[trackmap[track - 1]].contd = 1;
901 else if (! strcmp(keyword, "volume"))
903 fscanf(fp, "%d", &i);
904 if (trackmap[i - 1] == -1)
905 SWALLOW_LINE(fp)
906 else
908 i = trackmap[i - 1];
909 fscanf(fp, "%d", &cd->trk[i].volume);
910 cd->trk[i].volume = (cd->trk[i].volume*100)/32;
911 if (cd->trk[i].volume > 32)
912 cd->trk[i].volume = 0;
916 else if (! strcmp(keyword, "playlist"))
918 getc(fp);
919 fscanf(fp, "%s", listname);
921 /* XXX take this out at some point */
922 if (! strcmp(listname, "Default"))
923 strcpy(listname, "List A");
925 for (i = 0; listname[i]; i++)
926 if (listname[i] == '_')
927 listname[i] = ' ';
929 l = new_list(cd, listname);
930 if (l == NULL)
932 plnomem:
933 perror("playlist read");
934 exit(1);
937 fscanf(fp, "%d", &listsize);
939 l->list = malloc(sizeof(int) * (listsize + 1));
940 if (l->list == NULL)
941 goto plnomem;
943 /* Leave out tracks that weren't in .workmandb. */
944 j = 0;
945 for (i = 0; i < listsize; i++)
947 fscanf(fp, "%d", &scratch);
948 scratch = trackmap[scratch - 1];
949 if (scratch != -1)
950 l->list[j++] = scratch + 1;
953 l->list[j] = 0;
956 else if (! strcmp(keyword, "mark"))
958 int mark_val = -1, mark_namelen;
959 char mark_name[32];
961 fscanf(fp, "%d", &mark_val);
962 if (mark_val == -1)
963 goto chomp;
965 if (getc(fp) != ' ')
966 continue;
968 fgets(mark_name, sizeof(mark_name), fp);
969 if( ( mark_namelen = strlen(mark_name)) )
970 mark_name[mark_namelen - 1] = '\0';
972 if (! strcmp(mark_name, "START"))
973 set_abtimer(0, mark_val);
974 else if (! strcmp(mark_name, "END"))
975 set_abtimer(1, mark_val);
978 /* Unrecognized keyword. Put it in the right place. */
979 else
981 char **buf, input[BUFSIZ];
983 if (track && trackmap[track - 1] == -1)
985 SWALLOW_LINE(fp);
986 continue;
989 i = track ? trackmap[track - 1] : 0;
990 buf = prefs ? i ? &cd->trk[i].otherrc : &cd->otherrc :
991 i ? &cd->trk[i].otherdb : &cd->otherdb;
992 if (firstpos == -1) {
993 if (prefs) {
994 buf = &otherrc;
995 } else {
996 goto chomp;
997 } /* if() else */
998 } /* if() */
999 wm_strmcat(buf, keyword);
1000 do {
1001 input[sizeof(input) - 1] = 'x';
1002 fgets(input, sizeof(input), fp);
1003 wm_strmcat(buf, input);
1004 } while (input[sizeof(input) - 1] != 'x');
1008 if (rclen == 0 && !searching)
1009 rclen = pos - rcpos;
1011 if (searching > 1) /* A near match has been found. Good enough. */
1012 searching = 0;
1014 cddb_struct2cur();
1015 return (! searching);
1017 } /* search_db() */
1020 * Delay some amount of time without using interval timers.
1022 void
1023 spinwheels(int secs) {
1024 struct timeval tv;
1026 tv.tv_usec = 0;
1027 tv.tv_sec = secs;
1028 select(0, NULL, NULL, NULL, &tv);
1029 } /* spinwheels() */
1032 * lockit(fd, type)
1034 * fd file descriptor
1035 * type lock type
1037 * Lock a file. Time out after a little while if we can't get a lock;
1038 * this usually means the locking system is broken.
1040 * Unfortunately, if there are lots of people contending for a lock,
1041 * this can result in the file not getting locked when it probably should.
1044 lockit(int fd, int type)
1046 struct flock fl;
1047 int result, timer = 0;
1049 if (suppress_locking)
1050 return (0);
1052 fl.l_type = type;
1053 fl.l_whence = 0;
1054 fl.l_start = 0;
1055 fl.l_len = 0;
1057 while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
1059 if (errno != EACCES || errno != EAGAIN)
1060 break;
1061 if (timer++ == 30)
1063 errno = ETIMEDOUT;
1064 break;
1067 spinwheels(1);
1070 return (result);
1071 } /* lockit */
1074 * Search all the database files and our personal preference file for
1075 * more information about the current CD.
1077 void
1078 load( void )
1080 FILE *fp;
1081 char **dbfile;
1082 int locked = 0;
1083 int dbfound = 0, *trklist, i;
1084 unsigned long dbpos;
1086 /* This is some kind of profiling code. I don't change it
1087 to wm_lib_message() for now... */
1088 #ifdef DEBUG
1089 long t1, t2;
1090 if( getenv( "WORKMAN_DEBUG" ) != NULL )
1092 time(&t1);
1093 printf("%s (%d): search start = %ld\n", __FILE__, __LINE__, t1);
1094 fflush(stdout);
1096 #endif
1098 dbfile = databases;
1100 found_in_db = 0;
1102 /* Turn the cd->trk array into a simple array of ints. */
1103 trklist = (int *)malloc(sizeof(int) * cd->ntracks);
1104 for (i = 0; i < cd->ntracks; i++)
1105 trklist[i] = cd->trk[i].start;
1107 do {
1108 if (*dbfile && idx_find_entry(*dbfile, cd->ntracks, trklist,
1109 cd->length * 75, 0, &dbpos) == 0)
1110 dbfound = 1;
1112 fp = *dbfile ? open_rcfile(*dbfile, "r") : NULL;
1113 if (fp != NULL)
1115 if (lockit(fileno(fp), F_RDLCK))
1116 perror("Couldn't get read (db) lock");
1117 else
1118 locked = 1;
1120 if (dbfound)
1121 fseek(fp, dbpos, 0);
1123 if (search_db(fp, 0, 0, 0))
1125 found_in_db = 1;
1126 cd->whichdb = *dbfile;
1129 if (locked && lockit(fileno(fp), F_UNLCK))
1130 perror("Couldn't relinquish (db) lock");
1132 fclose(fp);
1134 } while (*++dbfile != NULL && cd->whichdb == NULL);
1136 #ifdef DEBUG
1137 if( getenv( "WORKMAN_DEBUG" ) != NULL )
1139 time(&t2);
1140 printf("%s (%d): db search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
1141 fflush(stdout);
1143 #endif
1145 fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
1146 if (fp != NULL)
1148 locked = 0;
1149 if (lockit(fileno(fp), F_RDLCK))
1150 perror("Couldn't get read (rc) lock");
1151 else
1152 locked = 1;
1154 rcpos = 0;
1155 found_in_rc = search_db(fp, 1, 0, 0);
1156 if (! found_in_rc)
1157 cd->autoplay = wm_db_get_playnew();
1159 if (locked && lockit(fileno(fp), F_UNLCK))
1160 perror("Couldn't relinquish (rc) lock");
1162 fclose(fp);
1165 free(trklist);
1167 if (cur_playnew == -1)
1168 cur_playnew = 0;
1170 #ifdef DEBUG
1171 if( getenv( "WORKMAN_DEBUG" ) != NULL )
1173 time(&t2);
1174 printf("%s (%d): search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
1175 fflush(stdout);
1177 #endif
1178 } /* load() */
1181 * Load program settings from the rcfile.
1183 void
1184 load_settings( void )
1186 FILE *fp;
1187 int locked;
1189 fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
1190 if (fp != NULL)
1192 locked = 0;
1193 if (lockit(fileno(fp), F_RDLCK))
1194 perror("Couldn't get read (rc) lock");
1195 else
1196 locked = 1;
1198 rcpos = 0;
1199 found_in_rc = search_db(fp, 2, 0, 0);
1200 if (! found_in_rc)
1201 cd->autoplay = wm_db_get_playnew();
1203 if (locked && lockit(fileno(fp), F_UNLCK))
1204 perror("Couldn't relinquish (rc) lock");
1206 fclose(fp);
1208 } /* load_settings() */
1211 * save_globals()
1213 * Save the global preferences, scooting CD entries to the end if needed.
1214 * The assumption here is that the rcfile is locked, and that firstpos has
1215 * been set by a previous scan.
1217 void
1218 save_globals(FILE *fp)
1220 char *globes = NULL, *cdentry = NULL, temp[100];
1221 long curpos;
1222 int globesize, hit_cdent = 0, c = 0;
1224 if (otherrc)
1225 wm_strmcpy(&globes, otherrc);
1227 if (cddb.protocol)
1229 sprintf(temp, "cddbprotocol ");
1230 switch(cddb.protocol)
1232 case 1: /* cddbp */
1233 sprintf(temp + strlen(temp), "cddbp\n");
1234 break;
1235 case 2: /* http */
1236 sprintf(temp + strlen(temp), "http\n");
1237 break;
1238 case 3: /* proxy */
1239 sprintf(temp + strlen(temp), "proxy\n");
1240 break;
1241 default:
1242 break;
1244 wm_strmcat(&globes, temp);
1246 if(cddb.mail_adress[0])
1248 sprintf(temp,"cddbmailadress %s\n",
1249 cddb.mail_adress);
1250 wm_strmcat(&globes, temp);
1253 if(cddb.cddb_server[0])
1255 sprintf(temp,"cddbserver %s\n",
1256 cddb.cddb_server);
1257 wm_strmcat(&globes, temp);
1260 if(cddb.path_to_cgi[0])
1262 sprintf(temp,"cddbpathtocgi %s\n",
1263 cddb.mail_adress);
1264 wm_strmcat(&globes, temp);
1267 if(cddb.proxy_server[0])
1269 sprintf(temp,"cddbproxy %s\n",
1270 cddb.mail_adress);
1271 wm_strmcat(&globes, temp);
1275 if (cur_stopmode == 1 || cur_stopmode == 2)
1277 sprintf(temp, "whendone %s\n", cur_stopmode == 1 ? "repeat" :
1278 "eject");
1279 wm_strmcat(&globes, temp);
1282 if (cur_playnew == 1)
1283 wm_strmcat(&globes, "playnew\n");
1285 curpos = firstpos;
1286 if (curpos < 0)
1287 curpos = 0;
1289 fseek(fp, curpos, SEEK_SET);
1291 if (firstpos < (globesize = globes != NULL ? strlen(globes) : 0))
1293 while (1)
1295 temp[sizeof(temp)-1] = 'x';
1297 if (fgets(temp, sizeof(temp), fp) == NULL)
1299 fseek(fp, 0, SEEK_SET);
1300 if (globes != NULL)
1302 fwrite(globes, globesize, 1, fp);
1303 free(globes);
1305 if (cdentry != NULL)
1307 fwrite(cdentry, strlen(cdentry), 1, fp);
1308 free(cdentry);
1310 return;
1313 if (! strncmp(temp, "tracks ", 7))
1315 hit_cdent = 1;
1316 if (curpos >= globesize)
1317 break;
1320 if (! hit_cdent)
1322 curpos += strlen(temp);
1323 if (temp[sizeof(temp)-1] == '\0')
1324 while ((c = getc(fp)) != '\n' &&
1325 c != EOF)
1326 curpos++;
1327 if (c == '\n')
1328 curpos++;
1330 continue;
1333 wm_strmcat(&cdentry, temp);
1334 curpos += strlen(temp);
1335 while (temp[sizeof(temp)-1] == '\0')
1337 temp[sizeof(temp)-1] = 'x';
1338 if (fgets(temp, sizeof(temp), fp) == NULL)
1339 break;
1340 wm_strmcat(&cdentry, temp);
1341 curpos += strlen(temp);
1345 if (cdentry != NULL)
1347 fseek(fp, 0, SEEK_END);
1348 fwrite(cdentry, strlen(cdentry), 1, fp);
1349 free(cdentry);
1353 if (globes != NULL)
1355 fseek(fp, 0, SEEK_SET);
1356 fwrite(globes, globesize, 1, fp);
1357 free(globes);
1360 while (globesize++ < curpos)
1361 putc('\n', fp);
1362 } /* save_globals() */
1365 * save_entry()
1367 * Save the CD information to one database.
1369 * filename Database to save to.
1370 * pref 0 for hard data, 1 for preferences.
1372 * If an entry for this CD exists already, overwrite it with the new entry
1373 * if the new entry is the same size or smaller, or with newlines if the new
1374 * entry is larger (in which case the new entry is appended to the file.)
1376 * Also, if the preference information is being updated, save it to the
1377 * file while we've got it locked. Scoot stuff from the beginning of
1378 * the file to the end as needed to facilitate this.
1380 * XXX Preference-saving should probably be done elsewhere, like in an
1381 * Apply button on the Goodies popup, and in any case needs to go to a
1382 * different file (.Xdefaults?)
1384 * Returns 0 on success.
1387 save_entry(char *filename, int pref)
1389 FILE *fp;
1390 char *buf;
1391 int len, i, locked = 0;
1394 if( filename == NULL )
1395 return (-1);
1397 fp = open_rcfile(filename, "r+");
1398 if (fp == NULL)
1400 if (errno == ENOENT) /* doesn't exist already */
1401 fp = open_rcfile(filename, "w");
1402 if (fp == NULL)
1403 return (-1);
1406 if (lockit(fileno(fp), F_WRLCK))
1407 perror("Warning: Couldn't get write lock");
1408 else
1409 locked = 1;
1411 buf = print_cdinfo(cd, pref);
1412 len = strlen(buf); /* doesn't return if there's an error */
1414 rcpos = -1;
1415 search_db(fp, pref, 1, len);
1416 if (rcpos != -1) /* XXX */
1419 * Jump to the entry's position in the database file, if
1420 * it was found.
1422 fseek(fp, rcpos, SEEK_SET);
1424 if (rclen >= len && holepos == -1)
1427 * If the new entry will fit in the space occupied by
1428 * the old one, overwrite the old one and make a hole
1429 * of the appropriate size at its end.
1431 * No need to update the index file in this case, as
1432 * the entry's position hasn't changed.
1434 fputs(buf, fp);
1435 for (i = len; i < rclen; i++)
1436 fputc('\n', fp);
1438 else
1441 * Overwrite the old entry with a hole and delete
1442 * its pointer in the index file.
1444 for (i = 0; i < rclen; i++)
1445 fputc('\n', fp);
1446 idx_delete_entry(filename, cd->trk[cd->ntracks-1].start,
1447 0, rcpos);
1449 rcpos = -1;
1454 * Old entry wasn't found, or its new version wouldn't fit where
1455 * the old one was.
1457 if (rcpos == -1)
1460 * Write the new entry in a hole, if there is one,
1461 * or at the end of the file.
1463 if (holepos >= 0)
1465 fseek(fp, holepos, SEEK_SET);
1466 if (holepos < firstpos)
1467 firstpos = holepos;
1469 else
1471 fseek(fp, 0, SEEK_END);
1472 holepos = ftell(fp);
1474 fputs(buf, fp);
1477 * Write a new index entry for this CD.
1479 idx_write_entry(filename, cd->trk[cd->ntracks - 1].start,
1480 holepos);
1483 if (pref)
1484 save_globals(fp);
1486 fflush(fp);
1488 if (locked && lockit(fileno(fp), F_UNLCK))
1489 perror("Warning: Couldn't relinquish write lock");
1491 fclose(fp);
1493 return (0);
1494 } /* save_entry() */
1497 * save()
1499 * Save CD information to the appropriate datafile (the first file in the
1500 * list, unless the entry came from another database file) and to the
1501 * personal prefs file.
1504 save( void )
1507 if( wm_db_save_disabled == FALSE )
1509 if (save_entry(rcfile, 1))
1510 return (0);
1512 if (cd->whichdb == NULL || access(cd->whichdb, W_OK))
1513 cd->whichdb = databases[0];
1515 if (save_entry(cd->whichdb, 0))
1516 return (0);
1518 return( WM_DB_SAVE_ERROR );
1519 } else {
1520 return( WM_DB_SAVE_DISABLED );
1522 } /* save() */