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"
43 #include <sys/types.h>
44 #include <sys/param.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
;
78 extern int cur_ntracks
, cur_nsections
;
87 int wm_db_get_playnew( void )
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.
105 split_workmandb( void )
109 int no_rc
= 0, no_db
= 0;
113 if ((home
= getenv("HOME")) != NULL
)
115 rcfile
= malloc(strlen(home
) + sizeof(RCFILE
));
120 perror("split_workmandb()");
124 strcpy(rcfile
, home
);
125 strcat(rcfile
, RCFILE
);
132 if ((wmdb
= dbfiles
) == NULL
)
134 if ((home
= getenv("HOME")) != NULL
)
136 wmdb
= malloc(strlen(home
) + sizeof(DBFILE
));
140 databases
= malloc(2 * sizeof (databases
[0]));
141 if (databases
== NULL
)
145 strcat(wmdb
, DBFILE
);
151 static char *emptydb
= NULL
;
153 databases
= &emptydb
;
160 for (home
= wmdb
; *home
; home
++)
167 databases
= malloc((ndbs
+ 1) * sizeof(databases
[0]));
168 if (databases
== NULL
)
171 for (i
= 0; i
< ndbs
; i
++)
174 wmdb
+= strlen(wmdb
) + 1;
183 "WorkMan was run without a home directory, probably by a system daemon.\n");
184 fprintf(stderr
, "It doesn't know where to find ");
187 fprintf(stderr
, "your personal preferences file ");
189 fprintf(stderr
, "or the\ndatabase of CD descriptions");
192 fprintf(stderr
, "the database of CD descriptions");
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().
213 print_cdinfo(struct wm_cdinfo
*cd
, int prefs
)
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",
225 sprintf(tempbuf
+ strlen(tempbuf
), " %d\n", cd
->length
);
227 wm_strmcpy(&cdibuf
, tempbuf
);
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",
237 sprintf(tempbuf
+ strlen(tempbuf
), "\n");
239 wm_strmcat(&cdibuf
, tempbuf
);
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
);
253 if (cdibuf
[i
] == ' ' || cdibuf
[i
] == '\t')
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");
270 wm_strmcat(&cdibuf
, " 0\n");
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
);
285 sprintf(tempbuf
, "playmode %d\n", cd
->playmode
);
286 wm_strmcat(&cdibuf
, tempbuf
);
291 sprintf(tempbuf
, "mark %d START\n", mark_a
);
292 wm_strmcat(&cdibuf
, tempbuf
);
296 sprintf(tempbuf
, "mark %d END\n", mark_b
);
297 wm_strmcat(&cdibuf
, tempbuf
);
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
);
324 wm_strmcat(&cdibuf
, "cdname ");
325 wm_strmcat(&cdibuf
, cd
->cdname
);
326 wm_strmcat(&cdibuf
, "\n");
331 wm_strmcat(&cdibuf
, "artist ");
332 wm_strmcat(&cdibuf
, cd
->artist
);
333 wm_strmcat(&cdibuf
, "\n");
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
);
359 } /* print_cdinfo() */
362 * Open the rcfile for reading or writing.
368 open_rcfile(char *name
, char *mode
)
373 fp
= fopen(name
, mode
);
376 if (errno
!= ENOENT
|| mode
[0] == 'w')
381 /* Don't let people open directories or devices */
382 if (fstat(fileno(fp
), &st
) < 0)
390 if (! S_ISREG(st
.st_mode
))
392 if ((st
.st_mode
& S_IFMT
) != S_IFREG
)
401 if (mode
[0] == 'w') /* create -- put data in so locks work */
403 fputs("# WorkMan database file\n", fp
);
405 fp
= fopen(name
, "r+");
417 * allocate and clear "trackmap".
420 int *reset_tracks(void)
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
)
437 for (i
= 0; i
< cd
->ntracks
; i
++)
440 while (cd
->trk
[++j
].section
> 1)
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
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
;
480 /* We may not find any holes at all! */
485 trackmap
= reset_tracks();
496 while (b
!= EOF
&& b
!= '\n' && isspace(b
));
498 if (b
== EOF
|| feof(fp
))
504 fscanf(fp
, "%s", &keyword
[1]);
506 if (keyword
[0] == '\0') /* Blank line. */
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"))
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
;
537 /* Is it the start of the CD entries? */
541 /* Is this the end of the entry we really wanted? */
548 /* If we have a near match, indicate that we
549 should stop reading tracks, etc now */
556 fscanf(fp
, "%d", &ntracks
);
558 if (ntracks
!= cd
->ntracks
)
566 maxfudge
= (ntracks
* fuzz_frames
) >> 1;
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
||
579 while (cd
->trk
[++track
].section
> 1)
585 if (fudge
> 0) /* best near match? */
587 if (fudge
> maxfudge
)
589 if (bestfudge
== 0 || fudge
< bestfudge
)
597 else /* probably exact match */
599 fscanf(fp
, "%d", &scratch
);
601 if (scratch
!= -1 && scratch
!= cd
->length
)
610 SWALLOW_LINE(fp
); /* Get rid of newline */
613 /* Global mode stuff goes here */
614 else if (! strcmp(keyword
, "cddbprotocol"))
617 i
= getc(fp
); /* only first letter is used */
618 cddb
.protocol
= i
== 'c' ? 1 :
622 while (i
!= '\n' && i
!= EOF
);
625 else if (! strcmp(keyword
, "cddbserver"))
627 getc(fp
); /* lose the space */
628 if (cddb
.cddb_server
[0])
631 while (i
!= '\n' && i
!= EOF
);
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])
647 while (i
!= '\n' && i
!= EOF
);
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])
663 while (i
!= '\n' && i
!= EOF
);
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])
679 while (i
!= '\n' && i
!= EOF
);
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"))
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;
697 while (i
!= '\n' && i
!= EOF
);
700 else if (! strcmp(keyword
, "playnew"))
702 if (cur_playnew
== -1)
707 /* If we're searching, skip to the next "tracks" line. */
708 else if (((searching
& 1)|| scan
)
709 && !(prefs
&& firstpos
== -1))
712 else if (! strcmp(keyword
, "sections"))
715 fscanf(fp
, "%d", &ntracks
);
718 trackmap
= (int *) malloc(sizeof(int) *
719 (cur_ntracks
+ ntracks
));
720 if (trackmap
== NULL
)
722 perror("section mapping");
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
741 fscanf(fp
, "%d", &scratch
);
742 while (scratch
> cd
->trk
[track
].start
)
744 if (cd
->trk
[track
].section
< 2)
745 trackmap
[i
++] = track
;
748 if (track
== cur_ntracks
)
752 /* rc has later sections than db... */
753 if (track
== cur_ntracks
)
756 /* Matches can be approximate */
757 if (scratch
+75 > cd
->trk
[track
].start
&&
758 scratch
-75 < cd
->trk
[track
].start
)
759 trackmap
[i
++] = track
++;
763 if (track
== cur_ntracks
)
767 /* This only happens if track == cur_ntracks */
771 while (track
< cur_ntracks
)
773 if (cd
->trk
[track
].section
< 2)
774 trackmap
[i
++] = track
;
785 fscanf(fp
, "%d", &scratch
);
786 split_trackinfo(scratch
);
789 for (i
= 0; i
< cur_ntracks
; i
++)
792 /* split_trackinfo() sets this */
793 cd
->trk
[i
].contd
= 0;
800 else if (! strcmp(keyword
, "track"))
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))
809 else if (cd
->trk
[trackmap
[track
]].songname
&&
810 cd
->trk
[trackmap
[track
]].songname
[0])
813 while (i
!= '\n' && i
!= EOF
);
816 fgets(buf
, sizeof(buf
), fp
);
817 if( (i
= strlen(buf
)) )
819 wm_strmcpy(&cd
->trk
[trackmap
[track
]].songname
,
825 else if (! strcmp(keyword
, "playmode"))
826 fscanf(fp
, "%d", &cd
->playmode
);
828 else if (! strcmp(keyword
, "autoplay"))
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
839 * nasty bug was here. Was it? BUGBUGBUG
842 */ trackmap
= reset_tracks();
844 getc(fp
); /* lose the space */
845 /* don't overwrite existing cd name. */
846 if (cd
->cdname
[0] && (searching
== 2))
849 while (i
!= '\n' && i
!= EOF
);
854 strcpy(cd
->cdname
, "Probably://");
855 fgets(cd
->cdname
+ strlen(cd
->cdname
), sizeof(cd
->cdname
), fp
);
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. */
873 while (i
!= '\n' && i
!= EOF
);
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)
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"))
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
] == '_')
929 l
= new_list(cd
, listname
);
933 perror("playlist read");
937 fscanf(fp
, "%d", &listsize
);
939 l
->list
= malloc(sizeof(int) * (listsize
+ 1));
943 /* Leave out tracks that weren't in .workmandb. */
945 for (i
= 0; i
< listsize
; i
++)
947 fscanf(fp
, "%d", &scratch
);
948 scratch
= trackmap
[scratch
- 1];
950 l
->list
[j
++] = scratch
+ 1;
956 else if (! strcmp(keyword
, "mark"))
958 int mark_val
= -1, mark_namelen
;
961 fscanf(fp
, "%d", &mark_val
);
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. */
981 char **buf
, input
[BUFSIZ
];
983 if (track
&& trackmap
[track
- 1] == -1)
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) {
999 wm_strmcat(buf
, keyword
);
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. */
1015 return (! searching
);
1020 * Delay some amount of time without using interval timers.
1023 spinwheels(int secs
) {
1028 select(0, NULL
, NULL
, NULL
, &tv
);
1029 } /* spinwheels() */
1034 * fd file descriptor
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
)
1047 int result
, timer
= 0;
1049 if (suppress_locking
)
1057 while ((result
= fcntl(fd
, F_SETLK
, &fl
)) < 0)
1059 if (errno
!= EACCES
|| errno
!= EAGAIN
)
1074 * Search all the database files and our personal preference file for
1075 * more information about the current CD.
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... */
1090 if( getenv( "WORKMAN_DEBUG" ) != NULL
)
1093 printf("%s (%d): search start = %ld\n", __FILE__
, __LINE__
, t1
);
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
;
1108 if (*dbfile
&& idx_find_entry(*dbfile
, cd
->ntracks
, trklist
,
1109 cd
->length
* 75, 0, &dbpos
) == 0)
1112 fp
= *dbfile
? open_rcfile(*dbfile
, "r") : NULL
;
1115 if (lockit(fileno(fp
), F_RDLCK
))
1116 perror("Couldn't get read (db) lock");
1121 fseek(fp
, dbpos
, 0);
1123 if (search_db(fp
, 0, 0, 0))
1126 cd
->whichdb
= *dbfile
;
1129 if (locked
&& lockit(fileno(fp
), F_UNLCK
))
1130 perror("Couldn't relinquish (db) lock");
1134 } while (*++dbfile
!= NULL
&& cd
->whichdb
== NULL
);
1137 if( getenv( "WORKMAN_DEBUG" ) != NULL
)
1140 printf("%s (%d): db search end = %ld, elapsed = %ld\n", __FILE__
, __LINE__
, t2
, t2
- t1
);
1145 fp
= rcfile
? open_rcfile(rcfile
, "r") : NULL
;
1149 if (lockit(fileno(fp
), F_RDLCK
))
1150 perror("Couldn't get read (rc) lock");
1155 found_in_rc
= search_db(fp
, 1, 0, 0);
1157 cd
->autoplay
= wm_db_get_playnew();
1159 if (locked
&& lockit(fileno(fp
), F_UNLCK
))
1160 perror("Couldn't relinquish (rc) lock");
1167 if (cur_playnew
== -1)
1171 if( getenv( "WORKMAN_DEBUG" ) != NULL
)
1174 printf("%s (%d): search end = %ld, elapsed = %ld\n", __FILE__
, __LINE__
, t2
, t2
- t1
);
1181 * Load program settings from the rcfile.
1184 load_settings( void )
1189 fp
= rcfile
? open_rcfile(rcfile
, "r") : NULL
;
1193 if (lockit(fileno(fp
), F_RDLCK
))
1194 perror("Couldn't get read (rc) lock");
1199 found_in_rc
= search_db(fp
, 2, 0, 0);
1201 cd
->autoplay
= wm_db_get_playnew();
1203 if (locked
&& lockit(fileno(fp
), F_UNLCK
))
1204 perror("Couldn't relinquish (rc) lock");
1208 } /* load_settings() */
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.
1218 save_globals(FILE *fp
)
1220 char *globes
= NULL
, *cdentry
= NULL
, temp
[100];
1222 int globesize
, hit_cdent
= 0, c
= 0;
1225 wm_strmcpy(&globes
, otherrc
);
1229 sprintf(temp
, "cddbprotocol ");
1230 switch(cddb
.protocol
)
1233 sprintf(temp
+ strlen(temp
), "cddbp\n");
1236 sprintf(temp
+ strlen(temp
), "http\n");
1239 sprintf(temp
+ strlen(temp
), "proxy\n");
1244 wm_strmcat(&globes
, temp
);
1246 if(cddb
.mail_adress
[0])
1248 sprintf(temp
,"cddbmailadress %s\n",
1250 wm_strmcat(&globes
, temp
);
1253 if(cddb
.cddb_server
[0])
1255 sprintf(temp
,"cddbserver %s\n",
1257 wm_strmcat(&globes
, temp
);
1260 if(cddb
.path_to_cgi
[0])
1262 sprintf(temp
,"cddbpathtocgi %s\n",
1264 wm_strmcat(&globes
, temp
);
1267 if(cddb
.proxy_server
[0])
1269 sprintf(temp
,"cddbproxy %s\n",
1271 wm_strmcat(&globes
, temp
);
1275 if (cur_stopmode
== 1 || cur_stopmode
== 2)
1277 sprintf(temp
, "whendone %s\n", cur_stopmode
== 1 ? "repeat" :
1279 wm_strmcat(&globes
, temp
);
1282 if (cur_playnew
== 1)
1283 wm_strmcat(&globes
, "playnew\n");
1289 fseek(fp
, curpos
, SEEK_SET
);
1291 if (firstpos
< (globesize
= globes
!= NULL
? strlen(globes
) : 0))
1295 temp
[sizeof(temp
)-1] = 'x';
1297 if (fgets(temp
, sizeof(temp
), fp
) == NULL
)
1299 fseek(fp
, 0, SEEK_SET
);
1302 fwrite(globes
, globesize
, 1, fp
);
1305 if (cdentry
!= NULL
)
1307 fwrite(cdentry
, strlen(cdentry
), 1, fp
);
1313 if (! strncmp(temp
, "tracks ", 7))
1316 if (curpos
>= globesize
)
1322 curpos
+= strlen(temp
);
1323 if (temp
[sizeof(temp
)-1] == '\0')
1324 while ((c
= getc(fp
)) != '\n' &&
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
)
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
);
1355 fseek(fp
, 0, SEEK_SET
);
1356 fwrite(globes
, globesize
, 1, fp
);
1360 while (globesize
++ < curpos
)
1362 } /* save_globals() */
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
)
1391 int len
, i
, locked
= 0;
1394 if( filename
== NULL
)
1397 fp
= open_rcfile(filename
, "r+");
1400 if (errno
== ENOENT
) /* doesn't exist already */
1401 fp
= open_rcfile(filename
, "w");
1406 if (lockit(fileno(fp
), F_WRLCK
))
1407 perror("Warning: Couldn't get write lock");
1411 buf
= print_cdinfo(cd
, pref
);
1412 len
= strlen(buf
); /* doesn't return if there's an error */
1415 search_db(fp
, pref
, 1, len
);
1416 if (rcpos
!= -1) /* XXX */
1419 * Jump to the entry's position in the database file, if
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.
1435 for (i
= len
; i
< rclen
; i
++)
1441 * Overwrite the old entry with a hole and delete
1442 * its pointer in the index file.
1444 for (i
= 0; i
< rclen
; i
++)
1446 idx_delete_entry(filename
, cd
->trk
[cd
->ntracks
-1].start
,
1454 * Old entry wasn't found, or its new version wouldn't fit where
1460 * Write the new entry in a hole, if there is one,
1461 * or at the end of the file.
1465 fseek(fp
, holepos
, SEEK_SET
);
1466 if (holepos
< firstpos
)
1471 fseek(fp
, 0, SEEK_END
);
1472 holepos
= ftell(fp
);
1477 * Write a new index entry for this CD.
1479 idx_write_entry(filename
, cd
->trk
[cd
->ntracks
- 1].start
,
1488 if (locked
&& lockit(fileno(fp
), F_UNLCK
))
1489 perror("Warning: Couldn't relinquish write lock");
1494 } /* save_entry() */
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.
1507 if( wm_db_save_disabled
== FALSE
)
1509 if (save_entry(rcfile
, 1))
1512 if (cd
->whichdb
== NULL
|| access(cd
->whichdb
, W_OK
))
1513 cd
->whichdb
= databases
[0];
1515 if (save_entry(cd
->whichdb
, 0))
1518 return( WM_DB_SAVE_ERROR
);
1520 return( WM_DB_SAVE_DISABLED
);