home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #27 / NN_1992_27.iso / spool / alt / sources / 2525 / database.c next >
Encoding:
C/C++ Source or Header  |  1992-11-15  |  22.2 KB  |  1,129 lines

  1. /*
  2.  * @(#)database.c    1.23    11/15/92
  3.  *
  4.  * Manage the CD database.  All these routines assume that the "cd" global
  5.  * structure contains current information (as far as the outside world knows;
  6.  * obviously it won't contain track titles just after a CD is inserted.)
  7.  */
  8. #define RCFILE "/.workmanrc"
  9. #define DBFILE "/.workmandb"
  10. static char *ident = "@(#)database.c    1.23 11/15/92";
  11.  
  12. #include <errno.h>
  13. #include <stdio.h>
  14. #include <sys/types.h>
  15. #include <fcntl.h>
  16. #include <sys/param.h>
  17. #include <unistd.h>
  18. #include <sys/time.h>
  19. #include "struct.h"
  20.  
  21. #define SWALLOW_LINE(fp) { int c; while ((c = getc(fp)) != '\n' && c != EOF); }
  22.  
  23. void *malloc(), *realloc();
  24. char *strchr(), *getenv();
  25.  
  26. int    suppress_locking = 0;    /* Turn off locking of datafile (dangerous) */
  27.  
  28. char    *rcfile = NULL;        /* Personal rcfile */
  29. char    **databases = NULL;    /* NULL-terminated list of databases */
  30.  
  31. char    *otherrc = NULL;    /* Unrecognized cruft from start of rcfile */
  32.  
  33. long    rcpos, rclen;        /* XXX */
  34.  
  35. int    found_in_db, found_in_rc;
  36. long    holepos, firstpos;
  37.  
  38. extern int cur_stopmode, cur_playnew, cur_ntracks, cur_nsections;
  39. extern int mark_a, mark_b;
  40.  
  41. /* Copy into a malloced string. */
  42. void
  43. strmcpy(t, s)
  44.     char    **t, *s;
  45. {
  46.     if (*t != NULL)
  47.         free(*t);
  48.  
  49.     *t = malloc(strlen(s) + 1);
  50.     if (*t == NULL)
  51.     {
  52.         perror("strmcpy");
  53.         exit(1);
  54.     }
  55.  
  56.     (void) strcpy(*t, s);
  57. }
  58.  
  59. /* Add to a malloced string. */
  60. void
  61. strmcat(t, s)
  62.     char    **t, *s;
  63. {
  64.     int    len = strlen(s) + 1;
  65.  
  66.     if (*s == '\0')
  67.         return;
  68.  
  69.     if (*t != NULL)
  70.     {
  71.         len += strlen(*t);
  72.         *t = realloc(*t, len);
  73.         if (*t == NULL)
  74.         {
  75.             perror("strmcat");
  76.             exit(1);
  77.         }
  78.         strcat(*t, s);
  79.     }
  80.     else
  81.         strmcpy(t, s);
  82. }
  83.  
  84. /*
  85.  * split_workmandb()
  86.  *
  87.  * Split the WORKMANDB environment variable, if any, into a list of database
  88.  * files in the global "databases".  If WORKMANDB is not available, make
  89.  * a single entry with $HOME/DBFILE.
  90.  *
  91.  * Also, fill the "rcfile" global with the personal preferences filename.
  92.  */
  93. void
  94. split_workmandb()
  95. {
  96.     int    ndbs, i;
  97.     char    *home, *wmdb;
  98.  
  99.     if (rcfile == NULL && (rcfile = getenv("WORKMANRC")) == NULL)
  100.     {
  101.         if ((home = getenv("HOME")) == NULL)
  102.         {
  103. nohome:
  104.             fprintf(stderr, "$HOME is not set!  Despair!\n");
  105.             exit(1);
  106.         }
  107.  
  108.         rcfile = malloc(strlen(home) + sizeof(RCFILE));
  109.         if (rcfile == NULL)
  110.         {
  111. nomem:
  112.             perror("split_workmandb()");
  113.             exit(1);
  114.         }
  115.  
  116.         strcpy(rcfile, home);
  117.         strcat(rcfile, RCFILE);
  118.     }
  119.  
  120.     if (databases == NULL)
  121.     {
  122.         if ((wmdb = getenv("WORKMANDB")) == NULL)
  123.         {
  124.             if ((home = getenv("HOME")) == NULL)
  125.                 goto nohome;
  126.  
  127.             wmdb = malloc(strlen(home) + sizeof(DBFILE));
  128.             if (wmdb == NULL)
  129.                 goto nomem;
  130.  
  131.             databases = malloc(2 * sizeof (databases[0]));
  132.             if (databases == NULL)
  133.                 goto nomem;
  134.  
  135.             strcpy(wmdb, home);
  136.             strcat(wmdb, DBFILE);
  137.             databases[0] = wmdb;
  138.             databases[1] = NULL;
  139.         }
  140.         else
  141.         {
  142.             ndbs = 1;
  143.             for (home = wmdb; *home; home++)
  144.                 if (*home == ':')
  145.                 {
  146.                     *home = '\0';
  147.                     ndbs++;
  148.                 }
  149.             
  150.             databases = malloc((ndbs + 1) * sizeof(databases[0]));
  151.             if (databases == NULL)
  152.                 goto nomem;
  153.             
  154.             for (i = 0; i < ndbs; i++)
  155.             {
  156.                 databases[i] = wmdb;
  157.                 wmdb += strlen(wmdb) + 1;
  158.             }
  159.  
  160.             databases[i] = NULL;
  161.         }
  162.     }
  163. }
  164.  
  165. /*
  166.  * print_cdinfo(cd, prefs)
  167.  *
  168.  * cd        A pointer to a cdinfo struct.
  169.  * prefs    Flag: write personal preferences?
  170.  *
  171.  * Print a CD's information (in more or less readable form) to a buffer.
  172.  * Returns a pointer to the buffer.
  173.  *
  174.  * XXX - could be more efficient about calling strmcat() and strlen().
  175.  */
  176. char *
  177. print_cdinfo(cd, prefs)
  178.     struct cdinfo    *cd;
  179.     int        prefs;
  180. {
  181.     int        i;
  182.     char        tempbuf[2000];    /* XXX - is this always big enough? */
  183.     static char    *cdibuf = NULL;
  184.     struct playlist    *l;
  185.  
  186.     sprintf(tempbuf, "tracks %d", cd->ntracks);
  187.     for (i = 0; i < cur_ntracks; i++)
  188.         if (cd->trk[i].section < 2)
  189.             sprintf(tempbuf + strlen(tempbuf), " %d",
  190.                 cd->trk[i].start);
  191.     sprintf(tempbuf + strlen(tempbuf), " %d\n", cd->length);
  192.  
  193.     strmcpy(&cdibuf, tempbuf);
  194.  
  195.     if (cur_nsections)
  196.     {
  197.         sprintf(tempbuf, "sections %d", cur_nsections);
  198.         for (i = 0; i < cur_ntracks; i++)
  199.             if (cd->trk[i].section > 1)
  200.                 sprintf(tempbuf + strlen(tempbuf), " %d",
  201.                     cd->trk[i].start);
  202.         sprintf(tempbuf + strlen(tempbuf), "\n");
  203.  
  204.         strmcat(&cdibuf, tempbuf);
  205.     }
  206.  
  207.     if (prefs)
  208.     {
  209.         if (cd->autoplay)
  210.             strmcat(&cdibuf, "autoplay\n");
  211.         for (l = cd->lists; l != NULL && l->name != NULL; l++)
  212.         {
  213.             strmcat(&cdibuf, "playlist ");
  214.  
  215.             i = strlen(cdibuf) - 1;
  216.             strmcat(&cdibuf, l->name);
  217.             while (cdibuf[++i])
  218.                 if (cdibuf[i] == ' ' || cdibuf[i] == '\t')
  219.                     cdibuf[i] = '_';
  220.  
  221.             if (l->list != NULL)
  222.             {
  223.                 for (i = 0; l->list[i]; i++)
  224.                     ;
  225.                 sprintf(tempbuf, " %d", i);
  226.                 strmcat(&cdibuf, tempbuf);
  227.                 for (i = 0; l->list[i]; i++)
  228.                 {
  229.                     sprintf(tempbuf, " %d", l->list[i]);
  230.                     strmcat(&cdibuf, tempbuf);
  231.                 }
  232.                 strmcat(&cdibuf, "\n");
  233.             }
  234.             else
  235.                 strmcat(&cdibuf, " 0\n");
  236.         }
  237.  
  238.         if (cd->volume)
  239.         {
  240.             sprintf(tempbuf, "cdvolume %d\n", cd->volume);
  241.             strmcat(&cdibuf, tempbuf);
  242.         }
  243.  
  244.         if (cd->playmode)
  245.         {
  246.             sprintf(tempbuf, "playmode %d\n", cd->playmode);
  247.             strmcat(&cdibuf, tempbuf);
  248.         }
  249.  
  250.         if (mark_a)
  251.         {
  252.             sprintf(tempbuf, "mark %d START\n", mark_a);
  253.             strmcat(&cdibuf, tempbuf);
  254.         }
  255.         if (mark_b)
  256.         {
  257.             sprintf(tempbuf, "mark %d END\n", mark_b);
  258.             strmcat(&cdibuf, tempbuf);
  259.         }
  260.  
  261.         if (cd->otherrc)
  262.             strmcat(&cdibuf, cd->otherrc);
  263.  
  264.         for (i = 0; i < cur_ntracks; i++)
  265.         {
  266.             if (cd->trk[i].avoid)
  267.             {
  268.                 sprintf(tempbuf, "dontplay %d\n", i + 1);
  269.                 strmcat(&cdibuf, tempbuf);
  270.             }
  271.             if (cd->trk[i].volume)
  272.             {
  273.                 sprintf(tempbuf, "volume %d %d\n", i + 1,
  274.                     cd->trk[i].volume);
  275.                 strmcat(&cdibuf, tempbuf);
  276.             }
  277.             if (cd->trk[i].otherrc)
  278.                 strmcat(&cdibuf, cd->trk[i].otherrc);
  279.         }
  280.     }
  281.     else
  282.     {
  283.         if (cd->cdname[0])
  284.         {
  285.             strmcat(&cdibuf, "cdname ");
  286.             strmcat(&cdibuf, cd->cdname);
  287.             strmcat(&cdibuf, "\n");
  288.         }
  289.  
  290.         if (cd->artist[0])
  291.         {
  292.             strmcat(&cdibuf, "artist ");
  293.             strmcat(&cdibuf, cd->artist);
  294.             strmcat(&cdibuf, "\n");
  295.         }
  296.  
  297.         if (cd->otherdb)
  298.             strmcat(&cdibuf, cd->otherdb);
  299.  
  300.         for (i = 0; i < cur_ntracks; i++)
  301.         {
  302.             if (cd->trk[i].section > 1)
  303.                 strmcat(&cdibuf, "s-");
  304.             strmcat(&cdibuf, "track ");
  305.             if (cd->trk[i].songname != NULL)
  306.                 strmcat(&cdibuf, cd->trk[i].songname);
  307.             strmcat(&cdibuf, "\n");
  308.             if (cd->trk[i].contd)
  309.             {
  310.                 if (cd->trk[i].section > 1)
  311.                     strmcat(&cdibuf, "s-");
  312.                 strmcat(&cdibuf, "continue\n");
  313.             }
  314.             if (cd->trk[i].otherdb)
  315.                 strmcat(&cdibuf, cd->trk[i].otherdb);
  316.         }
  317.     }
  318.  
  319.     return (cdibuf);
  320. }
  321.  
  322. /*
  323.  * Open the rcfile for reading or writing.
  324.  *
  325.  *    name        Filename
  326.  *    mode        "r" or "w"
  327.  */
  328. FILE *
  329. open_rcfile(name, mode)
  330.     char    *name, *mode;
  331. {
  332.     FILE    *fp;
  333.  
  334.     fp = fopen(name, mode);
  335.     if (fp == NULL)
  336.     {
  337.         if (errno != ENOENT || mode[0] == 'w')
  338.             perror(name);
  339.     }
  340.     else if (mode[0] == 'w') /* create -- put data in so locks work */
  341.     {
  342.         fputs("# WorkMan database file\n", fp);
  343.         fclose(fp);
  344.         fp = fopen(name, "r+");
  345.         if (fp == NULL)
  346.             if (errno != ENOENT)
  347.                 perror(name);
  348.     }
  349.  
  350.     return (fp);
  351. }
  352.  
  353. /*
  354.  * Load a new-format database file, searching for a match with the currently
  355.  * inserted CD.  Modify the in-core copy of the CD info based on what's found
  356.  * in the database.
  357.  *
  358.  * Returns 1 if there was a match or 0 if not.
  359.  *
  360.  *    fp        FILE* of database or rcfile.
  361.  *    prefs        1 if we're searching .workmanrc, 0 for .workmandb
  362.  *    scan        Scan for "tracks" location and entry size only
  363.  *    holesize_wanted    How big a hole we're looking for, if any.
  364.  *
  365.  * If a hole was found along the way, update the global "holepos" with its
  366.  * starting offset in the file.  A hole is defined as a bunch of blank lines
  367.  * preceding a "tracks" line.  Holepos will contain the best match.
  368.  *
  369.  * In addition, "firstpos" will be filled with the position of the first
  370.  * "tracks" keyword, so we know how much room is available for global
  371.  * settings at the rcfile's start.
  372.  */
  373. search_db(fp, prefs, scan, holesize_wanted)
  374.     FILE    *fp;
  375.     int    prefs, scan, holesize_wanted;
  376. {
  377.     char    keyword[64], listname[64], *c;
  378.     int    i, j, track, listsize, ntracks, scratch, searching = 1;
  379.     int    *trackmap, gotsections = 0;
  380.     long    pos, thisholepos = -1, holesize = 99991239;
  381.     struct playlist    *l;
  382.  
  383.     rclen = 0;
  384.  
  385.     /* We may not find any holes at all! */
  386.     if (holesize_wanted)
  387.         holepos = -1;
  388.  
  389.     /*
  390.      * Since we access track numbers indirectly (to handle sections
  391.      * with at least a little elegance), the track mapping needs to be
  392.      * set up before we read anything.  Initially it must assume that
  393.      * no sections will appear in this datafile.
  394.      */
  395.     trackmap = malloc(sizeof(int) * cur_ntracks);
  396.     if (trackmap == NULL)
  397.     {
  398.         perror("trackmap");
  399.         exit(1);
  400.     }
  401.     j = 0;
  402.     for (i = 0; i < cd->ntracks; i++)
  403.     {
  404.         trackmap[i] = j;
  405.         while (cd->trk[++j].section > 1)
  406.             ;
  407.     }
  408.  
  409.     if (prefs)
  410.         freeup(&otherrc);
  411.     firstpos = -1;
  412.  
  413.     while (! feof(fp))
  414.     {
  415.         pos = ftell(fp);
  416.  
  417.         keyword[0] = '\0';
  418.         fscanf(fp, "%s", keyword);
  419.         if (keyword[0] == '\0')        /* Blank line. */
  420.         {
  421.             if (thisholepos < 0)
  422.                 thisholepos = pos;
  423.             continue;
  424.         }
  425.  
  426.         /* Strip off "s-" if we've seen a "sections" keyword */
  427.         if (gotsections && keyword[0] == 's' && keyword[1] == '-')
  428.             for (c = &keyword[2]; (c[-2] = *c) != '\0'; c++)
  429.                 ;
  430.  
  431.         /* If this is the start of a CD entry, see if it matches. */
  432.         if (! strcmp(keyword, "tracks"))
  433.         {
  434.             /* Is this the end of a hole? */
  435.             if (holesize_wanted && (thisholepos >= 0))
  436.             {
  437.                 /* Yep.  Is it better than the last one? */
  438.                 if (pos - thisholepos < holesize && pos -
  439.                         thisholepos >= holesize_wanted)
  440.                 {
  441.                     holepos = thisholepos;
  442.                     holesize = pos - thisholepos;
  443.                 }
  444.                 thisholepos = -1;
  445.             }
  446.  
  447.             /* Is it the start of the CD entries? */
  448.             if (firstpos == -1)
  449.                 firstpos = pos;
  450.  
  451.             /* Is this the end of the entry we really wanted? */
  452.             if (! searching)
  453.             {
  454.                 rclen = pos - rcpos;
  455.                 break;
  456.             }
  457.  
  458.             fscanf(fp, "%d", &ntracks);
  459.  
  460.             if (ntracks != cd->ntracks)
  461.             {
  462. chomp:
  463.                 SWALLOW_LINE(fp);
  464.                 continue;
  465.             }
  466.  
  467.             track = 0;
  468.             for (i = 0; i < ntracks; i++)
  469.             {
  470.                 fscanf(fp, "%d", &scratch);
  471.                 if (scratch != cd->trk[track].start)
  472.                     break;
  473.                 while (cd->trk[++track].section > 1)
  474.                     ;
  475.             }
  476.             if (i != ntracks)
  477.                 goto chomp;
  478.  
  479.             fscanf(fp, "%d", &scratch);
  480.  
  481.             if (scratch != cd->length)
  482.                 goto chomp;
  483.  
  484.             /* Found it! */
  485.             rcpos = pos;
  486.             track = 0;
  487.             searching = 0;
  488.  
  489.             SWALLOW_LINE(fp);    /* Get rid of newline */
  490.         }
  491.  
  492.         /* Global mode stuff goes here */
  493.         else if (! strcmp(keyword, "whendone"))
  494.         {
  495.             getc(fp);
  496.             i = getc(fp);    /* only first letter is used */
  497.             if (cur_stopmode == -1)    /* user's setting preferred */
  498.                 cur_stopmode = i == 's' ? 0 : i == 'r' ? 1 : 2;
  499.             do
  500.                 i = getc(fp);
  501.             while (i != '\n' && i != EOF);
  502.         }
  503.  
  504.         else if (! strcmp(keyword, "playnew"))
  505.         {
  506.             if (cur_playnew == -1)
  507.                 cur_playnew = 1;
  508.         }
  509.  
  510.         /* If we're searching, skip to the next "tracks" line. */
  511.         else if ((searching || scan) && !(prefs && firstpos == -1))
  512.             SWALLOW_LINE(fp)
  513.  
  514.         else if (! strcmp(keyword, "sections"))
  515.         {
  516.             gotsections = 1;
  517.             fscanf(fp, "%d", &ntracks);
  518.  
  519.             free(trackmap);
  520.             trackmap = (int *) malloc(sizeof(int) *
  521.                         (cur_ntracks + ntracks));
  522.             if (trackmap == NULL)
  523.             {
  524.                 perror("section mapping");
  525.                 exit(1);
  526.             }
  527.  
  528.             /*
  529.              * If sections are already defined, use these as a
  530.              * reference, mapping this CD entry's section numbers
  531.              * to the ones in core.
  532.              *
  533.              * Otherwise, split the CD up according to the sections
  534.              * listed here.
  535.              */
  536.             if (cur_nsections)
  537.             {
  538.                 track = 0;
  539.                 i = 0;
  540.                 while (ntracks)
  541.                 {
  542.                     ntracks--;
  543.                     fscanf(fp, "%d", &scratch);
  544.                     while (scratch > cd->trk[track].start)
  545.                     {
  546.                         if (cd->trk[track].section < 2)
  547.                             trackmap[i++] = track;
  548.                         ++track;
  549.  
  550.                         if (track == cur_ntracks)
  551.                             break;
  552.                     }
  553.  
  554.                     /* rc has later sections than db... */
  555.                     if (track == cur_ntracks)
  556.                         break;
  557.  
  558.                     /* Matches can be approximate */
  559.                     if (scratch+75 > cd->trk[track].start &&
  560.                         scratch-75 < cd->trk[track].start)
  561.                         trackmap[i++] = track++;
  562.                     else
  563.                         trackmap[i++] = -1;
  564.                     
  565.                     if (track == cur_ntracks)
  566.                         break;
  567.                 }
  568.  
  569.                 /* This only happens if track == cur_ntracks */
  570.                 while (ntracks--)
  571.                     trackmap[i++] = -1;
  572.  
  573.                 while (track < cur_ntracks)
  574.                 {
  575.                     if (cd->trk[track].section < 2)
  576.                         trackmap[i++] = track;
  577.                     track++;
  578.                 }
  579.  
  580.                 track = 0;
  581.                 SWALLOW_LINE(fp);
  582.             }
  583.             else
  584.             {
  585.                 while (ntracks--)
  586.                 {
  587.                     fscanf(fp, "%d", &scratch);
  588.                     split_trackinfo(scratch);
  589.                 }
  590.  
  591.                 for (i = 0; i < cur_ntracks; i++)
  592.                 {
  593.                     trackmap[i] = i;
  594.                     /* split_trackinfo() sets this */
  595.                     cd->trk[i].contd = 0;
  596.                 }
  597.  
  598.                 SWALLOW_LINE(fp);
  599.             }
  600.         }
  601.  
  602.         else if (! strcmp(keyword, "track"))
  603.         {
  604.             char buf[502];
  605.  
  606.             getc(fp);    /* lose the space */
  607.             /* don't overwrite existing track names. */
  608.             if (trackmap[track] == -1)
  609.                 SWALLOW_LINE(fp)
  610.             else if (cd->trk[trackmap[track]].songname &&
  611.                     cd->trk[trackmap[track]].songname[0])
  612.                 do
  613.                     i = getc(fp);
  614.                 while (i != '\n' && i != EOF);
  615.             else
  616.             {
  617.                 fgets(buf, sizeof(buf), fp);
  618.                 if (i = strlen(buf))
  619.                     buf[i - 1] = '\0';
  620.                 strmcpy(&cd->trk[trackmap[track]].songname,
  621.                                     buf);
  622.             }
  623.             track++;
  624.         }
  625.  
  626.         else if (! strcmp(keyword, "playmode"))
  627.             fscanf(fp, "%d", &cd->playmode);
  628.  
  629.         else if (! strcmp(keyword, "autoplay"))
  630.             cd->autoplay = 1;
  631.  
  632.         else if (! strcmp(keyword, "cdname"))
  633.         {
  634.             getc(fp);    /* lose the space */
  635.             /* don't overwrite existing cd name. */
  636.             if (cd->cdname[0])
  637.                 do
  638.                     i = getc(fp);
  639.                 while (i != '\n' && i != EOF);
  640.             else
  641.             {
  642.                 fgets(cd->cdname, sizeof(cd->cdname), fp);
  643.                 if (i = strlen(cd->cdname))
  644.                     cd->cdname[i - 1] = '\0';
  645.             }
  646.         }
  647.  
  648.         else if (! strcmp(keyword, "artist"))
  649.         {
  650.             getc(fp);    /* lose the space */
  651.             /* don't overwrite existing artist names. */
  652.             if (cd->artist[0])
  653.                 do
  654.                     i = getc(fp);
  655.                 while (i != '\n' && i != EOF);
  656.             else
  657.             {
  658.                 fgets(cd->artist, sizeof(cd->artist), fp);
  659.                 if (i = strlen(cd->artist))
  660.                     cd->artist[i - 1] = '\0';
  661.             }
  662.         }
  663.  
  664.         else if (! strcmp(keyword, "cdvolume"))
  665.             fscanf(fp, "%d", &cd->volume);
  666.  
  667.         else if (! strcmp(keyword, "dontplay"))
  668.         {
  669.             fscanf(fp, "%d", &i);
  670.             if (trackmap[i - 1] != -1)
  671.                 cd->trk[trackmap[i - 1]].avoid = 1;
  672.         }
  673.  
  674.         else if (! strcmp(keyword, "continue"))
  675.         {
  676.             if (trackmap[track - 1] != -1)
  677.                 cd->trk[trackmap[track - 1]].contd = 1;
  678.         }
  679.  
  680.         else if (! strcmp(keyword, "volume"))
  681.         {
  682.             fscanf(fp, "%d", &i);
  683.             if (trackmap[i - 1] == -1)
  684.                 SWALLOW_LINE(fp)
  685.             else
  686.             {
  687.                 i = trackmap[i - 1];
  688.                 fscanf(fp, "%d", &cd->trk[i].volume);
  689.                 if (cd->trk[i].volume > 32)
  690.                     cd->trk[i].volume = 0;
  691.             }
  692.         }
  693.  
  694.         else if (! strcmp(keyword, "playlist"))
  695.         {
  696.             getc(fp);
  697.             fscanf(fp, "%s", listname);
  698.  
  699. /* XXX take this out at some point */
  700.             if (! strcmp(listname, "Default"))
  701.                 strcpy(listname, "List A");
  702.  
  703.             for (i = 0; listname[i]; i++)
  704.                 if (listname[i] == '_')
  705.                     listname[i] = ' ';
  706.  
  707.             l = new_list(cd, listname);
  708.             if (l == NULL)
  709.             {
  710. plnomem:
  711.                 perror("playlist read");
  712.                 exit(1);
  713.             }
  714.  
  715.             fscanf(fp, "%d", &listsize);
  716.  
  717.             l->list = malloc(sizeof(int) * (listsize + 1));
  718.             if (l->list == NULL)
  719.                 goto plnomem;
  720.  
  721.             /* Leave out tracks that weren't in .workmandb. */
  722.             j = 0;
  723.             for (i = 0; i < listsize; i++)
  724.             {
  725.                 fscanf(fp, "%d", &scratch);
  726.                 scratch = trackmap[scratch - 1];
  727.                 if (scratch != -1)
  728.                     l->list[j++] = scratch + 1;
  729.             }
  730.  
  731.             l->list[j] = 0;
  732.         }
  733.  
  734.         else if (! strcmp(keyword, "mark"))
  735.         {
  736.             int mark_val = -1, mark_namelen;
  737.             char mark_name[32];
  738.  
  739.             fscanf(fp, "%d", &mark_val);
  740.             if (mark_val == -1)
  741.                 goto chomp;
  742.  
  743.             if (getc(fp) != ' ')
  744.                 continue;
  745.  
  746.             fgets(mark_name, sizeof(mark_name), fp);
  747.             if (mark_namelen = strlen(mark_name))
  748.                 mark_name[mark_namelen - 1] = '\0';
  749.  
  750.             if (! strcmp(mark_name, "START"))
  751.                 set_abtimer(0, mark_val);
  752.             else if (! strcmp(mark_name, "END"))
  753.                 set_abtimer(1, mark_val);
  754.         }
  755.  
  756.         /* Unrecognized keyword.  Put it in the right place. */
  757.         else
  758.         {
  759.             char    **buf, input[BUFSIZ];
  760.  
  761.             if (track && trackmap[track - 1] == -1)
  762.             {
  763.                 SWALLOW_LINE(fp);
  764.                 continue;
  765.             }
  766.  
  767.             i = track ? trackmap[track - 1] : 0;
  768.             buf = prefs ? i ? &cd->trk[i].otherrc : &cd->otherrc :
  769.                 i ? &cd->trk[i].otherdb : &cd->otherdb;
  770.             if (firstpos == -1)
  771.                 if (prefs)
  772.                     buf = &otherrc;
  773.                 else
  774.                     goto chomp;
  775.  
  776.             strmcat(buf, keyword);
  777.             do {
  778.                 input[sizeof(input) - 1] = 'x';
  779.                 fgets(input, sizeof(input), fp);
  780.                 strmcat(buf, input);
  781.             } while (input[sizeof(input) - 1] != 'x');
  782.         }
  783.     }
  784.  
  785.     if (rclen == 0 && !searching)
  786.         rclen = pos - rcpos;
  787.  
  788.     return (! searching);
  789. }
  790.  
  791. /*
  792.  * Delay some amount of time without using interval timers.
  793.  */
  794. void
  795. spinwheels(secs)
  796.     int    secs;
  797. {
  798.     struct timeval    tv;
  799.  
  800.     tv.tv_usec = 0;
  801.     tv.tv_sec = secs;
  802.     select(0, NULL, NULL, NULL, &tv);
  803. }
  804.  
  805. /*
  806.  * Lock a file.  Time out after a little while if we can't get a lock;
  807.  * this usually means the locking system is broken.
  808.  *
  809.  * Unfortunately, if there are lots of people contending for a lock,
  810.  * this can result in the file not getting locked when it probably should.
  811.  */
  812. int
  813. lockit(fd, type)
  814.     int    fd;
  815.     int    type;
  816. {
  817.     struct flock    fl;
  818.     int        result, timer = 0;
  819.  
  820.     if (suppress_locking)
  821.         return (0);
  822.  
  823.     fl.l_type = type;
  824.     fl.l_whence = 0;
  825.     fl.l_start = 0;
  826.     fl.l_len = 0;
  827.  
  828.     while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
  829.     {
  830.         if (errno != EACCES || errno != EAGAIN)
  831.             break;
  832.         if (timer++ == 30)
  833.         {
  834.             errno = ETIMEDOUT;
  835.             break;
  836.         }
  837.  
  838.         spinwheels(1);
  839.     }
  840.  
  841.     return (result);
  842. }
  843.  
  844. /*
  845.  * Search all the database files and our personal preference file for
  846.  * more information about the current CD.
  847.  */
  848. void
  849. load()
  850. {
  851.     FILE        *fp;
  852.     char        **dbfile;
  853.     int        locked = 0;
  854.  
  855.     if (databases == NULL)
  856.         split_workmandb();
  857.  
  858.     dbfile = databases;
  859.  
  860.     found_in_db = 0;
  861.  
  862.     do {
  863.         fp = open_rcfile(*dbfile, "r");
  864.         if (fp != NULL)
  865.         {
  866.             if (lockit(fileno(fp), F_RDLCK))
  867.                 perror("Couldn't get read lock");
  868.             else
  869.                 locked = 1;
  870.  
  871.             if (search_db(fp, 0, 0, 0))
  872.             {
  873.                 found_in_db = 1;
  874.                 cd->whichdb = *dbfile;
  875.             }
  876.  
  877.             if (locked && lockit(fileno(fp), F_UNLCK))
  878.                 perror("Couldn't relinquish lock");
  879.  
  880.             fclose(fp);
  881.         }
  882.     } while (*++dbfile != NULL && cd->whichdb == NULL);
  883.  
  884.     fp = open_rcfile(rcfile, "r");
  885.     if (fp != NULL)
  886.     {
  887.         locked = 0;
  888.         if (lockit(fileno(fp), F_RDLCK))
  889.             perror("Couldn't get read lock");
  890.         else
  891.             locked = 1;
  892.  
  893.         rcpos = 0;
  894.         found_in_rc = search_db(fp, 1, 0, 0);
  895.         if (! found_in_rc)
  896.             cd->autoplay = get_playnew();
  897.  
  898.         if (locked && lockit(fileno(fp), F_UNLCK))
  899.             perror("Couldn't relinquish lock");
  900.  
  901.         fclose(fp);
  902.     }
  903.  
  904.     if (cur_playnew == -1)
  905.         cur_playnew = 0;
  906. }
  907.  
  908. /*
  909.  * save_globals()
  910.  *
  911.  * Save the global preferences, scooting CD entries to the end if needed.
  912.  * The assumption here is that the rcfile is locked, and that firstpos has
  913.  * been set by a previous scan.
  914.  */
  915. void
  916. save_globals(fp)
  917. FILE *fp;
  918. {
  919.     char    *globes = NULL, *cdentry = NULL, temp[100];
  920.     long    curpos;
  921.     int    globesize, hit_cdent = 0, c;
  922.  
  923.     if (otherrc)
  924.         strmcpy(&globes, otherrc);
  925.  
  926.     if (cur_stopmode == 1 || cur_stopmode == 2)
  927.     {
  928.         sprintf(temp, "whendone %s\n", cur_stopmode == 1 ? "repeat" :
  929.             "eject");
  930.         strmcat(&globes, temp);
  931.     }
  932.  
  933.     if (cur_playnew == 1)
  934.         strmcat(&globes, "playnew\n");
  935.  
  936.     curpos = firstpos;
  937.     if (curpos < 0)
  938.         curpos = 0;
  939.  
  940.     fseek(fp, curpos, SEEK_SET);
  941.  
  942.     if (firstpos < (globesize = globes != NULL ? strlen(globes) : 0))
  943.     {
  944.         while (1)
  945.         {
  946.             temp[sizeof(temp)-1] = 'x';
  947.  
  948.             if (fgets(temp, sizeof(temp), fp) == NULL)
  949.             {
  950.                 fseek(fp, 0, SEEK_SET);
  951.                 if (globes != NULL)
  952.                 {
  953.                     fwrite(globes, globesize, 1, fp);
  954.                     free(globes);
  955.                 }
  956.                 if (cdentry != NULL)
  957.                 {
  958.                     fwrite(cdentry, strlen(cdentry), 1, fp);
  959.                     free(cdentry);
  960.                 }
  961.                 return;
  962.             }
  963.  
  964.             if (! strncmp(temp, "tracks ", 7))
  965.             {
  966.                 hit_cdent = 1;
  967.                 if (curpos >= globesize)
  968.                     break;
  969.             }
  970.  
  971.             if (! hit_cdent)
  972.             {
  973.                 curpos += strlen(temp);
  974.                 if (temp[sizeof(temp)-1] == '\0')
  975.                     while ((c = getc(fp)) != '\n' &&
  976.                                 c != EOF)
  977.                         curpos++;
  978.                 if (c == '\n')
  979.                     curpos++;
  980.  
  981.                 continue;
  982.             }
  983.  
  984.             strmcat(&cdentry, temp);
  985.             curpos += strlen(temp);
  986.             while (temp[sizeof(temp)-1] == '\0')
  987.             {
  988.                 temp[sizeof(temp)-1] = 'x';
  989.                 if (fgets(temp, sizeof(temp), fp) == NULL)
  990.                     break;
  991.                 strmcat(&cdentry, temp);
  992.                 curpos += strlen(temp);
  993.             }
  994.         } 
  995.  
  996.         if (cdentry != NULL)
  997.         {
  998.             fseek(fp, 0, SEEK_END);
  999.             fwrite(cdentry, strlen(cdentry), 1, fp);
  1000.             free(cdentry);
  1001.         }
  1002.     }
  1003.  
  1004.     if (globes != NULL)
  1005.     {
  1006.         fseek(fp, 0, SEEK_SET);
  1007.         fwrite(globes, globesize, 1, fp);
  1008.         free(globes);
  1009.     }
  1010.  
  1011.     while (globesize++ < curpos)
  1012.         putc('\n', fp);
  1013. }
  1014.  
  1015. /*
  1016.  * save_entry()
  1017.  *
  1018.  * Save the CD information to one database.
  1019.  *
  1020.  *    fp    FILE* of database to save to.
  1021.  *    pref    0 for hard data, 1 for preferences.
  1022.  *
  1023.  * If an entry for this CD exists already, overwrite it with the new entry
  1024.  * if the new entry is the same size or smaller, or with newlines if the new
  1025.  * entry is larger (in which case the new entry is appended to the file.)
  1026.  *
  1027.  * Also, if the preference information is being updated, save it to the
  1028.  * file while we've got it locked.  Scoot stuff from the beginning of
  1029.  * the file to the end as needed to facilitate this.
  1030.  *
  1031.  * XXX Preference-saving should probably be done elsewhere, like in an
  1032.  * Apply button on the Goodies popup.
  1033.  */
  1034. save_entry(fp, pref)
  1035.     FILE    *fp;
  1036.     int    pref;
  1037. {
  1038.     char        *buf;
  1039.     int        len, i, locked = 0;
  1040.  
  1041.     if (lockit(fileno(fp), F_WRLCK))
  1042.         perror("Warning: Couldn't get write lock");
  1043.     else
  1044.         locked = 1;
  1045.  
  1046.     buf = print_cdinfo(cd, pref);
  1047.     len = strlen(buf);    /* doesn't return if there's an error */
  1048.  
  1049.     rcpos = -1;
  1050.     search_db(fp, pref, 1, len);
  1051.     if (rcpos != -1)        /* XXX */
  1052.     {
  1053.         fseek(fp, rcpos, SEEK_SET);
  1054.         if (rclen >= len && holepos == -1)
  1055.         {
  1056.             fputs(buf, fp);
  1057.             for (i = len; i < rclen; i++)
  1058.                 fputc('\n', fp);
  1059.         }
  1060.         else
  1061.         {
  1062.             for (i = 0; i < rclen; i++)
  1063.                 fputc('\n', fp);
  1064.             if (holepos >= 0)
  1065.                 fseek(fp, holepos, SEEK_SET);
  1066.             else
  1067.                 fseek(fp, 0, SEEK_END);
  1068.             fputs(buf, fp);
  1069.         }
  1070.     }
  1071.     else
  1072.     {
  1073.         if (holepos >= 0)
  1074.             fseek(fp, holepos, SEEK_SET);
  1075.         else
  1076.             fseek(fp, 0, SEEK_END);
  1077.         fputs(buf, fp);
  1078.     }
  1079.  
  1080.     if (pref)
  1081.         save_globals(fp);
  1082.  
  1083.     fflush(fp);
  1084.  
  1085.     if (locked && lockit(fileno(fp), F_UNLCK))
  1086.         perror("Warning: Couldn't relinquish write lock");
  1087. }
  1088.  
  1089. /*
  1090.  * save()
  1091.  *
  1092.  * Save CD information to the appropriate datafile (the first file in the
  1093.  * list, unless the entry came from another database file) and to the
  1094.  * personal prefs file.
  1095.  */
  1096. save()
  1097. {
  1098.     FILE        *fp;
  1099.  
  1100.     fp = open_rcfile(rcfile, "r+");
  1101.     if (fp == NULL)
  1102.     {
  1103.         if (errno == ENOENT)    /* doesn't exist already */
  1104.             fp = open_rcfile(rcfile, "w");
  1105.         if (fp == NULL)
  1106.             return (0);
  1107.     }
  1108.  
  1109.     save_entry(fp, 1);
  1110.  
  1111.     fclose(fp);
  1112.  
  1113.     if (cd->whichdb == NULL || access(cd->whichdb, W_OK))
  1114.         cd->whichdb = databases[0];
  1115.  
  1116.     if ((fp = open_rcfile(cd->whichdb, "r+")) == NULL)
  1117.     {
  1118.         if (errno == ENOENT)
  1119.             fp = open_rcfile(cd->whichdb, "w");
  1120.         if (fp == NULL)
  1121.             return (0);
  1122.     }
  1123.  
  1124.     save_entry(fp, 0);
  1125.     fclose(fp);
  1126.  
  1127.     return (1);
  1128. }
  1129.