home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xmcd-1.4 / cda.d / cda.c next >
Encoding:
C/C++ Source or Header  |  1995-05-10  |  65.6 KB  |  3,546 lines

  1. /*
  2.  *   cda - Command-line CD Audio Player
  3.  *
  4.  *   Copyright (C) 1995  Ti Kan
  5.  *   E-mail: ti@amb.org
  6.  *
  7.  *   This program is free software; you can redistribute it and/or modify
  8.  *   it under the terms of the GNU General Public License as published by
  9.  *   the Free Software Foundation; either version 2 of the License, or
  10.  *   (at your option) any later version.
  11.  *
  12.  *   This program is distributed in the hope that it will be useful,
  13.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  *   GNU General Public License for more details.
  16.  *
  17.  *   You should have received a copy of the GNU General Public License
  18.  *   along with this program; if not, write to the Free Software
  19.  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  *
  21.  */
  22. #ifndef LINT
  23. static char *_cda_c_ident_ = "@(#)cda.c    5.19 95/01/29";
  24. #endif
  25.  
  26. #define _CDA_
  27.  
  28. #include "common.d/appenv.h"
  29. #include "common.d/util.h"
  30. #include "common.d/patchlevel.h"
  31. #include "libdi.d/libdi.h"
  32. #include "cda.d/cda.h"
  33. #include "cda.d/visual.h"
  34.  
  35.  
  36. #define DBPATH_SEPCHAR    ':'            /* Database path separator */
  37. #define PGM_SEPCHAR    ','            /* Program seq separator */
  38.  
  39.  
  40. extern char        *ttyname();
  41.  
  42. appdata_t        app_data;        /* Option settings */
  43. database_t        cur_db;            /* Database entry of CD */
  44. curstat_t        status;            /* Current CD player status */
  45. char            *errmsg = NULL,        /* Error msg for use on exit */
  46.             emsg[ERR_BUF_SZ],    /* Error message buffer */
  47.             spipe[FILE_PATH_SZ],    /* Send pipe path */
  48.             rpipe[FILE_PATH_SZ];    /* Receive pipe path */
  49. int            cda_sfd[2] = {-1,-1},    /* Send pipe file desc */
  50.             cda_rfd[2] = {-1,-1};    /* Receive pipe file desc */
  51. FILE            *errfp = stderr;    /* Error message stream */
  52.  
  53. STATIC char        lockfile[FILE_PATH_SZ],    /* Lock file path */
  54.             **dbdirs = NULL;    /* CD database directories */
  55. STATIC int        cont_delay = 1;        /* Status display interval */
  56. STATIC dev_t        cd_rdev;        /* CD device number */
  57. STATIC bool_t        visual = FALSE,        /* Visual (curses) mode */
  58.             isdaemon = FALSE,    /* Am I the daemon process */
  59.             stat_cont = FALSE;    /* Continuous display status */
  60. STATIC FILE        *ttyfp;            /* /dev/tty */
  61.  
  62.  
  63.  
  64. /***********************
  65.  *  internal routines  *
  66.  ***********************/
  67.  
  68.  
  69. /*
  70.  * onsig
  71.  *    Signal handler to shut down application.
  72.  *
  73.  * Args:
  74.  *    signo - The signal number.
  75.  *
  76.  * Return:
  77.  *    Nothing.
  78.  */
  79. /* ARGSUSED */
  80. STATIC void
  81. onsig(int signo)
  82. {
  83.     cd_quit(&status);
  84.     exit(3);
  85. }
  86.  
  87.  
  88. /*
  89.  * onintr
  90.  *    Signal handler to stop continuous status display.
  91.  *
  92.  * Args:
  93.  *    signo - The signal number.
  94.  *
  95.  * Return:
  96.  *    Nothing.
  97.  */
  98. /* ARGSUSED */
  99. STATIC void
  100. onintr(int signo)
  101. {
  102.     signal(signo, SIG_IGN);
  103.     stat_cont = FALSE;
  104.     printf("\r");
  105. }
  106.  
  107.  
  108. /*
  109.  * common_parminit
  110.  *    Read the common configuration file and initialize parameters.
  111.  *
  112.  * Args:
  113.  *    path - Path name to the file to read.
  114.  *
  115.  * Return:
  116.  *    Nothing.
  117.  */
  118. STATIC void
  119. common_parminit(char *path, bool_t priv)
  120. {
  121.     FILE        *fp;
  122.     char        buf[STR_BUF_SZ * 16],
  123.             parm[128];
  124.     static bool_t    dev_cmdline = FALSE,
  125.             force_debug = FALSE;
  126.  
  127.     /* Default maximum number of CD database directories */
  128.     app_data.max_dbdirs = MAX_DBDIRS;
  129.  
  130.     /* Initialize error messages */
  131.     app_data.str_nomethod = STR_NOMETHOD;
  132.     app_data.str_novu = STR_NOVU;
  133.     app_data.str_nomemory = STR_NOMEMORY;
  134.     app_data.str_nocfg = STR_NOCFG;
  135.     app_data.str_notrom = STR_NOTROM;
  136.     app_data.str_notscsi2 = STR_NOTSCSI2;
  137.     app_data.str_moderr = STR_MODERR;
  138.     app_data.str_staterr = STR_STATERR;
  139.     app_data.str_noderr = STR_NODERR;
  140.     app_data.str_dbdirserr = STR_DBDIRSERR;
  141.     app_data.str_recoverr = STR_RECOVERR;
  142.     app_data.str_maxerr = STR_MAXERR;
  143.     app_data.str_tmpdirerr = STR_TMPDIRERR;
  144.  
  145.     if (di_isdemo())
  146.         app_data.device = "(none)";
  147.  
  148.     /* Open common config file */
  149.     if ((fp = fopen(path, "r")) == NULL) {
  150.         if (priv && !di_isdemo()) {
  151.             /* Cannot open config file. */
  152.             sprintf(emsg, app_data.str_nocfg, path);
  153.             cd_fatal_popup(NULL, emsg);
  154.         }
  155.         return;
  156.     }
  157.  
  158.     if (priv && app_data.debug)
  159.         force_debug = TRUE;
  160.  
  161.     /* Read in common parameters */
  162.     while (fgets(buf, sizeof(buf), fp) != NULL) {
  163.         /* Skip comments */
  164.         if (buf[0] == '#' || buf[0] == '!' || buf[0] == '\n')
  165.             continue;
  166.  
  167.         if (!di_isdemo() && sscanf(buf, "device: %s\n", parm) > 0) {
  168.             if (priv && app_data.device != NULL)
  169.                 dev_cmdline = TRUE;
  170.             if (!dev_cmdline) {
  171.                 if (app_data.device != NULL)
  172.                     MEM_FREE(app_data.device);
  173.  
  174.                 app_data.device = (char *) MEM_ALLOC(
  175.                     strlen(parm) + 1
  176.                 );
  177.                 if (app_data.device == NULL) {
  178.                     cd_fatal_popup(
  179.                         NULL,
  180.                         app_data.str_nomemory
  181.                     );
  182.                 }
  183.                 strcpy(app_data.device, parm);
  184.             }
  185.             continue;
  186.         }
  187.         if (sscanf(buf, "dbdir: %s\n", parm) > 0) {
  188.             app_data.dbdir = (char *) MEM_ALLOC(strlen(parm) + 1);
  189.             if (app_data.dbdir == NULL)
  190.                 cd_fatal_popup(NULL, app_data.str_nomemory);
  191.  
  192.             strcpy(app_data.dbdir, parm);
  193.             continue;
  194.         }
  195.         if (sscanf(buf, "maxDbdirs: %s\n", parm) > 0) {
  196.             app_data.max_dbdirs = atoi(parm);
  197.             continue;
  198.         }
  199.         if (sscanf(buf, "previousThreshold: %s\n", parm) > 0) {
  200.             app_data.prev_threshold = atoi(parm);
  201.             continue;
  202.         }
  203.         if (sscanf(buf, "solaris2VolumeManager: %s\n", parm) > 0) {
  204.             app_data.sol2_volmgt = stob(parm);
  205.             continue;
  206.         }
  207.         if (sscanf(buf, "showScsiErrMsg: %s\n", parm) > 0) {
  208.             app_data.scsierr_msg = stob(parm);
  209.             continue;
  210.         }
  211.         if (sscanf(buf, "debugMode: %s\n", parm) > 0) {
  212.             if (!force_debug)
  213.                 app_data.debug = stob(parm);
  214.             continue;
  215.         }
  216.     }
  217.  
  218.     fclose(fp);
  219. }
  220.  
  221.  
  222. /*
  223.  * devspec_parminit
  224.  *    Read the specified device-specific configuration file and
  225.  *    initialize parameters.
  226.  *
  227.  * Args:
  228.  *    path - Path name to the file to read.
  229.  *    priv - Whether the privileged keywords are to be recognized.
  230.  *
  231.  * Return:
  232.  *    Nothing.
  233.  */
  234. STATIC void
  235. devspec_parminit(char *path, bool_t priv)
  236. {
  237.     FILE    *fp;
  238.     char    buf[STR_BUF_SZ * 16],
  239.         parm[128];
  240.  
  241.     if ((fp = fopen(path, "r")) == NULL) {
  242.         if (priv && !di_isdemo()) {
  243.             /* Cannot open master device-specific
  244.              * config file.
  245.              */
  246.             sprintf(emsg, app_data.str_nocfg, path);
  247.             cd_fatal_popup(NULL, emsg);
  248.         }
  249.         return;
  250.     }
  251.  
  252.     /* Read in device-specific parameters */
  253.     while (fgets(buf, sizeof(buf), fp) != NULL) {
  254.         /* Skip comments */
  255.         if (buf[0] == '#' || buf[0] == '!' || buf[0] == '\n')
  256.             continue;
  257.  
  258.         /* These are privileged parameters and users
  259.          * cannot overide them in their .xmcdcfg file.
  260.          */
  261.         if (priv) {
  262.             if (sscanf(buf, "logicalDriveNumber: %s\n",
  263.                    parm) > 0) {
  264.                 app_data.devnum = atoi(parm);
  265.                 continue;
  266.             }
  267.             if (sscanf(buf, "deviceInterfaceMethod: %s\n",
  268.                    parm) > 0) {
  269.                 app_data.di_method = atoi(parm);
  270.                 continue;
  271.             }
  272.             if (sscanf(buf, "driveVendorCode: %s\n",
  273.                    parm) > 0) {
  274.                 app_data.vendor_code = atoi(parm);
  275.                 continue;
  276.             }
  277.             if (sscanf(buf, "scsiAudioVolumeBase: %s\n",
  278.                    parm) > 0) {
  279.                 app_data.base_scsivol = atoi(parm);
  280.                 continue;
  281.             }
  282.             if (sscanf(buf, "minimumPlayBlocks: %s\n",
  283.                    parm) > 0) {
  284.                 app_data.min_playblks = atoi(parm);
  285.                 continue;
  286.             }
  287.             if (sscanf(buf, "playAudio10Support: %s\n",
  288.                    parm) > 0) {
  289.                 app_data.play10_supp = stob(parm);
  290.                 continue;
  291.             }
  292.             if (sscanf(buf, "playAudio12Support: %s\n",
  293.                    parm) > 0) {
  294.                 app_data.play12_supp = stob(parm);
  295.                 continue;
  296.             }
  297.             if (sscanf(buf, "playAudioMSFSupport: %s\n",
  298.                    parm) > 0) {
  299.                 app_data.playmsf_supp = stob(parm);
  300.                 continue;
  301.             }
  302.             if (sscanf(buf, "playAudioTISupport: %s\n",
  303.                    parm) > 0) {
  304.                 app_data.playti_supp = stob(parm);
  305.                 continue;
  306.             }
  307.             if (sscanf(buf, "loadSupport: %s\n",
  308.                    parm) > 0) {
  309.                 app_data.load_supp = stob(parm);
  310.                 continue;
  311.             }
  312.             if (sscanf(buf, "ejectSupport: %s\n",
  313.                    parm) > 0) {
  314.                 app_data.eject_supp = stob(parm);
  315.                 continue;
  316.             }
  317.             if (sscanf(buf, "modeSenseSetDBD: %s\n",
  318.                    parm) > 0) {
  319.                 app_data.msen_dbd = stob(parm);
  320.                 continue;
  321.             }
  322.             if (sscanf(buf, "volumeControlSupport: %s\n",
  323.                    parm) > 0) {
  324.                 app_data.mselvol_supp = stob(parm);
  325.                 continue;
  326.             }
  327.             if (sscanf(buf, "balanceControlSupport: %s\n",
  328.                    parm) > 0) {
  329.                 app_data.balance_supp = stob(parm);
  330.                 continue;
  331.             }
  332.             if (sscanf(buf, "channelRouteSupport: %s\n",
  333.                    parm) > 0) {
  334.                 app_data.chroute_supp = stob(parm);
  335.                 continue;
  336.             }
  337.             if (sscanf(buf, "pauseResumeSupport: %s\n",
  338.                    parm) > 0) {
  339.                 app_data.pause_supp = stob(parm);
  340.                 continue;
  341.             }
  342.             if (sscanf(buf, "caddyLockSupport: %s\n",
  343.                    parm) > 0) {
  344.                 app_data.caddylock_supp = stob(parm);
  345.                 continue;
  346.             }
  347.             if (sscanf(buf, "curposFormat: %s\n",
  348.                    parm) > 0) {
  349.                 app_data.curpos_fmt = stob(parm);
  350.                 continue;
  351.             }
  352.             if (sscanf(buf, "noTURWhenPlaying: %s\n",
  353.                    parm) > 0) {
  354.                 app_data.play_notur = stob(parm);
  355.                 continue;
  356.             }
  357.         }
  358.  
  359.         /* These are general parameters that can be
  360.          * changed by the user.
  361.          */
  362.         if (sscanf(buf, "volumeControlTaper: %s\n", parm) > 0) {
  363.             app_data.vol_taper = atoi(parm);
  364.             continue;
  365.         }
  366.         if (sscanf(buf, "channelRoute: %s\n", parm) > 0) {
  367.             app_data.ch_route = atoi(parm);
  368.             continue;
  369.         }
  370.         if (sscanf(buf, "spinDownOnLoad: %s\n", parm) > 0) {
  371.             app_data.load_spindown = stob(parm);
  372.             continue;
  373.         }
  374.         if (sscanf(buf, "playOnLoad: %s\n", parm) > 0) {
  375.             app_data.load_play = stob(parm);
  376.             continue;
  377.         }
  378.         if (sscanf(buf, "ejectOnDone: %s\n", parm) > 0) {
  379.             app_data.done_eject = stob(parm);
  380.             continue;
  381.         }
  382.         if (sscanf(buf, "ejectOnExit: %s\n", parm) > 0) {
  383.             app_data.exit_eject = stob(parm);
  384.             continue;
  385.         }
  386.         if (sscanf(buf, "stopOnExit: %s\n", parm) > 0) {
  387.             app_data.exit_stop = stob(parm);
  388.             continue;
  389.         }
  390.         if (sscanf(buf, "exitOnEject: %s\n", parm) > 0) {
  391.             app_data.eject_exit = stob(parm);
  392.             continue;
  393.         }
  394.         if (sscanf(buf, "closeOnEject: %s\n", parm) > 0) {
  395.             app_data.eject_close = stob(parm);
  396.             continue;
  397.         }
  398.         if (sscanf(buf, "caddyLock: %s\n", parm) > 0) {
  399.             app_data.caddy_lock = stob(parm);
  400.             continue;
  401.         }
  402.     }
  403.  
  404.     fclose(fp);
  405.  
  406.     if (!priv) {
  407.         /* If the drive does not support software eject, then we
  408.          * can't lock the caddy.
  409.          */
  410.         if (!app_data.eject_supp) {
  411.             app_data.caddylock_supp = FALSE;
  412.             app_data.done_eject = FALSE;
  413.             app_data.exit_eject = FALSE;
  414.         }
  415.  
  416.         /* playOnLoad overrides spinDownOnLoad */
  417.         if (app_data.load_play)
  418.             app_data.load_spindown = FALSE;
  419.  
  420.         /* If the drive does not support locking the caddy, don't
  421.          * attempt to lock it.
  422.          */
  423.         if (!app_data.caddylock_supp)
  424.             app_data.caddy_lock = FALSE;
  425.  
  426.         /* If the drive does not support software volume
  427.          * control, then it can't support the balance
  428.          * control either.  Also, force the volume control
  429.          * taper selector to the linear position.
  430.          */
  431.         if (!app_data.mselvol_supp) {
  432.             app_data.balance_supp = FALSE;
  433.             app_data.vol_taper = 0;
  434.         }
  435.  
  436.         /* If the drive does not support channel routing,
  437.          * * force the channel routing setting to normal.
  438.          */
  439.         if (!app_data.chroute_supp)
  440.             app_data.ch_route = 0;
  441.     }
  442. }
  443.  
  444.  
  445. /*
  446.  * dbprog_sum
  447.  *    Convert an integer to its text string representation, and
  448.  *    compute its checksum.  Used by dbprog_discid to derive the
  449.  *    disc ID.
  450.  *
  451.  * Args:
  452.  *    n - The integer value.
  453.  *
  454.  * Return:
  455.  *    The integer checksum.
  456.  */
  457. STATIC int
  458. dbprog_sum(int n)
  459. {
  460.     char    buf[12],
  461.         *p;
  462.     int    ret = 0;
  463.  
  464.     /* For backward compatibility this algorithm must not change */
  465.     sprintf(buf, "%lu", n);
  466.     for (p = buf; *p != '\0'; p++)
  467.         ret += (*p - '0');
  468.  
  469.     return (ret);
  470. }
  471.  
  472.  
  473. /*
  474.  * dbprog_discid
  475.  *    Compute a magic disc ID based on the number of tracks,
  476.  *    the length of each track, and a checksum of the string
  477.  *    that represents the offset of each track.
  478.  *
  479.  * Args:
  480.  *    s - Pointer to the curstat_t structure.
  481.  *
  482.  * Return:
  483.  *    The integer disc ID.
  484.  */
  485. STATIC word32_t
  486. dbprog_discid(curstat_t *s)
  487. {
  488.     int    i,
  489.         t = 0,
  490.         n = 0;
  491.  
  492.     /* For backward compatibility this algorithm must not change */
  493.     for (i = 0; i < (int) s->tot_trks; i++) {
  494.         n += dbprog_sum((s->trkinfo[i].min * 60) + s->trkinfo[i].sec);
  495.  
  496.         t += ((s->trkinfo[i+1].min * 60) + s->trkinfo[i+1].sec) -
  497.              ((s->trkinfo[i].min * 60) + s->trkinfo[i].sec);
  498.     }
  499.  
  500.     return ((n % 0xff) << 24 | t << 8 | s->tot_trks);
  501. }
  502.  
  503.  
  504. /*
  505.  * dbprog_strcat
  506.  *    Concatenate two text strings with special handling for newline
  507.  *    and tab character translations.
  508.  *
  509.  * Args:
  510.  *    s1 - The first text string and destination string.
  511.  *    s2 - The second text string.
  512.  *
  513.  * Return:
  514.  *    Pointer to the resultant string, or NULL if failed.
  515.  */
  516. STATIC char *
  517. dbprog_strcat(char *s1, char *s2)
  518. {
  519.     char    *cp = s1;
  520.  
  521.     if (s1 == NULL || s2 == NULL)
  522.         return NULL;
  523.  
  524.     /* Concatenate two strings, with special handling for newline
  525.      * and tab characters.
  526.      */
  527.     for (s1 += strlen(s1); *s2 != '\0'; s1++, s2++) {
  528.         if (*s2 == '\\') {
  529.             switch (*(s2 + 1)) {
  530.             case 'n':
  531.                 *s1 = '\n';
  532.                 s2++;
  533.                 break;
  534.             case 't':
  535.                 *s1 = '\t';
  536.                 s2++;
  537.                 break;
  538.             default:
  539.                 *s1 = *s2;
  540.                 break;
  541.             }
  542.         }
  543.         else
  544.             *s1 = *s2;
  545.     }
  546.     *s1 = '\0';
  547.  
  548.     return (cp);
  549. }
  550.  
  551.  
  552. /*
  553.  * dbprog_parse
  554.  *    Parse the shuffle/program mode play sequence text string, and
  555.  *    update the playorder table in the curstat_t structure.
  556.  *
  557.  * Args:
  558.  *    s - Pointer to the curstat_t structure.
  559.  *
  560.  * Return:
  561.  *    TRUE=success, FALSE=error.
  562.  */
  563. STATIC bool_t
  564. dbprog_parse(curstat_t *s)
  565. {
  566.     int    i,
  567.         j,
  568.         n;
  569.     char    *p,
  570.         *q,
  571.         *tmpbuf;
  572.     bool_t    last = FALSE;
  573.  
  574.     if (cur_db.playorder == NULL)
  575.         return FALSE;
  576.  
  577.     n = strlen(cur_db.playorder) + 1;
  578.     if ((tmpbuf = (char *) MEM_ALLOC(n)) == NULL)
  579.         cd_fatal_popup(NULL, app_data.str_nomemory);
  580.  
  581.     strcpy(tmpbuf, cur_db.playorder);
  582.  
  583.     s->prog_tot = 0;
  584.  
  585.     for (i = 0, p = q = tmpbuf; i < MAXTRACK; i++, p = ++q) {
  586.         /* Skip p to the next digit */
  587.         for (; !isdigit(*p) && *p != '\0'; p++)
  588.             ;
  589.  
  590.         if (*p == '\0')
  591.             /* No more to do */
  592.             break;
  593.  
  594.         /* Skip q to the next non-digit */
  595.         for (q = p; isdigit(*q); q++)
  596.             ;
  597.  
  598.         if (*q == PGM_SEPCHAR)
  599.             *q = '\0';
  600.         else if (*q == '\0')
  601.             last = TRUE;
  602.         else {
  603.             MEM_FREE(tmpbuf);
  604.             return FALSE;
  605.         }
  606.  
  607.         if (q > p) {
  608.             /* Update play sequence */
  609.             for (j = 0; j < MAXTRACK; j++) {
  610.                 if (s->trkinfo[j].trkno == atoi(p)) {
  611.                     s->playorder[i] = j;
  612.                     s->prog_tot++;
  613.                     break;
  614.                 }
  615.             }
  616.  
  617.             if (j >= MAXTRACK) {
  618.                 MEM_FREE(tmpbuf);
  619.                 return FALSE;
  620.             }
  621.         }
  622.  
  623.         if (last)
  624.             break;
  625.     }
  626.  
  627.     MEM_FREE(tmpbuf);
  628.     return TRUE;
  629. }
  630.  
  631.  
  632. /*
  633.  * cda_init
  634.  *    Initialize the CD interface subsystem.
  635.  *
  636.  * Args:
  637.  *    s - Pointer to the curstat_t structure.
  638.  *
  639.  * Return:
  640.  *    Nothing.
  641.  */
  642. STATIC void
  643. cda_init(curstat_t *s)
  644. {
  645.     char    str[FILE_PATH_SZ];
  646.  
  647.     /* Get system-wide device-specific configuration parameters */
  648.     sprintf(str, "%s/config/%s",
  649.         app_data.libdir, basename(app_data.device));
  650.     devspec_parminit(str, TRUE);
  651.  
  652.     /* Get user device-specific configuration parameters */
  653.     sprintf(str, "%s/.xmcdcfg/%s",
  654.         homedir(get_ouid()), basename(app_data.device));
  655.     devspec_parminit(str, FALSE);
  656.  
  657.     /* Initialize the CD hardware */
  658.     di_init(s);
  659. }
  660.  
  661.  
  662. /*
  663.  * cda_start
  664.  *    Start the CD interface subsystem.
  665.  *
  666.  * Args:
  667.  *    s - Pointer to the curstat_t structure.
  668.  *
  669.  * Return:
  670.  *    Nothing.
  671.  */
  672. STATIC void
  673. cda_start(curstat_t *s)
  674. {
  675.     /* Debug information */
  676.     if (app_data.debug) {
  677.         fprintf(errfp, "\ndevnum=%d\n",
  678.             app_data.devnum);
  679.         fprintf(errfp, "device=%s\n",
  680.             app_data.device);
  681.         fprintf(errfp, "libdir=%s\n",
  682.             app_data.libdir);
  683.         fprintf(errfp, "deviceInterfaceMethod=%d\n",
  684.             app_data.di_method);
  685.         fprintf(errfp, "minimumPlayBlocks=%d\n",
  686.             app_data.min_playblks);
  687.         fprintf(errfp, "scsiAudioVolumeBase=%d\n",
  688.             app_data.base_scsivol);
  689.         fprintf(errfp, "volumeControlTaper=%d\n",
  690.             app_data.vol_taper);
  691.         fprintf(errfp, "channelRoute=%d\n",
  692.             app_data.ch_route);
  693.         fprintf(errfp, "driveVendorCode=%d\n",
  694.             app_data.vendor_code);
  695.         fprintf(errfp, "playAudio10Support=%d\n",
  696.             app_data.play10_supp);
  697.         fprintf(errfp, "playAudio12Support=%d\n",
  698.             app_data.play12_supp);
  699.         fprintf(errfp, "playAudioMSFSupport=%d\n",
  700.             app_data.playmsf_supp);
  701.         fprintf(errfp, "playAudioTISupport=%d\n",
  702.             app_data.playti_supp);
  703.         fprintf(errfp, "loadSupport=%d\n",
  704.             app_data.load_supp);
  705.         fprintf(errfp, "ejectSupport=%d\n",
  706.             app_data.eject_supp);
  707.         fprintf(errfp, "modeSenseSetDBD=%d\n",
  708.             app_data.msen_dbd);
  709.         fprintf(errfp, "volumeControlSupport=%d\n",
  710.             app_data.mselvol_supp);
  711.         fprintf(errfp, "balanceControlSupport=%d\n",
  712.             app_data.balance_supp);
  713.         fprintf(errfp, "channelRouteSupport=%d\n",
  714.             app_data.chroute_supp);
  715.         fprintf(errfp, "pauseResumeSupport=%d\n",
  716.             app_data.pause_supp);
  717.         fprintf(errfp, "caddyLockSupport=%d\n",
  718.             app_data.caddylock_supp);
  719.         fprintf(errfp, "curposFormat=%d\n",
  720.             app_data.curpos_fmt);
  721.         fprintf(errfp, "noTURWhenPlaying=%d\n",
  722.             app_data.play_notur);
  723.         fprintf(errfp, "spinDownOnLoad=%d\n",
  724.             app_data.load_spindown);
  725.         fprintf(errfp, "ejectOnExit=%d\n",
  726.             app_data.exit_eject);
  727.         fprintf(errfp, "stopOnExit=%d\n",
  728.             app_data.exit_stop);
  729.         fprintf(errfp, "exitOnEject=%d\n",
  730.             app_data.eject_exit);
  731.         fprintf(errfp, "closeOnEject=%d\n",
  732.             app_data.eject_close);
  733.         fprintf(errfp, "caddyLock=%d\n",
  734.             app_data.caddy_lock);
  735.         fprintf(errfp, "solaris2VolumeManager=%d\n",
  736.             app_data.sol2_volmgt);
  737.         fprintf(errfp, "showScsiErrMsg=%d\n",
  738.             app_data.scsierr_msg);
  739.  
  740.         fprintf(errfp, "\n");
  741.     }
  742.  
  743.     /* Start up I/O interface */
  744.     di_start(s);
  745.  
  746.     /* Open FIFOs - daemon side */
  747.     if ((cda_sfd[0] = open(spipe, O_RDONLY)) < 0) {
  748.         perror("CD audio daemon: cannot open send pipe");
  749.         cd_quit(s);
  750.         exit(4);
  751.     }
  752.     if ((cda_rfd[0] = open(rpipe, O_WRONLY)) < 0) {
  753.         perror("CD audio daemon: cannot open recv pipe");
  754.         cd_quit(s);
  755.         exit(5);
  756.     }
  757. }
  758.  
  759.  
  760. /*
  761.  * cda_poll
  762.  *    Periodic polling function - used to manage program, shuffle,
  763.  *    and repeat modes.
  764.  *
  765.  * Args:
  766.  *    s - Pointer to the curstat_t structure.
  767.  *
  768.  * Return:
  769.  *    Nothing.
  770.  */
  771. STATIC void
  772. cda_poll(curstat_t *s)
  773. {
  774.     static int    n = 0;
  775.  
  776.     if (++n > 100)
  777.         n = 0;
  778.  
  779.     if (s->mode != M_PLAY || n % 2)
  780.         return;
  781.  
  782.     if (s->prog_tot > 0 || s->repeat) {
  783.         if (di_check_disc(s))
  784.             di_status_upd(s);
  785.     }
  786. }
  787.  
  788.  
  789. /*
  790.  * cda_sendpkt
  791.  *    Write a CDA packet down the pipe.
  792.  *
  793.  * Args:
  794.  *    name - The text string describing the caller module
  795.  *    fd - Pipe file descriptor
  796.  *    s - Pointer to the packet data
  797.  *
  798.  * Return:
  799.  *    TRUE - pipe write successful
  800.  *    FALSE - pipe write failed
  801.  */
  802. STATIC bool_t
  803. cda_sendpkt(char *name, int fd, cdapkt_t *s)
  804. {
  805.     byte_t    *p = (byte_t *) s;
  806.     int    i,
  807.         ret;
  808.  
  809.     if (fd < 0)
  810.         return FALSE;
  811.  
  812.     /* Brand packet with magic number */
  813.     s->magic = CDA_MAGIC;
  814.  
  815.     /* Send a packet */
  816.     i = sizeof(cdapkt_t);
  817.     while ((ret = write(fd, p, i)) < i) {
  818.         if (ret < 0) {
  819.             if (errno == EBADF || errno == EINTR) {
  820.                 /* Avoid hogging CPU */
  821.                 sleep(1);
  822.             }
  823.             else {
  824.                 sprintf(emsg,
  825.                     "%s: packet write error (errno=%d)\n",
  826.                     name, errno);
  827.                 cd_warning_popup(NULL, emsg);
  828.                 return FALSE;
  829.             }
  830.         }
  831.         else if (ret == 0) {
  832.             /* Avoid hogging CPU */
  833.             sleep(1);
  834.         }
  835.         else {
  836.             i -= ret;
  837.             p += ret;
  838.         }
  839.     }
  840.  
  841.     return TRUE;
  842. }
  843.  
  844.  
  845. /*
  846.  * cda_getpkt
  847.  *    Read a CDA packet from the pipe.
  848.  *
  849.  * Args:
  850.  *    name - The text string describing the caller module
  851.  *    fd - Pipe file descriptor
  852.  *    s - Pointer to the packet data
  853.  *
  854.  * Return:
  855.  *    TRUE - pipe read successful
  856.  *    FALSE - pipe read failed
  857.  */
  858. STATIC bool_t
  859. cda_getpkt(char *name, int fd, cdapkt_t *r)
  860. {
  861.     byte_t    *p = (byte_t *) r;
  862.     int    i,
  863.         ret;
  864.  
  865.     if (fd < 0)
  866.         return FALSE;
  867.  
  868.     /* Get a packet */
  869.     i = sizeof(cdapkt_t);
  870.     while ((ret = read(fd, p, i)) < i) {
  871.         if (ret < 0) {
  872.             if (errno == EBADF || errno == EINTR) {
  873.                 /* Avoid hogging CPU */
  874.                 sleep(1);
  875.             }
  876.             else {
  877.                 sprintf(emsg,
  878.                     "%s: packet read error (errno=%d)\n",
  879.                     name, errno);
  880.                 cd_warning_popup(NULL, emsg);
  881.                 return FALSE;
  882.             }
  883.         }
  884.         else if (ret == 0) {
  885.             /* Use this occasion to perform polling function */
  886.             cda_poll(&status);
  887.  
  888.             /* Avoid hogging CPU */
  889.             sleep(1);
  890.         }
  891.         else {
  892.             i -= ret;
  893.             p += ret;
  894.         }
  895.     }
  896.  
  897.     /* Check packet for magic number */
  898.     if (r->magic != CDA_MAGIC) {
  899.         sprintf(emsg, "%s: bad packet magic number.", name);
  900.         cd_warning_popup(NULL, emsg);
  901.         return FALSE;
  902.     }
  903.  
  904.     return TRUE;
  905. }
  906.  
  907.  
  908. /*
  909.  * cda_docmd
  910.  *    Perform the command received.
  911.  *
  912.  * Args:
  913.  *    s - Pointer to the curstat_t structure.
  914.  *    p - Pointer to the CDA packet structure.
  915.  *
  916.  * Return:
  917.  *    TRUE - Received a CDA_OFF command: daemon should shut down
  918.  *    FALSE - Received normal command.
  919.  */
  920. STATIC bool_t
  921. cda_docmd(curstat_t *s, cdapkt_t *p)
  922. {
  923.     int        i,
  924.             j,
  925.             min,
  926.             sec;
  927.     word32_t    blkno;
  928.     bool_t        stopfirst,
  929.             offsets;
  930.  
  931.     /* Update CD status */
  932.     if (di_check_disc(s) && p->cmd != CDA_OFF)
  933.         di_status_upd(s);
  934.  
  935.     /* Default status */
  936.     p->retcode = CDA_OK;
  937.  
  938.     switch (p->cmd) {
  939.     case CDA_ON:
  940.         p->arg[0] = getpid();
  941.         break;
  942.  
  943.     case CDA_OFF:
  944.         p->arg[0] = getpid();
  945.         return TRUE;
  946.  
  947.     case CDA_DISC:
  948.         if (p->arg[0] == 0) {
  949.             /* Load */
  950.             if (s->mode == M_NODISC)
  951.                 di_load_eject(s);
  952.             else
  953.                 p->retcode = CDA_INVALID;
  954.         }
  955.         else {
  956.             /* Eject */
  957.             if (s->mode != M_NODISC)
  958.                 di_load_eject(s);
  959.             else
  960.                 p->retcode = CDA_INVALID;
  961.         }
  962.  
  963.         break;
  964.  
  965.     case CDA_LOCK:
  966.         if (s->mode == M_NODISC)
  967.             p->retcode = CDA_INVALID;
  968.         else if (p->arg[0] == 0) {
  969.             /* Unlock */
  970.             if (s->caddy_lock) {
  971.                 s->caddy_lock = FALSE;
  972.                 di_lock(s, FALSE);
  973.             }
  974.             else
  975.                 p->retcode = CDA_INVALID;
  976.         }
  977.         else {
  978.             /* Lock */
  979.             if (!s->caddy_lock) {
  980.                 s->caddy_lock = TRUE;
  981.                 di_lock(s, TRUE);
  982.             }
  983.             else
  984.                 p->retcode = CDA_INVALID;
  985.         }
  986.  
  987.         break;
  988.  
  989.     case CDA_PLAY:
  990.         switch (s->mode) {
  991.         case M_PLAY:
  992.             stopfirst = TRUE;
  993.             break;
  994.         case M_NODISC:
  995.             p->retcode = CDA_INVALID;
  996.             return FALSE;
  997.         default:
  998.             stopfirst = FALSE;
  999.             break;
  1000.         }
  1001.  
  1002.         /* Starting track number */
  1003.         i = -1;
  1004.         if (p->arg[0] != 0) {
  1005.             if (s->shuffle || s->prog_cnt > 0) {
  1006.                 p->retcode = CDA_INVALID;
  1007.                 break;
  1008.             }
  1009.  
  1010.             for (i = 0; i < (int) s->tot_trks; i++) {
  1011.                 if (s->trkinfo[i].trkno == p->arg[0])
  1012.                     break;
  1013.             }
  1014.  
  1015.             if (i >= (int) s->tot_trks) {
  1016.                 /* Invalid track specified */
  1017.                 p->retcode = CDA_PARMERR;
  1018.                 break;
  1019.             }
  1020.  
  1021.             s->cur_trk = p->arg[0];
  1022.         }
  1023.  
  1024.         if (stopfirst) {
  1025.             /* Stop current playback first */
  1026.             di_stop(s, TRUE);
  1027.  
  1028.             /*
  1029.              * Restore s->cur_trk value because di_stop() zaps it
  1030.              */
  1031.             if (p->arg[0] != 0)
  1032.                 s->cur_trk = p->arg[0];
  1033.         }
  1034.  
  1035.         if (p->arg[0] != 0 &&
  1036.             (int) p->arg[1] >= 0 && (int) p->arg[2] >= 0) {
  1037.             /* Track offset specified */
  1038.             if (p->arg[2] > 59) {
  1039.                 p->retcode = CDA_PARMERR;
  1040.                 break;
  1041.             }
  1042.  
  1043.             msftoblk((byte_t) p->arg[1], (byte_t) p->arg[2],
  1044.                  0, &blkno, 0);
  1045.  
  1046.             if (blkno >=
  1047.                 (s->trkinfo[i+1].addr - s->trkinfo[i].addr)) {
  1048.                 p->retcode = CDA_PARMERR;
  1049.                 break;
  1050.             }
  1051.  
  1052.             s->cur_trk_addr = blkno;
  1053.             s->cur_trk_min = (byte_t) p->arg[1];
  1054.             s->cur_trk_sec = (byte_t) p->arg[2];
  1055.             s->cur_trk_frame = 0;
  1056.  
  1057.             s->cur_tot_addr = s->trkinfo[i].addr + s->cur_trk_addr;
  1058.             blktomsf(s->cur_tot_addr, &s->cur_tot_min,
  1059.                  &s->cur_tot_sec, &s->cur_tot_frame,
  1060.                  MSF_OFFSET(s));
  1061.         }
  1062.  
  1063.         /* Start playback */
  1064.         di_play_pause(s);
  1065.  
  1066.         break;
  1067.  
  1068.     case CDA_PAUSE:
  1069.         if (s->mode == M_PLAY)
  1070.             di_play_pause(s);
  1071.         else
  1072.             p->retcode = CDA_INVALID;
  1073.  
  1074.         break;
  1075.  
  1076.     case CDA_STOP:
  1077.         di_stop(s, TRUE);
  1078.         break;
  1079.  
  1080.     case CDA_TRACK:
  1081.         if (p->arg[0] == 0) {
  1082.             /* Previous track */
  1083.             if (s->mode == M_PLAY) {
  1084.                 if ((i = curtrk_pos(s)) > 0)
  1085.                     s->cur_tot_addr = s->trkinfo[i].addr;
  1086.                 di_prevtrk(s);
  1087.             }
  1088.             else
  1089.                 p->retcode = CDA_INVALID;
  1090.         }
  1091.         else {
  1092.             /* Next track */
  1093.             if (s->mode == M_PLAY)
  1094.                 di_nexttrk(s);
  1095.             else
  1096.                 p->retcode = CDA_INVALID;
  1097.         }
  1098.  
  1099.         break;
  1100.  
  1101.     case CDA_INDEX:
  1102.         if (p->arg[0] == 0) {
  1103.             /* Previous index */
  1104.             if (s->mode == M_PLAY && s->prog_tot <= 0) {
  1105.                 if (s->cur_idx > 1)
  1106.                     s->cur_tot_addr = s->sav_iaddr;
  1107.                 di_previdx(s);
  1108.             }
  1109.             else
  1110.                 p->retcode = CDA_INVALID;
  1111.         }
  1112.         else {
  1113.             /* Next index */
  1114.             if (s->mode == M_PLAY && s->prog_tot <= 0)
  1115.                 di_nextidx(s);
  1116.             else
  1117.                 p->retcode = CDA_INVALID;
  1118.         }
  1119.  
  1120.         break;
  1121.  
  1122.     case CDA_PROGRAM:
  1123.         if (s->mode == M_NODISC)
  1124.             p->retcode = CDA_INVALID;
  1125.         else if ((int) p->arg[0] > 0) {
  1126.             /* Query */
  1127.             p->arg[0] = (word32_t) s->prog_tot;
  1128.             p->arg[1] = (word32_t) -1;
  1129.  
  1130.             for (i = 0; i < (int) s->prog_tot; i++) {
  1131.                 p->arg[i+1] = (word32_t)
  1132.                     s->trkinfo[s->playorder[i]].trkno;
  1133.             }
  1134.         }
  1135.         else if (p->arg[0] == 0) {
  1136.             /* Clear */
  1137.             if (s->shuffle) {
  1138.                 /* program and shuffle modes are mutually-
  1139.                  * exclusive.
  1140.                  */
  1141.                 p->retcode = CDA_INVALID;
  1142.             }
  1143.             else {
  1144.                 p->arg[1] = 0;
  1145.                 s->prog_tot = 0;
  1146.             }
  1147.         }
  1148.         else if ((int) p->arg[0] < 0) {
  1149.             /* Define */
  1150.             if (s->shuffle) {
  1151.                 /* program and shuffle modes are mutually-
  1152.                  * exclusive.
  1153.                  */
  1154.                 p->retcode = CDA_INVALID;
  1155.                 break;
  1156.             }
  1157.  
  1158.             s->prog_tot = -(p->arg[0]);
  1159.  
  1160.             for (i = 0; i < (int) s->prog_tot; i++) {
  1161.                 for (j = 0; j < (int) s->tot_trks; j++) {
  1162.                     if (s->trkinfo[j].trkno == p->arg[i+1])
  1163.                         break;
  1164.                 }
  1165.  
  1166.                 if (j >= (int) s->tot_trks) {
  1167.                     s->prog_tot = 0;
  1168.                     p->retcode = CDA_PARMERR;
  1169.                     break;
  1170.                 }
  1171.  
  1172.                 s->playorder[i] = j;
  1173.             }
  1174.         }
  1175.         else
  1176.             p->retcode = CDA_PARMERR;
  1177.  
  1178.         break;
  1179.  
  1180.     case CDA_SHUFFLE:
  1181.         if (!s->shuffle && s->prog_tot > 0) {
  1182.             p->retcode = CDA_INVALID;
  1183.             break;
  1184.         }
  1185.  
  1186.         if (s->mode != M_NODISC && s->mode != M_STOP) {
  1187.             p->retcode = CDA_INVALID;
  1188.             break;
  1189.         }
  1190.  
  1191.         di_shuffle(s, (bool_t) (p->arg[0] == 1));
  1192.         break;
  1193.  
  1194.     case CDA_REPEAT:
  1195.         di_repeat(s, (bool_t) (p->arg[0] == 1));
  1196.         break;
  1197.  
  1198.     case CDA_VOLUME:
  1199.         if (p->arg[0] == 0)
  1200.             /* Query */
  1201.             p->arg[1] = (word32_t) s->level;
  1202.         else if ((int) p->arg[1] >= 0 && (int) p->arg[1] <= 100)
  1203.             /* Set */
  1204.             di_level(s, (byte_t) p->arg[1], FALSE);
  1205.         else
  1206.             p->retcode = CDA_PARMERR;
  1207.  
  1208.         break;
  1209.  
  1210.     case CDA_BALANCE:
  1211.         if (p->arg[0] == 0) {
  1212.             /* Query */
  1213.             p->arg[1] = (word32_t)
  1214.                 ((int) (s->level_right - s->level_left) / 2) + 50;
  1215.         }
  1216.         else if ((int) p->arg[1] == 50) {
  1217.             /* Center setting */
  1218.             s->level_left = s->level_right = 100;
  1219.             di_level(s, (byte_t) s->level, FALSE);
  1220.         }
  1221.         else if ((int) p->arg[1] < 50 && (int) p->arg[1] >= 0) {
  1222.             /* Attenuate the right channel */
  1223.             s->level_left = 100;
  1224.             s->level_right = 100 + (((int) p->arg[1] - 50) * 2);
  1225.             di_level(s, (byte_t) s->level, FALSE);
  1226.         }
  1227.         else if ((int) p->arg[1] > 50 && (int) p->arg[1] <= 100) {
  1228.             /* Attenuate the left channel */
  1229.             s->level_left = 100 - (((int) p->arg[1] - 50) * 2);
  1230.             s->level_right = 100;
  1231.             di_level(s, (byte_t) s->level, FALSE);
  1232.         }
  1233.         else
  1234.             p->retcode = CDA_PARMERR;
  1235.  
  1236.         break;
  1237.  
  1238.     case CDA_ROUTE:
  1239.         if (p->arg[0] == 0) {
  1240.             /* Query */
  1241.             p->arg[1] = (word32_t) app_data.ch_route;
  1242.         }
  1243.         else if ((int) p->arg[1] >= 0 && (int) p->arg[1] <= 4) {
  1244.             /* Set */
  1245.             app_data.ch_route = (int) p->arg[1];
  1246.             di_route(s);
  1247.         }
  1248.         else
  1249.             p->retcode = CDA_PARMERR;
  1250.  
  1251.         break;
  1252.  
  1253.     case CDA_STATUS:
  1254.         /* Initialize */
  1255.         memset(p->arg, 0, CDA_NARGS * sizeof(word32_t));
  1256.  
  1257.         WR_ARG_MODE(p->arg[0], s->mode);
  1258.  
  1259.         if (s->caddy_lock)
  1260.             WR_ARG_LOCK(p->arg[0]);
  1261.         if (s->shuffle)
  1262.             WR_ARG_SHUF(p->arg[0]);
  1263.         if (s->repeat)
  1264.             WR_ARG_REPT(p->arg[0]);
  1265.         if (!s->shuffle && s->prog_tot > 0)
  1266.             WR_ARG_PROG(p->arg[0]);
  1267.  
  1268.         WR_ARG_TRK(p->arg[1], s->cur_trk);
  1269.         WR_ARG_IDX(p->arg[1], s->cur_idx);
  1270.         WR_ARG_MIN(p->arg[1], s->cur_trk_min);
  1271.         WR_ARG_SEC(p->arg[1], s->cur_trk_sec);
  1272.  
  1273.         if (s->repeat && s->mode == M_PLAY)
  1274.             p->arg[2] = (word32_t) s->rptcnt;
  1275.         else
  1276.             p->arg[2] = (word32_t) -1;
  1277.  
  1278.         break;
  1279.  
  1280.     case CDA_TOC:
  1281.         if (s->mode == M_NODISC) {
  1282.             p->retcode = CDA_INVALID;
  1283.             break;
  1284.         }
  1285.  
  1286.         offsets = (p->arg[0] == 1);
  1287.  
  1288.         /* Initialize */
  1289.         memset(p->arg, 0, CDA_NARGS * sizeof(word32_t));
  1290.  
  1291.         p->arg[0] = dbprog_discid(s);
  1292.  
  1293.         if (offsets) {
  1294.             for (i = 0; i < (int) s->tot_trks; i++) {
  1295.                 WR_ARG_TOC(p->arg[i+1], s->trkinfo[i].trkno,
  1296.                        s->cur_trk,
  1297.                        s->trkinfo[i].min,
  1298.                        s->trkinfo[i].sec);
  1299.             }
  1300.         }
  1301.         else {
  1302.             for (i = 0; i < (int) s->tot_trks; i++) {
  1303.                 j = ((s->trkinfo[i+1].min * 60 +
  1304.                       s->trkinfo[i+1].sec) - 
  1305.                      (s->trkinfo[i].min * 60 +
  1306.                       s->trkinfo[i].sec));
  1307.                 min = j / 60;
  1308.                 sec = j % 60;
  1309.  
  1310.                 WR_ARG_TOC(p->arg[i+1], s->trkinfo[i].trkno,
  1311.                        s->cur_trk, min, sec);
  1312.             }
  1313.         }
  1314.  
  1315.         /* Lead-out track */
  1316.         WR_ARG_TOC(p->arg[i+1], s->trkinfo[i].trkno,
  1317.                0, s->trkinfo[i].min, s->trkinfo[i].sec);
  1318.  
  1319.         break;
  1320.  
  1321.     case CDA_EXTINFO:
  1322.         if (s->mode == M_NODISC) {
  1323.             p->retcode = CDA_INVALID;
  1324.             break;
  1325.         }
  1326.  
  1327.         p->arg[0] = dbprog_discid(s);
  1328.         p->arg[2] = (word32_t) -1;
  1329.  
  1330.         if ((int) p->arg[1] == -1) {
  1331.             if (s->mode != M_PLAY) {
  1332.                 p->arg[1] = p->arg[2] = (word32_t) -1;
  1333.                 break;
  1334.             }
  1335.  
  1336.             p->arg[1] = (word32_t) s->cur_trk;
  1337.             j = (int) s->cur_trk;
  1338.         }
  1339.         else
  1340.             j = (int) p->arg[1];
  1341.  
  1342.         for (i = 0; i < (int) s->tot_trks; i++) {
  1343.             if ((int) s->trkinfo[i].trkno == j)
  1344.                 break;
  1345.         }
  1346.         if (i < (int) s->tot_trks)
  1347.             p->arg[2] = i;
  1348.         else
  1349.             p->retcode = CDA_PARMERR;
  1350.  
  1351.         break;
  1352.  
  1353.     case CDA_DEVICE:
  1354.         sprintf((char *) p->arg,
  1355.             "CD-ROM: %s %s (%s)\nMode:   %s",
  1356.             s->vendor, s->prod, s->revnum, di_mode());
  1357.         break;
  1358.  
  1359.     case CDA_VERSION:
  1360.         sprintf((char *) p->arg, "%s%s PL%d\n%s",
  1361.             VERSION, VERSION_EXT, PATCHLEVEL, di_vers());
  1362.         break;
  1363.  
  1364.     default:
  1365.         p->retcode = CDA_FAILED;
  1366.         break;
  1367.     }
  1368.  
  1369.     return FALSE;
  1370. }
  1371.  
  1372.  
  1373. /*
  1374.  * prn_program
  1375.  *    Print current program sequence, if any.
  1376.  *
  1377.  * Args:
  1378.  *    arg - Argument array from CD audio daemon response packet.
  1379.  *
  1380.  * Return:
  1381.  *    Nothing.
  1382.  */
  1383. STATIC void
  1384. prn_program(word32_t arg[])
  1385. {
  1386.     int    i;
  1387.  
  1388.     if ((int) arg[0] > 0) {
  1389.         printf("Current program:");
  1390.         for (i = 0; i < arg[0]; i++)
  1391.             printf(" %d", arg[i+1]);
  1392.         printf("\n");
  1393.     }
  1394.     else if (arg[0] == 0 && (int) arg[1] == -1)
  1395.         printf("No play sequence defined.\n");
  1396. }
  1397.  
  1398.  
  1399. /*
  1400.  * prn_vol
  1401.  *    Print current volume setting.
  1402.  *
  1403.  * Args:
  1404.  *    arg - Argument array from CD audio daemon response packet.
  1405.  *
  1406.  * Return:
  1407.  *    Nothing.
  1408.  */
  1409. STATIC void
  1410. prn_vol(word32_t arg[])
  1411. {
  1412.     if (arg[0] == 0)
  1413.         printf("Current volume: %u (range 0-100)\n", arg[1]);
  1414. }
  1415.  
  1416.  
  1417. /*
  1418.  * prn_bal
  1419.  *    Print current balance setting.
  1420.  *
  1421.  * Args:
  1422.  *    arg - Argument array from CD audio daemon response packet.
  1423.  *
  1424.  * Return:
  1425.  *    Nothing.
  1426.  */
  1427. STATIC void
  1428. prn_bal(word32_t arg[])
  1429. {
  1430.     if (arg[0] == 0) {
  1431.         printf("Current balance: %u (range 0-100, center:50)\n",
  1432.             arg[1]);
  1433.     }
  1434. }
  1435.  
  1436.  
  1437. /*
  1438.  * prn_route
  1439.  *    Print current channel routing setting.
  1440.  *
  1441.  * Args:
  1442.  *    arg - Argument array from CD audio daemon response packet.
  1443.  *
  1444.  * Return:
  1445.  *    Nothing.
  1446.  */
  1447. STATIC void
  1448. prn_route(word32_t arg[])
  1449. {
  1450.     if (arg[0] == 0) {
  1451.         printf("Current routing: %u ", arg[1]);
  1452.  
  1453.         switch (arg[1]) {
  1454.         case 0:
  1455.             printf("(normal stereo)\n");
  1456.             break;
  1457.         case 1:
  1458.             printf("(reverse stereo)\n");
  1459.             break;
  1460.         case 2:
  1461.             printf("(mono-L)\n");
  1462.             break;
  1463.         case 3:
  1464.             printf("(mono-R)\n");
  1465.             break;
  1466.         case 4:
  1467.             printf("(mono-L+R)\n");
  1468.             break;
  1469.         }
  1470.     }
  1471. }
  1472.  
  1473.  
  1474. /*
  1475.  * prn_stat
  1476.  *    Print current CD status.
  1477.  *
  1478.  * Args:
  1479.  *    arg - Argument array from CD audio daemon response packet.
  1480.  *
  1481.  * Return:
  1482.  *    Nothing.
  1483.  */
  1484. STATIC void
  1485. prn_stat(word32_t arg[])
  1486. {
  1487.     if (stat_cont)
  1488.         printf("\r");
  1489.  
  1490.     switch (RD_ARG_MODE(arg[0])) {
  1491.     case M_NODISC:
  1492.         printf("No_Disc    -- -- --:--");
  1493.         break;
  1494.     case M_STOP:
  1495.         printf("CD_Stopped -- -- --:--");
  1496.         break;
  1497.     case M_PLAY:
  1498.         printf("CD_Playing %02u %02u %02u:%02u",
  1499.             RD_ARG_TRK(arg[1]), RD_ARG_IDX(arg[1]),
  1500.             RD_ARG_MIN(arg[1]), RD_ARG_SEC(arg[1]));
  1501.         break;
  1502.     case M_PAUSE:
  1503.         printf("CD_Paused  %02u %02u %02u:%02u",
  1504.             RD_ARG_TRK(arg[1]), RD_ARG_IDX(arg[1]),
  1505.             RD_ARG_MIN(arg[1]), RD_ARG_SEC(arg[1]));
  1506.         break;
  1507.     default:
  1508.         printf("Inv_status -- -- --:--");
  1509.         break;
  1510.     }
  1511.  
  1512.     printf(" %slock", RD_ARG_LOCK(arg[0]) ? "+" : "-");
  1513.     printf(" %sshuf", RD_ARG_SHUF(arg[0]) ? "+" : "-");
  1514.     printf(" %sprog", RD_ARG_PROG(arg[0]) ? "+" : "-");
  1515.     printf(" %srept", RD_ARG_REPT(arg[0]) ? "+" : "-");
  1516.  
  1517.     if ((int) arg[2] >= 0)
  1518.         printf(" %u", arg[2]);
  1519.     else
  1520.         printf(" -");
  1521.  
  1522.     if (!stat_cont)
  1523.         printf("\n");
  1524. }
  1525.  
  1526.  
  1527. /*
  1528.  * prn_toc
  1529.  *    Print current CD Table Of Contents.
  1530.  *
  1531.  * Args:
  1532.  *    arg - Argument array from CD audio daemon response packet.
  1533.  *
  1534.  * Return:
  1535.  *    Nothing.
  1536.  */
  1537. STATIC void
  1538. prn_toc(word32_t arg[])
  1539. {
  1540.     int        i;
  1541.     byte_t        ntrks,
  1542.             trkno,
  1543.             min,
  1544.             sec;
  1545.     bool_t        cddb,
  1546.             playing;
  1547.  
  1548.     /* Load CD database entry */
  1549.     cddb = dbprog_dbload(arg[0]);
  1550.  
  1551.     ntrks = arg[0] & 0xff;
  1552.  
  1553.     printf("Disc ID: %s %08x%s\n",
  1554.         (cur_db.category[0] == '\0') ?
  1555.             "(no category)" : cur_db.category,
  1556.         arg[0],
  1557.         (cur_db.extd == NULL) ? "" : " *");
  1558.  
  1559.     printf("%s\n\n", cddb ? cur_db.dtitle : "(unknown disc title)");
  1560.  
  1561.     for (i = 0; i < (int) ntrks; i++) {
  1562.         RD_ARG_TOC(arg[i+1], trkno, playing, min, sec);
  1563.         printf("%s%02u %02u:%02u %s %s\n",
  1564.             playing ? ">" : " ",
  1565.             trkno, min, sec,
  1566.             (cur_db.extt[i] == NULL) ? " " : "*",
  1567.             cddb ? cur_db.trklist[i] : "??");
  1568.     }
  1569.  
  1570.     RD_ARG_TOC(arg[i+1], trkno, playing, min, sec);
  1571.     printf("\nTotal Time: %02u:%02u\n", min, sec);
  1572. }
  1573.  
  1574.  
  1575. /*
  1576.  * prn_extinfo
  1577.  *    Print current Disc or Track Extended Information.
  1578.  *
  1579.  * Args:
  1580.  *    arg - Argument array from CD audio daemon response packet.
  1581.  *
  1582.  * Return:
  1583.  *    Nothing.
  1584.  */
  1585. STATIC void
  1586. prn_extinfo(word32_t arg[])
  1587. {
  1588.     bool_t    cddb = FALSE;
  1589.  
  1590.     /* Load CD database entry */
  1591.     cddb = dbprog_dbload(arg[0]);
  1592.  
  1593.     if (!cddb) {
  1594.         printf("No CD database entry found for this CD\n");
  1595.         return;
  1596.     }
  1597.  
  1598.     printf("-------- Disc Extended Information --------\n");
  1599.  
  1600.     if (cur_db.extd == NULL)
  1601.         printf("(none)\n");
  1602.     else {
  1603.         printf("%s\n\n", cur_db.dtitle);
  1604.         printf("%s\n", cur_db.extd);
  1605.     }
  1606.  
  1607.     if ((int) arg[1] < 0)
  1608.         return;
  1609.  
  1610.     printf("\n------ Track %02u Extended Information ------\n", arg[1]);
  1611.  
  1612.     if (cur_db.extt[arg[2]] == NULL)
  1613.         printf("(none)\n");
  1614.     else {
  1615.         printf("%s\n\n", cur_db.trklist[arg[2]]);
  1616.         printf("%s\n", cur_db.extt[arg[2]]);
  1617.     }
  1618. }
  1619.  
  1620.  
  1621. /*
  1622.  * prn_device
  1623.  *    Print device information.
  1624.  *
  1625.  * Args:
  1626.  *    arg - Argument array from CD audio daemon response packet.
  1627.  *
  1628.  * Return:
  1629.  *    Nothing.
  1630.  */
  1631. STATIC void
  1632. prn_device(word32_t arg[])
  1633. {
  1634.     printf("Device: %s\n", app_data.device);
  1635.     printf("%s\n", (char *) arg);
  1636. }
  1637.  
  1638.  
  1639. /*
  1640.  * prn_ver
  1641.  *    Print version number and other information.
  1642.  *
  1643.  * Args:
  1644.  *    arg - Argument array from CD audio daemon response packet.
  1645.  *
  1646.  * Return:
  1647.  *    Nothing.
  1648.  */
  1649. STATIC void
  1650. prn_ver(word32_t arg[])
  1651. {
  1652.     printf("CDA - Command Line CD Audio Player\n\n");
  1653.     printf("CD audio        v%s%s PL%d\n",
  1654.         VERSION, VERSION_EXT, PATCHLEVEL);
  1655.     printf("CD audio daemon v%s\n", (char *) arg);
  1656.     printf(COPYRIGHT);
  1657. }
  1658.  
  1659.  
  1660. /*
  1661.  * usage
  1662.  *    Display command line usage syntax
  1663.  *
  1664.  * Args:
  1665.  *    argc, argv
  1666.  *
  1667.  * Return:
  1668.  *    Nothing.
  1669.  */
  1670. STATIC void
  1671. usage(char *progname)
  1672. {
  1673.     fprintf(errfp, "Usage: %s [-dev device] [-debug] command\n",
  1674.         progname);
  1675.     fprintf(errfp, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
  1676.         "Valid commands are:\n",
  1677.         "\ton\n",
  1678.         "\toff\n",
  1679.         "\tdisc <load | eject>\n",
  1680.         "\tlock <on | off>\n",
  1681.         "\tplay [track# [min:sec]]\n",
  1682.         "\tpause\n",
  1683.         "\tstop\n",
  1684.         "\ttrack <prev | next>\n",
  1685.         "\tindex <prev | next>\n",
  1686.         "\tprogram [clear | track# ...]\n",
  1687.         "\tshuffle <on | off>\n",
  1688.         "\trepeat <on | off>\n",
  1689.         "\tvolume [value#]    (range 0-100)\n",
  1690.         "\tbalance [value#]   (range 0-100, center:50)\n",
  1691.         "\troute [value#]     (0:stereo 1:reverse 2:mono-L 3:mono-R 4:mono-L+R)\n",
  1692.         "\tstatus [cont [secs#]]\n",
  1693.         "\ttoc [offsets]\n",
  1694.         "\textinfo [track#]\n",
  1695.         "\tdevice\n",
  1696.         "\tversion\n",
  1697.         "\tvisual\n");
  1698. }
  1699.  
  1700.  
  1701. /*
  1702.  * parse_time
  1703.  *    Parse a string of the form "min:sec" and convert to integer
  1704.  *    minute and second values.
  1705.  *
  1706.  * Args:
  1707.  *    str - Pointer to the "min:sec" string.
  1708.  *    min - pointer to where the minute value is to be written.
  1709.  *    sec - pointer to where the second value is to be written.
  1710.  *
  1711.  * Return:
  1712.  *    TRUE - success
  1713.  *    FALSE - failure
  1714.  */
  1715. STATIC bool_t
  1716. parse_time(char *str, int *min, int *sec)
  1717. {
  1718.     char    *p;
  1719.  
  1720.     if ((p = strchr(str, ':')) == NULL)
  1721.         return FALSE;
  1722.     
  1723.     if (!isdigit(*str) || !isdigit(*(p+1)))
  1724.         return FALSE;
  1725.  
  1726.     *p = '\0';
  1727.     *min = atoi(str);
  1728.     *sec = atoi(p+1);
  1729.     *p = ':';
  1730.  
  1731.     return TRUE;
  1732. }
  1733.  
  1734.  
  1735. /*
  1736.  * cda_parse_args
  1737.  *    Parse CDA command line arguments.
  1738.  *
  1739.  * Args:
  1740.  *    argc, argv
  1741.  *    cmd - Pointer to the command code.
  1742.  *    arg - Command argument array.
  1743.  *
  1744.  * Return:
  1745.  *    TRUE - success
  1746.  *    FALSE - failure
  1747.  */
  1748. STATIC bool_t
  1749. cda_parse_args(int argc, char **argv, word32_t *cmd, word32_t arg[])
  1750. {
  1751.     int    i,
  1752.         j,
  1753.         min,
  1754.         sec;
  1755.  
  1756.     /* Default values */
  1757.     *cmd = 0;
  1758.     memset(arg, 0, CDA_NARGS * sizeof(word32_t));
  1759.  
  1760.     /* Command line args handling */
  1761.     for (i = 1; i < argc; i++) {
  1762.         if (*cmd != 0) {
  1763.             /* Multiple commands specified */
  1764.             usage(argv[0]);
  1765.             return FALSE;
  1766.         }
  1767.  
  1768.         if (strcmp(argv[i], "-dev") == 0) {
  1769.             if (++i < argc) {
  1770.                 if (!di_isdemo())
  1771.                     app_data.device = argv[i];
  1772.             }
  1773.             else {
  1774.                 usage(argv[0]);
  1775.                 return FALSE;
  1776.             }
  1777.         }
  1778.         else if (strcmp(argv[i], "-debug") == 0) {
  1779.             app_data.debug = TRUE;
  1780.         }
  1781.         else if (strcmp(argv[i], "on") == 0) {
  1782.             *cmd = CDA_ON;
  1783.         }
  1784.         else if (strcmp(argv[i], "off") == 0) {
  1785.             *cmd = CDA_OFF;
  1786.         }
  1787.         else if (strcmp(argv[i], "disc") == 0) {
  1788.             /* <load | eject> */
  1789.             if (++i < argc) {
  1790.                 if (strcmp(argv[i], "load") == 0)
  1791.                     arg[0] = 0;
  1792.                 else if (strcmp(argv[i], "eject") == 0)
  1793.                     arg[0] = 1;
  1794.                 else {
  1795.                     /* Wrong arg */
  1796.                     usage(argv[0]);
  1797.                     return FALSE;
  1798.                 }
  1799.             }
  1800.             else {
  1801.                 /* Missing arg */
  1802.                 usage(argv[0]);
  1803.                 return FALSE;
  1804.             }
  1805.             *cmd = CDA_DISC;
  1806.         }
  1807.         else if (strcmp(argv[i], "lock") == 0) {
  1808.             /* <on | off> */
  1809.             if (++i < argc) {
  1810.                 if (strcmp(argv[i], "off") == 0)
  1811.                     arg[0] = 0;
  1812.                 else if (strcmp(argv[i], "on") == 0)
  1813.                     arg[0] = 1;
  1814.                 else {
  1815.                     /* Wrong arg */
  1816.                     usage(argv[0]);
  1817.                     return FALSE;
  1818.                 }
  1819.             }
  1820.             else {
  1821.                 /* Missing arg */
  1822.                 usage(argv[0]);
  1823.                 return FALSE;
  1824.             }
  1825.             *cmd = CDA_LOCK;
  1826.         }
  1827.         else if (strcmp(argv[i], "play") == 0) {
  1828.             /* [track# [min:sec]] */
  1829.             if ((i+1) < argc && isdigit(argv[i+1][0])) {
  1830.                 /* The user specified the track number */
  1831.                 if ((arg[0] = atoi(argv[++i])) == 0) {
  1832.                     /* Wrong arg */
  1833.                     usage(argv[0]);
  1834.                     return FALSE;
  1835.                 }
  1836.  
  1837.                 if ((i+1) < argc &&
  1838.                     parse_time(argv[i+1], &min, &sec)) {
  1839.                     /* The user specified a time offset */
  1840.                     arg[1] = min;
  1841.                     arg[2] = sec;
  1842.                     i++;
  1843.                 }
  1844.                 else {
  1845.                     arg[1] = arg[2] = (word32_t) -1;
  1846.                 }
  1847.             }
  1848.             *cmd = CDA_PLAY;
  1849.         }
  1850.         else if (strcmp(argv[i], "pause") == 0) {
  1851.             *cmd = CDA_PAUSE;
  1852.         }
  1853.         else if (strcmp(argv[i], "stop") == 0) {
  1854.             *cmd = CDA_STOP;
  1855.         }
  1856.         else if (strcmp(argv[i], "track") == 0) {
  1857.             /* <prev | next> */
  1858.             if (++i < argc) {
  1859.                 if (strcmp(argv[i], "prev") == 0)
  1860.                     arg[0] = 0;
  1861.                 else if (strcmp(argv[i], "next") == 0)
  1862.                     arg[0] = 1;
  1863.                 else {
  1864.                     /* Wrong arg */
  1865.                     usage(argv[0]);
  1866.                     return FALSE;
  1867.                 }
  1868.             }
  1869.             else {
  1870.                 /* Missing arg */
  1871.                 usage(argv[0]);
  1872.                 return FALSE;
  1873.             }
  1874.             *cmd = CDA_TRACK;
  1875.         }
  1876.         else if (strcmp(argv[i], "index") == 0) {
  1877.             /* <prev | next> */
  1878.             if (++i < argc) {
  1879.                 if (strcmp(argv[i], "prev") == 0)
  1880.                     arg[0] = 0;
  1881.                 else if (strcmp(argv[i], "next") == 0)
  1882.                     arg[0] = 1;
  1883.                 else {
  1884.                     /* Wrong arg */
  1885.                     usage(argv[0]);
  1886.                     return FALSE;
  1887.                 }
  1888.             }
  1889.             else {
  1890.                 /* Missing arg */
  1891.                 usage(argv[0]);
  1892.                 return FALSE;
  1893.             }
  1894.             *cmd = CDA_INDEX;
  1895.         }
  1896.         else if (strcmp(argv[i], "program") == 0) {
  1897.             /* [clear | track# ...] */
  1898.             arg[0] = 1;
  1899.  
  1900.             if ((i+1) < argc) {
  1901.                 if (strcmp(argv[i+1], "clear") == 0) {
  1902.                     i++;
  1903.                     arg[0] = 0;
  1904.                 }
  1905.                 else {
  1906.                     j = 0;
  1907.                     while ((i+1) < argc &&
  1908.                            isdigit(argv[i+1][0]) &&
  1909.                            j < (CDA_NARGS-1)) {
  1910.                         arg[++j] = atoi(argv[++i]);
  1911.                     }
  1912.                     if (j > 0)
  1913.                         arg[0] = (word32_t) -j;
  1914.                 }
  1915.             }
  1916.             *cmd = CDA_PROGRAM;
  1917.         }
  1918.         else if (strcmp(argv[i], "shuffle") == 0) {
  1919.             /* <on | off> */
  1920.             if (++i < argc) {
  1921.                 if (strcmp(argv[i], "off") == 0)
  1922.                     arg[0] = 0;
  1923.                 else if (strcmp(argv[i], "on") == 0)
  1924.                     arg[0] = 1;
  1925.                 else {
  1926.                     /* Wrong arg */
  1927.                     usage(argv[0]);
  1928.                     return FALSE;
  1929.                 }
  1930.             }
  1931.             else {
  1932.                 /* Missing arg */
  1933.                 usage(argv[0]);
  1934.                 return FALSE;
  1935.             }
  1936.             *cmd = CDA_SHUFFLE;
  1937.         }
  1938.         else if (strcmp(argv[i], "repeat") == 0) {
  1939.             /* <on | off> */
  1940.             if (++i < argc) {
  1941.                 if (strcmp(argv[i], "off") == 0)
  1942.                     arg[0] = 0;
  1943.                 else if (strcmp(argv[i], "on") == 0)
  1944.                     arg[0] = 1;
  1945.                 else {
  1946.                     /* Wrong arg */
  1947.                     usage(argv[0]);
  1948.                     return FALSE;
  1949.                 }
  1950.             }
  1951.             else {
  1952.                 /* Missing arg */
  1953.                 usage(argv[0]);
  1954.                 return FALSE;
  1955.             }
  1956.             *cmd = CDA_REPEAT;
  1957.         }
  1958.         else if (strcmp(argv[i], "volume") == 0) {
  1959.             /* [value#] */
  1960.             if ((i+1) >= argc || !isdigit(argv[i+1][0]))
  1961.                 /* Query */
  1962.                 arg[0] = 0;
  1963.             else {
  1964.                 /* Set */
  1965.                 arg[0] = 1;
  1966.                 arg[1] = (word32_t) atoi(argv[++i]);
  1967.             }
  1968.             *cmd = CDA_VOLUME;
  1969.         }
  1970.         else if (strcmp(argv[i], "balance") == 0) {
  1971.             /* [value#] */
  1972.             if ((i+1) >= argc || !isdigit(argv[i+1][0]))
  1973.                 /* Query */
  1974.                 arg[0] = 0;
  1975.             else {
  1976.                 /* Set */
  1977.                 arg[0] = 1;
  1978.                 arg[1] = (word32_t) atoi(argv[++i]);
  1979.             }
  1980.             *cmd = CDA_BALANCE;
  1981.         }
  1982.         else if (strcmp(argv[i], "route") == 0) {
  1983.             /* [value#] */
  1984.             if ((i+1) >= argc || !isdigit(argv[i+1][0]))
  1985.                 /* Query */
  1986.                 arg[0] = 0;
  1987.             else {
  1988.                 /* Set */
  1989.                 arg[0] = 1;
  1990.                 arg[1] = (word32_t) atoi(argv[++i]);
  1991.             }
  1992.             *cmd = CDA_ROUTE;
  1993.         }
  1994.         else if (strcmp(argv[i], "status") == 0) {
  1995.             /* [cont [secs#]] */
  1996.             if ((i+1) >= argc || strcmp(argv[i+1], "cont") != 0)
  1997.                 stat_cont = FALSE;
  1998.             else {
  1999.                 i++;
  2000.                 stat_cont = TRUE;
  2001.                 if ((i+1) < argc && isdigit(argv[i+1][0]))
  2002.                     cont_delay = atoi(argv[++i]);
  2003.             }
  2004.             *cmd = CDA_STATUS;
  2005.         }
  2006.         else if (strcmp(argv[i], "toc") == 0) {
  2007.             /* [offsets] */
  2008.             if ((i+1) >= argc || strcmp(argv[i+1], "offsets") != 0)
  2009.                 arg[0] = 0;
  2010.             else {
  2011.                 i++;
  2012.                 arg[0] = 1;
  2013.             }
  2014.             *cmd = CDA_TOC;
  2015.         }
  2016.         else if (strcmp(argv[i], "extinfo") == 0) {
  2017.             /* [track#] */
  2018.             arg[0] = 0;
  2019.             if ((i+1) >= argc || !isdigit(argv[i+1][0]))
  2020.                 arg[1] = (word32_t) -1;
  2021.             else
  2022.                 arg[1] = atoi(argv[++i]);
  2023.  
  2024.             *cmd = CDA_EXTINFO;
  2025.         }
  2026.         else if (strcmp(argv[i], "device") == 0) {
  2027.             *cmd = CDA_DEVICE;
  2028.         }
  2029.         else if (strcmp(argv[i], "version") == 0) {
  2030.             *cmd = CDA_VERSION;
  2031.         }
  2032.         else if (strcmp(argv[i], "visual") == 0) {
  2033. #ifdef NOVISUAL
  2034.             fprintf(errfp, "%s %s\n",
  2035.                 "Cannot start visual mode:",
  2036.                 "curses support is not compiled in!");
  2037.             return FALSE;
  2038. #else
  2039.             visual = TRUE;
  2040.             *cmd = CDA_STATUS;
  2041.             /* Make sure simulator/debug output is redirectable */
  2042.             ttyfp = stderr;
  2043. #endif
  2044.         }
  2045.         else {
  2046.             usage(argv[0]);
  2047.             return FALSE;
  2048.         }
  2049.     }
  2050.  
  2051.     if (*cmd == 0) {
  2052.         /* User did not specify a command */
  2053.         usage(argv[0]);
  2054.         return FALSE;
  2055.     }
  2056.  
  2057.     return TRUE;
  2058. }
  2059.  
  2060.  
  2061. /***********************
  2062.  *   public routines   *
  2063.  ***********************/
  2064.  
  2065.  
  2066. /*
  2067.  * cd_beep
  2068.  *    Empty stub routine.
  2069.  *
  2070.  * Args:
  2071.  *    Nothing.
  2072.  *
  2073.  * Return:
  2074.  *    Nothing.
  2075.  */
  2076. void
  2077. cd_beep(void)
  2078. {
  2079.     /* Null stub function */
  2080. }
  2081.  
  2082.  
  2083. /*
  2084.  * cd_quit
  2085.  *      Shut down CD audio
  2086.  *
  2087.  * Args:
  2088.  *      s - Pointer to the curstat_t structure.
  2089.  *
  2090.  * Return:
  2091.  *      Nothing.
  2092.  */
  2093. void
  2094. cd_quit(curstat_t *s)
  2095. {
  2096.     if (isdaemon) {
  2097.         /* Shut down CD interface subsystem */
  2098.         di_halt(s);
  2099.  
  2100.         /* Close FIFOs - daemon side */
  2101.         if (cda_sfd[0] >= 0)
  2102.             close(cda_sfd[0]);
  2103.         if (cda_rfd[0] >= 0)
  2104.             close(cda_rfd[0]);
  2105.  
  2106.         /* Remove FIFOs */
  2107.         if (spipe[0] != '\0')
  2108.             unlink(spipe);
  2109.         if (rpipe[0] != '\0')
  2110.             unlink(rpipe);
  2111.  
  2112.         /* Remove lock file */
  2113.         if (lockfile[0] != '\0')
  2114.             unlink(lockfile);
  2115.     }
  2116. #ifndef NOVISUAL
  2117.     else {
  2118.         cda_vtidy();
  2119.     }
  2120. #endif
  2121. }
  2122.  
  2123.  
  2124. /*
  2125.  * cd_timeout
  2126.  *    Stub routine.
  2127.  *
  2128.  * Args:
  2129.  *    msec - Not used.
  2130.  *    handler - Not used.
  2131.  *    arg - Not used.
  2132.  *
  2133.  * Return:
  2134.  *    Always 0.
  2135.  */
  2136. /*ARGSUSED*/
  2137. long
  2138. cd_timeout(word32_t msec, void (*handler)(), byte_t *arg)
  2139. {
  2140.     return 0;
  2141. }
  2142.  
  2143.  
  2144. /*
  2145.  * cd_untimeout
  2146.  *    Empty stub routine.
  2147.  *
  2148.  * Args:
  2149.  *    id - Not used.
  2150.  *
  2151.  * Return:
  2152.  *    Nothing.
  2153.  */
  2154. /*ARGSUSED*/
  2155. void
  2156. cd_untimeout(long id)
  2157. {
  2158.     /* Null stub function */
  2159. }
  2160.  
  2161.  
  2162. /*
  2163.  * cd_warning_popup
  2164.  *    Print warning message.
  2165.  *
  2166.  * Args:
  2167.  *    title - Not used.
  2168.  *    msg - The warning message text string.
  2169.  *
  2170.  * Return:
  2171.  *    Nothing.
  2172.  */
  2173. /*ARGSUSED*/
  2174. void
  2175. cd_warning_popup(char *title, char *msg)
  2176. {
  2177.     fprintf(errfp, "CD audio Warning: %s\n", msg);
  2178. }
  2179.  
  2180.  
  2181. /*
  2182.  * cd_fatal_popup
  2183.  *    Print fatal error message.
  2184.  *
  2185.  * Args:
  2186.  *    title - Not used..
  2187.  *    msg - The fatal error message text string.
  2188.  *
  2189.  * Return:
  2190.  *    Nothing.
  2191.  */
  2192. /*ARGSUSED*/
  2193. void
  2194. cd_fatal_popup(char *title, char *msg)
  2195. {
  2196.     fprintf(errfp, "CD audio Fatal Error: %s\n", msg);
  2197.     cd_quit(&status);
  2198.     exit(6);
  2199. }
  2200.  
  2201.  
  2202. /*
  2203.  * cd_devlock
  2204.  *    Create a lock to prevent another cda process from accessing
  2205.  *    the same CD-ROM device.
  2206.  *
  2207.  * Args:
  2208.  *    path - The lock file path name.
  2209.  *
  2210.  * Return:
  2211.  *    TRUE if the lock was successful.  If another cda process
  2212.  *    is currently has the lock, then this function does not
  2213.  *    return.  The program is terminated instead.
  2214.  */
  2215. bool_t
  2216. cd_devlock(char *path)
  2217. {
  2218.     int        fd;
  2219.     pid_t        pid,
  2220.             mypid;
  2221.     char        buf[12];
  2222.  
  2223.     if (di_isdemo())
  2224.         return TRUE;    /* No need for locks in demo mode */
  2225.  
  2226.     sprintf(lockfile, "%s/lock.%x", TEMP_DIR, cd_rdev);
  2227.     mypid = getpid();
  2228.  
  2229.     errmsg = "CD busy.";
  2230.  
  2231.     for (;;) {
  2232.         fd = open(lockfile, O_CREAT | O_EXCL | O_WRONLY);
  2233.         if (fd < 0) {
  2234.             if (errno == EEXIST) {
  2235.                 if ((fd = open(lockfile, O_RDONLY)) < 0)
  2236.                     cd_fatal_popup(NULL, errmsg);
  2237.  
  2238.                 if (read(fd, buf, 12) > 0)
  2239.                     pid = (pid_t) atoi(buf);
  2240.                 else {
  2241.                     close(fd);
  2242.                     cd_fatal_popup(NULL, errmsg);
  2243.                 }
  2244.  
  2245.                 close(fd);
  2246.  
  2247.                 if (pid == mypid)
  2248.                     /* Our own lock */
  2249.                     return TRUE;
  2250.  
  2251.                 if (pid <= 0 ||
  2252.                     (kill(pid, 0) < 0 && errno == ESRCH)) {
  2253.                     /* Pid died, steal its lockfile */
  2254.                     unlink(lockfile);
  2255.  
  2256.                     if (rpipe[0] != '\0')
  2257.                         unlink(rpipe);
  2258.                     if (spipe[0] != '\0')
  2259.                         unlink(spipe);
  2260.                 }
  2261.                 else {
  2262.                     /* Pid still running: clash */
  2263.                     cd_fatal_popup(NULL, errmsg);
  2264.                 }
  2265.             }
  2266.             else
  2267.                 cd_fatal_popup(NULL, errmsg);
  2268.         }
  2269.         else {
  2270.             sprintf(buf, "%d\n", mypid);
  2271.             write(fd, buf, strlen(buf));
  2272.  
  2273.             close(fd);
  2274.             chmod(lockfile, 0644);
  2275.  
  2276.             return TRUE;
  2277.         }
  2278.     }
  2279. }
  2280.  
  2281.  
  2282. /*
  2283.  * curtrk_pos
  2284.  *    Return the trkinfo table offset location of the current playing
  2285.  *    CD track.
  2286.  *
  2287.  * Args:
  2288.  *    s - Pointer to the curstat_t structure.
  2289.  *
  2290.  * Return:
  2291.  *    Integer offset into the trkinfo table, or -1 if not currently
  2292.  *    playing audio.
  2293.  */
  2294. int
  2295. curtrk_pos(curstat_t *s)
  2296. {
  2297.     int    i;
  2298.  
  2299.     if ((int) s->cur_trk <= 0)
  2300.         return -1;
  2301.  
  2302.     i = (int) s->cur_trk - 1;
  2303.  
  2304.     if (s->trkinfo[i].trkno == s->cur_trk)
  2305.         return (i);
  2306.  
  2307.     for (i = 0; i < MAXTRACK; i++) {
  2308.         if (s->trkinfo[i].trkno == s->cur_trk)
  2309.             return (i);
  2310.     }
  2311.     return -1;
  2312. }
  2313.  
  2314.  
  2315. /*
  2316.  * curprog_pos
  2317.  *    Return an integer representing the position of the current
  2318.  *    program or shuffle mode playing order (0 = first, 1 = second, ...).
  2319.  *    This routine should be used only when in program or shuffle play
  2320.  *    mode.
  2321.  *
  2322.  * Arg:
  2323.  *    s - Pointer to the curstat_t structure.
  2324.  *
  2325.  * Return:
  2326.  *    An integer representing the position of the current program
  2327.  *    or shuffle mode playing order, or -1 if not in the appropriate mode.
  2328.  */
  2329. int
  2330. curprog_pos(curstat_t *s)
  2331. {
  2332.     return ((int) s->playorder[s->prog_cnt]);
  2333. }
  2334.  
  2335.  
  2336. /*
  2337.  * cd_pause_blink
  2338.  *    Empty stub routine.
  2339.  *
  2340.  * Args:
  2341.  *    s - not used.
  2342.  *    enable - not used.
  2343.  *
  2344.  * Return:
  2345.  *    Nothing.
  2346.  */
  2347. void
  2348. cd_pause_blink(curstat_t *s, bool_t enable)
  2349. {
  2350.     /* Null stub function */
  2351. }
  2352.  
  2353.  
  2354. /*
  2355.  * dpy_track
  2356.  *    Empty stub routine.
  2357.  *
  2358.  * Args:
  2359.  *    s - Not used.
  2360.  *
  2361.  * Return:
  2362.  *    Nothing.
  2363.  */
  2364. /*ARGSUSED*/
  2365. void
  2366. dpy_track(curstat_t *s)
  2367. {
  2368.     /* Null stub function */
  2369. }
  2370.  
  2371.  
  2372. /*
  2373.  * dpy_index
  2374.  *    Empty stub routine.
  2375.  *
  2376.  * Args:
  2377.  *    s - Not used.
  2378.  *
  2379.  * Return:
  2380.  *    Nothing.
  2381.  */
  2382. /*ARGSUSED*/
  2383. void
  2384. dpy_index(curstat_t *s)
  2385. {
  2386.     /* Null stub function */
  2387. }
  2388.  
  2389.  
  2390. /*
  2391.  * dpy_time
  2392.  *    Empty stub routine.
  2393.  *
  2394.  * Args:
  2395.  *    s - Not used.
  2396.  *    blank - Not used.
  2397.  *
  2398.  * Return:
  2399.  *    Nothing.
  2400.  */
  2401. /*ARGSUSED*/
  2402. void
  2403. dpy_time(curstat_t *s, bool_t blank)
  2404. {
  2405.     /* Null stub function */
  2406. }
  2407.  
  2408.  
  2409. /*
  2410.  * dpy_rptcnt
  2411.  *    Empty stub routine.
  2412.  *
  2413.  * Args:
  2414.  *    s - Not used.
  2415.  *
  2416.  * Return:
  2417.  *    Nothing.
  2418.  */
  2419. /*ARGSUSED*/
  2420. void
  2421. dpy_rptcnt(curstat_t *s)
  2422. {
  2423.     /* Null stub function */
  2424. }
  2425.  
  2426.  
  2427. /*
  2428.  * dpy_playmode
  2429.  *    Empty stub routine.
  2430.  *
  2431.  * Args:
  2432.  *    s - Not used.
  2433.  *
  2434.  * Return:
  2435.  *    Nothing.
  2436.  */
  2437. /*ARGSUSED*/
  2438. void
  2439. dpy_playmode(curstat_t *s, bool_t blank)
  2440. {
  2441.     /* Null stub function */
  2442. }
  2443.  
  2444.  
  2445. /*
  2446.  * dpy_all
  2447.  *    Empty stub routine.
  2448.  *
  2449.  * Args:
  2450.  *    s - Not used.
  2451.  *
  2452.  * Return:
  2453.  *    Nothing.
  2454.  */
  2455. /*ARGSUSED*/
  2456. void
  2457. dpy_all(curstat_t *s)
  2458. {
  2459.     /* Null stub function */
  2460. }
  2461.  
  2462.  
  2463. /*
  2464.  * reset_curstat
  2465.  *    Reset the curstat_t structure to initial defaults.
  2466.  *
  2467.  * Args:
  2468.  *    s - Pointer to the curstat_t structure.
  2469.  *    clear_toc - Whether the trkinfo CD table-of-contents 
  2470.  *        should be cleared.
  2471.  *
  2472.  * Return:
  2473.  *    Nothing.
  2474.  */
  2475. void
  2476. reset_curstat(curstat_t *s, bool_t clear_toc)
  2477. {
  2478.     sword32_t    i;
  2479.     static bool_t    first_time = TRUE;
  2480.  
  2481.     s->cur_trk = s->cur_idx = -1;
  2482.     s->cur_tot_min = s->cur_tot_sec = s->cur_tot_frame = 0;
  2483.     s->cur_trk_min = s->cur_trk_sec = s->cur_trk_frame = 0;
  2484.     s->cur_tot_addr = s->cur_trk_addr = 0;
  2485.     s->sav_iaddr = 0;
  2486.     s->prog_cnt = 0;
  2487.     s->program = FALSE;
  2488.  
  2489.     if (clear_toc) {
  2490.         s->mode = M_NODISC;
  2491.         s->first_trk = s->last_trk = -1;
  2492.         s->tot_min = s->tot_sec = 0;
  2493.         s->tot_trks = 0;
  2494.         s->tot_addr = 0;
  2495.         s->prog_tot = 0;
  2496.  
  2497.         for (i = 0; i < MAXTRACK; i++) {
  2498.             s->trkinfo[i].trkno = -1;
  2499.             s->trkinfo[i].min = 0;
  2500.             s->trkinfo[i].sec = 0;
  2501.             s->trkinfo[i].frame = 0;
  2502.             s->trkinfo[i].addr = 0;
  2503.             s->playorder[i] = -1;
  2504.         }
  2505.     }
  2506.  
  2507.     if (first_time) {
  2508.         /* These are to be initialized only once */
  2509.         first_time = FALSE;
  2510.  
  2511.         s->time_dpy = T_ELAPSED;
  2512.         s->repeat = s->shuffle = FALSE;
  2513.         s->cddb = FALSE;
  2514.         s->level = 0;
  2515.         s->caddy_lock = FALSE;
  2516.         s->vendor[0] = '\0';
  2517.         s->prod[0] = '\0';
  2518.         s->revnum[0] = '\0';
  2519.     }
  2520. }
  2521.  
  2522.  
  2523. /*
  2524.  * reset_shuffle
  2525.  *    Recompute a new shuffle play sequence.  Updates the playorder
  2526.  *    table in the curstat_t structure.
  2527.  *
  2528.  * Args:
  2529.  *    s - Pointer to the curstat_t structure.
  2530.  *
  2531.  * Return:
  2532.  *    Nothing.
  2533.  */
  2534. void
  2535. reset_shuffle(curstat_t *s)
  2536. {
  2537.     sword32_t    i,
  2538.             j,
  2539.             n;
  2540.  
  2541.     srand((unsigned) time(NULL));
  2542.     s->prog_cnt = 0;
  2543.     s->prog_tot = s->tot_trks;
  2544.  
  2545.     for (i = 0; i < MAXTRACK; i++) {
  2546.         if (i >= (int) s->prog_tot) {
  2547.             s->playorder[i] = -1;
  2548.             continue;
  2549.         }
  2550.  
  2551.         do {
  2552.             n = rand() % (int) s->prog_tot;
  2553.             for (j = 0; j < i; j++) {
  2554.                 if (n == s->playorder[j])
  2555.                     break;
  2556.             }
  2557.         } while (j < i);
  2558.  
  2559.         s->playorder[i] = n;
  2560.     }
  2561.  
  2562.  
  2563.     if (app_data.debug) {
  2564.         fprintf(errfp, "\nShuffle tracks: ");
  2565.  
  2566.         for (i = 0; i < (int) s->prog_tot; i++)
  2567.             fprintf(errfp, "%d ",
  2568.                 s->trkinfo[s->playorder[i]].trkno);
  2569.  
  2570.         fprintf(errfp, "\n");
  2571.     }
  2572. }
  2573.  
  2574.  
  2575. /*
  2576.  * set_lock_btn
  2577.  *    Empty stub routine.
  2578.  *
  2579.  * Args:
  2580.  *    state - Not used.
  2581.  *
  2582.  * Return:
  2583.  *    Nothing.
  2584.  */
  2585. /*ARGSUSED*/
  2586. void
  2587. set_lock_btn(bool_t state)
  2588. {
  2589.     /* Null stub function */
  2590. }
  2591.  
  2592.  
  2593. /*
  2594.  * set_shuffle_btn
  2595.  *    Empty stub routine.
  2596.  *
  2597.  * Args:
  2598.  *    state - Not used.
  2599.  *
  2600.  * Return:
  2601.  *    Nothing.
  2602.  */
  2603. /*ARGSUSED*/
  2604. void
  2605. set_shuffle_btn(bool_t state)
  2606. {
  2607.     /* Null stub function */
  2608. }
  2609.  
  2610.  
  2611. /*
  2612.  * set_vol_slider
  2613.  *    Empty stub routine.
  2614.  *
  2615.  * Args:
  2616.  *    val - Not used.
  2617.  *
  2618.  * Return:
  2619.  *    Nothing.
  2620.  */
  2621. /*ARGSUSED*/
  2622. void
  2623. set_vol_slider(int val)
  2624. {
  2625.     /* Null stub function */
  2626. }
  2627.  
  2628.  
  2629. /*
  2630.  * set_bal_slider
  2631.  *    Empty stub routine.
  2632.  *
  2633.  * Args:
  2634.  *    val - Not used.
  2635.  *
  2636.  * Return:
  2637.  *    Nothing.
  2638.  */
  2639. /*ARGSUSED*/
  2640. void
  2641. set_bal_slider(int val)
  2642. {
  2643.     /* Null stub function */
  2644. }
  2645.  
  2646.  
  2647. /*
  2648.  * taper_vol
  2649.  *    Translate the volume level based on the configured taper
  2650.  *    characteristics.
  2651.  *
  2652.  * Args:
  2653.  *    v - The linear volume value.
  2654.  *
  2655.  * Return:
  2656.  *    The curved volume value.
  2657.  */
  2658. int
  2659. taper_vol(int v)
  2660. {
  2661.     switch (app_data.vol_taper) {
  2662.     case 1:
  2663.         /* inverse-squared taper */
  2664.         return (MAX_VOL - (SQR(MAX_VOL - v) / MAX_VOL));
  2665.     case 2:
  2666.         /* squared taper */
  2667.         return (SQR(v) / MAX_VOL);
  2668.     case 0:
  2669.     default:
  2670.         /* linear taper */
  2671.         return (v);
  2672.     }
  2673.     /*NOTREACHED*/
  2674. }
  2675.  
  2676.  
  2677. /*
  2678.  * untaper_vol
  2679.  *    Translate the volume level based on the configured taper
  2680.  *    characteristics.
  2681.  *
  2682.  * Args:
  2683.  *    v - The curved volume value.
  2684.  *
  2685.  * Return:
  2686.  *    The linear volume value.
  2687.  */
  2688. int
  2689. untaper_vol(int v)
  2690. {
  2691.     switch (app_data.vol_taper) {
  2692.     case 1:
  2693.         /* inverse-squared taper */
  2694.         return (MAX_VOL - isqrt(SQR(MAX_VOL) - (MAX_VOL * v)));
  2695.     case 2:
  2696.         /* squared taper */
  2697.         return (isqrt(v) * 10);
  2698.     case 0:
  2699.     default:
  2700.         /* linear taper */
  2701.         return (v);
  2702.     }
  2703.     /*NOTREACHED*/
  2704. }
  2705.  
  2706.  
  2707. /*
  2708.  * scale_vol
  2709.  *    Scale logical audio volume value (0-100) to an 8-bit value
  2710.  *    (0-0xff) range.
  2711.  *
  2712.  * Args:
  2713.  *    v - The logical volume value.
  2714.  *
  2715.  * Return:
  2716.  *    The scaled volume value.
  2717.  */
  2718. int
  2719. scale_vol(int v)
  2720. {
  2721.     /* Convert logical audio volume value to 8-bit volume */
  2722.     return ((v * (0xff - app_data.base_scsivol) / MAX_VOL) +
  2723.             app_data.base_scsivol);
  2724. }
  2725.  
  2726.  
  2727. /*
  2728.  * unscale_vol
  2729.  *    Scale an 8-bit audio volume parameter value (0-0xff) to the
  2730.  *    logical volume value (0-100).
  2731.  *
  2732.  * Args:
  2733.  *    v - The 8-bit volume value.
  2734.  *
  2735.  * Return:
  2736.  *    The logical volume value.
  2737.  */
  2738. int
  2739. unscale_vol(int v)
  2740. {
  2741.     register int    val;
  2742.  
  2743.     /* Convert 8-bit audio volume value to logical volume */
  2744.     val = (v - app_data.base_scsivol) * MAX_VOL /
  2745.           (0xff - app_data.base_scsivol);
  2746.  
  2747.     return ((val < 0) ? 0 : val);
  2748. }
  2749.  
  2750.  
  2751. /*
  2752.  * dbprog_dbclear
  2753.  *    Clear in-core CD database entry.
  2754.  *
  2755.  * Args:
  2756.  *    s - Pointer to the curstat_t structure.
  2757.  *
  2758.  * Return:
  2759.  *    Nothing.
  2760.  */
  2761. void
  2762. dbprog_dbclear(curstat_t *s)
  2763. {
  2764.     int        i;
  2765.  
  2766.     if (cur_db.discid == 0)
  2767.         /* Already cleared */
  2768.         return;
  2769.  
  2770.     /* Clear database entry structure */
  2771.  
  2772.     cur_db.category[0] = '\0';
  2773.  
  2774.     if (cur_db.dtitle != NULL) {
  2775.         MEM_FREE(cur_db.dtitle);
  2776.         cur_db.dtitle = NULL;
  2777.     }
  2778.  
  2779.     if (cur_db.extd != NULL) {
  2780.         MEM_FREE(cur_db.extd);
  2781.         cur_db.extd = NULL;
  2782.     }
  2783.  
  2784.     for (i = MAXTRACK-1; i >= 0; i--) {
  2785.         if (cur_db.trklist[i] != NULL) {
  2786.             MEM_FREE(cur_db.trklist[i]);
  2787.             cur_db.trklist[i] = NULL;
  2788.         }
  2789.  
  2790.         if (cur_db.extt[i] != NULL) {
  2791.             MEM_FREE(cur_db.extt[i]);
  2792.             cur_db.extt[i] = NULL;
  2793.         }
  2794.     }
  2795.  
  2796.     if (cur_db.playorder != NULL) {
  2797.         MEM_FREE(cur_db.playorder);
  2798.         cur_db.playorder = NULL;
  2799.     }
  2800.  
  2801.     cur_db.discid = 0;
  2802. }
  2803.  
  2804.  
  2805. /*
  2806.  * dbprog_dbget
  2807.  *    Load CD database entry for a CD.
  2808.  *
  2809.  * Args:
  2810.  *    s - Pointer to the curstat_t structure.
  2811.  *
  2812.  * Return:
  2813.  *    Nothing.
  2814.  */
  2815. /*ARGSUSED*/
  2816. void
  2817. dbprog_dbget(curstat_t *s)
  2818. {
  2819.     dbprog_dbload(dbprog_discid(s));
  2820. }
  2821.  
  2822.  
  2823. /*
  2824.  * curstat_addr
  2825.  *    Return the address of the curstat_t structure.
  2826.  *
  2827.  * Args:
  2828.  *    Nothing.
  2829.  *
  2830.  * Return:
  2831.  *    Nothing.
  2832.  */
  2833. curstat_t *
  2834. curstat_addr(void)
  2835. {
  2836.     return (&status);
  2837. }
  2838.  
  2839.  
  2840. /*
  2841.  * cda_sendcmd
  2842.  *    Send command down the pipe and handle response.
  2843.  *
  2844.  * Args:
  2845.  *    cmd - The command code
  2846.  *    arg - Command arguments
  2847.  *
  2848.  * Return:
  2849.  *    TRUE - success
  2850.  *    FALSE - failure
  2851.  */
  2852. bool_t
  2853. cda_sendcmd(word32_t cmd, word32_t arg[])
  2854. {
  2855.     cdapkt_t    p,
  2856.             r;
  2857.  
  2858.     /* Fill in command packet */
  2859.     memset(&p, 0, sizeof(cdapkt_t));
  2860.     p.pktid = getpid();
  2861.     p.cmd = cmd;
  2862.     p.retcode = 0;
  2863.     memcpy(p.arg, arg, CDA_NARGS * sizeof(word32_t));
  2864.  
  2865.     /* Send command packet */
  2866.     if (!cda_sendpkt("CD audio", cda_sfd[1], &p)) {
  2867.         errmsg = "Cannot send packet to CD audio daemon.";
  2868.         return FALSE;
  2869.     }
  2870.  
  2871.     /* Get response packet */
  2872.     if (!cda_getpkt("CD audio", cda_rfd[1], &r)) {
  2873.         errmsg = "Cannot get packet from CD audio daemon.";
  2874.         return FALSE;
  2875.     }
  2876.  
  2877.     /* Sanity check */
  2878.     if (p.pktid != r.pktid) {
  2879.         errmsg = "CD audio pipe packet sequence error.";
  2880.         return FALSE;
  2881.     }
  2882.  
  2883.     /* Return args */
  2884.     memcpy(arg, r.arg, CDA_NARGS * sizeof(word32_t));
  2885.  
  2886.     /* Check return code */
  2887.     switch (r.retcode) {
  2888.     case CDA_OK:
  2889.         return TRUE;
  2890.     case CDA_INVALID:
  2891.         errmsg = "This command is not valid in the current state.";
  2892.         return FALSE;
  2893.     case CDA_PARMERR:
  2894.         errmsg = "Command argument error.";
  2895.         return FALSE;
  2896.     case CDA_FAILED:
  2897.         errmsg = "The CD audio daemon does not support this command.";
  2898.         return FALSE;
  2899.     default:
  2900.         errmsg = "The CD audio daemon returned an invalid status.";
  2901.         return FALSE;
  2902.     }
  2903.     /*NOTREACHED*/
  2904. }
  2905.  
  2906.  
  2907. /*
  2908.  * dbprog_dbload
  2909.  *    Read in the CD database file entry pertaining to the
  2910.  *    currently loaded disc, if available.
  2911.  *
  2912.  * Args:
  2913.  *    s - Pointer to the curstat_t structure.
  2914.  *
  2915.  * Return:
  2916.  *    Nothing.
  2917.  */
  2918. bool_t
  2919. dbprog_dbload(word32_t discid)
  2920. {
  2921.     int        i,
  2922.             pos,
  2923.             lineno;
  2924.     FILE        *fp = NULL;
  2925.     char        *cp,
  2926.             *path,
  2927.             dbfile[FILE_PATH_SZ],
  2928.             buf[STR_BUF_SZ + 16],
  2929.             tmpbuf[STR_BUF_SZ + 16];
  2930.     static bool_t    first_time = TRUE;
  2931.  
  2932.  
  2933.     cur_db.discid = discid;
  2934.  
  2935.     if (first_time) {
  2936.         first_time = FALSE;
  2937.  
  2938.         /* Allocate memory for the database
  2939.          * directories string pointers array
  2940.          */
  2941.         dbdirs = (char **)(void *) MEM_ALLOC(
  2942.             app_data.max_dbdirs * sizeof(char *)
  2943.         );
  2944.         if (dbdirs == NULL)
  2945.             cd_fatal_popup(NULL, app_data.str_nomemory);
  2946.  
  2947.         if ((cp = getenv("XMCD_DBPATH")) != NULL) {
  2948.             app_data.dbdir = (char *) MEM_ALLOC(strlen(cp) + 1);
  2949.             if (app_data.dbdir == NULL)
  2950.                 cd_fatal_popup(NULL, app_data.str_nomemory);
  2951.  
  2952.             strcpy(app_data.dbdir, cp);
  2953.         }
  2954.  
  2955.         /* Create the global array of strings each of which is a
  2956.          * path to a CD database directory.
  2957.          */
  2958.  
  2959.         i = 0;
  2960.         if (app_data.dbdir == NULL || app_data.dbdir[0] == '\0')
  2961.             dbdirs[0] = NULL;
  2962.         else {
  2963.             for (path = app_data.dbdir;
  2964.                  (cp = strchr(path, DBPATH_SEPCHAR)) != NULL &&
  2965.                  i < app_data.max_dbdirs - 1;
  2966.                  path = cp + 1, i++) {
  2967.                 *cp = '\0';
  2968.  
  2969.                 if (path[0] == '/') {
  2970.                     dbdirs[i] = (char *) MEM_ALLOC(
  2971.                         strlen(path) + 1
  2972.                     );
  2973.                 }
  2974.                 else if (path[0] == '~') {
  2975.                     dbdirs[i] = (char *) MEM_ALLOC(
  2976.                         strlen(path) + 128
  2977.                     );
  2978.                 }
  2979.                 else {
  2980.                     dbdirs[i] = (char *) MEM_ALLOC(
  2981.                         strlen(path) +
  2982.                         strlen(app_data.libdir) + 7
  2983.                     );
  2984.                 }
  2985.  
  2986.                 if (dbdirs[i] == NULL) {
  2987.                     cd_fatal_popup(
  2988.                         NULL,
  2989.                         app_data.str_nomemory
  2990.                     );
  2991.                 }
  2992.  
  2993.                 if (path[0] == '/') {
  2994.                     /* Absolute path name specified */
  2995.                     strcpy(dbdirs[i], path);
  2996.                 }
  2997.                 else if (path[0] == '~') {
  2998.                     /* Perform tilde expansion a la
  2999.                      * [ck]sh
  3000.                      */
  3001.                     if (path[1] == '/') {
  3002.                         sprintf(dbdirs[i], "%s%s",
  3003.                             homedir(get_ouid()),
  3004.                             &path[1]);
  3005.                     }
  3006.                     else if (path[1] == '\0') {
  3007.                         strcpy(dbdirs[i],
  3008.                                homedir(get_ouid()));
  3009.                     }
  3010.                     else {
  3011.                         char    *cp1;
  3012.  
  3013.                         cp1 = strchr(path, '/');
  3014.                         if (cp1 == NULL) {
  3015.                             strcpy(dbdirs[i],
  3016.                                uhomedir(&path[1]));
  3017.                         }
  3018.                         else {
  3019.                             *cp1 = '\0';
  3020.                             sprintf(dbdirs[i],
  3021.                                 "%s/%s",
  3022.                                 uhomedir(&path[1]),
  3023.                                 cp1+1);
  3024.                         }
  3025.                     }
  3026.                 }
  3027.                 else {
  3028.                     /* Relative path name specified */
  3029.                     sprintf(dbdirs[i], "%s/cddb/%s",
  3030.                         app_data.libdir, path);
  3031.                 }
  3032.  
  3033.                 *cp = DBPATH_SEPCHAR;
  3034.             }
  3035.  
  3036.             if (cp != NULL && *cp == DBPATH_SEPCHAR)
  3037.                 *cp = '\0';
  3038.  
  3039.             if (path[0] == '/')
  3040.                 dbdirs[i] = (char *)
  3041.                     MEM_ALLOC(strlen(path) + 1);
  3042.             else
  3043.                 dbdirs[i] = (char *) MEM_ALLOC(
  3044.                     strlen(path) +
  3045.                     strlen(app_data.libdir) + 7
  3046.                 );
  3047.  
  3048.             if (dbdirs[i] == NULL)
  3049.                 cd_fatal_popup(NULL, app_data.str_nomemory);
  3050.  
  3051.             if (path[0] == '/')
  3052.                 strcpy(dbdirs[i], path);
  3053.             else
  3054.                 sprintf(dbdirs[i], "%s/cddb/%s",
  3055.                     app_data.libdir, path);
  3056.         }
  3057.  
  3058.         for (i++; i < app_data.max_dbdirs; i++)
  3059.             dbdirs[i] = NULL;
  3060.  
  3061.     }
  3062.  
  3063.     /* Loop through all the database directories
  3064.      * and try to open the matching file for reading.
  3065.      */
  3066.     for (fp = NULL, i = 0; i < app_data.max_dbdirs; i++) {
  3067.         if (dbdirs[i] == NULL)
  3068.             break;
  3069.  
  3070.         sprintf(dbfile, "%s/%08x", dbdirs[i], discid);
  3071.  
  3072.         if ((fp = fopen(dbfile, "r")) != NULL)
  3073.             break;
  3074.     }
  3075.  
  3076.     if (fp == NULL)
  3077.         /* File does not exist or not readable */
  3078.         return FALSE;
  3079.  
  3080.     /* Record the category */
  3081.     strcpy(cur_db.category, basename(dbdirs[i]));
  3082.  
  3083.     /* Read first line of database file */
  3084.     if (fgets(buf, sizeof(buf), fp) == NULL) {
  3085.         fclose(fp);
  3086.         return FALSE;
  3087.     }
  3088.  
  3089.     /* Database file signature check */
  3090.     if (strncmp(buf, "# xmcd ", 7) != 0) {
  3091.         /* Not a supported database file */
  3092.         fclose(fp);
  3093.         return FALSE;
  3094.     }
  3095.  
  3096.     /* Read the rest of the database file */
  3097.     for (lineno = 0; fgets(buf, sizeof(buf), fp) != NULL; lineno++) {
  3098.         /* Comment line */
  3099.         if (buf[0] == '#') {
  3100.             lineno--;
  3101.             continue;
  3102.         }
  3103.  
  3104.         buf[strlen(buf)-1] = '\n';
  3105.  
  3106.         /* Disk ID sanity check */
  3107.         if (lineno == 0) {
  3108.             if (strncmp(buf, "DISCID=", 7) == 0)
  3109.                 /* Okay */
  3110.                 continue;
  3111.  
  3112.             /* Sanity check failed */
  3113.             fclose(fp);
  3114.             return FALSE;
  3115.         }
  3116.  
  3117.         /* Disk title */
  3118.         if (!isdaemon && sscanf(buf, "DTITLE=%[^\n]\n", tmpbuf) > 0) {
  3119.             if (cur_db.dtitle == NULL) {
  3120.                 cur_db.dtitle = (char *)
  3121.                     MEM_ALLOC(strlen(tmpbuf) + 1);
  3122.  
  3123.                 if (cur_db.dtitle != NULL)
  3124.                     cur_db.dtitle[0] = '\0';
  3125.             }
  3126.             else {
  3127.                 cur_db.dtitle = (char *)
  3128.                     MEM_REALLOC(cur_db.dtitle,
  3129.                         strlen(cur_db.dtitle) +
  3130.                         strlen(tmpbuf) + 1);
  3131.             }
  3132.  
  3133.             if (cur_db.dtitle == NULL)
  3134.                 cd_fatal_popup(NULL, app_data.str_nomemory);
  3135.  
  3136.             dbprog_strcat(cur_db.dtitle, tmpbuf);
  3137.             continue;
  3138.         }
  3139.  
  3140.         /* Track title */
  3141.         if (!isdaemon &&
  3142.             sscanf(buf, "TTITLE%u=%[^\n]\n", &pos, tmpbuf) >= 2) {
  3143.             if (pos >= (int) (discid & 0xff))
  3144.                 continue;
  3145.  
  3146.             if (cur_db.trklist[pos] == NULL) {
  3147.                 cur_db.trklist[pos] = (char *)
  3148.                     MEM_ALLOC(strlen(tmpbuf) + 1);
  3149.  
  3150.                 if (cur_db.trklist[pos] != NULL)
  3151.                     cur_db.trklist[pos][0] = '\0';
  3152.                 
  3153.             }
  3154.             else {
  3155.                 cur_db.trklist[pos] = (char *)
  3156.                     MEM_REALLOC(cur_db.trklist[pos],
  3157.                         strlen(cur_db.trklist[pos]) +
  3158.                         strlen(tmpbuf) + 1);
  3159.             }
  3160.  
  3161.             if (cur_db.trklist[pos] == NULL)
  3162.                 cd_fatal_popup(NULL, app_data.str_nomemory);
  3163.  
  3164.             dbprog_strcat(cur_db.trklist[pos], tmpbuf);
  3165.             continue;
  3166.         }
  3167.  
  3168.         /* Disk extended info */
  3169.         if (!isdaemon && sscanf(buf, "EXTD=%[^\n]\n", tmpbuf) > 0) {
  3170.             if (cur_db.extd == NULL) {
  3171.                 cur_db.extd = (char *)
  3172.                     MEM_ALLOC(strlen(tmpbuf) + 1);
  3173.  
  3174.                 if (cur_db.extd != NULL)
  3175.                     cur_db.extd[0] = '\0';
  3176.             }
  3177.             else {
  3178.                 cur_db.extd = (char *)
  3179.                     MEM_REALLOC(cur_db.extd,
  3180.                         strlen(cur_db.extd) +
  3181.                         strlen(tmpbuf) + 1);
  3182.             }
  3183.  
  3184.             if (cur_db.extd == NULL)
  3185.                 cd_fatal_popup(NULL, app_data.str_nomemory);
  3186.  
  3187.             dbprog_strcat(cur_db.extd, tmpbuf);
  3188.             continue;
  3189.         }
  3190.  
  3191.         /* Track extended info */
  3192.         if (!isdaemon &&
  3193.             sscanf(buf, "EXTT%u=%[^\n]\n", &pos, tmpbuf) >= 2) {
  3194.             if (pos >= (int) (discid & 0xff))
  3195.                 continue;
  3196.  
  3197.             if (cur_db.extt[pos] == NULL) {
  3198.                 cur_db.extt[pos] = (char *)
  3199.                     MEM_ALLOC(strlen(tmpbuf) + 1);
  3200.  
  3201.                 if (cur_db.extt[pos] != NULL)
  3202.                     cur_db.extt[pos][0] = '\0';
  3203.             }
  3204.             else {
  3205.                 cur_db.extt[pos] = (char *)
  3206.                     MEM_REALLOC(cur_db.extt[pos],
  3207.                         strlen(cur_db.extt[pos]) +
  3208.                         strlen(tmpbuf) + 1);
  3209.             }
  3210.  
  3211.             if (cur_db.extt[pos] == NULL)
  3212.                 cd_fatal_popup(NULL, app_data.str_nomemory);
  3213.  
  3214.             dbprog_strcat(cur_db.extt[pos], tmpbuf);
  3215.             continue;
  3216.         }
  3217.  
  3218.         /* Play order */
  3219.         if (isdaemon &&
  3220.             sscanf(buf, "PLAYORDER=%[^\n]\n", tmpbuf) > 0) {
  3221.             if (cur_db.playorder == NULL) {
  3222.                 cur_db.playorder = (char *)
  3223.                     MEM_ALLOC(strlen(tmpbuf) + 1);
  3224.  
  3225.                 if (cur_db.playorder != NULL)
  3226.                     cur_db.playorder[0] = '\0';
  3227.                     
  3228.             }
  3229.             else {
  3230.                 cur_db.playorder = (char *)
  3231.                     MEM_REALLOC(cur_db.playorder,
  3232.                         strlen(cur_db.playorder) +
  3233.                         strlen(tmpbuf) + 1);
  3234.             }
  3235.  
  3236.             if (cur_db.playorder == NULL)
  3237.                 cd_fatal_popup(NULL, app_data.str_nomemory);
  3238.  
  3239.             dbprog_strcat(cur_db.playorder, tmpbuf);
  3240.  
  3241.             /* Parse sequence string */
  3242.             dbprog_parse(&status);
  3243.  
  3244.             continue;
  3245.         }
  3246.     }
  3247.  
  3248.     fclose(fp);
  3249.     return TRUE;
  3250. }
  3251.  
  3252.  
  3253. /*
  3254.  * cda_daemon
  3255.  *    CD audio daemon main loop function.
  3256.  *
  3257.  * Args:
  3258.  *    s - Pointer to the curstat_t structure.
  3259.  *
  3260.  * Return:
  3261.  *    The CD audio daemon exit status.
  3262.  */
  3263. int
  3264. cda_daemon(curstat_t *s)
  3265. {
  3266.     cdapkt_t    p;
  3267.     bool_t        done = FALSE;
  3268.     FILE        *fp;
  3269.     struct stat    stbuf;
  3270.  
  3271.     /* Make temporary directory, if needed */
  3272.     sprintf(emsg, app_data.str_tmpdirerr, TEMP_DIR);
  3273.     if (stat(TEMP_DIR, &stbuf) < 0) {
  3274.         if (errno == ENOENT) {
  3275.             int    omask;
  3276.  
  3277.             /* The permissions should be writable by all */
  3278.             omask = umask(0);
  3279.             if (mkdir(TEMP_DIR, 0777) < 0) {
  3280.                 (void) umask(omask);
  3281.                 fprintf(errfp, "%s\n", emsg);
  3282.                 return 1;
  3283.             }
  3284.             (void) umask(omask);
  3285.         }
  3286.         else {
  3287.             fprintf(errfp, "%s\n", emsg);
  3288.             return 1;
  3289.         }
  3290.     }
  3291.     else if (!S_ISDIR(stbuf.st_mode)) {
  3292.         fprintf(errfp, "%s\n", emsg);
  3293.         return 1;
  3294.     }
  3295.  
  3296.     /* Create FIFOs */
  3297.     if (MKFIFO(spipe, 0600) < 0) {
  3298.         perror("CD audio: Cannot create send pipe");
  3299.         cd_quit(s);
  3300.         return 1;
  3301.     }
  3302.     if (MKFIFO(rpipe, 0600) < 0) {
  3303.         perror("CD audio: Cannot create recv pipe");
  3304.         cd_quit(s);
  3305.         return 1;
  3306.     }
  3307.  
  3308.     /* Become a daemon process */
  3309.     switch (fork()) {
  3310.     case -1:
  3311.         perror("Cannot fork CD audio daemon");
  3312.         cd_quit(s);
  3313.         return 1;
  3314.     case 0:
  3315.         /* Child process */
  3316.         isdaemon = TRUE;
  3317.  
  3318.         if (ttyfp != stderr) {
  3319.             errfp = ttyfp;
  3320.             fclose(stdin);
  3321.             fclose(stdout);
  3322.             fclose(stderr);
  3323.         }
  3324.  
  3325.         break;
  3326.     default:
  3327.         /* Parent process */
  3328.         signal(SIGCLD, SIG_IGN);
  3329.         return 0;
  3330.     }
  3331.  
  3332.     /* Initialize and start drive interfaces */
  3333.     cda_init(s);
  3334.     cda_start(s);
  3335.  
  3336.     /* Handle some signals */
  3337.     signal(SIGHUP, onsig);
  3338.     signal(SIGTERM, SIG_IGN);
  3339.     signal(SIGINT, SIG_IGN);
  3340.     signal(SIGQUIT, SIG_IGN);
  3341. #if defined(SIGTSTP) && defined(SIGCONT)
  3342.     signal(SIGTSTP, SIG_IGN);
  3343.     signal(SIGCONT, SIG_IGN);
  3344. #endif
  3345.  
  3346.     /* Main command handling loop */
  3347.     while (!done) {
  3348.         /* Get command packet */
  3349.         if (!cda_getpkt("CD audio daemon", cda_sfd[0], &p)) {
  3350.             cd_quit(s);
  3351.             return 1;
  3352.         }
  3353.  
  3354.         /* Interpret and carry out command */
  3355.         done = cda_docmd(s, &p);
  3356.  
  3357.         /* Send response packet */
  3358.         if (!cda_sendpkt("CD audio daemon", cda_rfd[0], &p)) {
  3359.             cd_quit(s);
  3360.             return 1;
  3361.         }
  3362.     }
  3363.  
  3364.     /* Stop the drive */
  3365.     cd_quit(s);
  3366.  
  3367.     exit(0);
  3368. }
  3369.  
  3370.  
  3371. /*
  3372.  * main
  3373.  *    The main function
  3374.  */
  3375. void
  3376. main(int argc, char **argv)
  3377. {
  3378.     int        ret;
  3379.     word32_t    cmd;
  3380.     word32_t    arg[CDA_NARGS];
  3381.     struct stat    stbuf;
  3382.     char        *ttypath,
  3383.             *cp,
  3384.             str[FILE_PATH_SZ];
  3385.  
  3386.     /* Initialize */
  3387.     spipe[0] = rpipe[0] = lockfile[0] = '\0';
  3388.     if ((ttypath = ttyname(2)) == NULL)
  3389.         ttypath = "/dev/tty";
  3390.     if ((ttyfp = fopen(ttypath, "w")) != NULL)
  3391.         setbuf(ttyfp, NULL);
  3392.     else
  3393.         ttyfp = stderr;
  3394.  
  3395.     /* Parse command line args */
  3396.     if (!cda_parse_args(argc, argv, &cmd, arg))
  3397.         exit(1);
  3398.  
  3399.     /* Initialize libutil */
  3400.     util_init();
  3401.  
  3402.     /* Set library directory path */
  3403.     if ((cp = getenv("XMCD_LIBDIR")) == NULL)
  3404.         cd_fatal_popup(NULL, "XMCD_LIBDIR environment not defined.");
  3405.  
  3406.     app_data.libdir = (char *) MEM_ALLOC(strlen(cp) + 1);
  3407.     if (app_data.libdir == NULL)
  3408.         cd_fatal_popup(NULL, app_data.str_nomemory);
  3409.     strcpy(app_data.libdir, cp);
  3410.  
  3411.     /* Get system common configuration parameters */
  3412.     sprintf(str, "%s/config/common.cfg", app_data.libdir);
  3413.     common_parminit(str, TRUE);
  3414.  
  3415.     /* Get user common configuration parameters */
  3416.     sprintf(str, "%s/.xmcdcfg/common.cfg", homedir(get_ouid()));
  3417.     common_parminit(str, FALSE);
  3418.  
  3419.     /* Sanity check */
  3420.     if (app_data.max_dbdirs <= 0 || app_data.max_dbdirs > 100)
  3421.         cd_fatal_popup(NULL, app_data.str_dbdirserr);
  3422.  
  3423.     /* Check validity of device */
  3424.     if (di_isdemo())
  3425.         cd_rdev = 0;
  3426.     else {
  3427.         if (stat(app_data.device, &stbuf) < 0) {
  3428.             sprintf(emsg, "Cannot stat %s.", app_data.device);
  3429.             cd_fatal_popup(NULL, emsg);
  3430.         }
  3431.         cd_rdev = stbuf.st_rdev;
  3432.     }
  3433.  
  3434.     /* FIFO paths */
  3435.     sprintf(spipe, "%s/send.%x", TEMP_DIR, cd_rdev);
  3436.     sprintf(rpipe, "%s/recv.%x", TEMP_DIR, cd_rdev);
  3437.  
  3438. #ifndef NOVISUAL
  3439.     if (visual) {
  3440.         /* Handle some signals */
  3441.         signal(SIGINT, onsig);
  3442.         signal(SIGQUIT, onsig);
  3443.         signal(SIGTERM, onsig);
  3444.  
  3445.         /* Start visual mode */
  3446.         cda_visual();
  3447.     }
  3448. #endif
  3449.  
  3450.     if (cmd == CDA_ON) {
  3451.         /* Start CDA daemon */
  3452.         if ((ret = cda_daemon(&status)))
  3453.             exit(ret);
  3454.     }
  3455.  
  3456.     /* Open FIFOs - command side */
  3457.     if ((cda_sfd[1] = open(spipe, O_WRONLY)) < 0) {
  3458.         perror("CD audio: cannot open send pipe");
  3459.         cd_fatal_popup(
  3460.             NULL,
  3461.             "Run \"cda on\" first to initialize CD audio daemon."
  3462.         );
  3463.     }
  3464.     if ((cda_rfd[1] = open(rpipe, O_RDONLY)) < 0) {
  3465.         perror("CD audio: cannot open recv pipe");
  3466.         cd_fatal_popup(
  3467.             NULL,
  3468.             "Run \"cda on\" first to initialize CD audio daemon."
  3469.         );
  3470.     }
  3471.  
  3472.     /* Handle some signals */
  3473.     signal(SIGINT, onintr);
  3474.     signal(SIGQUIT, onintr);
  3475.     signal(SIGTERM, onintr);
  3476.  
  3477.     for (;;) {
  3478.         /* Send command to cda daemon */
  3479.         if (!cda_sendcmd(cmd, arg)) {
  3480.             printf("%s\n", errmsg);
  3481.             exit(2);
  3482.         }
  3483.  
  3484.         /* Display status */
  3485.         switch (cmd) {
  3486.         case CDA_ON:
  3487.             fprintf(errfp,
  3488.                 "CD audio daemon pid=%d dev=%s started.\n",
  3489.                 arg[0], app_data.device);
  3490.             break;
  3491.         case CDA_OFF:
  3492.             fprintf(errfp,
  3493.                 "CD audio daemon pid=%d dev=%s exited.\n",
  3494.                 arg[0], app_data.device);
  3495.             break;
  3496.         case CDA_PROGRAM:
  3497.             prn_program(arg);
  3498.             break;
  3499.         case CDA_VOLUME:
  3500.             prn_vol(arg);
  3501.             break;
  3502.         case CDA_BALANCE:
  3503.             prn_bal(arg);
  3504.             break;
  3505.         case CDA_ROUTE:
  3506.             prn_route(arg);
  3507.             break;
  3508.         case CDA_STATUS:
  3509.             prn_stat(arg);
  3510.             break;
  3511.         case CDA_TOC:
  3512.             prn_toc(arg);
  3513.             break;
  3514.         case CDA_EXTINFO:
  3515.             prn_extinfo(arg);
  3516.             break;
  3517.         case CDA_DEVICE:
  3518.             prn_device(arg);
  3519.             break;
  3520.         case CDA_VERSION:
  3521.             prn_ver(arg);
  3522.             break;
  3523.         default:
  3524.             break;
  3525.         }
  3526.  
  3527.         fflush(stdout);
  3528.  
  3529.         if (!stat_cont)
  3530.             break;
  3531.  
  3532.         if (cont_delay > 0)
  3533.             sleep(cont_delay);
  3534.     }
  3535.  
  3536.     /* Close FIFOs - command side */
  3537.     if (cda_sfd[1] >= 0)
  3538.         close(cda_sfd[1]);
  3539.     if (cda_rfd[1] >= 0)
  3540.         close(cda_rfd[1]);
  3541.  
  3542.     exit(0);
  3543. }
  3544.  
  3545.  
  3546.