1 /*
2 * cda - Command-line CD Audio Player/Ripper
3 *
4 * Copyright (C) 1993-2004 Ti Kan
5 * E-mail: xmcd@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 6.352 04/04/20";
24 #endif
25
26 #include "common_d/appenv.h"
27 #include "common_d/util.h"
28 #include "common_d/version.h"
29 #include "libdi_d/libdi.h"
30 #include "cdda_d/cdda.h"
31 #include "cdinfo_d/cdinfo.h"
32 #include "cdinfo_d/motd.h"
33 #include "cda_d/cda.h"
34 #include "cda_d/visual.h"
35 #include "cda_d/userreg.h"
36
37 #ifdef HAS_ICONV
38 #include <iconv.h>
39 #include <locale.h>
40 #endif
41
42
43 #define PGM_SEPCHAR ',' /* Program seq separator */
44 #define MAX_PKT_RETRIES 200 /* Max retries reading pkt */
45 #define PIPE_TIMEOUT 5 /* 5 seconds to time out */
46
47
48 extern char *ttyname();
49
50 appdata_t app_data; /* Option settings */
51 cdinfo_incore_t *dbp; /* CD information pointer */
52 curstat_t status; /* Current CD player status */
53 char *emsgp, /* Error msg for use on exit */
54 **cda_devlist, /* Device list table */
55 errmsg[ERR_BUF_SZ], /* Error message buffer */
56 spipe[FILE_PATH_SZ], /* Send pipe path */
57 rpipe[FILE_PATH_SZ]; /* Receive pipe path */
58 int cda_sfd[2] = {-1,-1}, /* Send pipe file desc */
59 cda_rfd[2] = {-1,-1}, /* Receive pipe file desc */
60 exit_status; /* Exit status */
61 pid_t daemon_pid; /* CDA daemon pid */
62 FILE *errfp; /* Error message stream */
63
64 STATIC int cont_delay = 1, /* Status display interval */
65 inetoffln = 0; /* Inet Offline cmd line arg */
66 STATIC dev_t cd_rdev; /* CD device number */
67 STATIC bool_t isdaemon = FALSE, /* Am I the daemon process */
68 stat_cont = FALSE, /* Continuous display status */
69 batch = FALSE; /* Non-interactive */
70 STATIC FILE *ttyfp; /* /dev/tty */
71 STATIC cdinfo_client_t cdinfo_cldata; /* Client info for libcdinfo */
72 STATIC di_client_t di_cldata; /* Client info for libdi */
73 STATIC char dlock[FILE_PATH_SZ]; /* Daemon lock file path */
74 STATIC cdapkt_t *curr_pkt; /* Current received packet */
75 #ifndef NOVISUAL
76 STATIC bool_t visual = FALSE; /* Visual (curses) mode */
77 #endif
78
79
80 /***********************
81 * internal routines *
82 ***********************/
83
84
85 /*
86 * onsig0
87 * Signal handler to shut down application.
88 *
89 * Args:
90 * signo - The signal number.
91 *
92 * Return:
93 * Nothing.
94 */
95 /*ARGSUSED*/
96 STATIC void
onsig0(int signo)97 onsig0(int signo)
98 {
99 exit_status = 3;
100 cda_quit(&status);
101 /*NOTREACHED*/
102 }
103
104
105 /*
106 * onsig1
107 * SIGPIPE signal handler.
108 *
109 * Args:
110 * signo - The signal number.
111 *
112 * Return:
113 * Nothing.
114 */
115 /*ARGSUSED*/
116 STATIC void
onsig1(int signo)117 onsig1(int signo)
118 {
119 /* Do nothing */
120 #if !defined(USE_SIGACTION) && !defined(BSDCOMPAT)
121 /* Re-arm the handler */
122 (void) util_signal(signo, onsig1);
123 #endif
124 }
125
126
127 /*
128 * onintr
129 * Signal handler to stop continuous status display.
130 *
131 * Args:
132 * signo - The signal number.
133 *
134 * Return:
135 * Nothing.
136 */
137 /*ARGSUSED*/
138 STATIC void
onintr(int signo)139 onintr(int signo)
140 {
141 stat_cont = FALSE;
142 (void) printf("\r");
143 #if !defined(USE_SIGACTION) && !defined(BSDCOMPAT)
144 (void) util_signal(signo, SIG_IGN);
145 #endif
146 }
147
148
149 /*
150 * cda_pgm_parse
151 * Parse the shuffle/program mode play sequence text string, and
152 * update the playorder table in the curstat_t structure.
153 *
154 * Args:
155 * s - Pointer to the curstat_t structure.
156 *
157 * Return:
158 * TRUE = success,
159 * FALSE = error.
160 */
161 STATIC bool_t
cda_pgm_parse(curstat_t * s)162 cda_pgm_parse(curstat_t *s)
163 {
164 int i,
165 j;
166 char *p,
167 *q,
168 *tmpbuf;
169 bool_t last = FALSE,
170 skipped = FALSE;
171
172 if (dbp->playorder == NULL)
173 /* Nothing to do */
174 return TRUE;
175
176 tmpbuf = NULL;
177 if (!util_newstr(&tmpbuf, dbp->playorder)) {
178 CDA_FATAL(app_data.str_nomemory);
179 return FALSE;
180 }
181
182 s->prog_tot = 0;
183
184 for (i = 0, p = q = tmpbuf; i < MAXTRACK; p = ++q) {
185 /* Skip p to the next digit */
186 for (; !isdigit((int) *p) && *p != '\0'; p++)
187 ;
188
189 if (*p == '\0')
190 /* No more to do */
191 break;
192
193 /* Skip q to the next non-digit */
194 for (q = p; isdigit((int) *q); q++)
195 ;
196
197 if (*q == ',')
198 *q = '\0';
199 else if (*q == '\0')
200 last = TRUE;
201 else {
202 MEM_FREE(tmpbuf);
203 CDA_WARNING(app_data.str_seqfmterr);
204 return FALSE;
205 }
206
207 if (q > p) {
208 /* Update play sequence */
209 for (j = 0; j < MAXTRACK; j++) {
210 if (s->trkinfo[j].trkno == atoi(p)) {
211 s->trkinfo[i].playorder = j;
212 s->prog_tot++;
213 i++;
214 break;
215 }
216 }
217
218 if (j >= MAXTRACK)
219 skipped = TRUE;
220 }
221
222 if (last)
223 break;
224 }
225
226 if (skipped) {
227 /* Delete invalid tracks from list */
228
229 tmpbuf[0] = '\0';
230 for (i = 0; i < (int) s->prog_tot; i++) {
231 if (i == 0)
232 (void) sprintf(tmpbuf, "%u",
233 s->trkinfo[s->trkinfo[i].playorder].trkno);
234 else
235 (void) sprintf(tmpbuf, "%s,%u", tmpbuf,
236 s->trkinfo[s->trkinfo[i].playorder].trkno);
237 }
238
239 CDA_WARNING(app_data.str_invpgmtrk);
240 }
241
242 MEM_FREE(tmpbuf);
243
244 return TRUE;
245 }
246
247
248 /*
249 * cda_mkdirs
250 * Called at startup time to create some needed directories, if
251 * they aren't already there.
252 * Currently these are:
253 * $HOME/.xmcdcfg
254 * $HOME/.xmcdcfg/prog
255 * /tmp/.cdaudio
256 *
257 * Args:
258 * None.
259 *
260 * Return:
261 * Nothing.
262 */
263 STATIC void
cda_mkdirs(void)264 cda_mkdirs(void)
265 {
266 pid_t cpid;
267 waitret_t wstat;
268 char *errmsg,
269 *homepath,
270 path[FILE_PATH_SZ + 16];
271 struct stat stbuf;
272
273 #ifndef NOMKTMPDIR
274 errmsg = (char *) MEM_ALLOC(
275 "errmsg",
276 strlen(app_data.str_tmpdirerr) + strlen(TEMP_DIR)
277 );
278 if (errmsg == NULL) {
279 CDA_FATAL(app_data.str_nomemory);
280 return;
281 }
282
283 /* Make temporary directory, if needed */
284 (void) sprintf(errmsg, app_data.str_tmpdirerr, TEMP_DIR);
285 if (LSTAT(TEMP_DIR, &stbuf) < 0) {
286 if (!util_mkdir(TEMP_DIR, 0777)) {
287 CDA_FATAL(errmsg);
288 return;
289 }
290 }
291 else if (!S_ISDIR(stbuf.st_mode)) {
292 CDA_FATAL(errmsg);
293 return;
294 }
295
296 MEM_FREE(errmsg);
297 #endif /* NOMKTMPDIR */
298
299 homepath = util_homedir(util_get_ouid());
300 if ((int) strlen(homepath) >= FILE_PATH_SZ) {
301 CDA_FATAL(app_data.str_longpatherr);
302 return;
303 }
304
305 switch (cpid = FORK()) {
306 case 0:
307 /* Child process */
308
309 /* Force uid and gid to original setting */
310 if (!util_set_ougid())
311 _exit(1);
312
313 /* Create the per-user config directory */
314 (void) sprintf(path, USR_CFG_PATH, homepath);
315 if (LSTAT(path, &stbuf) < 0) {
316 if (errno == ENOENT && !util_mkdir(path, 0755)) {
317 DBGPRN(DBG_GEN)(errfp,
318 "cd_mkdirs: cannot mkdir %s.\n", path);
319 }
320 else {
321 DBGPRN(DBG_GEN)(errfp,
322 "cd_mkdirs: cannot stat %s.\n", path);
323 }
324 }
325 else if (!S_ISDIR(stbuf.st_mode)) {
326 DBGPRN(DBG_GEN)(errfp,
327 "cd_mkdirs: %s is not a directory.\n", path);
328 }
329
330 /* Create the per-user track program directory */
331 (void) sprintf(path, USR_PROG_PATH, homepath);
332 if (LSTAT(path, &stbuf) < 0) {
333 if (errno == ENOENT && !util_mkdir(path, 0755)) {
334 DBGPRN(DBG_GEN)(errfp,
335 "cd_mkdirs: cannot mkdir %s.\n", path);
336 }
337 else {
338 DBGPRN(DBG_GEN)(errfp,
339 "cd_mkdirs: cannot stat %s.\n", path);
340 }
341 }
342 else if (!S_ISDIR(stbuf.st_mode)) {
343 DBGPRN(DBG_GEN)(errfp,
344 "cd_mkdirs: %s is not a directory.\n", path);
345 }
346
347 _exit(0);
348 /*NOTREACHED*/
349
350 case -1:
351 /* fork failed */
352 break;
353
354 default:
355 /* Parent: wait for child to finish */
356 (void) util_waitchild(cpid, app_data.srv_timeout + 5,
357 NULL, 0, FALSE, &wstat);
358 break;
359 }
360 }
361
362
363 /*
364 * cda_strcpy
365 * Similar to strcpy(3), but skips some punctuation characters.
366 *
367 * Args:
368 * tgt - Target string buffer
369 * src - Source string buffer
370 *
371 * Return:
372 * The number of characters copied
373 */
374 STATIC size_t
cda_strcpy(char * tgt,char * src)375 cda_strcpy(char *tgt, char *src)
376 {
377 size_t n = 0;
378 bool_t prev_space = FALSE;
379
380 if (src == NULL)
381 return 0;
382
383 for (; *src != '\0'; src++) {
384 switch (*src) {
385 case '\'':
386 case '"':
387 case ',':
388 case ':':
389 case ';':
390 case '|':
391 /* Skip some punctuation characters */
392 break;
393
394 case ' ':
395 case '\t':
396 case '/':
397 case '\\':
398 /* Substitute these with underscores */
399 if (!prev_space) {
400 *tgt++ = app_data.subst_underscore ? '_' : ' ';
401 n++;
402 prev_space = TRUE;
403 }
404 break;
405
406 default:
407 if (!isprint((int) *src))
408 /* Skip unprintable characters */
409 break;
410
411 *tgt++ = *src;
412 n++;
413 prev_space = FALSE;
414 break;
415 }
416 }
417 *tgt = '\0';
418 return (n);
419 }
420
421
422 /*
423 * cda_unlink
424 * Unlink the specified file
425 *
426 * Args:
427 * path - The file path to unlink
428 *
429 * Return:
430 * TRUE - success
431 * FALSE - failure
432 */
433 STATIC bool_t
cda_unlink(char * path)434 cda_unlink(char *path)
435 {
436 pid_t cpid;
437 int ret,
438 wstat;
439 bool_t success;
440
441 if (path == NULL)
442 return FALSE;
443
444 switch (cpid = FORK()) {
445 case -1:
446 DBGPRN(DBG_GEN)(errfp,
447 "cda_unlink: fork failed (errno=%d)\n",
448 errno);
449 success = FALSE;
450 break;
451
452 case 0:
453 util_set_ougid();
454
455 DBGPRN(DBG_GEN)(errfp, "Unlinking [%s]\n", path);
456
457 if ((ret = UNLINK(path)) != 0 && errno == ENOENT)
458 ret = 0;
459
460 if (ret != 0) {
461 DBGPRN(DBG_GEN)(errfp,
462 "unlink failed (errno=%d)\n",
463 errno);
464 }
465 _exit((int) (ret != 0));
466 /*NOTREACHED*/
467
468 default:
469 /* Parent: wait for child to finish */
470 ret = util_waitchild(cpid, app_data.srv_timeout + 5,
471 NULL, 0, FALSE, &wstat);
472 if (ret < 0) {
473 DBGPRN(DBG_GEN)(errfp,
474 "waitpid failed (errno=%d)\n", errno);
475 success = FALSE;
476 }
477 else if (WIFEXITED(wstat)) {
478 if ((ret = WEXITSTATUS(wstat)) != 0) {
479 DBGPRN(DBG_GEN)(errfp,
480 "unlink child exited (status=%d)\n",
481 ret);
482 }
483 success = (bool_t) (ret == 0);
484 }
485 else if (WIFSIGNALED(wstat)) {
486 DBGPRN(DBG_GEN)(errfp,
487 "unlink child killed (signal=%d)\n",
488 WTERMSIG(wstat));
489 success = FALSE;
490 }
491 else {
492 DBGPRN(DBG_GEN)(errfp, "unlink child error\n");
493 success = FALSE;
494 }
495 break;
496 }
497
498 return (success);
499 }
500
501
502 /*
503 * cda_mkfname
504 * Construct a file name based on the supplied template, and append
505 * it to the target string.
506 *
507 * Args:
508 * s - Pointer to the curstat_t structure
509 * trkidx - Track index number (0-based), or -1 if not applicable
510 * tgt - The target string
511 * tmpl - The template string
512 * tgtlen - Target string buffer size
513 *
514 * Returns:
515 * TRUE - success
516 * FALSE - failure
517 */
518 STATIC bool_t
cda_mkfname(curstat_t * s,int trkidx,char * tgt,char * tmpl,int tgtlen)519 cda_mkfname(curstat_t *s, int trkidx, char *tgt, char *tmpl, int tgtlen)
520 {
521 int len,
522 n,
523 remain;
524 char *cp,
525 *cp2,
526 *p,
527 tmp[(STR_BUF_SZ * 4) + 4];
528 bool_t err;
529
530 err = FALSE;
531 n = 0;
532 len = strlen(tgt);
533 cp = tgt + len;
534
535 for (cp2 = tmpl; *cp2 != '\0'; cp2++, cp += n, len += n) {
536 if ((remain = (tgtlen - len)) <= 0)
537 break;
538
539 switch (*cp2) {
540 case '%':
541 switch (*(++cp2)) {
542 case 'X':
543 n = strlen(PROGNAME);
544 if (n < remain)
545 (void) strcpy(cp, PROGNAME);
546 else
547 err = TRUE;
548 break;
549
550 case 'V':
551 n = strlen(VERSION_MAJ) +
552 strlen(VERSION_MIN) + 1;
553 if (n < remain)
554 (void) sprintf(cp, "%s.%s",
555 VERSION_MAJ,
556 VERSION_MIN);
557 else
558 err = TRUE;
559 break;
560
561 case 'N':
562 p = util_loginname();
563 n = strlen(p);
564 if (n < remain)
565 (void) strcpy(cp, p);
566 else
567 err = TRUE;
568 break;
569
570 case '~':
571 p = util_homedir(util_get_ouid());
572 n = strlen(p);
573 if (n < remain)
574 (void) strcpy(cp, p);
575 else
576 err = TRUE;
577 break;
578
579 case 'H':
580 p = util_get_uname()->nodename;
581 n = strlen(p);
582 if (n < remain)
583 (void) strcpy(cp, p);
584 else
585 err = TRUE;
586 break;
587
588 case 'L':
589 p = app_data.libdir;
590 n = strlen(p);
591 if (n < remain)
592 (void) strcpy(cp, p);
593 else
594 err = TRUE;
595 break;
596
597 case 'S':
598 p = app_data.libdir;
599 n = strlen(p) + strlen("discog") + 1;
600 if (n < remain) {
601 (void) strcpy(cp, p);
602 (void) strcat(cp, "/discog");
603 }
604 else
605 err = TRUE;
606 break;
607
608 case 'C':
609 p = cdinfo_genre_path(dbp->disc.genre);
610 n = strlen(p);
611 if (n < remain)
612 (void) strcpy(cp, p);
613 else
614 err = TRUE;
615 break;
616
617 case 'I':
618 n = 8;
619 if (n < remain)
620 (void) sprintf(cp, "%08x",
621 dbp->discid);
622 else
623 err = TRUE;
624 break;
625
626 case 'A':
627 case 'a':
628 p = dbp->disc.artist;
629 if (p == NULL)
630 p = "artist";
631 if (*cp2 == 'a') {
632 p = util_text_reduce(p);
633 if (p == NULL) {
634 CDA_FATAL(app_data.str_nomemory);
635 return FALSE;
636 }
637 }
638
639 n = strlen(p);
640 if (n < remain)
641 n = cda_strcpy(cp, p);
642 else
643 err = TRUE;
644
645 if (*cp2 == 'a')
646 MEM_FREE(p);
647 break;
648
649 case 'D':
650 case 'd':
651 p = dbp->disc.title;
652 if (p == NULL)
653 p = "title";
654 if (*cp2 == 'd') {
655 p = util_text_reduce(p);
656 if (p == NULL) {
657 CDA_FATAL(app_data.str_nomemory);
658 return FALSE;
659 }
660 }
661
662 n = strlen(p);
663 if (n < remain)
664 n = cda_strcpy(cp, p);
665 else
666 err = TRUE;
667
668 if (*cp2 == 'd')
669 MEM_FREE(p);
670 break;
671
672 case 'R':
673 case 'r':
674 p = dbp->track[trkidx].artist;
675 if (p == NULL)
676 p = "trackartist";
677 if (*cp2 == 'r') {
678 p = util_text_reduce(p);
679 if (p == NULL) {
680 CDA_FATAL(app_data.str_nomemory);
681 return FALSE;
682 }
683 }
684
685 n = strlen(p);
686 if (n < remain)
687 n = cda_strcpy(cp, p);
688 else
689 err = TRUE;
690
691 if (*cp2 == 'r')
692 MEM_FREE(p);
693 break;
694
695 case 'T':
696 case 't':
697 p = dbp->track[trkidx].title;
698 if (p == NULL)
699 p = "track";
700 if (*cp2 == 't') {
701 p = util_text_reduce(p);
702 if (p == NULL) {
703 CDA_FATAL(app_data.str_nomemory);
704 return FALSE;
705 }
706 }
707
708 n = strlen(p);
709 if (n < remain)
710 n = cda_strcpy(cp, p);
711 else
712 err = TRUE;
713
714 if (*cp2 == 't')
715 MEM_FREE(p);
716 break;
717
718 case 'B':
719 case 'b':
720 (void) sprintf(tmp, "%.127s%s%.127s",
721 (dbp->disc.artist == NULL) ?
722 "artist" : dbp->disc.artist,
723 (dbp->disc.artist != NULL &&
724 dbp->disc.title != NULL) ?
725 "-" : "",
726 (dbp->disc.title == NULL) ?
727 "title" : dbp->disc.title);
728 p = tmp;
729 if (*cp2 == 'b') {
730 p = util_text_reduce(p);
731 if (p == NULL) {
732 CDA_FATAL(app_data.str_nomemory);
733 return FALSE;
734 }
735 }
736
737 n = strlen(p);
738 if (n < remain)
739 n = cda_strcpy(cp, p);
740 else
741 err = TRUE;
742
743 if (*cp2 == 'b')
744 MEM_FREE(p);
745 break;
746
747 case '#':
748 n = 2;
749 if (n < remain)
750 (void) sprintf(cp, "%02d",
751 s->trkinfo[trkidx].trkno);
752 else
753 err = TRUE;
754 break;
755
756 case '%':
757 case ' ':
758 case '\t':
759 case '\'':
760 case '"':
761 case ',':
762 case ';':
763 case ':':
764 case '|':
765 case '\\':
766 /* Skip some punctuation characters */
767 n = 1;
768 if (n < remain)
769 *cp = '%';
770 else
771 err = TRUE;
772 break;
773
774 default:
775 n = 2;
776 if (n < remain)
777 *cp = '%';
778 else
779 err = TRUE;
780 break;
781 }
782 break;
783
784 case ' ':
785 case '\t':
786 n = 1;
787 if (n < remain) {
788 if (app_data.subst_underscore)
789 *cp = '_';
790 else
791 *cp = ' ';
792 }
793 else
794 err = TRUE;
795 break;
796
797 case '\'':
798 case '"':
799 case ',':
800 case ';':
801 case ':':
802 case '|':
803 case '\\':
804 /* Skip some punctuation characters */
805 n = 0;
806 break;
807
808 default:
809 if (!isprint((int) *cp2)) {
810 /* Skip unprintable characters */
811 n = 0;
812 break;
813 }
814
815 n = 1;
816 if (n < remain)
817 *cp = *cp2;
818 else
819 err = TRUE;
820 break;
821 }
822
823 if (err)
824 break;
825 }
826 *cp = '\0';
827
828 if (err) {
829 (void) fprintf(errfp, "%s\n", app_data.str_longpatherr);
830 return FALSE;
831 }
832
833 return TRUE;
834 }
835
836
837 /*
838 * cda_mkoutpath
839 * Construct audio output file names and check for collision
840 *
841 * Args:
842 * s - Pointer to the curstat_t structure
843 *
844 * Return
845 * TRUE - passed
846 * FALSE - failed
847 */
848 STATIC bool_t
cda_mkoutpath(curstat_t * s)849 cda_mkoutpath(curstat_t *s)
850 {
851 char *dpath = NULL,
852 *path;
853 int i,
854 j;
855 struct stat stbuf;
856
857 if (s->outf_tmpl == NULL) {
858 (void) fprintf(errfp, "%s\n", app_data.str_noaudiopath);
859 return FALSE;
860 }
861
862 if (app_data.cdda_trkfile) {
863 /* Normal play, program, shuffle and sample modes */
864 for (i = 0; i < (int) s->tot_trks; i++) {
865 if (s->program) {
866 bool_t inprog;
867
868 inprog = FALSE;
869 for (j = 0; j < (int) s->prog_tot; j++) {
870 if (i == s->trkinfo[j].playorder) {
871 inprog = TRUE;
872 break;
873 }
874 }
875
876 if (!inprog)
877 /* This track is not in the program */
878 continue;
879 }
880 else if (!s->shuffle &&
881 s->cur_trk > s->trkinfo[i].trkno) {
882 continue;
883 }
884
885 path = (char *) MEM_ALLOC("path", FILE_PATH_SZ);
886 if (path == NULL) {
887 CDA_FATAL(app_data.str_nomemory);
888 return FALSE;
889 }
890 path[0] = '\0';
891
892 if (!cda_mkfname(s, i, path, s->outf_tmpl,
893 FILE_PATH_SZ)) {
894 MEM_FREE(path);
895 return FALSE;
896 }
897
898 if (!util_newstr(&s->trkinfo[i].outfile, path)) {
899 MEM_FREE(path);
900 CDA_FATAL(app_data.str_nomemory);
901 return FALSE;
902 }
903
904 if (LSTAT(path, &stbuf) == 0 &&
905 (!S_ISREG(stbuf.st_mode) || !cda_unlink(path))) {
906 (void) fprintf(errfp, "%s: %s\n",
907 path,
908 app_data.str_audiopathexists);
909 MEM_FREE(path);
910 return FALSE;
911 }
912
913 if (dpath == NULL &&
914 (dpath = util_dirname(path)) == NULL) {
915 MEM_FREE(path);
916 CDA_FATAL("File dirname error");
917 return FALSE;
918 }
919
920 MEM_FREE(path);
921 }
922
923 if (dpath != NULL) {
924 /* Show output directory path */
925 (void) printf("%s: %s\n",
926 "CDDA save-to-file output directory",
927 dpath
928 );
929 (void) fflush(stdout);
930 util_delayms(2000);
931
932 MEM_FREE(dpath);
933 }
934 }
935 else {
936 path = (char *) MEM_ALLOC("path", FILE_PATH_SZ);
937 if (path == NULL) {
938 CDA_FATAL(app_data.str_nomemory);
939 return FALSE;
940 }
941 path[0] = '\0';
942
943 if (!cda_mkfname(s, -1, path, s->outf_tmpl, FILE_PATH_SZ)) {
944 MEM_FREE(path);
945 return FALSE;
946 }
947
948 if (!util_newstr(&s->trkinfo[0].outfile, path)) {
949 MEM_FREE(path);
950 CDA_FATAL(app_data.str_nomemory);
951 return FALSE;
952 }
953
954 if (LSTAT(path, &stbuf) == 0 &&
955 (!S_ISREG(stbuf.st_mode) || !cda_unlink(path))) {
956 (void) fprintf(errfp, "%s: %s\n",
957 path, app_data.str_audiopathexists);
958 MEM_FREE(path);
959 return FALSE;
960 }
961
962 /* Show output directory path */
963 if ((dpath = util_dirname(path)) == NULL) {
964 MEM_FREE(path);
965 CDA_FATAL("File dirname error");
966 return FALSE;
967 }
968
969 (void) printf("%s: %s\n",
970 "CDDA save-to-file output directory",
971 dpath
972 );
973 (void) fflush(stdout);
974
975 MEM_FREE(dpath);
976 MEM_FREE(path);
977
978 util_delayms(2000);
979 }
980
981 return TRUE;
982 }
983
984
985 /*
986 * cda_ckpipeprog
987 * Check pipe to program path
988 *
989 * Args:
990 * s - Pointer to the curstat_t structure
991 *
992 * Return
993 * TRUE - passed
994 * FALSE - failed
995 */
996 /*ARGSUSED*/
997 STATIC bool_t
cda_ckpipeprog(curstat_t * s)998 cda_ckpipeprog(curstat_t *s)
999 {
1000 char *str;
1001
1002 if (app_data.pipeprog == NULL) {
1003 (void) fprintf(errfp, "%s\n", app_data.str_noprog);
1004 return FALSE;
1005 }
1006
1007 if (!util_checkcmd(app_data.pipeprog)) {
1008 str = (char *) MEM_ALLOC("str",
1009 strlen(app_data.pipeprog) +
1010 strlen(app_data.str_cannotinvoke) + 8
1011 );
1012 if (str == NULL) {
1013 CDA_FATAL(app_data.str_nomemory);
1014 return FALSE;
1015 }
1016
1017 (void) sprintf(str, app_data.str_cannotinvoke,
1018 app_data.pipeprog);
1019 (void) fprintf(errfp, "%s\n", str);
1020
1021 MEM_FREE(str);
1022 return FALSE;
1023 }
1024
1025 return TRUE;
1026 }
1027
1028
1029 /*
1030 * cda_fix_outfile_path
1031 * Fix the CDDA audio output file path to make sure there is a proper
1032 * suffix based on the file format. Also, replace white spaces in
1033 * the file path string with underscores.
1034 *
1035 * Args:
1036 * s - Pointer to the curstat_t structure.
1037 *
1038 * Return:
1039 * Nothing.
1040 */
1041 void
cda_fix_outfile_path(curstat_t * s)1042 cda_fix_outfile_path(curstat_t *s)
1043 {
1044 filefmt_t *fmp;
1045 char *suf,
1046 *cp,
1047 tmp[FILE_PATH_SZ * 2 + 8];
1048
1049 /* File suffix */
1050 if ((fmp = cdda_filefmt(app_data.cdda_filefmt)) == NULL)
1051 suf = "";
1052 else
1053 suf = fmp->suf;
1054
1055 if (s->outf_tmpl == NULL) {
1056 /* No default outfile, set it */
1057 (void) sprintf(tmp,
1058 "%%S/%%C/%%I/%s%s",
1059 app_data.cdda_trkfile ? FILEPATH_TRACK : FILEPATH_DISC,
1060 suf
1061 );
1062
1063 if (!util_newstr(&s->outf_tmpl, tmp)) {
1064 CDA_FATAL(app_data.str_nomemory);
1065 return;
1066 }
1067 }
1068 else if ((cp = strrchr(s->outf_tmpl, '.')) != NULL) {
1069 char *cp2 = strrchr(s->outf_tmpl, DIR_END);
1070
1071 if (cp2 == NULL || cp > cp2) {
1072 /* Change suffix if necessary */
1073 if (strcmp(cp, suf) != 0) {
1074 *cp = '\0';
1075 (void) strncpy(tmp, s->outf_tmpl,
1076 FILE_PATH_SZ - strlen(suf) - 1);
1077 (void) strcat(tmp, suf);
1078
1079 if (!util_newstr(&s->outf_tmpl, tmp)) {
1080 CDA_FATAL(app_data.str_nomemory);
1081 return;
1082 }
1083 }
1084 }
1085 else {
1086 /* No suffix, add one */
1087 (void) strncpy(tmp, s->outf_tmpl,
1088 FILE_PATH_SZ - strlen(suf) - 1);
1089 (void) strcat(tmp, suf);
1090
1091 if (!util_newstr(&s->outf_tmpl, tmp)) {
1092 CDA_FATAL(app_data.str_nomemory);
1093 return;
1094 }
1095 }
1096 }
1097 else {
1098 /* No suffix, add one */
1099 (void) strncpy(tmp, s->outf_tmpl,
1100 FILE_PATH_SZ - strlen(suf) - 1);
1101 (void) strcat(tmp, suf);
1102
1103 if (!util_newstr(&s->outf_tmpl, tmp)) {
1104 CDA_FATAL(app_data.str_nomemory);
1105 return;
1106 }
1107 }
1108 }
1109
1110
1111 /*
1112 * cda_hist_new
1113 * Add current CD to the history list.
1114 *
1115 * Args:
1116 * s - Pointer to the curstat_t structure.
1117 *
1118 * Return:
1119 * Nothing.
1120 */
1121 STATIC void
cda_hist_new(curstat_t * s)1122 cda_hist_new(curstat_t *s)
1123 {
1124 cdinfo_dlist_t h;
1125
1126 if (dbp->discid == 0)
1127 /* Don't add to history if no disc */
1128 return;
1129
1130 h.device = s->curdev;
1131 h.discno = (int) s->cur_disc;
1132 h.type = ((dbp->flags & CDINFO_MATCH) == 0) ? 0 :
1133 ((dbp->flags & (CDINFO_FROMLOC | CDINFO_FROMCDT)) ?
1134 CDINFO_DLIST_LOCAL : CDINFO_DLIST_REMOTE);
1135 h.discid = dbp->discid;
1136 h.genre = dbp->disc.genre;
1137 h.artist = dbp->disc.artist;
1138 h.title = dbp->disc.title;
1139 h.time = time(NULL);
1140
1141 /* Add to in-core history list */
1142 (void) cdinfo_hist_addent(&h, TRUE);
1143 }
1144
1145
1146 /*
1147 * cda_dbget
1148 * Load the CD information pertaining to the current disc, if available.
1149 * This is for use by the cda daemon, and only as a callback from the
1150 * libdi module. Its sole purpose is to allow the daemon to properly
1151 * expand the CDDA save-to-file output path names, and to do the
1152 * initial connection to the MOTD service. Otherwise the daemon
1153 * doesn't really need the CD information to operate.
1154 *
1155 * Args:
1156 * s - Pointer to the curstat_t structure
1157 *
1158 * Return:
1159 * Nothing.
1160 */
1161 STATIC void
cda_dbget(curstat_t * s)1162 cda_dbget(curstat_t *s)
1163 {
1164 cdinfo_ret_t ret;
1165 static bool_t do_motd = TRUE;
1166
1167 /* Clear the in-core entry */
1168 cda_dbclear(s, TRUE);
1169
1170 /* Load user-defined track program, if available */
1171 if ((ret = cdinfo_load_prog(s)) != 0) {
1172 DBGPRN(DBG_CDI)(errfp, "cdinfo_load_prog: status=%d arg=%d\n",
1173 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
1174 }
1175 else
1176 s->program = TRUE;
1177
1178 /* Parse play order string and set the play order */
1179 (void) cda_pgm_parse(s);
1180
1181 /* Load CD information */
1182 if ((ret = cdinfo_load(s)) != 0) {
1183 DBGPRN(DBG_CDI)(errfp, "cdinfo_load: status=%d arg=%d\n",
1184 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
1185 }
1186
1187 switch (CDINFO_GET_STAT(ret)) {
1188 case 0:
1189 /* Success */
1190 if (dbp->matchlist != NULL) {
1191 cdinfo_match_t *mp;
1192 int n = 0;
1193
1194 for (mp = dbp->matchlist; mp != NULL; mp = mp->next)
1195 n++;
1196
1197 if (n != 1) {
1198 /* Note: The daemon cannot invoke a dialog
1199 * with the user to select from multiple
1200 * matches, so we do the next best thing
1201 * and just select the first one. There is
1202 * a chance that it's wrong, but it is better
1203 * than just failing every time. We display
1204 * a warning to notify the user.
1205 */
1206 CDA_WARNING("CDDB has located multiple "
1207 "matches for this CD. The first "
1208 "match is being used for the CDDA "
1209 "output file path name expansion.");
1210 }
1211
1212 dbp->match_tag = (long) 1;
1213
1214 /* Load CD information */
1215 if ((ret = cdinfo_load_matchsel(s)) != 0) {
1216 /* Failed to load */
1217 DBGPRN(DBG_CDI)(errfp,
1218 "\ncdinfo_load_matchsel (daemon): "
1219 "status=%d arg=%d\n",
1220 CDINFO_GET_STAT(ret),
1221 CDINFO_GET_ARG(ret));
1222 s->qmode = QMODE_NONE;
1223 }
1224 else if ((dbp->flags & CDINFO_MATCH) != 0)
1225 /* Exact match now */
1226 s->qmode = QMODE_MATCH;
1227 else
1228 s->qmode = QMODE_NONE;
1229 }
1230 else if ((dbp->flags & CDINFO_NEEDREG) != 0)
1231 /* User not registered with CDDB.
1232 * Note: The daemon cannot invoke a dialog to let
1233 * the user register with CDDB, but this should have
1234 * been done on the client side already. If it's
1235 * not done, just fail.
1236 */
1237 s->qmode = QMODE_NONE;
1238 else if ((dbp->flags & CDINFO_MATCH) != 0)
1239 /* Exact match found */
1240 s->qmode = QMODE_MATCH;
1241 else
1242 /* No match found */
1243 s->qmode = QMODE_NONE;
1244
1245 /* Get MOTD: do this automatically just once if configured */
1246 if (!app_data.automotd_dsbl &&
1247 !app_data.cdinfo_inetoffln && do_motd) {
1248 do_motd = FALSE;
1249 motd_get(NULL);
1250 }
1251
1252 break;
1253
1254 case AUTH_ERR:
1255 /* Authorization failed.
1256 * Note: The daemon cannot invoke a dialog with the user
1257 * to get the login and password, so all we could do is
1258 * fail here. A client should have passed the info
1259 * to us if it had asked the user once, but it's possible
1260 * that the client hasn't done a CDDB lookup yet.
1261 */
1262 s->qmode = QMODE_NONE;
1263 break;
1264
1265 default:
1266 /* Query error */
1267 s->qmode = QMODE_NONE;
1268 break;
1269 }
1270
1271 /* Update CD history */
1272 cda_hist_new(s);
1273 }
1274
1275
1276 /*
1277 * cda_init
1278 * Initialize the CD interface subsystem.
1279 *
1280 * Args:
1281 * s - Pointer to the curstat_t structure.
1282 *
1283 * Return:
1284 * Nothing.
1285 */
1286 STATIC void
cda_init(curstat_t * s)1287 cda_init(curstat_t *s)
1288 {
1289 char *bdevname,
1290 path[FILE_PATH_SZ * 2];
1291
1292 /* Set address to cdinfo incore structure */
1293 dbp = cdinfo_addr();
1294
1295 if ((bdevname = util_basename(app_data.device)) == NULL)
1296 CDA_FATAL("Device path basename error: Aborting.");
1297
1298 /* Get system-wide device-specific configuration parameters */
1299 (void) sprintf(path, SYS_DSCFG_PATH, app_data.libdir, bdevname);
1300 di_devspec_parmload(path, TRUE, FALSE);
1301
1302 /* Get user device-specific configuration parameters */
1303 (void) sprintf(path, USR_DSCFG_PATH, util_homedir(util_get_ouid()),
1304 bdevname);
1305 di_devspec_parmload(path, FALSE, FALSE);
1306
1307 MEM_FREE(bdevname);
1308
1309 /* Initialize CD info */
1310 cda_dbclear(s, FALSE);
1311
1312 if (isdaemon) {
1313 /* Initialize CD history mechanism */
1314 cdinfo_hist_init();
1315
1316 /* Initialize the CD hardware */
1317 di_cldata.curstat_addr = curstat_addr;
1318 di_cldata.mkoutpath = cda_mkoutpath;
1319 di_cldata.ckpipeprog = cda_ckpipeprog;
1320 di_cldata.quit = cda_quit;
1321 di_cldata.dbclear = cda_dbclear;
1322 di_cldata.dbget = cda_dbget;
1323 di_cldata.fatal_msg = cda_fatal_msg;
1324 di_cldata.warning_msg = cda_warning_msg;
1325 di_cldata.info_msg = cda_info_msg;
1326 di_init(&di_cldata);
1327
1328 /* Set default modes */
1329 di_repeat(s, app_data.repeat_mode);
1330 di_shuffle(s, app_data.shuffle_mode);
1331
1332 /* Set default output file format and path */
1333 if (!cdda_filefmt_supp(app_data.cdda_filefmt)) {
1334 /* Specified format not supported: force to RAW */
1335 (void) fprintf(errfp,
1336 "The cddaFileFormat (%d) is not supported: "
1337 "reset to RAW\n", app_data.cdda_filefmt);
1338 app_data.cdda_filefmt = FILEFMT_RAW;
1339 }
1340 if (app_data.cdda_tmpl != NULL &&
1341 !util_newstr(&s->outf_tmpl, app_data.cdda_tmpl)) {
1342 (void) fprintf(errfp,
1343 "%s: Aborting.\n", app_data.str_nomemory);
1344 exit_status = 1;
1345 cda_quit(s);
1346 /*NOTREACHED*/
1347 }
1348 cda_fix_outfile_path(s);
1349
1350 /* Play mode and capabilities */
1351 if ((di_cldata.capab & CAPAB_PLAYAUDIO) == 0) {
1352 (void) fprintf(errfp,
1353 "No CD playback capability. Aborting.\n");
1354 exit_status = 1;
1355 cda_quit(s);
1356 /*NOTREACHED*/
1357 }
1358
1359 if (PLAYMODE_IS_CDDA(app_data.play_mode)) {
1360 if ((di_cldata.capab & CAPAB_RDCDDA) == 0) {
1361 app_data.play_mode = PLAYMODE_STD;
1362 }
1363 else {
1364 if ((di_cldata.capab & CAPAB_WRDEV) == 0)
1365 app_data.play_mode &= ~PLAYMODE_CDDA;
1366 if ((di_cldata.capab & CAPAB_WRFILE) == 0)
1367 app_data.play_mode &= ~PLAYMODE_FILE;
1368 if ((di_cldata.capab & CAPAB_WRPIPE) == 0)
1369 app_data.play_mode &= ~PLAYMODE_PIPE;
1370 }
1371
1372 /* Set the default play mode */
1373 if (PLAYMODE_IS_CDDA(app_data.play_mode) &&
1374 !di_playmode(s)) {
1375 app_data.play_mode = PLAYMODE_STD;
1376 }
1377
1378 if (PLAYMODE_IS_STD(app_data.play_mode)) {
1379 (void) fprintf(errfp,
1380 "Reverted to standard playback mode.\n");
1381 }
1382 }
1383 }
1384 else
1385 di_reset_curstat(s, TRUE, TRUE);
1386
1387 /* Force CD-TEXT config to False if it doesn't appear in the
1388 * CD info path list.
1389 */
1390 if (!cdinfo_cdtext_iscfg())
1391 app_data.cdtext_dsbl = TRUE;
1392 }
1393
1394
1395 /*
1396 * cda_start
1397 * Secondary startup functions.
1398 *
1399 * Args:
1400 * s - Pointer to the curstat_t structure.
1401 *
1402 * Return:
1403 * Nothing.
1404 */
1405 STATIC void
cda_start(curstat_t * s)1406 cda_start(curstat_t *s)
1407 {
1408 struct stat stbuf;
1409
1410 /* Start up libutil */
1411 util_start();
1412
1413 /* Start up I/O interface */
1414 di_start(s);
1415
1416 /* Open FIFOs - daemon side */
1417 cda_sfd[0] = open(spipe, O_RDONLY
1418 #ifdef O_NOFOLLOW
1419 | O_NOFOLLOW
1420 #endif
1421 );
1422 if (cda_sfd[0] < 0) {
1423 perror("CD audio daemon: cannot open send pipe");
1424 exit_status = 4;
1425 cda_quit(s);
1426 /*NOTREACHED*/
1427 }
1428 cda_rfd[0] = open(rpipe, O_WRONLY
1429 #ifdef O_NOFOLLOW
1430 | O_NOFOLLOW
1431 #endif
1432 );
1433 if (cda_rfd[0] < 0) {
1434 perror("CD audio daemon: cannot open recv pipe");
1435 exit_status = 4;
1436 cda_quit(s);
1437 /*NOTREACHED*/
1438 }
1439
1440 /* Check FIFOs */
1441 if (fstat(cda_sfd[0], &stbuf) < 0 || !S_ISFIFO(stbuf.st_mode)) {
1442 (void) fprintf(errfp,
1443 "CD audio daemon: Send pipe error: Not a FIFO\n");
1444 di_halt(s);
1445 /* Remove lock */
1446 if (dlock[0] != '\0')
1447 (void) UNLINK(dlock);
1448 _exit(4);
1449 }
1450 if (fstat(cda_rfd[0], &stbuf) < 0 || !S_ISFIFO(stbuf.st_mode)) {
1451 (void) fprintf(errfp,
1452 "CD audio daemon: Recv pipe error: Not a FIFO\n");
1453 di_halt(s);
1454 /* Remove lock */
1455 if (dlock[0] != '\0')
1456 (void) UNLINK(dlock);
1457 _exit(4);
1458 }
1459 }
1460
1461
1462 /*
1463 * cda_daemonlock
1464 * Check and set a lock to prevent multiple CD audio daemon
1465 * processes.
1466 *
1467 * Args:
1468 * None.
1469 *
1470 * Return:
1471 * TRUE - Lock successful
1472 * FALSE - Daemon already running
1473 */
1474 STATIC bool_t
cda_daemonlock(void)1475 cda_daemonlock(void)
1476 {
1477 int fd;
1478 pid_t pid,
1479 mypid;
1480 char buf[32];
1481
1482 (void) sprintf(dlock, "%s/cdad.%x", TEMP_DIR, (int) cd_rdev);
1483 mypid = getpid();
1484
1485 for (;;) {
1486 fd = open(dlock, O_CREAT | O_EXCL | O_WRONLY);
1487 if (fd < 0) {
1488 if (errno == EEXIST) {
1489 fd = open(dlock, O_RDONLY
1490 #ifdef O_NOFOLLOW
1491 | O_NOFOLLOW
1492 #endif
1493 );
1494 if (fd < 0)
1495 return FALSE;
1496
1497 if (read(fd, buf, 32) > 0) {
1498 if (strncmp(buf, "cdad ", 5) != 0) {
1499 (void) close(fd);
1500 return FALSE;
1501 }
1502 pid = (pid_t) atoi(buf + 5);
1503 }
1504 else {
1505 (void) close(fd);
1506 return FALSE;
1507 }
1508
1509 (void) close(fd);
1510
1511 if (pid == mypid)
1512 /* Our own lock */
1513 return TRUE;
1514
1515 if (pid <= 0 ||
1516 (kill(pid, 0) < 0 && errno == ESRCH)) {
1517 /* Pid died, steal its lockfile */
1518 (void) UNLINK(dlock);
1519 }
1520 else {
1521 /* Pid still running: clash */
1522 return FALSE;
1523 }
1524 }
1525 else
1526 return FALSE;
1527 }
1528 else {
1529 (void) sprintf(buf, "cdad %d\n", (int) mypid);
1530 (void) write(fd, buf, strlen(buf));
1531
1532 #ifdef NO_FCHMOD
1533 (void) close(fd);
1534 (void) chmod(dlock, 0644);
1535 #else
1536 (void) fchmod(fd, 0644);
1537 (void) close(fd);
1538 #endif
1539
1540 return TRUE;
1541 }
1542 }
1543 }
1544
1545
1546 /*
1547 * cda_poll
1548 * Periodic polling function - used to manage program, shuffle,
1549 * and repeat modes.
1550 *
1551 * Args:
1552 * s - Pointer to the curstat_t structure.
1553 *
1554 * Return:
1555 * Nothing.
1556 */
1557 STATIC void
cda_poll(curstat_t * s)1558 cda_poll(curstat_t *s)
1559 {
1560 static int n = 0;
1561
1562 if (++n > 100)
1563 n = 0;
1564
1565 if (n % 3)
1566 return;
1567
1568 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY ||
1569 (s->mode == MOD_STOP && app_data.load_play))
1570 (void) di_check_disc(s);
1571 else if (s->mode != MOD_STOP && s->mode != MOD_PAUSE &&
1572 s->mode != MOD_NODISC)
1573 di_status_upd(s);
1574 }
1575
1576
1577 /*
1578 * cda_sendpkt
1579 * Write a CDA packet down the pipe.
1580 *
1581 * Args:
1582 * name - The text string describing the caller module
1583 * fd - Pipe file descriptor
1584 * s - Pointer to the packet data
1585 *
1586 * Return:
1587 * TRUE - pipe write successful
1588 * FALSE - pipe write failed
1589 */
1590 STATIC bool_t
cda_sendpkt(char * name,int fd,cdapkt_t * s)1591 cda_sendpkt(char *name, int fd, cdapkt_t *s)
1592 {
1593 byte_t *p = (byte_t *) s;
1594 int i,
1595 ret;
1596
1597 if (fd < 0)
1598 return FALSE;
1599
1600 /* Brand packet with magic number */
1601 s->magic = CDA_MAGIC;
1602
1603 /* Set timeout */
1604 (void) util_signal(SIGALRM, onsig1);
1605 (void) alarm(PIPE_TIMEOUT);
1606
1607 /* Send a packet */
1608 i = sizeof(cdapkt_t);
1609 while ((ret = write(fd, p, i)) < i) {
1610 if (ret < 0) {
1611 switch (errno) {
1612 case EINTR:
1613 case EPIPE:
1614 case EBADF:
1615 (void) alarm(0);
1616
1617 if (isdaemon)
1618 return FALSE;
1619
1620 if (!cda_daemon_alive())
1621 return FALSE;
1622
1623 (void) util_signal(SIGALRM, onsig1);
1624 (void) alarm(PIPE_TIMEOUT);
1625 break;
1626 default:
1627 (void) alarm(0);
1628 (void) sprintf(errmsg,
1629 "%s: packet write error (errno=%d)\n",
1630 name, errno);
1631 CDA_WARNING(errmsg);
1632 return FALSE;
1633 }
1634 }
1635 else if (ret == 0) {
1636 (void) alarm(0);
1637 util_delayms(1000);
1638 (void) util_signal(SIGALRM, onsig1);
1639 (void) alarm(PIPE_TIMEOUT);
1640 }
1641 else {
1642 i -= ret;
1643 p += ret;
1644 }
1645 }
1646
1647 (void) alarm(0);
1648 return TRUE;
1649 }
1650
1651
1652 /*
1653 * cda_getpkt
1654 * Read a CDA packet from the pipe.
1655 *
1656 * Args:
1657 * name - The text string describing the caller module
1658 * fd - Pipe file descriptor
1659 * s - Pointer to the packet data
1660 *
1661 * Return:
1662 * TRUE - pipe read successful
1663 * FALSE - pipe read failed
1664 */
1665 STATIC bool_t
cda_getpkt(char * name,int fd,cdapkt_t * r)1666 cda_getpkt(char *name, int fd, cdapkt_t *r)
1667 {
1668 byte_t *p = (byte_t *) r;
1669 int i,
1670 ret;
1671
1672 if (fd < 0)
1673 return FALSE;
1674
1675 /* Get a packet */
1676 i = sizeof(cdapkt_t);
1677 while ((ret = read(fd, p, i)) < i) {
1678 if (ret < 0) {
1679 switch (errno) {
1680 case EINTR:
1681 case EPIPE:
1682 case EBADF:
1683 if (!isdaemon)
1684 return FALSE;
1685
1686 /* Use this occasion to perform
1687 * polling function
1688 */
1689 cda_poll(&status);
1690
1691 util_delayms(1000);
1692 break;
1693 default:
1694 (void) sprintf(errmsg,
1695 "%s: packet read error (errno=%d)\n",
1696 name, errno);
1697 CDA_WARNING(errmsg);
1698 return FALSE;
1699 }
1700 }
1701 else if (ret == 0) {
1702 if (isdaemon) {
1703 /* Use this occasion to
1704 * perform polling function
1705 */
1706 cda_poll(&status);
1707 }
1708
1709 util_delayms(1000);
1710 }
1711 else {
1712 i -= ret;
1713 p += ret;
1714 }
1715 }
1716
1717 /* Check packet for magic number */
1718 if (r->magic != CDA_MAGIC) {
1719 (void) sprintf(errmsg, "%s: bad packet magic number.", name);
1720 CDA_WARNING(errmsg);
1721 return FALSE;
1722 }
1723
1724 return TRUE;
1725 }
1726
1727
1728 /*
1729 * cda_do_onoff
1730 * Perform the "on" and "off" commands.
1731 *
1732 * Args:
1733 * s - Pointer to the curstat_t structure.
1734 * p - Pointer to the CDA packet structure.
1735 *
1736 * Return:
1737 * Nothing.
1738 */
1739 /*ARGSUSED*/
1740 STATIC void
cda_do_onoff(curstat_t * s,cdapkt_t * p)1741 cda_do_onoff(curstat_t *s, cdapkt_t *p)
1742 {
1743 p->arg[0] = getpid();
1744 }
1745
1746
1747 /*
1748 * cda_do_disc
1749 * Perform the "disc" command.
1750 *
1751 * Args:
1752 * s - Pointer to the curstat_t structure.
1753 * p - Pointer to the CDA packet structure.
1754 *
1755 * Return:
1756 * Nothing.
1757 */
1758 STATIC void
cda_do_disc(curstat_t * s,cdapkt_t * p)1759 cda_do_disc(curstat_t *s, cdapkt_t *p)
1760 {
1761 int i;
1762
1763 if (s->mode == MOD_BUSY)
1764 p->retcode = CDA_INVALID;
1765 else if (p->arg[0] == 0) {
1766 /* Load */
1767 if (s->mode == MOD_NODISC)
1768 di_load_eject(s);
1769 else
1770 p->retcode = CDA_INVALID;
1771 }
1772 else if (p->arg[0] == 1) {
1773 /* Eject */
1774 if (s->mode != MOD_NODISC)
1775 di_load_eject(s);
1776 else
1777 p->retcode = CDA_INVALID;
1778 }
1779 else if (p->arg[0] == 2) {
1780 /* Next */
1781 if (s->cur_disc < s->last_disc) {
1782 s->prev_disc = s->cur_disc;
1783 s->cur_disc++;
1784 di_chgdisc(s);
1785 }
1786 else
1787 p->retcode = CDA_INVALID;
1788 }
1789 else if (p->arg[0] == 3) {
1790 /* Prev */
1791 if (s->cur_disc > s->first_disc) {
1792 s->prev_disc = s->cur_disc;
1793 s->cur_disc--;
1794 di_chgdisc(s);
1795 }
1796 else
1797 p->retcode = CDA_INVALID;
1798 }
1799 else if (p->arg[0] == 4) {
1800 /* disc# */
1801 i = p->arg[1];
1802 if (i >= s->first_disc && i <= s->last_disc) {
1803 if (s->cur_disc != i) {
1804 s->prev_disc = s->cur_disc;
1805 s->cur_disc = i;
1806 di_chgdisc(s);
1807 }
1808 }
1809 else
1810 p->retcode = CDA_INVALID;
1811 }
1812 }
1813
1814
1815 /*
1816 * cda_do_lock
1817 * Perform the "lock" command.
1818 *
1819 * Args:
1820 * s - Pointer to the curstat_t structure.
1821 * p - Pointer to the CDA packet structure.
1822 *
1823 * Return:
1824 * Nothing.
1825 */
1826 STATIC void
cda_do_lock(curstat_t * s,cdapkt_t * p)1827 cda_do_lock(curstat_t *s, cdapkt_t *p)
1828 {
1829 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY)
1830 p->retcode = CDA_INVALID;
1831 else if (p->arg[0] == 0) {
1832 /* Unlock */
1833 if (s->caddy_lock) {
1834 s->caddy_lock = FALSE;
1835 di_lock(s, FALSE);
1836 }
1837 else
1838 p->retcode = CDA_INVALID;
1839 }
1840 else if (p->arg[0] == 1) {
1841 /* Lock */
1842 if (!s->caddy_lock) {
1843 s->caddy_lock = TRUE;
1844 di_lock(s, TRUE);
1845 }
1846 else
1847 p->retcode = CDA_INVALID;
1848 }
1849 else
1850 p->retcode = CDA_INVALID;
1851 }
1852
1853
1854 /*
1855 * cda_do_play
1856 * Perform the "play" command.
1857 *
1858 * Args:
1859 * s - Pointer to the curstat_t structure.
1860 * p - Pointer to the CDA packet structure.
1861 *
1862 * Return:
1863 * Nothing.
1864 */
1865 STATIC void
cda_do_play(curstat_t * s,cdapkt_t * p)1866 cda_do_play(curstat_t *s, cdapkt_t *p)
1867 {
1868 int i;
1869 sword32_t blkno;
1870 bool_t stopfirst;
1871
1872 switch (s->mode) {
1873 case MOD_PLAY:
1874 stopfirst = TRUE;
1875 break;
1876 case MOD_BUSY:
1877 case MOD_NODISC:
1878 p->retcode = CDA_INVALID;
1879 return;
1880 case MOD_STOP:
1881 default:
1882 stopfirst = FALSE;
1883
1884 if (p->arg[0] != 0)
1885 s->cur_trk = p->arg[0];
1886
1887 break;
1888 }
1889
1890 /* Starting track number */
1891 i = -1;
1892 if (p->arg[0] != 0) {
1893 if (s->shuffle || s->prog_cnt > 0) {
1894 p->retcode = CDA_INVALID;
1895 return;
1896 }
1897
1898 for (i = 0; i < (int) s->tot_trks; i++) {
1899 if (s->trkinfo[i].trkno == p->arg[0])
1900 break;
1901 }
1902
1903 if (i >= (int) s->tot_trks) {
1904 /* Invalid track specified */
1905 p->retcode = CDA_PARMERR;
1906 return;
1907 }
1908
1909 s->cur_trk = p->arg[0];
1910 }
1911
1912 if (stopfirst) {
1913 /* Stop current playback first */
1914 di_stop(s, FALSE);
1915
1916 /*
1917 * Restore s->cur_trk value because di_stop() zaps it
1918 */
1919 if (p->arg[0] != 0)
1920 s->cur_trk = p->arg[0];
1921 }
1922
1923 if (p->arg[0] != 0 && (int) p->arg[1] >= 0 && (int) p->arg[2] >= 0) {
1924 sword32_t eot;
1925
1926 /* Track offset specified */
1927 if (p->arg[2] > 59) {
1928 p->retcode = CDA_PARMERR;
1929 return;
1930 }
1931
1932 util_msftoblk(
1933 (byte_t) p->arg[1],
1934 (byte_t) p->arg[2],
1935 0,
1936 &blkno,
1937 0
1938 );
1939
1940 eot = s->trkinfo[i+1].addr;
1941
1942 /* "Enhanced CD" / "CD Extra" hack */
1943 if (eot > s->discpos_tot.addr)
1944 eot = s->discpos_tot.addr;
1945
1946 if (blkno >= (eot - s->trkinfo[i].addr)) {
1947 p->retcode = CDA_PARMERR;
1948 return;
1949 }
1950
1951 s->curpos_trk.addr = blkno;
1952 util_blktomsf(
1953 s->curpos_trk.addr,
1954 &s->curpos_trk.min,
1955 &s->curpos_trk.sec,
1956 &s->curpos_trk.frame,
1957 0
1958 );
1959
1960 s->curpos_tot.addr = s->trkinfo[i].addr + s->curpos_trk.addr;
1961 util_blktomsf(
1962 s->curpos_tot.addr,
1963 &s->curpos_tot.min,
1964 &s->curpos_tot.sec,
1965 &s->curpos_tot.frame,
1966 MSF_OFFSET
1967 );
1968 }
1969
1970 /* Start playback */
1971 di_play_pause(s);
1972 }
1973
1974
1975 /*
1976 * cda_do_pause
1977 * Perform the "pause" command.
1978 *
1979 * Args:
1980 * s - Pointer to the curstat_t structure.
1981 * p - Pointer to the CDA packet structure.
1982 *
1983 * Return:
1984 * Nothing.
1985 */
1986 STATIC void
cda_do_pause(curstat_t * s,cdapkt_t * p)1987 cda_do_pause(curstat_t *s, cdapkt_t *p)
1988 {
1989 if (s->mode == MOD_PLAY)
1990 di_play_pause(s);
1991 else
1992 p->retcode = CDA_INVALID;
1993 }
1994
1995
1996 /*
1997 * cda_do_stop
1998 * Perform the "stop" command.
1999 *
2000 * Args:
2001 * s - Pointer to the curstat_t structure.
2002 * p - Pointer to the CDA packet structure.
2003 *
2004 * Return:
2005 * Nothing.
2006 */
2007 STATIC void
cda_do_stop(curstat_t * s,cdapkt_t * p)2008 cda_do_stop(curstat_t *s, cdapkt_t *p)
2009 {
2010 if (s->mode == MOD_BUSY)
2011 p->retcode = CDA_INVALID;
2012 else
2013 di_stop(s, TRUE);
2014 }
2015
2016
2017 /*
2018 * cda_do_track
2019 * Perform the "track" command.
2020 *
2021 * Args:
2022 * s - Pointer to the curstat_t structure.
2023 * p - Pointer to the CDA packet structure.
2024 *
2025 * Return:
2026 * Nothing.
2027 */
2028 STATIC void
cda_do_track(curstat_t * s,cdapkt_t * p)2029 cda_do_track(curstat_t *s, cdapkt_t *p)
2030 {
2031 int i;
2032
2033 if (p->arg[0] == 0) {
2034 /* Previous track */
2035 if (s->mode == MOD_PLAY) {
2036 if ((i = di_curtrk_pos(s)) > 0)
2037 s->curpos_tot.addr = s->trkinfo[i].addr;
2038 di_prevtrk(s);
2039 }
2040 else
2041 p->retcode = CDA_INVALID;
2042 }
2043 else {
2044 /* Next track */
2045 if (s->mode == MOD_PLAY)
2046 di_nexttrk(s);
2047 else
2048 p->retcode = CDA_INVALID;
2049 }
2050 }
2051
2052
2053 /*
2054 * cda_do_index
2055 * Perform the "index" command.
2056 *
2057 * Args:
2058 * s - Pointer to the curstat_t structure.
2059 * p - Pointer to the CDA packet structure.
2060 *
2061 * Return:
2062 * Nothing.
2063 */
2064 STATIC void
cda_do_index(curstat_t * s,cdapkt_t * p)2065 cda_do_index(curstat_t *s, cdapkt_t *p)
2066 {
2067 if (p->arg[0] == 0) {
2068 /* Previous index */
2069 if (s->mode == MOD_PLAY && s->prog_tot <= 0) {
2070 if (s->cur_idx > 1)
2071 s->curpos_tot.addr = s->sav_iaddr;
2072 di_previdx(s);
2073 }
2074 else
2075 p->retcode = CDA_INVALID;
2076 }
2077 else {
2078 /* Next index */
2079 if (s->mode == MOD_PLAY && s->prog_tot <= 0)
2080 di_nextidx(s);
2081 else
2082 p->retcode = CDA_INVALID;
2083 }
2084 }
2085
2086
2087 /*
2088 * cda_do_program
2089 * Perform the "program" command.
2090 *
2091 * Args:
2092 * s - Pointer to the curstat_t structure.
2093 * p - Pointer to the CDA packet structure.
2094 *
2095 * Return:
2096 * Nothing.
2097 */
2098 STATIC void
cda_do_program(curstat_t * s,cdapkt_t * p)2099 cda_do_program(curstat_t *s, cdapkt_t *p)
2100 {
2101 int i,
2102 j;
2103 cdinfo_ret_t ret;
2104
2105 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY)
2106 p->retcode = CDA_INVALID;
2107 else if ((int) p->arg[0] == 1) {
2108 /* Query */
2109 if ((int) s->prog_tot > (CDA_NARGS - 2)) {
2110 p->retcode = CDA_INVALID;
2111 return;
2112 }
2113 p->arg[0] = (word32_t) s->prog_tot;
2114 p->arg[1] = (word32_t) s->prog_cnt;
2115 p->arg[2] = (word32_t) -1;
2116
2117 for (i = 0; i < (int) s->prog_tot; i++) {
2118 p->arg[i+2] = (word32_t)
2119 s->trkinfo[s->trkinfo[i].playorder].trkno;
2120 }
2121 }
2122 else if ((int) p->arg[0] == 2) {
2123 /* Save */
2124 if (s->shuffle) {
2125 p->retcode = CDA_INVALID;
2126 return;
2127 }
2128
2129 p->arg[0] = (word32_t) 0;
2130 p->arg[2] = (word32_t) -2;
2131 if (!s->program) {
2132 p->arg[2] = (word32_t) -1;
2133 return;
2134 }
2135
2136 if ((ret = cdinfo_save_prog(s)) != 0) {
2137 DBGPRN(DBG_CDI)(errfp,
2138 "cdinfo_save_prog: status=%d arg=%d\n",
2139 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
2140 }
2141 }
2142 else if ((int) p->arg[0] == 0) {
2143 /* Clear */
2144 if (s->shuffle) {
2145 /* program and shuffle modes are mutually-exclusive */
2146 p->retcode = CDA_INVALID;
2147 return;
2148 }
2149
2150 p->arg[1] = (word32_t) 0;
2151 p->arg[2] = (word32_t) -3;
2152 s->prog_tot = 0;
2153 s->program = FALSE;
2154
2155 if ((ret = cdinfo_del_prog()) != 0) {
2156 DBGPRN(DBG_CDI)(errfp,
2157 "cdinfo_del_prog: status=%d arg=%d\n",
2158 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
2159 }
2160
2161 if (dbp->playorder != NULL) {
2162 MEM_FREE(dbp->playorder);
2163 dbp->playorder = NULL;
2164 }
2165 }
2166 else if ((int) p->arg[0] < 0) {
2167 /* Define */
2168 if (s->shuffle) {
2169 /* program and shuffle modes are mutually-exclusive */
2170 p->retcode = CDA_INVALID;
2171 return;
2172 }
2173
2174 s->prog_tot = -(p->arg[0]);
2175 s->program = TRUE;
2176
2177 if (dbp->playorder != NULL) {
2178 MEM_FREE(dbp->playorder);
2179 dbp->playorder = NULL;
2180 }
2181
2182 for (i = 0; i < (int) s->prog_tot; i++) {
2183 char num[8];
2184
2185 for (j = 0; j < (int) s->tot_trks; j++) {
2186 if (s->trkinfo[j].trkno == p->arg[i+1])
2187 break;
2188 }
2189
2190 if (j >= (int) s->tot_trks) {
2191 s->prog_tot = 0;
2192 s->program = FALSE;
2193 p->retcode = CDA_PARMERR;
2194 return;
2195 }
2196
2197 s->trkinfo[i].playorder = j;
2198
2199 (void) sprintf(num, "%d", (int) s->trkinfo[j].trkno);
2200 if (dbp->playorder == NULL) {
2201 if (!util_newstr(&dbp->playorder, num)) {
2202 p->retcode = CDA_FAILED;
2203 return;
2204 }
2205 }
2206 else {
2207 dbp->playorder = (char *) MEM_REALLOC(
2208 "playorder",
2209 dbp->playorder,
2210 strlen(dbp->playorder) + strlen(num) + 2
2211 );
2212 if (dbp->playorder == NULL) {
2213 p->retcode = CDA_FAILED;
2214 return;
2215 }
2216 (void) sprintf(dbp->playorder, "%s,%s",
2217 dbp->playorder, num);
2218 }
2219 }
2220 }
2221 else
2222 p->retcode = CDA_PARMERR;
2223 }
2224
2225
2226 /*
2227 * cda_do_shuffle
2228 * Perform the "shuffle" command.
2229 *
2230 * Args:
2231 * s - Pointer to the curstat_t structure.
2232 * p - Pointer to the CDA packet structure.
2233 *
2234 * Return:
2235 * Nothing.
2236 */
2237 STATIC void
cda_do_shuffle(curstat_t * s,cdapkt_t * p)2238 cda_do_shuffle(curstat_t *s, cdapkt_t *p)
2239 {
2240 if (s->program) {
2241 p->retcode = CDA_INVALID;
2242 return;
2243 }
2244
2245 if (s->mode != MOD_NODISC && s->mode != MOD_STOP) {
2246 p->retcode = CDA_INVALID;
2247 return;
2248 }
2249
2250 di_shuffle(s, (bool_t) (p->arg[0] == 1));
2251 }
2252
2253
2254 /*
2255 * cda_do_repeat
2256 * Perform the "repeat" command.
2257 *
2258 * Args:
2259 * s - Pointer to the curstat_t structure.
2260 * p - Pointer to the CDA packet structure.
2261 *
2262 * Return:
2263 * Nothing.
2264 */
2265 STATIC void
cda_do_repeat(curstat_t * s,cdapkt_t * p)2266 cda_do_repeat(curstat_t *s, cdapkt_t *p)
2267 {
2268 di_repeat(s, (bool_t) (p->arg[0] == 1));
2269 }
2270
2271
2272 /*
2273 * cda_do_volume
2274 * Perform the "volume" command.
2275 *
2276 * Args:
2277 * s - Pointer to the curstat_t structure.
2278 * p - Pointer to the CDA packet structure.
2279 *
2280 * Return:
2281 * Nothing.
2282 */
2283 STATIC void
cda_do_volume(curstat_t * s,cdapkt_t * p)2284 cda_do_volume(curstat_t *s, cdapkt_t *p)
2285 {
2286 if (p->arg[0] == 0) {
2287 /* Query */
2288 p->arg[1] = (word32_t) s->level;
2289 p->arg[2] = (word32_t) app_data.vol_taper;
2290 }
2291 else if (p->arg[0] == 1) {
2292 /* Set volume level */
2293 if ((int) p->arg[1] >= 0 && (int) p->arg[1] <= 100)
2294 di_level(s, (byte_t) p->arg[1], FALSE);
2295 }
2296 else if (p->arg[0] == 2) {
2297 /* Set volume taper */
2298 if ((int) p->arg[2] >= 0 && (int) p->arg[2] <= 100)
2299 app_data.vol_taper = (int) p->arg[2];
2300 }
2301 else
2302 p->retcode = CDA_PARMERR;
2303 }
2304
2305
2306 /*
2307 * cda_do_balance
2308 * Perform the "balance" command.
2309 *
2310 * Args:
2311 * s - Pointer to the curstat_t structure.
2312 * p - Pointer to the CDA packet structure.
2313 *
2314 * Return:
2315 * Nothing.
2316 */
2317 STATIC void
cda_do_balance(curstat_t * s,cdapkt_t * p)2318 cda_do_balance(curstat_t *s, cdapkt_t *p)
2319 {
2320 if (p->arg[0] == 0) {
2321 /* Query */
2322 p->arg[1] = (word32_t)
2323 ((int) (s->level_right - s->level_left) / 2) + 50;
2324 }
2325 else if ((int) p->arg[1] == 50) {
2326 /* Center setting */
2327 s->level_left = s->level_right = 100;
2328 di_level(s, (byte_t) s->level, FALSE);
2329 }
2330 else if ((int) p->arg[1] < 50 && (int) p->arg[1] >= 0) {
2331 /* Attenuate the right channel */
2332 s->level_left = 100;
2333 s->level_right = 100 + (((int) p->arg[1] - 50) * 2);
2334 di_level(s, (byte_t) s->level, FALSE);
2335 }
2336 else if ((int) p->arg[1] > 50 && (int) p->arg[1] <= 100) {
2337 /* Attenuate the left channel */
2338 s->level_left = 100 - (((int) p->arg[1] - 50) * 2);
2339 s->level_right = 100;
2340 di_level(s, (byte_t) s->level, FALSE);
2341 }
2342 else
2343 p->retcode = CDA_PARMERR;
2344 }
2345
2346
2347 /*
2348 * cda_do_route
2349 * Perform the "route" command.
2350 *
2351 * Args:
2352 * s - Pointer to the curstat_t structure.
2353 * p - Pointer to the CDA packet structure.
2354 *
2355 * Return:
2356 * Nothing.
2357 */
2358 STATIC void
cda_do_route(curstat_t * s,cdapkt_t * p)2359 cda_do_route(curstat_t *s, cdapkt_t *p)
2360 {
2361 if (p->arg[0] == 0) {
2362 /* Query */
2363 p->arg[1] = (word32_t) app_data.ch_route;
2364 }
2365 else if ((int) p->arg[1] >= 0 && (int) p->arg[1] <= 4) {
2366 /* Set */
2367 app_data.ch_route = (int) p->arg[1];
2368 di_route(s);
2369 }
2370 else
2371 p->retcode = CDA_PARMERR;
2372 }
2373
2374
2375 /*
2376 * cda_do_outport
2377 * Perform the "outport" command.
2378 *
2379 * Args:
2380 * s - Pointer to the curstat_t structure.
2381 * p - Pointer to the CDA packet structure.
2382 *
2383 * Return:
2384 * Nothing.
2385 */
2386 /*ARGSUSED*/
2387 STATIC void
cda_do_outport(curstat_t * s,cdapkt_t * p)2388 cda_do_outport(curstat_t *s, cdapkt_t *p)
2389 {
2390 switch ((int) p->arg[0]) {
2391 case 0:
2392 /* Query */
2393 p->arg[1] = (word32_t) app_data.outport;
2394 break;
2395 case 1:
2396 /* Toggle */
2397 app_data.outport ^= (word32_t) p->arg[1];
2398 break;
2399 case 2:
2400 /* Set */
2401 app_data.outport = (word32_t) p->arg[1];
2402 break;
2403 default:
2404 p->retcode = CDA_PARMERR;
2405 return;
2406 }
2407
2408 cdda_outport();
2409 }
2410
2411
2412 /*
2413 * cda_do_cdda_att
2414 * Perform the "cdda-att" command.
2415 *
2416 * Args:
2417 * s - Pointer to the curstat_t structure.
2418 * p - Pointer to the CDA packet structure.
2419 *
2420 * Return:
2421 * Nothing.
2422 */
2423 STATIC void
cda_do_cdda_att(curstat_t * s,cdapkt_t * p)2424 cda_do_cdda_att(curstat_t *s, cdapkt_t *p)
2425 {
2426 if (p->arg[0] == 0) {
2427 /* Query */
2428 p->arg[1] = (word32_t) s->cdda_att;
2429 }
2430 else if (p->arg[0] == 1) {
2431 /* Set CDDA attenuator level */
2432 if ((int) p->arg[1] >= 0 && (int) p->arg[1] <= 100) {
2433 s->cdda_att = (byte_t) p->arg[1];
2434 cdda_att(s);
2435 }
2436 }
2437 else
2438 p->retcode = CDA_PARMERR;
2439 }
2440
2441
2442 /*
2443 * cda_do_status
2444 * Perform the "status" command.
2445 *
2446 * Args:
2447 * s - Pointer to the curstat_t structure.
2448 * p - Pointer to the CDA packet structure.
2449 *
2450 * Return:
2451 * Nothing.
2452 */
2453 STATIC void
cda_do_status(curstat_t * s,cdapkt_t * p)2454 cda_do_status(curstat_t *s, cdapkt_t *p)
2455 {
2456 word32_t discid;
2457
2458 /* Initialize */
2459 (void) memset(p->arg, 0, CDA_NARGS * sizeof(word32_t));
2460
2461 WR_ARG_MODE(p->arg[0], s->mode);
2462
2463 if (s->caddy_lock)
2464 WR_ARG_LOCK(p->arg[0]);
2465 if (s->shuffle)
2466 WR_ARG_SHUF(p->arg[0]);
2467 if (s->repeat)
2468 WR_ARG_REPT(p->arg[0]);
2469 if (s->program)
2470 WR_ARG_PROG(p->arg[0]);
2471
2472 WR_ARG_TRK(p->arg[1], s->cur_trk);
2473 WR_ARG_IDX(p->arg[1], s->cur_idx);
2474 WR_ARG_MIN(p->arg[1], s->curpos_trk.min);
2475 WR_ARG_SEC(p->arg[1], s->curpos_trk.sec);
2476
2477 if (s->repeat && s->mode == MOD_PLAY)
2478 p->arg[2] = (word32_t) s->rptcnt;
2479 else
2480 p->arg[2] = (word32_t) -1;
2481
2482 p->arg[3] = (word32_t) s->level;
2483 p->arg[4] = (word32_t)
2484 ((int) (s->level_right - s->level_left) / 2) + 50;
2485 p->arg[5] = (word32_t) app_data.ch_route;
2486 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY)
2487 discid = 0;
2488 else
2489 discid = cdinfo_discid(s);
2490
2491 dbp->discid = discid;
2492 p->arg[7] = s->cur_disc;
2493 p->arg[6] = discid;
2494 }
2495
2496
2497 /*
2498 * cda_do_toc
2499 * Perform the "toc" command.
2500 *
2501 * Args:
2502 * s - Pointer to the curstat_t structure.
2503 * p - Pointer to the CDA packet structure.
2504 *
2505 * Return:
2506 * Nothing.
2507 */
2508 STATIC void
cda_do_toc(curstat_t * s,cdapkt_t * p)2509 cda_do_toc(curstat_t *s, cdapkt_t *p)
2510 {
2511 int i,
2512 j;
2513 word32_t min,
2514 sec;
2515 bool_t offsets;
2516
2517 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY) {
2518 p->retcode = CDA_INVALID;
2519 return;
2520 }
2521
2522 offsets = (p->arg[0] == 1);
2523
2524 /* Initialize */
2525 (void) memset(p->arg, 0, CDA_NARGS * sizeof(word32_t));
2526
2527 p->arg[0] = cdinfo_discid(s);
2528
2529 if (offsets) {
2530 for (i = 0; i < (int) s->tot_trks; i++) {
2531 WR_ARG_TOC(p->arg[i+1], s->trkinfo[i].trkno,
2532 s->cur_trk,
2533 s->trkinfo[i].min,
2534 s->trkinfo[i].sec);
2535 }
2536 }
2537 else for (i = 0; i < (int) s->tot_trks; i++) {
2538 j = ((s->trkinfo[i+1].min * 60 + s->trkinfo[i+1].sec) -
2539 (s->trkinfo[i].min * 60 + s->trkinfo[i].sec));
2540
2541 /* "Enhanced CD" / "CD Extra" hack */
2542 if (s->trkinfo[i].type == TYP_AUDIO &&
2543 s->trkinfo[i+1].addr > s->discpos_tot.addr) {
2544 j -= ((s->trkinfo[i+1].addr - s->discpos_tot.addr) /
2545 FRAME_PER_SEC);
2546 }
2547 min = j / 60;
2548 sec = j % 60;
2549
2550 WR_ARG_TOC(p->arg[i+1], s->trkinfo[i].trkno,
2551 s->cur_trk, min, sec);
2552 }
2553
2554 /* Lead-out track */
2555 WR_ARG_TOC(p->arg[i+1], s->trkinfo[i].trkno,
2556 0, s->trkinfo[i].min, s->trkinfo[i].sec);
2557 }
2558
2559
2560 /*
2561 * cda_do_toc2
2562 * Perform the "toc2" command.
2563 *
2564 * Args:
2565 * s - Pointer to the curstat_t structure.
2566 * p - Pointer to the CDA packet structure.
2567 *
2568 * Return:
2569 * Nothing.
2570 */
2571 STATIC void
cda_do_toc2(curstat_t * s,cdapkt_t * p)2572 cda_do_toc2(curstat_t *s, cdapkt_t *p)
2573 {
2574 int i;
2575
2576 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY) {
2577 p->retcode = CDA_INVALID;
2578 return;
2579 }
2580
2581 /* Initialize */
2582 (void) memset(p->arg, 0, CDA_NARGS * sizeof(word32_t));
2583
2584 p->arg[0] = cdinfo_discid(s);
2585
2586 for (i = 0; i < (int) s->tot_trks; i++) {
2587 WR_ARG_TOC2(p->arg[i+1], s->trkinfo[i].trkno,
2588 s->trkinfo[i].min,
2589 s->trkinfo[i].sec,
2590 s->trkinfo[i].frame
2591 );
2592 }
2593
2594 /* Lead-out track */
2595 WR_ARG_TOC2(p->arg[i+1], s->trkinfo[i].trkno,
2596 s->trkinfo[i].min,
2597 s->trkinfo[i].sec,
2598 s->trkinfo[i].frame
2599 );
2600 }
2601
2602
2603 /*
2604 * cda_do_extinfo
2605 * Perform the "extinfo" command.
2606 *
2607 * Args:
2608 * s - Pointer to the curstat_t structure.
2609 * p - Pointer to the CDA packet structure.
2610 *
2611 * Return:
2612 * Nothing.
2613 */
2614 STATIC void
cda_do_extinfo(curstat_t * s,cdapkt_t * p)2615 cda_do_extinfo(curstat_t *s, cdapkt_t *p)
2616 {
2617 int i,
2618 j;
2619
2620 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY) {
2621 p->retcode = CDA_INVALID;
2622 return;
2623 }
2624
2625 p->arg[0] = cdinfo_discid(s);
2626 p->arg[2] = (word32_t) -1;
2627
2628 if ((int) p->arg[1] == -1) {
2629 if (s->mode != MOD_PLAY) {
2630 p->arg[1] = p->arg[2] = (word32_t) -1;
2631 return;
2632 }
2633
2634 p->arg[1] = (word32_t) s->cur_trk;
2635 j = (int) s->cur_trk;
2636 }
2637 else
2638 j = (int) p->arg[1];
2639
2640 for (i = 0; i < (int) s->tot_trks; i++) {
2641 if ((int) s->trkinfo[i].trkno == j)
2642 break;
2643 }
2644 if (i < (int) s->tot_trks)
2645 p->arg[2] = i;
2646 else
2647 p->retcode = CDA_PARMERR;
2648 }
2649
2650
2651 /*
2652 * cda_doc_on_load
2653 * Perform the "on-load" command.
2654 *
2655 * Args:
2656 * s - Pointer to the curstat_t structure.
2657 * p - Pointer to the CDA packet structure.
2658 *
2659 * Return:
2660 * Nothing.
2661 */
2662 /*ARGSUSED*/
2663 STATIC void
cda_do_on_load(curstat_t * s,cdapkt_t * p)2664 cda_do_on_load(curstat_t *s, cdapkt_t *p)
2665 {
2666 if (p->arg[0] == 0) {
2667 /* Query */
2668 p->arg[1] = (word32_t) app_data.caddy_lock;
2669 p->arg[2] = (word32_t) app_data.load_spindown;
2670 p->arg[3] = (word32_t) app_data.load_play;
2671 }
2672 else {
2673 switch (p->arg[1]) {
2674 case 0: /* none */
2675 app_data.load_spindown = FALSE;
2676 app_data.load_play = FALSE;
2677 break;
2678 case 1: /* spindown */
2679 app_data.load_spindown = TRUE;
2680 app_data.load_play = FALSE;
2681 break;
2682 case 2: /* autoplay */
2683 app_data.load_spindown = FALSE;
2684 app_data.load_play = TRUE;
2685 break;
2686 case 3: /* noautolock */
2687 app_data.caddy_lock = FALSE;
2688 break;
2689 case 4: /* autolock */
2690 app_data.caddy_lock = TRUE;
2691 break;
2692 default:
2693 p->retcode = CDA_PARMERR;
2694 break;
2695 }
2696 }
2697 }
2698
2699
2700 /*
2701 * cda_do_on_exit
2702 * Perform the "on-exit" command.
2703 *
2704 * Args:
2705 * s - Pointer to the curstat_t structure.
2706 * p - Pointer to the CDA packet structure.
2707 *
2708 * Return:
2709 * Nothing.
2710 */
2711 /*ARGSUSED*/
2712 STATIC void
cda_do_on_exit(curstat_t * s,cdapkt_t * p)2713 cda_do_on_exit(curstat_t *s, cdapkt_t *p)
2714 {
2715 if (p->arg[0] == 0) {
2716 /* Query */
2717 p->arg[1] = (word32_t) app_data.exit_stop;
2718 p->arg[2] = (word32_t) app_data.exit_eject;
2719 }
2720 else {
2721 switch (p->arg[1]) {
2722 case 0: /* none */
2723 app_data.exit_stop = FALSE;
2724 app_data.exit_eject = FALSE;
2725 break;
2726 case 1: /* autostop */
2727 app_data.exit_stop = TRUE;
2728 app_data.exit_eject = FALSE;
2729 break;
2730 case 2: /* autoeject */
2731 app_data.exit_stop = FALSE;
2732 app_data.exit_eject = TRUE;
2733 break;
2734 default:
2735 p->retcode = CDA_PARMERR;
2736 break;
2737 }
2738 }
2739 }
2740
2741
2742 /*
2743 * cda_do_on_done
2744 * Perform the "on-done" command.
2745 *
2746 * Args:
2747 * s - Pointer to the curstat_t structure.
2748 * p - Pointer to the CDA packet structure.
2749 *
2750 * Return:
2751 * Nothing.
2752 */
2753 /*ARGSUSED*/
2754 STATIC void
cda_do_on_done(curstat_t * s,cdapkt_t * p)2755 cda_do_on_done(curstat_t *s, cdapkt_t *p)
2756 {
2757 if (p->arg[0] == 0) {
2758 /* Query */
2759 p->arg[1] = (word32_t) app_data.done_eject;
2760 p->arg[2] = (word32_t) app_data.done_exit;
2761 }
2762 else {
2763 switch (p->arg[1]) {
2764 case 0: /* noautoeject */
2765 app_data.done_eject = FALSE;
2766 break;
2767 case 1: /* autoeject */
2768 app_data.done_eject = TRUE;
2769 break;
2770 case 2: /* noautoexit */
2771 app_data.done_exit = FALSE;
2772 break;
2773 case 3: /* autoexit */
2774 app_data.done_exit = TRUE;
2775 break;
2776 default:
2777 p->retcode = CDA_PARMERR;
2778 break;
2779 }
2780 }
2781 }
2782
2783
2784 /*
2785 * cda_do_on_eject
2786 * Perform the "on-eject" command.
2787 *
2788 * Args:
2789 * s - Pointer to the curstat_t structure.
2790 * p - Pointer to the CDA packet structure.
2791 *
2792 * Return:
2793 * Nothing.
2794 */
2795 /*ARGSUSED*/
2796 STATIC void
cda_do_on_eject(curstat_t * s,cdapkt_t * p)2797 cda_do_on_eject(curstat_t *s, cdapkt_t *p)
2798 {
2799 if (p->arg[0] == 0) {
2800 /* Query */
2801 p->arg[1] = (word32_t) app_data.eject_exit;
2802 }
2803 else {
2804 /* set [no]autoeject */
2805 app_data.eject_exit = (p->arg[1] == 0) ? FALSE : TRUE;
2806 }
2807 }
2808
2809
2810 /*
2811 * cda_do_chgr
2812 * Perform the "changer" command.
2813 *
2814 * Args:
2815 * s - Pointer to the curstat_t structure.
2816 * p - Pointer to the CDA packet structure.
2817 *
2818 * Return:
2819 * Nothing.
2820 */
2821 /*ARGSUSED*/
2822 STATIC void
cda_do_chgr(curstat_t * s,cdapkt_t * p)2823 cda_do_chgr(curstat_t *s, cdapkt_t *p)
2824 {
2825 if (p->arg[0] == 0) {
2826 /* Query */
2827 p->arg[1] = (word32_t) app_data.multi_play;
2828 p->arg[2] = (word32_t) app_data.reverse;
2829 }
2830 else {
2831 switch (p->arg[1]) {
2832 case 0: /* nomultiplay */
2833 app_data.multi_play = FALSE;
2834 app_data.reverse = FALSE;
2835 break;
2836 case 1: /* multiplay */
2837 app_data.multi_play = TRUE;
2838 break;
2839 case 2: /* noreverse */
2840 app_data.reverse = FALSE;
2841 break;
2842 case 3: /* reverse */
2843 app_data.multi_play = TRUE;
2844 app_data.reverse = TRUE;
2845 break;
2846 default:
2847 p->retcode = CDA_PARMERR;
2848 break;
2849 }
2850 }
2851 }
2852
2853
2854 /*
2855 * cda_do_mode
2856 * Perform the "mode" command.
2857 *
2858 * Args:
2859 * s - Pointer to the curstat_t structure.
2860 * p - Pointer to the CDA packet structure.
2861 *
2862 * Return:
2863 * Nothing.
2864 */
2865 /*ARGSUSED*/
2866 STATIC void
cda_do_mode(curstat_t * s,cdapkt_t * p)2867 cda_do_mode(curstat_t *s, cdapkt_t *p)
2868 {
2869 if (p->arg[0] == 0) {
2870 /* Query */
2871 p->arg[1] = (word32_t) app_data.play_mode;
2872 }
2873 else {
2874 switch (s->mode) {
2875 case MOD_PLAY:
2876 case MOD_PAUSE:
2877 case MOD_SAMPLE:
2878 /* Disallow changing the mode while playing */
2879 p->retcode = CDA_INVALID;
2880 return;
2881
2882 default:
2883 break;
2884 }
2885
2886 if (p->arg[1] == 0)
2887 app_data.play_mode = PLAYMODE_STD;
2888 else {
2889 if ((app_data.play_mode & p->arg[1]) == 0)
2890 app_data.play_mode |= (int) p->arg[1];
2891 else
2892 app_data.play_mode &= (int) ~(p->arg[1]);
2893 }
2894
2895 if (!di_playmode(s)) {
2896 app_data.play_mode = PLAYMODE_STD;
2897 p->retcode = CDA_REFUSED;
2898 }
2899
2900 p->arg[1] = (word32_t) app_data.play_mode;
2901
2902 if (PLAYMODE_IS_STD(app_data.play_mode))
2903 app_data.vol_taper = VOLTAPER_LINEAR;
2904 }
2905 }
2906
2907
2908 /*
2909 * cda_do_jitter
2910 * Perform the "jittercorr" command.
2911 *
2912 * Args:
2913 * s - Pointer to the curstat_t structure.
2914 * p - Pointer to the CDA packet structure.
2915 *
2916 * Return:
2917 * Nothing.
2918 */
2919 STATIC void
cda_do_jitter(curstat_t * s,cdapkt_t * p)2920 cda_do_jitter(curstat_t *s, cdapkt_t *p)
2921 {
2922 if (p->arg[0] == 0) {
2923 /* Query */
2924 p->arg[1] = (word32_t) app_data.cdda_jitter_corr;
2925 }
2926 else if (app_data.cdda_jitter_corr != (bool_t) p->arg[1]) {
2927 /* Set */
2928
2929 if (di_isdemo())
2930 return; /* Don't actually do anything in demo mode */
2931
2932 app_data.cdda_jitter_corr = (bool_t) p->arg[1];
2933
2934 /* Notify device interface of the change */
2935 di_cddajitter(s);
2936 }
2937 }
2938
2939
2940 /*
2941 * cda_do_trkfile
2942 * Perform the "trackfile" command.
2943 *
2944 * Args:
2945 * s - Pointer to the curstat_t structure.
2946 * p - Pointer to the CDA packet structure.
2947 *
2948 * Return:
2949 * Nothing.
2950 */
2951 /*ARGSUSED*/
2952 STATIC void
cda_do_trkfile(curstat_t * s,cdapkt_t * p)2953 cda_do_trkfile(curstat_t *s, cdapkt_t *p)
2954 {
2955 if (p->arg[0] == 0) {
2956 /* Query */
2957 p->arg[1] = (word32_t) app_data.cdda_trkfile;
2958 }
2959 else if (app_data.cdda_trkfile != (bool_t) p->arg[1]) {
2960 /* Set */
2961 app_data.cdda_trkfile = (bool_t) p->arg[1];
2962
2963 /* Set new output file path template */
2964 cda_fix_outfile_path(s);
2965
2966 (void) fprintf(errfp,
2967 "Notice: %s\n",
2968 "The output file path template has been changed."
2969 );
2970 }
2971 }
2972
2973
2974 /*
2975 * cda_do_subst
2976 * Perform the "subst" command.
2977 *
2978 * Args:
2979 * s - Pointer to the curstat_t structure.
2980 * p - Pointer to the CDA packet structure.
2981 *
2982 * Return:
2983 * Nothing.
2984 */
2985 /*ARGSUSED*/
2986 STATIC void
cda_do_subst(curstat_t * s,cdapkt_t * p)2987 cda_do_subst(curstat_t *s, cdapkt_t *p)
2988 {
2989 if (p->arg[0] == 0) {
2990 /* Query */
2991 p->arg[1] = (word32_t) app_data.subst_underscore;
2992 }
2993 else if (app_data.subst_underscore != (bool_t) p->arg[1]) {
2994 /* Set */
2995 app_data.subst_underscore = (bool_t) p->arg[1];
2996 }
2997 }
2998
2999
3000 /*
3001 * cda_do_filefmt
3002 * Perform the "filefmt" command.
3003 *
3004 * Args:
3005 * s - Pointer to the curstat_t structure.
3006 * p - Pointer to the CDA packet structure.
3007 *
3008 * Return:
3009 * Nothing.
3010 */
3011 /*ARGSUSED*/
3012 STATIC void
cda_do_filefmt(curstat_t * s,cdapkt_t * p)3013 cda_do_filefmt(curstat_t *s, cdapkt_t *p)
3014 {
3015 if (p->arg[0] == 0) {
3016 /* Query */
3017 p->arg[1] = (word32_t) app_data.cdda_filefmt;
3018 }
3019 else if (app_data.cdda_filefmt != (int) p->arg[1]) {
3020 if (!cdda_filefmt_supp((int) p->arg[1])) {
3021 /* The requested file format is not supported */
3022 (void) fprintf(errfp, "Error: %s\n",
3023 "Support for the requested file format "
3024 "is not installed.\n"
3025 );
3026 return;
3027 }
3028
3029 /* Set */
3030 app_data.cdda_filefmt = (int) p->arg[1];
3031
3032 /* Set new output file path template */
3033 cda_fix_outfile_path(s);
3034
3035 (void) fprintf(errfp,
3036 "Notice: %s\n",
3037 "The output file path template has been changed."
3038 );
3039 }
3040 }
3041
3042
3043 /*
3044 * cda_do_file
3045 * Update the output file path template
3046 *
3047 * Args:
3048 * s - Pointer to the curstat_t structure.
3049 * p - Pointer to the CDA packet structure.
3050 *
3051 * Return:
3052 * Nothing.
3053 */
3054 /*ARGSUSED*/
3055 STATIC void
cda_do_file(curstat_t * s,cdapkt_t * p)3056 cda_do_file(curstat_t *s, cdapkt_t *p)
3057 {
3058 int i;
3059 filefmt_t *fmp,
3060 *fmp2;
3061 char *cp,
3062 *cp2,
3063 *cp3,
3064 *suf;
3065 size_t maxsz = (CDA_NARGS - 1) * sizeof(word32_t);
3066 int newfmt;
3067
3068 cp = (char *) &p->arg[1];
3069
3070 if (p->arg[0] == 0) {
3071 /* Query */
3072 if (s->outf_tmpl == NULL)
3073 *cp = '\0';
3074 else {
3075 (void) strncpy(cp, s->outf_tmpl, maxsz - 1);
3076 *(cp + maxsz - 1) = '\0';
3077 }
3078 }
3079 else {
3080 /* Set */
3081 if (s->outf_tmpl != NULL && strcmp(s->outf_tmpl, cp) == 0) {
3082 /* No change */
3083 return;
3084 }
3085
3086 /* File suffix */
3087 if ((fmp = cdda_filefmt(app_data.cdda_filefmt)) == NULL)
3088 suf = "";
3089 else
3090 suf = fmp->suf;
3091
3092 /* Check if file suffix indicates a change in file format */
3093 newfmt = app_data.cdda_filefmt;
3094 if ((cp2 = strrchr(cp, '.')) != NULL) {
3095 if ((cp3 = strrchr(cp, DIR_END)) != NULL &&
3096 cp2 > cp3) {
3097 if (strcmp(suf, cp2) != 0) {
3098 for (i = 0; i < MAX_FILEFMTS; i++) {
3099 if ((fmp2 = cdda_filefmt(i)) == NULL)
3100 continue;
3101
3102 if (util_strcasecmp(cp2, fmp2->suf) == 0)
3103 newfmt = fmp2->fmt;
3104 }
3105 }
3106 }
3107 else if (strcmp(suf, cp2) != 0) {
3108 for (i = 0; i < MAX_FILEFMTS; i++) {
3109 if ((fmp2 = cdda_filefmt(i)) == NULL)
3110 continue;
3111
3112 if (util_strcasecmp(cp2, fmp2->suf) == 0)
3113 newfmt = fmp2->fmt;
3114 }
3115 }
3116 }
3117
3118 if (!cdda_filefmt_supp(newfmt)) {
3119 /* The requested file format is not supported */
3120 (void) fprintf(errfp, "Error: %s\n",
3121 "Support for the requested file format "
3122 "is not installed.\n"
3123 );
3124 return;
3125 }
3126
3127 if (!util_newstr(&s->outf_tmpl, cp))
3128 CDA_FATAL(app_data.str_nomemory);
3129
3130 /* File format changed */
3131 if (newfmt != app_data.cdda_filefmt) {
3132 app_data.cdda_filefmt = newfmt;
3133 (void) fprintf(errfp,
3134 "Notice: %s\n",
3135 "The output file format has been changed."
3136 );
3137 }
3138
3139 /* Fix output file suffix to match the file type */
3140 cda_fix_outfile_path(s);
3141
3142 if (util_strstr(s->outf_tmpl, "%T") != NULL ||
3143 util_strstr(s->outf_tmpl, "%t") != NULL ||
3144 util_strstr(s->outf_tmpl, "%R") != NULL ||
3145 util_strstr(s->outf_tmpl, "%r") != NULL ||
3146 util_strstr(s->outf_tmpl, "%#") != NULL) {
3147 if (!app_data.cdda_trkfile) {
3148 app_data.cdda_trkfile = TRUE;
3149 (void) fprintf(errfp,
3150 "Notice: %s\n",
3151 "File-per-track has been enabled."
3152 );
3153 }
3154 }
3155 else if (app_data.cdda_trkfile) {
3156 app_data.cdda_trkfile = FALSE;
3157 (void) fprintf(errfp,
3158 "Notice: %s\n",
3159 "File-per-track has been disabled."
3160 );
3161 }
3162 }
3163 }
3164
3165
3166 /*
3167 * cda_do_pipeprog
3168 * Update the pipe-to-program path
3169 *
3170 * Args:
3171 * s - Pointer to the curstat_t structure.
3172 * p - Pointer to the CDA packet structure.
3173 *
3174 * Return:
3175 * Nothing.
3176 */
3177 /*ARGSUSED*/
3178 STATIC void
cda_do_pipeprog(curstat_t * s,cdapkt_t * p)3179 cda_do_pipeprog(curstat_t *s, cdapkt_t *p)
3180 {
3181 char *cp;
3182 size_t maxsz = (CDA_NARGS - 1) * sizeof(word32_t);
3183
3184 cp = (char *) &p->arg[1];
3185
3186 if (p->arg[0] == 0) {
3187 /* Query */
3188 if (app_data.pipeprog == NULL)
3189 *cp = '\0';
3190 else {
3191 (void) strncpy(cp, app_data.pipeprog, maxsz - 1);
3192 *(cp + maxsz - 1) = '\0';
3193 }
3194 }
3195 else {
3196 /* Set */
3197 if (!util_newstr(&app_data.pipeprog, cp))
3198 CDA_FATAL(app_data.str_nomemory);
3199 }
3200 }
3201
3202
3203 /*
3204 * cda_do_compress
3205 * Perform the "compress" command.
3206 *
3207 * Args:
3208 * s - Pointer to the curstat_t structure.
3209 * p - Pointer to the CDA packet structure.
3210 *
3211 * Return:
3212 * Nothing.
3213 */
3214 /*ARGSUSED*/
3215 STATIC void
cda_do_compress(curstat_t * s,cdapkt_t * p)3216 cda_do_compress(curstat_t *s, cdapkt_t *p)
3217 {
3218 if (p->arg[0] == 0) {
3219 /* Query */
3220 p->arg[1] = (word32_t) app_data.comp_mode;
3221 p->arg[3] = (word32_t) app_data.cdda_filefmt;
3222 switch (app_data.comp_mode) {
3223 case COMPMODE_0:
3224 case COMPMODE_3:
3225 p->arg[2] = (word32_t) app_data.bitrate;
3226 break;
3227
3228 case COMPMODE_1:
3229 case COMPMODE_2:
3230 p->arg[2] = (word32_t) app_data.qual_factor;
3231 break;
3232
3233 default:
3234 p->retcode = CDA_PARMERR;
3235 break;
3236 }
3237 }
3238 else {
3239 /* Set */
3240 int val = (int) p->arg[2];
3241
3242 switch ((int) p->arg[1]) {
3243 case COMPMODE_0:
3244 case COMPMODE_3:
3245 if (di_bitrate_valid(val)) {
3246 app_data.comp_mode = (int) p->arg[1];
3247 app_data.bitrate = val;
3248 }
3249 else
3250 p->retcode = CDA_PARMERR;
3251 break;
3252
3253 case COMPMODE_1:
3254 case COMPMODE_2:
3255 if (val <= 0 || val > 10) {
3256 p->retcode = CDA_PARMERR;
3257 break;
3258 }
3259 app_data.comp_mode = (int) p->arg[1];
3260 app_data.qual_factor = val;
3261 break;
3262
3263 default:
3264 p->retcode = CDA_PARMERR;
3265 break;
3266 }
3267 }
3268 }
3269
3270
3271 /*
3272 * cda_do_brate
3273 * Perform the "min-brate" and "max-brate" commands.
3274 *
3275 * Args:
3276 * s - Pointer to the curstat_t structure.
3277 * p - Pointer to the CDA packet structure.
3278 *
3279 * Return:
3280 * Nothing.
3281 */
3282 /*ARGSUSED*/
3283 STATIC void
cda_do_brate(curstat_t * s,cdapkt_t * p)3284 cda_do_brate(curstat_t *s, cdapkt_t *p)
3285 {
3286 if (p->arg[0] == 0) {
3287 /* Query */
3288 if (p->arg[1] == 1)
3289 p->arg[2] = (word32_t) app_data.bitrate_min;
3290 else if (p->arg[1] == 2)
3291 p->arg[2] = (word32_t) app_data.bitrate_max;
3292 else
3293 p->retcode = CDA_PARMERR;
3294 }
3295 else {
3296 /* Set */
3297 int val = (int) p->arg[2];
3298
3299 if (!di_bitrate_valid(val))
3300 p->retcode = CDA_PARMERR;
3301 else if (p->arg[1] == 1)
3302 app_data.bitrate_min = val;
3303 else if (p->arg[1] == 2)
3304 app_data.bitrate_max = val;
3305 else
3306 p->retcode = CDA_PARMERR;
3307 }
3308 }
3309
3310
3311 /*
3312 * cda_do_coding
3313 * Perform the "coding" command.
3314 *
3315 * Args:
3316 * s - Pointer to the curstat_t structure.
3317 * p - Pointer to the CDA packet structure.
3318 *
3319 * Return:
3320 * Nothing.
3321 */
3322 /*ARGSUSED*/
3323 STATIC void
cda_do_coding(curstat_t * s,cdapkt_t * p)3324 cda_do_coding(curstat_t *s, cdapkt_t *p)
3325 {
3326 if (p->arg[0] == 0) {
3327 /* Query */
3328 p->arg[1] = (word32_t) app_data.chan_mode;
3329 p->arg[2] = (word32_t) app_data.comp_algo;
3330 }
3331 else if (p->arg[0] == 1) {
3332 /* Set MP3 mode */
3333 switch ((int) p->arg[1]) {
3334 case CH_STEREO:
3335 case CH_JSTEREO:
3336 case CH_FORCEMS:
3337 case CH_MONO:
3338 app_data.chan_mode = (int) p->arg[1];
3339 break;
3340 default:
3341 p->retcode = CDA_PARMERR;
3342 break;
3343 }
3344 }
3345 else {
3346 /* Set compression algorithm */
3347 int val;
3348
3349 val = (int) p->arg[2];
3350 if (val <= 0 || val > 10)
3351 p->retcode = CDA_PARMERR;
3352 else
3353 app_data.comp_algo = val;
3354 }
3355 }
3356
3357
3358 /*
3359 * cda_do_filter
3360 * Perform the "lowpass" and "highpass" commands.
3361 *
3362 * Args:
3363 * s - Pointer to the curstat_t structure.
3364 * p - Pointer to the CDA packet structure.
3365 *
3366 * Return:
3367 * Nothing.
3368 */
3369 /*ARGSUSED*/
3370 STATIC void
cda_do_filter(curstat_t * s,cdapkt_t * p)3371 cda_do_filter(curstat_t *s, cdapkt_t *p)
3372 {
3373 if (p->arg[0] == 0) {
3374 /* Query */
3375 if (p->arg[2] == 1) {
3376 p->arg[1] = (word32_t) app_data.lowpass_mode;
3377 p->arg[3] = (word32_t) app_data.lowpass_freq;
3378 p->arg[4] = (word32_t) app_data.lowpass_width;
3379 }
3380 else if (p->arg[2] == 2) {
3381 p->arg[1] = (word32_t) app_data.highpass_mode;
3382 p->arg[3] = (word32_t) app_data.highpass_freq;
3383 p->arg[4] = (word32_t) app_data.highpass_width;
3384 }
3385 else
3386 p->retcode = CDA_PARMERR;
3387 }
3388 else {
3389 /* Set */
3390 int freq = (int) p->arg[3],
3391 width = (int) p->arg[4];
3392
3393 switch ((int) p->arg[2]) {
3394 case 1:
3395 app_data.lowpass_mode = (int) p->arg[1];
3396 if (app_data.lowpass_mode != FILTER_MANUAL)
3397 break;
3398
3399 if (freq < MIN_LOWPASS_FREQ ||
3400 freq > MAX_LOWPASS_FREQ) {
3401 p->retcode = CDA_PARMERR;
3402 break;
3403 }
3404
3405 app_data.lowpass_freq = freq;
3406 if (width >= 0)
3407 app_data.lowpass_width = width;
3408 break;
3409 case 2:
3410 app_data.highpass_mode = (int) p->arg[1];
3411 if (app_data.highpass_mode != FILTER_MANUAL)
3412 break;
3413
3414 if (freq < MIN_HIGHPASS_FREQ ||
3415 freq > MAX_HIGHPASS_FREQ) {
3416 p->retcode = CDA_PARMERR;
3417 break;
3418 }
3419
3420 app_data.highpass_freq = freq;
3421 if (width >= 0)
3422 app_data.highpass_width = width;
3423 break;
3424 default:
3425 p->retcode = CDA_PARMERR;
3426 break;
3427 }
3428 }
3429 }
3430
3431
3432 /*
3433 * cda_do_flags
3434 * Perform the "flags" command.
3435 *
3436 * Args:
3437 * s - Pointer to the curstat_t structure.
3438 * p - Pointer to the CDA packet structure.
3439 *
3440 * Return:
3441 * Nothing.
3442 */
3443 /*ARGSUSED*/
3444 STATIC void
cda_do_flags(curstat_t * s,cdapkt_t * p)3445 cda_do_flags(curstat_t *s, cdapkt_t *p)
3446 {
3447 if (p->arg[0] == 0) {
3448 /* Query */
3449 p->arg[1] = (word32_t) app_data.copyright;
3450 p->arg[2] = (word32_t) app_data.original;
3451 p->arg[3] = (word32_t) app_data.nores;
3452 p->arg[4] = (word32_t) app_data.checksum;
3453 p->arg[5] = (word32_t) app_data.strict_iso;
3454 }
3455 else {
3456 /* Set */
3457 if ((int) p->arg[1] >= 0)
3458 app_data.copyright = (bool_t) p->arg[1];
3459 if ((int) p->arg[2] >= 0)
3460 app_data.original = (bool_t) p->arg[2];
3461 if ((int) p->arg[3] >= 0)
3462 app_data.nores = (bool_t) p->arg[3];
3463 if ((int) p->arg[4] >= 0)
3464 app_data.checksum = (bool_t) p->arg[4];
3465 if ((int) p->arg[5] >= 0)
3466 app_data.strict_iso = (bool_t) p->arg[5];
3467 }
3468 }
3469
3470
3471 /*
3472 * cda_do_lameopts
3473 * Perform the "lameopts" command.
3474 *
3475 * Args:
3476 * s - Pointer to the curstat_t structure.
3477 * p - Pointer to the CDA packet structure.
3478 *
3479 * Return:
3480 * Nothing.
3481 */
3482 /*ARGSUSED*/
3483 STATIC void
cda_do_lameopts(curstat_t * s,cdapkt_t * p)3484 cda_do_lameopts(curstat_t *s, cdapkt_t *p)
3485 {
3486 char *cp;
3487 size_t maxsz = (CDA_NARGS - 2) * sizeof(word32_t);
3488
3489 cp = (char *) &p->arg[2];
3490
3491 if (p->arg[0] == 0) {
3492 /* Query */
3493 p->arg[1] = (word32_t) app_data.lameopts_mode;
3494 if (app_data.lame_opts == NULL)
3495 *cp = '\0';
3496 else {
3497 (void) strncpy(cp, app_data.lame_opts, maxsz - 1);
3498 *(cp + maxsz - 1) = '\0';
3499 }
3500 }
3501 else {
3502 /* Set */
3503 switch ((int) p->arg[1]) {
3504 case LAMEOPTS_DISABLE:
3505 case LAMEOPTS_INSERT:
3506 case LAMEOPTS_APPEND:
3507 case LAMEOPTS_REPLACE:
3508 app_data.lameopts_mode = (int) p->arg[1];
3509
3510 if (strcmp(cp, ".") != 0 &&
3511 !util_newstr(&app_data.lame_opts, cp)) {
3512 CDA_FATAL(app_data.str_nomemory);
3513 }
3514 break;
3515 default:
3516 p->retcode = CDA_PARMERR;
3517 break;
3518 }
3519 }
3520 }
3521
3522
3523 /*
3524 * cda_do_tag
3525 * Perform the "tag" command.
3526 *
3527 * Args:
3528 * s - Pointer to the curstat_t structure.
3529 * p - Pointer to the CDA packet structure.
3530 *
3531 * Return:
3532 * Nothing.
3533 */
3534 /*ARGSUSED*/
3535 STATIC void
cda_do_tag(curstat_t * s,cdapkt_t * p)3536 cda_do_tag(curstat_t *s, cdapkt_t *p)
3537 {
3538 if (p->arg[0] == 0) {
3539 /* Query */
3540 if (app_data.add_tag)
3541 p->arg[1] = (word32_t) app_data.id3tag_mode;
3542 else
3543 p->arg[1] = 0;
3544 }
3545 else {
3546 /* Set */
3547 switch ((int) p->arg[1]) {
3548 case 0:
3549 app_data.add_tag = FALSE;
3550 break;
3551 case ID3TAG_V1:
3552 case ID3TAG_V2:
3553 case ID3TAG_BOTH:
3554 app_data.add_tag = TRUE;
3555 app_data.id3tag_mode = (int) p->arg[1];
3556 break;
3557 default:
3558 p->retcode = CDA_PARMERR;
3559 break;
3560 }
3561 }
3562 }
3563
3564
3565 /*
3566 * cda_do_authuser
3567 * Pass the proxy authorization username from client to daemon.
3568 *
3569 * Args:
3570 * s - Pointer to the curstat_t structure.
3571 * p - Pointer to the CDA packet structure.
3572 *
3573 * Return:
3574 * Nothing.
3575 */
3576 /*ARGSUSED*/
3577 STATIC void
cda_do_authuser(curstat_t * s,cdapkt_t * p)3578 cda_do_authuser(curstat_t *s, cdapkt_t *p)
3579 {
3580 if (strncmp((char *) p->arg, "user", 4) != 0) {
3581 p->retcode = CDA_PARMERR;
3582 return;
3583 }
3584
3585 if (!util_newstr(&dbp->proxy_user, (char *) &p->arg[1]))
3586 CDA_FATAL(app_data.str_nomemory);
3587 }
3588
3589
3590 /*
3591 * cda_do_authpass
3592 * Pass the proxy authorization password from client to daemon.
3593 *
3594 * Args:
3595 * s - Pointer to the curstat_t structure.
3596 * p - Pointer to the CDA packet structure.
3597 *
3598 * Return:
3599 * Nothing.
3600 */
3601 /*ARGSUSED*/
3602 STATIC void
cda_do_authpass(curstat_t * s,cdapkt_t * p)3603 cda_do_authpass(curstat_t *s, cdapkt_t *p)
3604 {
3605 if (strncmp((char *) p->arg, "pass", 4) != 0) {
3606 p->retcode = CDA_PARMERR;
3607 return;
3608 }
3609
3610 if (!util_newstr(&dbp->proxy_passwd, (char *) &p->arg[1]))
3611 CDA_FATAL(app_data.str_nomemory);
3612 }
3613
3614
3615 /*
3616 * cda_do_motd
3617 * Perform the "motd" command.
3618 *
3619 * Args:
3620 * s - Pointer to the curstat_t structure.
3621 * p - Pointer to the CDA packet structure.
3622 *
3623 * Return:
3624 * Nothing.
3625 */
3626 /*ARGSUSED*/
3627 STATIC void
cda_do_motd(curstat_t * s,cdapkt_t * p)3628 cda_do_motd(curstat_t *s, cdapkt_t *p)
3629 {
3630 (void) printf("Retrieving messages...\n");
3631 (void) fflush(stdout);
3632 motd_get((byte_t *) 1);
3633 }
3634
3635
3636 /*
3637 * cda_do_device
3638 * Perform the "device" command.
3639 *
3640 * Args:
3641 * s - Pointer to the curstat_t structure.
3642 * p - Pointer to the CDA packet structure.
3643 *
3644 * Return:
3645 * Nothing.
3646 */
3647 STATIC void
cda_do_device(curstat_t * s,cdapkt_t * p)3648 cda_do_device(curstat_t *s, cdapkt_t *p)
3649 {
3650 (void) sprintf((char *) p->arg, "Drive: %s %s (%s)\n\n%s",
3651 s->vendor, s->prod, s->revnum, di_methodstr());
3652 }
3653
3654
3655 /*
3656 * cda_do_version
3657 * Perform the "version" command.
3658 *
3659 * Args:
3660 * s - Pointer to the curstat_t structure.
3661 * p - Pointer to the CDA packet structure.
3662 *
3663 * Return:
3664 * Nothing.
3665 */
3666 /*ARGSUSED*/
3667 STATIC void
cda_do_version(curstat_t * s,cdapkt_t * p)3668 cda_do_version(curstat_t *s, cdapkt_t *p)
3669 {
3670 (void) sprintf((char *) p->arg, "%s.%s.%s",
3671 VERSION_MAJ, VERSION_MIN, VERSION_TEENY);
3672 }
3673
3674
3675 /*
3676 * cda_do_debug
3677 * Perform the "debug" command.
3678 *
3679 * Args:
3680 * s - Pointer to the curstat_t structure.
3681 * p - Pointer to the CDA packet structure.
3682 *
3683 * Return:
3684 * Nothing.
3685 */
3686 /*ARGSUSED*/
3687 STATIC void
cda_do_debug(curstat_t * s,cdapkt_t * p)3688 cda_do_debug(curstat_t *s, cdapkt_t *p)
3689 {
3690 if (p->arg[0] == 0) {
3691 /* Query */
3692 p->arg[1] = (word32_t) app_data.debug;
3693 }
3694 else {
3695 /* Set level */
3696 app_data.debug = (word32_t) p->arg[1];
3697
3698 /* Debug level change notification */
3699 di_debug();
3700 }
3701 }
3702
3703
3704 /*
3705 * prn_program
3706 * Print current program sequence, if any.
3707 *
3708 * Args:
3709 * arg - Argument array from CD audio daemon response packet.
3710 *
3711 * Return:
3712 * Nothing.
3713 */
3714 STATIC void
prn_program(word32_t arg[])3715 prn_program(word32_t arg[])
3716 {
3717 int i;
3718
3719 if ((int) arg[0] > 0) {
3720 (void) printf("Current program:");
3721 for (i = 0; i < arg[0]; i++)
3722 (void) printf(" %d", (int) arg[i+2]);
3723 (void) printf("\n");
3724
3725 if ((int) arg[1] > 0) {
3726 (void) printf("Playing: ");
3727 for (i = 0; i < (int) (arg[1] - 1); i++) {
3728 (void) printf(" %s",
3729 ((int) arg[i+2] > 9) ? " " : "");
3730 }
3731 (void) printf(" %s^\n",
3732 ((int) arg[i+2] > 9) ? " " : "");
3733 }
3734 }
3735 else if ((int) arg[0] == 0) {
3736 if ((int) arg[2] == -1)
3737 (void) printf("No program sequence defined.\n");
3738 else if ((int) arg[2] == -2)
3739 (void) printf("Program sequence saved.\n");
3740 else if ((int) arg[2] == -3)
3741 (void) printf("Program sequence cleared.\n");
3742 }
3743 }
3744
3745
3746 /*
3747 * prn_volume
3748 * Print current volume setting.
3749 *
3750 * Args:
3751 * arg - Argument array from CD audio daemon response packet.
3752 *
3753 * Return:
3754 * Nothing.
3755 */
3756 STATIC void
prn_volume(word32_t arg[])3757 prn_volume(word32_t arg[])
3758 {
3759 char *tprstr;
3760
3761 if ((int) arg[0] == 0) {
3762 switch ((int) arg[2]) {
3763 case VOLTAPER_LINEAR:
3764 tprstr = "linear";
3765 break;
3766 case VOLTAPER_SQR:
3767 tprstr = "square";
3768 break;
3769 case VOLTAPER_INVSQR:
3770 tprstr = "invsqr";
3771 break;
3772 default:
3773 tprstr = "unknown";
3774 break;
3775 }
3776 (void) printf("Current volume: %d (range 0-100) %s taper\n",
3777 (int) arg[1], tprstr);
3778 }
3779 }
3780
3781
3782 /*
3783 * prn_balance
3784 * Print current balance setting.
3785 *
3786 * Args:
3787 * arg - Argument array from CD audio daemon response packet.
3788 *
3789 * Return:
3790 * Nothing.
3791 */
3792 STATIC void
prn_balance(word32_t arg[])3793 prn_balance(word32_t arg[])
3794 {
3795 if ((int) arg[0] == 0) {
3796 (void) printf("Current balance: %d (range 0-100, center:50)\n",
3797 (int) arg[1]);
3798 }
3799 }
3800
3801
3802 /*
3803 * prn_route
3804 * Print current channel routing setting.
3805 *
3806 * Args:
3807 * arg - Argument array from CD audio daemon response packet.
3808 *
3809 * Return:
3810 * Nothing.
3811 */
3812 STATIC void
prn_route(word32_t arg[])3813 prn_route(word32_t arg[])
3814 {
3815 if ((int) arg[0] == 0) {
3816 (void) printf("Current routing: %d ", (int) arg[1]);
3817
3818 switch ((int) arg[1]) {
3819 case CHROUTE_NORMAL:
3820 (void) printf("(normal stereo)\n");
3821 break;
3822 case CHROUTE_REVERSE:
3823 (void) printf("(reverse stereo)\n");
3824 break;
3825 case CHROUTE_L_MONO:
3826 (void) printf("(mono-L)\n");
3827 break;
3828 case CHROUTE_R_MONO:
3829 (void) printf("(mono-R)\n");
3830 break;
3831 case CHROUTE_MONO:
3832 (void) printf("(mono-L+R)\n");
3833 break;
3834 }
3835 }
3836 }
3837
3838
3839 /*
3840 * prn_outport
3841 * Print current CDDA output port setting.
3842 *
3843 * Args:
3844 * arg - Argument array from CD audio daemon response packet.
3845 *
3846 * Return:
3847 * Nothing.
3848 */
3849 STATIC void
prn_outport(word32_t arg[])3850 prn_outport(word32_t arg[])
3851 {
3852 if ((int) arg[0] == 0) {
3853 (void) printf("Current CDDA output port: %d ", (int) arg[1]);
3854
3855 if ((int) arg[1] != 0) {
3856 bool_t sep = FALSE;
3857
3858 (void) printf("(");
3859 if ((arg[1] & CDDA_OUT_SPEAKER) != 0) {
3860 (void) printf("speaker");
3861 sep = TRUE;
3862 }
3863 if ((arg[1] & CDDA_OUT_HEADPHONE) != 0) {
3864 (void) printf("%sheadphone", sep ? ", " : "");
3865 sep = TRUE;
3866 }
3867 if ((arg[1] & CDDA_OUT_LINEOUT) != 0) {
3868 (void) printf("%sline-out", sep ? ", " : "");
3869 }
3870 (void) printf(")");
3871 }
3872
3873 (void) printf("\n");
3874 }
3875 }
3876
3877
3878 /*
3879 * prn_cdda_att
3880 * Print current CDDA attenuator setting.
3881 *
3882 * Args:
3883 * arg - Argument array from CD audio daemon response packet.
3884 *
3885 * Return:
3886 * Nothing.
3887 */
3888 STATIC void
prn_cdda_att(word32_t arg[])3889 prn_cdda_att(word32_t arg[])
3890 {
3891 if ((int) arg[0] == 0) {
3892 (void) printf("Current CDDA attenuator: %d%% gain\n",
3893 (int) arg[1]);
3894 }
3895 }
3896
3897
3898 /*
3899 * prn_stat
3900 * Print current CD status.
3901 *
3902 * Args:
3903 * arg - Argument array from CD audio daemon response packet.
3904 *
3905 * Return:
3906 * Nothing.
3907 */
3908 STATIC void
prn_stat(word32_t arg[])3909 prn_stat(word32_t arg[])
3910 {
3911 unsigned int disc,
3912 trk,
3913 idx,
3914 min,
3915 sec;
3916
3917 if (stat_cont)
3918 (void) printf("\r");
3919
3920 switch (RD_ARG_MODE(arg[0])) {
3921 case MOD_BUSY:
3922 (void) printf("CD_Busy disc:- -- -- --:--");
3923 break;
3924 case MOD_NODISC:
3925 (void) printf("No_Disc disc:- -- -- --:--");
3926 break;
3927 case MOD_STOP:
3928 (void) printf("CD_Stopped disc:%u -- -- --:--",
3929 (unsigned int) arg[7]);
3930 break;
3931 case MOD_PLAY:
3932 disc = (unsigned int) arg[7];
3933 trk = (unsigned int) RD_ARG_TRK(arg[1]);
3934 idx = (unsigned int) RD_ARG_IDX(arg[1]);
3935 min = (unsigned int) RD_ARG_MIN(arg[1]);
3936 sec = (unsigned int) RD_ARG_SEC(arg[1]);
3937
3938 if (idx == 0 && app_data.subq_lba) {
3939 /* In LBA mode the time display is not meaningful
3940 * while in the lead-in, so just set to 0
3941 */
3942 min = sec = 0;
3943 }
3944
3945 (void) printf("CD_Playing disc:%u %02u %02u %s%02u:%02u",
3946 disc, trk, idx,
3947 (idx == 0) ? "-" : "+", min, sec);
3948 break;
3949 case MOD_PAUSE:
3950 disc = (unsigned int) arg[7];
3951 trk = (unsigned int) RD_ARG_TRK(arg[1]);
3952 idx = (unsigned int) RD_ARG_IDX(arg[1]);
3953 min = (unsigned int) RD_ARG_MIN(arg[1]);
3954 sec = (unsigned int) RD_ARG_SEC(arg[1]);
3955
3956 if (idx == 0 && app_data.subq_lba) {
3957 /* In LBA mode the time display is not meaningful
3958 * while in the lead-in, so just set to 0
3959 */
3960 min = sec = 0;
3961 }
3962
3963 (void) printf("CD_Paused disc:%u %02u %02u %s%02u:%02u",
3964 disc, trk, idx,
3965 (idx == 0) ? "-" : "+", min, sec);
3966 break;
3967 default:
3968 (void) printf("Inv_status disc:- -- -- --:--");
3969 break;
3970 }
3971
3972 (void) printf(" %slock", RD_ARG_LOCK(arg[0]) ? "+" : "-");
3973 (void) printf(" %sshuf", RD_ARG_SHUF(arg[0]) ? "+" : "-");
3974 (void) printf(" %sprog", RD_ARG_PROG(arg[0]) ? "+" : "-");
3975 (void) printf(" %srept", RD_ARG_REPT(arg[0]) ? "+" : "-");
3976
3977 if ((int) arg[2] >= 0)
3978 (void) printf(" %d", (int) arg[2]);
3979 else
3980 (void) printf(" -");
3981
3982 if (!stat_cont)
3983 (void) printf("\n");
3984 }
3985
3986
3987 /*
3988 * prn_toc
3989 * Print current CD Table Of Contents.
3990 *
3991 * Args:
3992 * arg - Argument array from CD audio daemon response packet.
3993 *
3994 * Return:
3995 * Nothing.
3996 */
3997 STATIC void
prn_toc(word32_t arg[])3998 prn_toc(word32_t arg[])
3999 {
4000 int i;
4001 byte_t ntrks,
4002 trkno,
4003 min,
4004 sec;
4005 bool_t playing;
4006 curstat_t *s = curstat_addr();
4007 chset_conv_t *up;
4008 char *p1,
4009 *p2,
4010 *q1,
4011 *q2;
4012
4013 /* Convert CD info from UTF-8 to local charset if possible */
4014 if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
4015 return;
4016
4017 ntrks = arg[0] & 0xff;
4018
4019 q1 = (dbp->disc.genre == NULL) ?
4020 "(unknown genre)" : cdinfo_genre_name(dbp->disc.genre);
4021 p1 = NULL;
4022 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4023 util_chset_close(up);
4024 return;
4025 }
4026 (void) printf("Genre: %s %s\n", p1 == NULL ? "" : p1,
4027 (dbp->disc.notes != NULL ||
4028 dbp->disc.credit_list != NULL) ? "*" : "");
4029 if (p1 != NULL)
4030 MEM_FREE(p1);
4031
4032 q1 = (dbp->disc.artist == NULL) ? "" : dbp->disc.artist;
4033 q2 = (dbp->disc.title == NULL) ? "(unknown title)" : dbp->disc.title;
4034 p1 = p2 = NULL;
4035 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4036 util_chset_close(up);
4037 return;
4038 }
4039 if (!util_chset_conv(up, q2, &p2, FALSE) && !util_newstr(&p2, q2)) {
4040 util_chset_close(up);
4041 return;
4042 }
4043 (void) printf("%s%s%s\n\n",
4044 p1 == NULL ? "" : p1,
4045 (dbp->disc.artist != NULL && dbp->disc.title != NULL) ?
4046 " / " : "",
4047 p2 == NULL ? "" : p2);
4048 if (p1 != NULL)
4049 MEM_FREE(p1);
4050 if (p2 != NULL)
4051 MEM_FREE(p2);
4052
4053 for (i = 0; i < (int) ntrks; i++) {
4054 RD_ARG_TOC(arg[i+1], trkno, playing, min, sec);
4055 (void) printf("%s%02u %02u:%02u ",
4056 playing ? ">" : " ", trkno, min, sec);
4057 if (s->qmode == QMODE_MATCH && dbp->track[i].title != NULL) {
4058 p1 = NULL;
4059 if (!util_chset_conv(up, dbp->track[i].title,
4060 &p1, FALSE) &&
4061 !util_newstr(&p1, dbp->track[i].title)) {
4062 util_chset_close(up);
4063 return;
4064 }
4065 (void) printf("%s%s\n", p1 == NULL ? "" : p1,
4066 (dbp->track[i].notes != NULL ||
4067 dbp->track[i].credit_list != NULL) ?
4068 "*" : "");
4069 if (p1 != NULL)
4070 MEM_FREE(p1);
4071 }
4072 else {
4073 (void) printf("??%s\n",
4074 (dbp->track[i].notes != NULL ||
4075 dbp->track[i].credit_list != NULL) ?
4076 "*" : "");
4077 }
4078 }
4079
4080 util_chset_close(up);
4081
4082 RD_ARG_TOC(arg[i+1], trkno, playing, min, sec);
4083 (void) printf("\nTotal Time: %02u:%02u\n", min, sec);
4084 }
4085
4086
4087 /*
4088 * prn_extinfo
4089 * Print current Disc and Track extended information.
4090 *
4091 * Args:
4092 * arg - Argument array from CD audio daemon response packet.
4093 *
4094 * Return:
4095 * Nothing.
4096 */
4097 STATIC void
prn_extinfo(word32_t arg[])4098 prn_extinfo(word32_t arg[])
4099 {
4100 curstat_t *s = curstat_addr();
4101 int i;
4102 cdinfo_credit_t *p;
4103 chset_conv_t *up;
4104 char *p1,
4105 *p2,
4106 *p3,
4107 *q1,
4108 *q2,
4109 *q3;
4110
4111 if (s->qmode != QMODE_MATCH) {
4112 (void) printf("No information was found for this CD\n");
4113 return;
4114 }
4115
4116 /* Convert CD info from UTF-8 to local charset if possible */
4117 if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
4118 return;
4119
4120 (void) printf("------- Album Information -------\n");
4121
4122 (void) printf("Xmcd disc ID: %08x\n", dbp->discid);
4123
4124 q1 = (dbp->disc.artist == NULL) ? "" : dbp->disc.artist;
4125 p1 = NULL;
4126 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4127 util_chset_close(up);
4128 return;
4129 }
4130 (void) printf("Artist: %s\n", p1);
4131 if (p1 != NULL)
4132 MEM_FREE(p1);
4133
4134 q1 = (dbp->disc.title == NULL) ? "" : dbp->disc.title;
4135 p1 = NULL;
4136 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4137 util_chset_close(up);
4138 return;
4139 }
4140 (void) printf("Title: %s\n", p1 == NULL ? "" : p1);
4141 if (p1 != NULL)
4142 MEM_FREE(p1);
4143
4144 q1 = (dbp->disc.artistfname.lastname == NULL) ?
4145 "" : dbp->disc.artistfname.lastname;
4146 q2 = (dbp->disc.artistfname.firstname == NULL) ?
4147 "" : dbp->disc.artistfname.firstname;
4148 q3 = (dbp->disc.artistfname.the == NULL) ?
4149 "" : dbp->disc.artistfname.the;
4150 p1 = p2 = p3 = NULL;
4151 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4152 util_chset_close(up);
4153 return;
4154 }
4155 if (!util_chset_conv(up, q2, &p2, FALSE) && !util_newstr(&p2, q2)) {
4156 util_chset_close(up);
4157 return;
4158 }
4159 if (!util_chset_conv(up, q3, &p3, FALSE) && !util_newstr(&p3, q3)) {
4160 util_chset_close(up);
4161 return;
4162 }
4163 (void) printf("Artist full name: %s%s%s%s%s\n",
4164 p1 == NULL ? "" : p1,
4165 dbp->disc.artistfname.lastname == NULL ? "" : ", ",
4166 p2 == NULL ? "" : p2,
4167 dbp->disc.artistfname.the == NULL ? "" : ", ",
4168 p3 == NULL ? "" : p3);
4169 if (p1 != NULL)
4170 MEM_FREE(p1);
4171 if (p2 != NULL)
4172 MEM_FREE(p2);
4173 if (p3 != NULL)
4174 MEM_FREE(p3);
4175
4176 q1 = (dbp->disc.sorttitle == NULL) ? "" : dbp->disc.sorttitle;
4177 q2 = (dbp->disc.title_the == NULL) ? "" : dbp->disc.title_the;
4178 p1 = p2 = NULL;
4179 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4180 util_chset_close(up);
4181 return;
4182 }
4183 if (!util_chset_conv(up, q2, &p2, FALSE) && !util_newstr(&p2, q2)) {
4184 util_chset_close(up);
4185 return;
4186 }
4187 (void) printf("Sort title: %s%s%s\n",
4188 p1 == NULL ? "" : p1,
4189 dbp->disc.title_the == NULL ? "" : ", ",
4190 p2 == NULL ? "" : p2);
4191 if (p1 != NULL)
4192 MEM_FREE(p1);
4193 if (p2 != NULL)
4194 MEM_FREE(p2);
4195
4196 q1 = (dbp->disc.year == NULL) ? "" : dbp->disc.year;
4197 p1 = NULL;
4198 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4199 util_chset_close(up);
4200 return;
4201 }
4202 (void) printf("Year: %s\n", p1 == NULL ? "" : p1);
4203 if (p1 != NULL)
4204 MEM_FREE(p1);
4205
4206 q1 = (dbp->disc.label == NULL) ? "" : dbp->disc.label;
4207 p1 = NULL;
4208 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4209 util_chset_close(up);
4210 return;
4211 }
4212 (void) printf("Record label: %s\n", p1 == NULL ? "" : p1);
4213 if (p1 != NULL)
4214 MEM_FREE(p1);
4215
4216 (void) printf("Compilation: %s\n",
4217 dbp->disc.compilation ? "Yes" : "No");
4218
4219 q1 = cdinfo_genre_name(dbp->disc.genre);
4220 p1 = NULL;
4221 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4222 util_chset_close(up);
4223 return;
4224 }
4225 (void) printf("Genre 1: %s\n", p1 == NULL ? "" : p1);
4226 if (p1 != NULL)
4227 MEM_FREE(p1);
4228
4229 q1 = cdinfo_genre_name(dbp->disc.genre2);
4230 p1 = NULL;
4231 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4232 util_chset_close(up);
4233 return;
4234 }
4235 (void) printf("Genre 2: %s\n", p1 == NULL ? "" : p1);
4236 if (p1 != NULL)
4237 MEM_FREE(p1);
4238
4239 q1 = dbp->disc.dnum == NULL ? "?" : dbp->disc.dnum;
4240 q2 = dbp->disc.tnum == NULL ? "?" : dbp->disc.tnum;
4241 p1 = p2 = NULL;
4242 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4243 util_chset_close(up);
4244 return;
4245 }
4246 if (!util_chset_conv(up, q2, &p2, FALSE) && !util_newstr(&p2, q2)) {
4247 util_chset_close(up);
4248 return;
4249 }
4250 (void) printf("Disc %s of %s\n",
4251 p1 == NULL ? "" : p1,
4252 p2 == NULL ? "" : p2);
4253 if (p1 != NULL)
4254 MEM_FREE(p1);
4255 if (p2 != NULL)
4256 MEM_FREE(p2);
4257
4258 (void) printf("Credits:\n");
4259 for (p = dbp->disc.credit_list; p != NULL; p = p->next) {
4260 q1 = p->crinfo.name == NULL ? "unknown" : p->crinfo.name;
4261 q2 = p->crinfo.role == NULL ?
4262 "unknown" : cdinfo_role_name(p->crinfo.role->id);
4263 p1 = p2 = NULL;
4264 if (!util_chset_conv(up, q1, &p1, FALSE) &&
4265 !util_newstr(&p1, q1)) {
4266 util_chset_close(up);
4267 return;
4268 }
4269 if (!util_chset_conv(up, q2, &p2, FALSE) &&
4270 !util_newstr(&p2, q2)) {
4271 util_chset_close(up);
4272 return;
4273 }
4274 (void) printf("\t%s (%s)\n",
4275 p1 == NULL ? "" : p1,
4276 p2 == NULL ? "" : p2);
4277 if (p1 != NULL)
4278 MEM_FREE(p1);
4279 if (p2 != NULL)
4280 MEM_FREE(p2);
4281 }
4282
4283 q1 = dbp->disc.region == NULL ?
4284 "" : cdinfo_region_name(dbp->disc.region);
4285 p1 = NULL;
4286 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4287 util_chset_close(up);
4288 return;
4289 }
4290 (void) printf("Region: %s\n", p1 == NULL ? "" : p1);
4291 if (p1 != NULL)
4292 MEM_FREE(p1);
4293
4294 q1 = dbp->disc.lang == NULL ? "" : cdinfo_lang_name(dbp->disc.lang);
4295 p1 = NULL;
4296 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4297 util_chset_close(up);
4298 return;
4299 }
4300 (void) printf("Language: %s\n", p1 == NULL ? "" : p1);
4301 if (p1 != NULL)
4302 MEM_FREE(p1);
4303
4304 q1 = dbp->disc.revision == NULL ? "" : dbp->disc.revision;
4305 p1 = NULL;
4306 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4307 util_chset_close(up);
4308 return;
4309 }
4310 (void) printf("Revision: %s\n", p1 == NULL ? "" : p1);
4311 if (p1 != NULL)
4312 MEM_FREE(p1);
4313
4314 q1 = dbp->disc.certifier == NULL ? "" : dbp->disc.certifier;
4315 p1 = NULL;
4316 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4317 util_chset_close(up);
4318 return;
4319 }
4320 (void) printf("Certifier: %s\n", p1 == NULL ? "" : p1);
4321 if (p1 != NULL)
4322 MEM_FREE(p1);
4323
4324 if ((int) arg[1] < 0) {
4325 util_chset_close(up);
4326 return;
4327 }
4328
4329 (void) printf("\n------ Track %02d Information ------\n",
4330 (int) arg[1]);
4331
4332 i = (int) arg[2];
4333
4334 q1 = dbp->track[i].title == NULL ? "" : dbp->track[i].title;
4335 p1 = NULL;
4336 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4337 util_chset_close(up);
4338 return;
4339 }
4340 (void) printf("Title: %s\n", p1 == NULL ? "" : p1);
4341 if (p1 != NULL)
4342 MEM_FREE(p1);
4343
4344 q1 = dbp->track[i].sorttitle == NULL ? "" : dbp->track[i].sorttitle;
4345 q2 = dbp->track[i].title_the == NULL ? "" : dbp->track[i].title_the;
4346 p1 = p2 = NULL;
4347 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4348 util_chset_close(up);
4349 return;
4350 }
4351 if (!util_chset_conv(up, q2, &p2, FALSE) && !util_newstr(&p2, q2)) {
4352 util_chset_close(up);
4353 return;
4354 }
4355 (void) printf("Sort Title: %s%s%s\n",
4356 p1 == NULL ? "" : p1,
4357 dbp->track[i].title_the == NULL ? "" : ", ",
4358 p2 == NULL ? "" : p2);
4359 if (p1 != NULL)
4360 MEM_FREE(p1);
4361 if (p2 != NULL)
4362 MEM_FREE(p2);
4363
4364 q1 = dbp->track[i].artist == NULL ? "" : dbp->track[i].artist;
4365 p1 = NULL;
4366 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4367 util_chset_close(up);
4368 return;
4369 }
4370 (void) printf("Artist: %s\n", p1);
4371 if (p1 != NULL)
4372 MEM_FREE(p1);
4373
4374 q1 = (dbp->track[i].artistfname.lastname == NULL) ?
4375 "" : dbp->track[i].artistfname.lastname;
4376 q2 = (dbp->track[i].artistfname.firstname == NULL) ?
4377 "" : dbp->track[i].artistfname.firstname;
4378 q3 = (dbp->track[i].artistfname.the == NULL) ?
4379 "" : dbp->track[i].artistfname.the;
4380 p1 = p2 = p3 = NULL;
4381 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4382 util_chset_close(up);
4383 return;
4384 }
4385 if (!util_chset_conv(up, q2, &p2, FALSE) && !util_newstr(&p2, q2)) {
4386 util_chset_close(up);
4387 return;
4388 }
4389 if (!util_chset_conv(up, q3, &p3, FALSE) && !util_newstr(&p3, q3)) {
4390 util_chset_close(up);
4391 return;
4392 }
4393 (void) printf("Artist full name: %s%s%s%s%s\n",
4394 p1 == NULL ? "" : p1,
4395 dbp->track[i].artistfname.lastname == NULL ? "" : ", ",
4396 p2 == NULL ? "" : p2,
4397 dbp->track[i].artistfname.the == NULL ? "" : ", ",
4398 p3 == NULL ? "" : p3);
4399 if (p1 != NULL)
4400 MEM_FREE(p1);
4401 if (p2 != NULL)
4402 MEM_FREE(p2);
4403 if (p3 != NULL)
4404 MEM_FREE(p3);
4405
4406 q1 = dbp->track[i].year == NULL ? "" : dbp->track[i].year;
4407 p1 = NULL;
4408 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4409 util_chset_close(up);
4410 return;
4411 }
4412 (void) printf("Year: %s\n", p1 == NULL ? "" : p1);
4413 if (p1 != NULL)
4414 MEM_FREE(p1);
4415
4416 q1 = dbp->track[i].label == NULL ? "" : dbp->track[i].label;
4417 p1 = NULL;
4418 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4419 util_chset_close(up);
4420 return;
4421 }
4422 (void) printf("Record Label: %s\n", p1 == NULL ? "" : p1);
4423 if (p1 != NULL)
4424 MEM_FREE(p1);
4425
4426 q1 = dbp->track[i].bpm == NULL ? "" : dbp->track[i].bpm;
4427 p1 = NULL;
4428 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4429 util_chset_close(up);
4430 return;
4431 }
4432 (void) printf("BPM: %s\n", p1 == NULL ? "" : p1);
4433 if (p1 != NULL)
4434 MEM_FREE(p1);
4435
4436 q1 = cdinfo_genre_name(dbp->track[i].genre);
4437 p1 = NULL;
4438 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4439 util_chset_close(up);
4440 return;
4441 }
4442 (void) printf("Genre 1: %s\n", p1 == NULL ? "" : p1);
4443 if (p1 != NULL)
4444 MEM_FREE(p1);
4445
4446 q1 = cdinfo_genre_name(dbp->track[i].genre2);
4447 p1 = NULL;
4448 if (!util_chset_conv(up, q1, &p1, FALSE) && !util_newstr(&p1, q1)) {
4449 util_chset_close(up);
4450 return;
4451 }
4452 (void) printf("Genre 2: %s\n", p1 == NULL ? "" : p1);
4453 if (p1 != NULL)
4454 MEM_FREE(p1);
4455
4456 (void) printf("Credits:\n");
4457 for (p = dbp->track[i].credit_list; p != NULL; p = p->next) {
4458 q1 = p->crinfo.name == NULL ? "unknown" : p->crinfo.name;
4459 q2 = p->crinfo.role == NULL ?
4460 "unknown" : cdinfo_role_name(p->crinfo.role->id);
4461 p1 = p2 = NULL;
4462 if (!util_chset_conv(up, q1, &p1, FALSE) &&
4463 !util_newstr(&p1, q1)) {
4464 util_chset_close(up);
4465 return;
4466 }
4467 if (!util_chset_conv(up, q2, &p2, FALSE) &&
4468 !util_newstr(&p2, q2)) {
4469 util_chset_close(up);
4470 return;
4471 }
4472 (void) printf("\t%s (%s)\n",
4473 p1 == NULL ? "" : p1,
4474 p2 == NULL ? "" : p2);
4475 if (p1 != NULL)
4476 MEM_FREE(p1);
4477 if (p2 != NULL)
4478 MEM_FREE(p2);
4479 }
4480
4481 util_chset_close(up);
4482 }
4483
4484
4485 /*
4486 * prn_notes
4487 * Print current Disc and Track Notes.
4488 *
4489 * Args:
4490 * arg - Argument array from CD audio daemon response packet.
4491 *
4492 * Return:
4493 * Nothing.
4494 */
4495 STATIC void
prn_notes(word32_t arg[])4496 prn_notes(word32_t arg[])
4497 {
4498 int i;
4499 curstat_t *s = curstat_addr();
4500 chset_conv_t *up;
4501 char *p1,
4502 *q1;
4503
4504 if (s->qmode != QMODE_MATCH) {
4505 (void) printf("No information was found for this CD\n");
4506 return;
4507 }
4508
4509 /* Convert CD info from UTF-8 to local charset if possible */
4510 if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
4511 return;
4512
4513 (void) printf("------- Album Notes -------\n");
4514
4515 if (dbp->disc.notes == NULL)
4516 (void) printf("(none)\n");
4517 else {
4518 q1 = (dbp->disc.title == NULL) ?
4519 app_data.str_unkndisc : dbp->disc.title;
4520 p1 = NULL;
4521 if (!util_chset_conv(up, q1, &p1, FALSE) &&
4522 !util_newstr(&p1, q1)) {
4523 util_chset_close(up);
4524 return;
4525 }
4526 (void) printf("%s%s%s\n\n",
4527 dbp->disc.title == NULL ? "(" : "",
4528 p1 == NULL ? "" : p1,
4529 dbp->disc.title == NULL ? ")" : "");
4530 if (p1 != NULL)
4531 MEM_FREE(p1);
4532
4533 q1 = dbp->disc.notes;
4534 p1 = NULL;
4535 if (!util_chset_conv(up, q1, &p1, FALSE) &&
4536 !util_newstr(&p1, q1)) {
4537 util_chset_close(up);
4538 return;
4539 }
4540 (void) printf("%s\n", p1 == NULL ? "" : p1);
4541 if (p1 != NULL)
4542 MEM_FREE(p1);
4543 }
4544
4545 if ((int) arg[1] < 0) {
4546 util_chset_close(up);
4547 return;
4548 }
4549
4550 (void) printf("\n------ Track %02d Notes ------\n",
4551 (int) arg[1]);
4552
4553 i = (int) arg[2];
4554 if (dbp->track[i].notes == NULL)
4555 (void) printf("(none)\n");
4556 else {
4557 q1 = (dbp->track[i].title == NULL) ?
4558 app_data.str_unkndisc : dbp->track[i].title;
4559 p1 = NULL;
4560 if (!util_chset_conv(up, q1, &p1, FALSE) &&
4561 !util_newstr(&p1, q1)) {
4562 util_chset_close(up);
4563 return;
4564 }
4565 (void) printf("%s%s%s\n\n",
4566 dbp->track[i].title == NULL ? "(" : "",
4567 p1 == NULL ? "" : p1,
4568 dbp->track[i].title == NULL ? ")" : "");
4569 if (p1 != NULL)
4570 MEM_FREE(p1);
4571
4572 q1 = dbp->track[i].notes;
4573 p1 = NULL;
4574 if (!util_chset_conv(up, q1, &p1, FALSE) &&
4575 !util_newstr(&p1, q1)) {
4576 util_chset_close(up);
4577 return;
4578 }
4579 (void) printf("%s\n", p1 == NULL ? "" : p1);
4580 if (p1 != NULL)
4581 MEM_FREE(p1);
4582 }
4583
4584 util_chset_close(up);
4585 }
4586
4587
4588 /*
4589 * prn_on_load
4590 * Print on-load mode.
4591 *
4592 * Args:
4593 * arg - Argument array from CD audio daemon response packet.
4594 *
4595 * Return:
4596 * Nothing.
4597 */
4598 STATIC void
prn_on_load(word32_t arg[])4599 prn_on_load(word32_t arg[])
4600 {
4601 if ((int) arg[0] == 0) {
4602 (void) printf("On load: %s%s%s\n",
4603 ((int) arg[1] == 0) ? "noautolock" : "autolock",
4604 ((int) arg[2] == 0) ? "" : " spindown",
4605 ((int) arg[3] == 0) ? "" : " autoplay");
4606 }
4607 }
4608
4609
4610 /*
4611 * prn_on_exit
4612 * Print on-exit mode.
4613 *
4614 * Args:
4615 * arg - Argument array from CD audio daemon response packet.
4616 *
4617 * Return:
4618 * Nothing.
4619 */
4620 STATIC void
prn_on_exit(word32_t arg[])4621 prn_on_exit(word32_t arg[])
4622 {
4623 if ((int) arg[0] == 0) {
4624 (void) printf("On exit: %s\n",
4625 ((int) arg[1] == 0 && (int) arg[2] == 0) ? "none" :
4626 ((int) arg[1] == 0) ? "autoeject" : "autostop");
4627 }
4628 }
4629
4630
4631 /*
4632 * prn_on_done
4633 * Print on-done mode.
4634 *
4635 * Args:
4636 * arg - Argument array from CD audio daemon response packet.
4637 *
4638 * Return:
4639 * Nothing.
4640 */
4641 STATIC void
prn_on_done(word32_t arg[])4642 prn_on_done(word32_t arg[])
4643 {
4644 if ((int) arg[0] == 0) {
4645 (void) printf("On done:%s%s%s\n",
4646 ((int) arg[1] == 0 && (int) arg[2] == 0) ?
4647 " none" : "",
4648 ((int) arg[1] == 0) ? "" : " autoeject",
4649 ((int) arg[2] == 0) ? "" : " autoexit");
4650 }
4651 }
4652
4653
4654 /*
4655 * prn_on_eject
4656 * Print on-eject mode.
4657 *
4658 * Args:
4659 * arg - Argument array from CD audio daemon response packet.
4660 *
4661 * Return:
4662 * Nothing.
4663 */
4664 STATIC void
prn_on_eject(word32_t arg[])4665 prn_on_eject(word32_t arg[])
4666 {
4667 if ((int) arg[0] == 0) {
4668 (void) printf("On eject: %s\n",
4669 ((int) arg[1] == 0) ? "none" : "autoexit");
4670 }
4671 }
4672
4673
4674 /*
4675 * prn_chgr
4676 * Print changer mode.
4677 *
4678 * Args:
4679 * arg - Argument array from CD audio daemon response packet.
4680 *
4681 * Return:
4682 * Nothing.
4683 */
4684 STATIC void
prn_chgr(word32_t arg[])4685 prn_chgr(word32_t arg[])
4686 {
4687 if ((int) arg[0] == 0) {
4688 (void) printf("Changer:%s%s%s\n",
4689 ((int) arg[1] == 0 && (int) arg[2] == 0) ?
4690 " none" : "",
4691 ((int) arg[1] == 0) ? "" : " multiplay",
4692 ((int) arg[2] == 0) ? "" : " reverse");
4693 }
4694 }
4695
4696
4697 /*
4698 * prn_mode
4699 * Print playback mode.
4700 *
4701 * Args:
4702 * arg - Argument array from CD audio daemon response packet.
4703 *
4704 * Return:
4705 * Nothing.
4706 */
4707 STATIC void
prn_mode(word32_t arg[])4708 prn_mode(word32_t arg[])
4709 {
4710 char modestr[STR_BUF_SZ];
4711
4712 if (PLAYMODE_IS_STD((byte_t) arg[1])) {
4713 (void) strcat(modestr, "standard");
4714 }
4715 else {
4716 (void) strcpy(modestr, "CDDA ");
4717 if ((arg[1] & PLAYMODE_CDDA) != 0) {
4718 (void) strcat(modestr, "cdda-play");
4719 }
4720 if ((arg[1] & PLAYMODE_FILE) != 0) {
4721 if ((arg[1] & PLAYMODE_CDDA) != 0)
4722 (void) strcat(modestr, "/cdda-save");
4723 else
4724 (void) strcat(modestr, "cdda-save");
4725 }
4726 if ((arg[1] & PLAYMODE_PIPE) != 0) {
4727 if ((arg[1] & PLAYMODE_CDDA) != 0 ||
4728 (arg[1] & PLAYMODE_FILE) != 0)
4729 (void) strcat(modestr, "/cdda-pipe");
4730 else
4731 (void) strcat(modestr, "cdda-pipe");
4732 }
4733 }
4734 (void) printf("Playback mode: %s\n", modestr);
4735 }
4736
4737
4738 /*
4739 * prn_jitter
4740 * Print jitter correction setting.
4741 *
4742 * Args:
4743 * arg - Argument array from CD audio daemon response packet.
4744 *
4745 * Return:
4746 * Nothing.
4747 */
4748 STATIC void
prn_jitter(word32_t arg[])4749 prn_jitter(word32_t arg[])
4750 {
4751 if ((int) arg[0] == 0) {
4752 (void) printf("Jitter correction is %s\n",
4753 ((int) arg[1]) ? "on" : "off");
4754 }
4755 }
4756
4757
4758 /*
4759 * prn_trkfile
4760 * Print trackfile setting.
4761 *
4762 * Args:
4763 * arg - Argument array from CD audio daemon response packet.
4764 *
4765 * Return:
4766 * Nothing.
4767 */
4768 STATIC void
prn_trkfile(word32_t arg[])4769 prn_trkfile(word32_t arg[])
4770 {
4771 if ((int) arg[0] == 0) {
4772 (void) printf("Trackfile is %s\n",
4773 ((int) arg[1]) ? "on" : "off");
4774 }
4775 }
4776
4777
4778 /*
4779 * prn_subst
4780 * Print subst setting.
4781 *
4782 * Args:
4783 * arg - Argument array from CD audio daemon response packet.
4784 *
4785 * Return:
4786 * Nothing.
4787 */
4788 STATIC void
prn_subst(word32_t arg[])4789 prn_subst(word32_t arg[])
4790 {
4791 if ((int) arg[0] == 0) {
4792 (void) printf("Underscore substitution is %s\n",
4793 ((int) arg[1]) ? "on" : "off");
4794 }
4795 }
4796
4797
4798 /*
4799 * prn_filefmt
4800 * Print output file format.
4801 *
4802 * Args:
4803 * arg - Argument array from CD audio daemon response packet.
4804 *
4805 * Return:
4806 * Nothing.
4807 */
4808 STATIC void
prn_filefmt(word32_t arg[])4809 prn_filefmt(word32_t arg[])
4810 {
4811 filefmt_t *fmp;
4812
4813 if ((int) arg[0] == 0) {
4814 if ((fmp = cdda_filefmt((int) arg[1])) != NULL)
4815 (void) printf("Output file format: %s (%s)\n",
4816 fmp->desc, fmp->suf);
4817 else
4818 (void) printf("Unknown file format.\n");
4819 }
4820 }
4821
4822
4823 /*
4824 * prn_file
4825 * Print output file.
4826 *
4827 * Args:
4828 * arg - Argument array from CD audio daemon response packet.
4829 *
4830 * Return:
4831 * Nothing.
4832 */
4833 STATIC void
prn_file(word32_t arg[])4834 prn_file(word32_t arg[])
4835 {
4836 char *cp;
4837
4838 if ((int) arg[0] == 0) {
4839 cp = (char *) &arg[1];
4840 if (*cp == '\0')
4841 (void) printf("No output file template defined.\n");
4842 else
4843 (void) printf("Output file template: %s\n", cp);
4844 }
4845 }
4846
4847
4848 /*
4849 * prn_pipeprog
4850 * Print pipe-to-program path.
4851 *
4852 * Args:
4853 * arg - Argument array from CD audio daemon response packet.
4854 *
4855 * Return:
4856 * Nothing.
4857 */
4858 STATIC void
prn_pipeprog(word32_t arg[])4859 prn_pipeprog(word32_t arg[])
4860 {
4861 char *cp;
4862
4863 if ((int) arg[0] == 0) {
4864 cp = (char *) &arg[1];
4865 if (*cp == '\0')
4866 (void) printf("No pipe-to program defined.\n");
4867 else
4868 (void) printf("Pipe-to program: %s\n", cp);
4869 }
4870 }
4871
4872
4873 /*
4874 * prn_compress
4875 * Print MP3/OggVorbis compression settings.
4876 *
4877 * Args:
4878 * arg - Argument array from CD audio daemon response packet.
4879 *
4880 * Return:
4881 * Nothing.
4882 */
4883 STATIC void
prn_compress(word32_t arg[])4884 prn_compress(word32_t arg[])
4885 {
4886 if ((int) arg[0] != 0)
4887 return;
4888
4889 switch ((int) arg[3]) {
4890 case FILEFMT_MP3:
4891 switch ((int) arg[1]) {
4892 case COMPMODE_0:
4893 (void) printf("CBR, bitrate %d kb/s\n",
4894 (int) arg[2]);
4895 break;
4896 case COMPMODE_1:
4897 (void) printf("VBR, quality %d\n", (int) arg[2]);
4898 break;
4899 case COMPMODE_2:
4900 (void) printf("VBR-2, quality %d\n", (int) arg[2]);
4901 break;
4902 case COMPMODE_3:
4903 (void) printf("ABR, average bitrate %d kb/s\n",
4904 (int) arg[2]);
4905 break;
4906 default:
4907 break;
4908 }
4909 break;
4910
4911 case FILEFMT_OGG:
4912 case FILEFMT_MP4:
4913 switch ((int) arg[1]) {
4914 case COMPMODE_0:
4915 case COMPMODE_3:
4916 (void) printf("VBR, average bitrate %d kb/s\n",
4917 (int) arg[2]);
4918 break;
4919 case COMPMODE_1:
4920 case COMPMODE_2:
4921 (void) printf("VBR, quality %d\n", (int) arg[2]);
4922 break;
4923 default:
4924 break;
4925 }
4926 break;
4927
4928 case FILEFMT_FLAC:
4929 /* TIKAN - not yet implemented */
4930 break;
4931
4932 case FILEFMT_AAC:
4933 switch ((int) arg[1]) {
4934 case COMPMODE_0:
4935 (void) printf("MPEG-2, VBR, average bitrate %d kb/s\n",
4936 (int) arg[2]);
4937 break;
4938 case COMPMODE_1:
4939 (void) printf("MPEG-2, VBR, quality %d\n",
4940 (int) arg[2]);
4941 break;
4942 case COMPMODE_2:
4943 (void) printf("MPEG-4, VBR, quality %d\n",
4944 (int) arg[2]);
4945 break;
4946 case COMPMODE_3:
4947 (void) printf("MPEG-4, VBR, average bitrate %d kb/s\n",
4948 (int) arg[2]);
4949 break;
4950 default:
4951 break;
4952 }
4953 break;
4954
4955 default:
4956 break;
4957 }
4958 }
4959
4960
4961 /*
4962 * prn_brate
4963 * Print min and max bitrate settings.
4964 *
4965 * Args:
4966 * arg - Argument array from CD audio daemon response packet.
4967 *
4968 * Return:
4969 * Nothing.
4970 */
4971 STATIC void
prn_brate(word32_t arg[])4972 prn_brate(word32_t arg[])
4973 {
4974 if ((int) arg[0] == 0) {
4975 (void) printf("%s bitrate: %d kb/s\n",
4976 (arg[1] == 1) ? "Minimum" : "Maximum",
4977 (int) arg[2]);
4978 }
4979 }
4980
4981
4982 /*
4983 * prn_coding
4984 * Print coding mode and algorithm settings.
4985 *
4986 * Args:
4987 * arg - Argument array from CD audio daemon response packet.
4988 *
4989 * Return:
4990 * Nothing.
4991 */
4992 STATIC void
prn_coding(word32_t arg[])4993 prn_coding(word32_t arg[])
4994 {
4995 char *modestr;
4996
4997 if ((int) arg[0] == 0) {
4998 switch ((int) arg[1]) {
4999 case CH_STEREO:
5000 modestr = "stereo";
5001 break;
5002 case CH_JSTEREO:
5003 modestr = "j-stereo";
5004 break;
5005 case CH_FORCEMS:
5006 modestr = "force-ms";
5007 break;
5008 case CH_MONO:
5009 modestr = "mono";
5010 break;
5011 default:
5012 modestr = "unknown";
5013 break;
5014 }
5015
5016 (void) printf("%s mode, algorithm %d\n",
5017 modestr, (int) arg[2]);
5018 }
5019 }
5020
5021
5022 /*
5023 * prn_filter
5024 * Print lowpass and highpass filter settings.
5025 *
5026 * Args:
5027 * arg - Argument array from CD audio daemon response packet.
5028 *
5029 * Return:
5030 * Nothing.
5031 */
5032 STATIC void
prn_filter(word32_t arg[])5033 prn_filter(word32_t arg[])
5034 {
5035 if ((int) arg[0] == 0) {
5036 char *fstr = (arg[2] == 1) ? "Lowpass" : "Highpass";
5037
5038 switch ((int) arg[1]) {
5039 case FILTER_OFF:
5040 (void) printf("%s filter: off\n", fstr);
5041 break;
5042 case FILTER_AUTO:
5043 (void) printf("%s filter: auto\n", fstr);
5044 break;
5045 case FILTER_MANUAL:
5046 (void) printf("%s filter: freq %d Hz, width %d Hz\n",
5047 fstr, (int) arg[3], (int) arg[4]);
5048 break;
5049 default:
5050 break;
5051 }
5052 }
5053 }
5054
5055
5056 /*
5057 * prn_flags
5058 * Print flags settings.
5059 *
5060 * Args:
5061 * arg - Argument array from CD audio daemon response packet.
5062 *
5063 * Return:
5064 * Nothing.
5065 */
5066 STATIC void
prn_flags(word32_t arg[])5067 prn_flags(word32_t arg[])
5068 {
5069 if ((int) arg[0] == 0) {
5070 (void) printf("%s\t%d\n%s\t%d\n%s\t%d\n%s\t%d\n%s\t%d\n",
5071 "Copyright:", arg[1],
5072 "Original:", arg[2],
5073 "No res:", arg[3],
5074 "Checksum:", arg[4],
5075 "Strict-ISO:", arg[5]);
5076 }
5077 }
5078
5079
5080 /*
5081 * prn_lameopts
5082 * Print direct LAME command line options.
5083 *
5084 * Args:
5085 * arg - Argument array from CD audio daemon response packet.
5086 *
5087 * Return:
5088 * Nothing.
5089 */
5090 STATIC void
prn_lameopts(word32_t arg[])5091 prn_lameopts(word32_t arg[])
5092 {
5093 char *cp,
5094 *str1,
5095 *str2;
5096
5097 if ((int) arg[0] == 0) {
5098 cp = (char *) &arg[2];
5099 (void) printf("Direct LAME command line options:%s%s\n",
5100 (cp[0] == '\0') ? " " : "\n",
5101 (cp[0] == '\0') ? "(none)" : cp);
5102
5103 str2 = " the standard settings";
5104
5105 switch ((int) arg[1]) {
5106 case LAMEOPTS_INSERT:
5107 str1 = "inserted before";
5108 break;
5109 case LAMEOPTS_APPEND:
5110 str1 = "appended after";
5111 break;
5112 case LAMEOPTS_REPLACE:
5113 str1 = "used instead of";
5114 break;
5115 case LAMEOPTS_DISABLE:
5116 default:
5117 str1 = "currently disabled";
5118 str2 = "";
5119 break;
5120 }
5121
5122 (void) printf("These options are %s%s.\n", str1, str2);
5123 }
5124 }
5125
5126
5127 /*
5128 * prn_tag
5129 * Print ID3 tag/comment tag settings.
5130 *
5131 * Args:
5132 * arg - Argument array from CD audio daemon response packet.
5133 *
5134 * Return:
5135 * Nothing.
5136 */
5137 STATIC void
prn_tag(word32_t arg[])5138 prn_tag(word32_t arg[])
5139 {
5140 if ((int) arg[0] == 0) {
5141 (void) printf("CD info tag: ");
5142
5143 switch ((int) arg[1]) {
5144 case 0:
5145 (void) printf("disabled.\n");
5146 break;
5147 case ID3TAG_V1:
5148 (void) printf("enabled, v1 only\n");
5149 break;
5150 case ID3TAG_V2:
5151 (void) printf("enabled, v2 only\n");
5152 break;
5153 case ID3TAG_BOTH:
5154 (void) printf("enabled, v1 and v2\n");
5155 break;
5156 default:
5157 (void) printf("invalid setting\n");
5158 break;
5159 }
5160 }
5161 }
5162
5163
5164 /*
5165 * prn_device
5166 * Print device information.
5167 *
5168 * Args:
5169 * arg - Argument array from CD audio daemon response packet.
5170 *
5171 * Return:
5172 * Nothing.
5173 */
5174 STATIC void
prn_device(word32_t arg[])5175 prn_device(word32_t arg[])
5176 {
5177 (void) printf("Device: %s\n", app_data.device);
5178 (void) printf("%s\n", (char *) arg);
5179 }
5180
5181
5182 /*
5183 * prn_version
5184 * Print version number and other information.
5185 *
5186 * Args:
5187 * arg - Argument array from CD audio daemon response packet.
5188 *
5189 * Return:
5190 * Nothing.
5191 */
5192 STATIC void
prn_version(word32_t arg[])5193 prn_version(word32_t arg[])
5194 {
5195 char *ctrlver;
5196
5197 ctrlver = cdinfo_cddbctrl_ver();
5198 (void) printf("CDA - Command Line CD Audio Player/Ripper\n\n");
5199 (void) printf("CD audio %s.%s.%s\n",
5200 VERSION_MAJ, VERSION_MIN, VERSION_TEENY);
5201 (void) printf("CD audio daemon %s\n", (char *) arg);
5202 (void) printf("\n%s\nURL: %s\nE-mail: %s\n\n%s\n\n",
5203 COPYRIGHT, XMCD_URL, EMAIL, GNU_BANNER);
5204 (void) printf("CDDB%s service%s%s\n%s\n",
5205 (cdinfo_cddb_ver() == 2) ? "\262" : " \"classic\"",
5206 (ctrlver[0] == '\0') ? "" : ": ",
5207 (ctrlver[0] == '\0') ? "\n" : ctrlver,
5208 CDDB_BANNER);
5209 }
5210
5211
5212 /*
5213 * prn_debug
5214 * Print debug mode.
5215 *
5216 * Args:
5217 * arg - Argument array from CD audio daemon response packet.
5218 *
5219 * Return:
5220 * Nothing.
5221 */
5222 STATIC void
prn_debug(word32_t arg[])5223 prn_debug(word32_t arg[])
5224 {
5225 (void) printf("Debug level is %s%u\n",
5226 ((int) arg[0] == 0) ? "" : "now ", arg[1]);
5227 }
5228
5229
5230 /* Service function mapping table */
5231 struct {
5232 word32_t cmd;
5233 void (*srvfunc)(curstat_t *, cdapkt_t *);
5234 void (*prnfunc)(word32_t *);
5235 } cmd_fmtab[] = {
5236 { CDA_ON, cda_do_onoff, NULL },
5237 { CDA_OFF, cda_do_onoff, NULL },
5238 { CDA_DISC, cda_do_disc, NULL },
5239 { CDA_LOCK, cda_do_lock, NULL },
5240 { CDA_PLAY, cda_do_play, NULL },
5241 { CDA_PAUSE, cda_do_pause, NULL },
5242 { CDA_STOP, cda_do_stop, NULL },
5243 { CDA_TRACK, cda_do_track, NULL },
5244 { CDA_INDEX, cda_do_index, NULL },
5245 { CDA_PROGRAM, cda_do_program, prn_program },
5246 { CDA_SHUFFLE, cda_do_shuffle, NULL },
5247 { CDA_REPEAT, cda_do_repeat, NULL },
5248 { CDA_VOLUME, cda_do_volume, prn_volume },
5249 { CDA_BALANCE, cda_do_balance, prn_balance },
5250 { CDA_ROUTE, cda_do_route, prn_route },
5251 { CDA_OUTPORT, cda_do_outport, prn_outport },
5252 { CDA_CDDA_ATT, cda_do_cdda_att, prn_cdda_att },
5253 { CDA_STATUS, cda_do_status, NULL },
5254 { CDA_TOC, cda_do_toc, prn_toc },
5255 { CDA_TOC2, cda_do_toc2, NULL },
5256 { CDA_EXTINFO, cda_do_extinfo, prn_extinfo },
5257 { CDA_ON_LOAD, cda_do_on_load, prn_on_load },
5258 { CDA_ON_EXIT, cda_do_on_exit, prn_on_exit },
5259 { CDA_ON_DONE, cda_do_on_done, prn_on_done },
5260 { CDA_ON_EJECT, cda_do_on_eject, prn_on_eject },
5261 { CDA_CHGR, cda_do_chgr, prn_chgr },
5262 { CDA_DEVICE, cda_do_device, prn_device },
5263 { CDA_VERSION, cda_do_version, prn_version },
5264 { CDA_DEBUG, cda_do_debug, prn_debug },
5265 { CDA_NOTES, cda_do_extinfo, prn_notes },
5266 { CDA_CDDBREG, NULL, NULL },
5267 { CDA_CDDBHINT, NULL, NULL },
5268 { CDA_MODE, cda_do_mode, prn_mode },
5269 { CDA_JITTER, cda_do_jitter, prn_jitter },
5270 { CDA_TRKFILE, cda_do_trkfile, prn_trkfile },
5271 { CDA_SUBST, cda_do_subst, prn_subst },
5272 { CDA_FILEFMT, cda_do_filefmt, prn_filefmt },
5273 { CDA_FILE, cda_do_file, prn_file },
5274 { CDA_PIPEPROG, cda_do_pipeprog, prn_pipeprog },
5275 { CDA_COMPRESS, cda_do_compress, prn_compress },
5276 { CDA_BRATE, cda_do_brate, prn_brate },
5277 { CDA_CODING, cda_do_coding, prn_coding },
5278 { CDA_FILTER, cda_do_filter, prn_filter },
5279 { CDA_FLAGS, cda_do_flags, prn_flags },
5280 { CDA_LAMEOPTS, cda_do_lameopts, prn_lameopts },
5281 { CDA_TAG, cda_do_tag, prn_tag },
5282 { CDA_AUTHUSER, cda_do_authuser, NULL },
5283 { CDA_AUTHPASS, cda_do_authpass, NULL },
5284 { CDA_MOTD, cda_do_motd, NULL },
5285 { 0, NULL, NULL }
5286 };
5287
5288
5289 /*
5290 * cda_docmd
5291 * Perform the command received.
5292 *
5293 * Args:
5294 * s - Pointer to the curstat_t structure.
5295 * p - Pointer to the CDA packet structure.
5296 *
5297 * Return:
5298 * TRUE - Received a CDA_OFF command: daemon should shut down
5299 * FALSE - Received normal command.
5300 */
5301 STATIC bool_t
cda_docmd(curstat_t * s,cdapkt_t * p)5302 cda_docmd(curstat_t *s, cdapkt_t *p)
5303 {
5304 int i;
5305 static time_t cur_time,
5306 prev_time;
5307
5308 /* Default status */
5309 p->retcode = CDA_OK;
5310
5311 /* Update CD status */
5312 if (p->cmd != CDA_OFF) {
5313 if (s->mode == MOD_NODISC || s->mode == MOD_BUSY ||
5314 (s->mode == MOD_STOP && app_data.load_play))
5315 (void) di_check_disc(s);
5316 else if (s->mode != MOD_STOP && s->mode != MOD_PAUSE) {
5317 prev_time = cur_time;
5318 cur_time = time(NULL);
5319
5320 if (cur_time != prev_time)
5321 di_status_upd(s);
5322 }
5323 }
5324
5325 p->retcode = CDA_INVALID;
5326
5327 /* Map the command to its service function and call it */
5328 for (i = 0; cmd_fmtab[i].cmd != 0; i++) {
5329 if (p->cmd == cmd_fmtab[i].cmd) {
5330 p->retcode = CDA_OK;
5331 (*cmd_fmtab[i].srvfunc)(s, p);
5332 break;
5333 }
5334 }
5335
5336 return (p->cmd == CDA_OFF);
5337 }
5338
5339
5340 /*
5341 * usage
5342 * Display command line usage syntax
5343 *
5344 * Args:
5345 * None.
5346 *
5347 * Return:
5348 * Nothing.
5349 */
5350 STATIC void
usage(void)5351 usage(void)
5352 {
5353 (void) fprintf(errfp,
5354 "%s %s.%s.%s Command-line CD player/ripper\n%s\n%s\n\n",
5355 PROGNAME, VERSION_MAJ, VERSION_MIN, VERSION_TEENY,
5356 COPYRIGHT, XMCD_URL
5357 );
5358
5359 (void) fprintf(errfp, "Usage: %s %s\n",
5360 PROGNAME,
5361 "[-dev device] [-batch] [-debug n#]\n"
5362 " [-online | -offline] command"
5363 );
5364
5365 (void) fprintf(errfp,
5366 "\nValid commands are:\n"
5367 "\ton\n"
5368 "\toff\n"
5369 "\tdisc <load | eject | next | prev | disc#>\n"
5370 "\tlock <on | off>\n"
5371 "\tplay [track# [min:sec]]\n"
5372 "\tpause\n"
5373 "\tstop\n"
5374 "\ttrack <prev | next>\n"
5375 "\tindex <prev | next>\n"
5376 "\tprogram [clear | save | track# ...]\n"
5377 "\tshuffle <on | off>\n"
5378 "\trepeat <on | off>\n"
5379 "\tvolume [value# | linear | square | invsqr] (value 0-100)\n"
5380 "\tbalance [value#] (value 0-100, center:50)\n"
5381 "\troute [stereo | reverse | mono-l | mono-r | mono | value#]\n"
5382 "\toutport [speaker | headphone | line-out | value#]\n"
5383 "\tcdda-att [value#] (value 0-100)\n"
5384 "\tstatus [cont [secs#]]\n"
5385 "\ttoc [offsets]\n"
5386 "\textinfo [track#]\n"
5387 "\tnotes [track#]\n"
5388 "\ton-load [none | spindown | autoplay | autolock | noautolock]\n"
5389 "\ton-exit [none | autostop | autoeject]\n"
5390 "\ton-done [autoeject | noautoeject | autoexit | noautoexit]\n"
5391 "\ton-eject [autoexit | noautoexit]\n"
5392 "\tchanger [multiplay | nomultiplay | reverse | noreverse]\n"
5393 "\tmode [standard | cdda-play | cdda-save | cdda-pipe]\n"
5394 "\tjittercorr [on | off]\n"
5395 "\ttrackfile [on | off]\n"
5396 "\tsubst [on | off]\n"
5397 "\tfilefmt [raw | au | wav | aiff | aiff-c | "
5398 "mp3 | ogg | flac | aac | mp4]\n"
5399 "\toutfile [\"template\"]\n"
5400 "\tpipeprog [\"path [arg ...]\"]\n"
5401 "\tcompress [<0 | 3> [bitrate#] | <1 | 2> [qual#]]\n"
5402 "\tmin-brate [bitrate#]\n"
5403 "\tmax-brate [bitrate#]\n"
5404 "\tcoding [stereo | j-stereo | force-ms | mono | algo#]\n"
5405 "\tlowpass [off | auto | freq# [width#]]\n"
5406 "\thighpass [off | auto | freq# [width#]]\n"
5407 "\tflags [C|c][O|o][N|n][E|e][I|i]\n"
5408 "\tlameopts [<disable | insert | append | replace> "
5409 "[\"options\"]]\n"
5410 "\ttag [off | v1 | v2 | both]\n"
5411 "\tmotd\n"
5412 "\tdevice\n"
5413 "\tversion\n"
5414 "\tcddbreg\n"
5415 "\tcddbhint\n"
5416 "\tdebug [level#]\n"
5417 "\tvisual\n"
5418 );
5419 }
5420
5421
5422 /*
5423 * parse_time
5424 * Parse a string of the form "min:sec" and convert to integer
5425 * minute and second values.
5426 *
5427 * Args:
5428 * str - Pointer to the "min:sec" string.
5429 * min - pointer to where the minute value is to be written.
5430 * sec - pointer to where the second value is to be written.
5431 *
5432 * Return:
5433 * TRUE - success
5434 * FALSE - failure
5435 */
5436 STATIC bool_t
parse_time(char * str,int * min,int * sec)5437 parse_time(char *str, int *min, int *sec)
5438 {
5439 char *p;
5440
5441 if ((p = strchr(str, ':')) == NULL)
5442 return FALSE;
5443
5444 if (!isdigit((int) *str) || !isdigit((int) *(p+1)))
5445 return FALSE;
5446
5447 *p = '\0';
5448 *min = atoi(str);
5449 *sec = atoi(p+1);
5450 *p = ':';
5451
5452 return TRUE;
5453 }
5454
5455
5456 /*
5457 * cda_parse_args
5458 * Parse CDA command line arguments.
5459 *
5460 * Args:
5461 * argc, argv
5462 * cmd - Pointer to the command code.
5463 * arg - Command argument array.
5464 *
5465 * Return:
5466 * TRUE - success
5467 * FALSE - failure
5468 */
5469 STATIC bool_t
cda_parse_args(int argc,char ** argv,word32_t * cmd,word32_t arg[])5470 cda_parse_args(int argc, char **argv, word32_t *cmd, word32_t arg[])
5471 {
5472 int i,
5473 j,
5474 min,
5475 sec;
5476
5477 /* Default values */
5478 *cmd = 0;
5479 (void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
5480
5481 /* Command line args handling */
5482 for (i = 1; i < argc; i++) {
5483 if (*cmd != 0) {
5484 /* Multiple commands specified */
5485 usage();
5486 return FALSE;
5487 }
5488
5489 if (strcmp(argv[i], "-dev") == 0) {
5490 if (++i < argc) {
5491 if (!di_isdemo())
5492 app_data.device = argv[i];
5493 }
5494 else {
5495 usage();
5496 return FALSE;
5497 }
5498 }
5499 else if (strcmp(argv[i], "-debug") == 0) {
5500 if (++i < argc && isdigit((int) argv[i][0]))
5501 app_data.debug = (word32_t) atoi(argv[i]);
5502 else {
5503 usage();
5504 return FALSE;
5505 }
5506 }
5507 else if (strcmp(argv[i], "-batch") == 0) {
5508 batch = TRUE;
5509 }
5510 else if (strcmp(argv[i], "-online") == 0) {
5511 inetoffln = 1;
5512 }
5513 else if (strcmp(argv[i], "-offline") == 0) {
5514 inetoffln = 2;
5515 }
5516 else if (strcmp(argv[i], "on") == 0) {
5517 *cmd = CDA_ON;
5518 }
5519 else if (strcmp(argv[i], "off") == 0) {
5520 *cmd = CDA_OFF;
5521 }
5522 else if (strcmp(argv[i], "disc") == 0) {
5523 /* <load | eject | next | prev | disc#> */
5524 if (++i < argc) {
5525 if (strcmp(argv[i], "load") == 0)
5526 arg[0] = 0;
5527 else if (strcmp(argv[i], "eject") == 0)
5528 arg[0] = 1;
5529 else if (strcmp(argv[i], "next") == 0)
5530 arg[0] = 2;
5531 else if (strcmp(argv[i], "prev") == 0)
5532 arg[0] = 3;
5533 else if (isdigit((int) argv[i][0])) {
5534 arg[0] = 4;
5535 arg[1] = atoi(argv[i]);
5536 }
5537 else {
5538 /* Wrong arg */
5539 usage();
5540 return FALSE;
5541 }
5542 }
5543 else {
5544 /* Missing arg */
5545 usage();
5546 return FALSE;
5547 }
5548 *cmd = CDA_DISC;
5549 }
5550 else if (strcmp(argv[i], "lock") == 0) {
5551 /* <on | off> */
5552 if (++i < argc) {
5553 if (strcmp(argv[i], "off") == 0)
5554 arg[0] = 0;
5555 else if (strcmp(argv[i], "on") == 0)
5556 arg[0] = 1;
5557 else {
5558 /* Wrong arg */
5559 usage();
5560 return FALSE;
5561 }
5562 }
5563 else {
5564 /* Missing arg */
5565 usage();
5566 return FALSE;
5567 }
5568 *cmd = CDA_LOCK;
5569 }
5570 else if (strcmp(argv[i], "play") == 0) {
5571 /* [track# [min:sec]] */
5572 if ((i+1) < argc && isdigit((int) argv[i+1][0])) {
5573 /* The user specified the track number */
5574 if ((arg[0] = atoi(argv[++i])) == 0) {
5575 /* Wrong arg */
5576 usage();
5577 return FALSE;
5578 }
5579
5580 if ((i+1) < argc &&
5581 parse_time(argv[i+1], &min, &sec)) {
5582 /* The user specified a time offset */
5583 arg[1] = min;
5584 arg[2] = sec;
5585 i++;
5586 }
5587 else {
5588 arg[1] = arg[2] = (word32_t) -1;
5589 }
5590 }
5591 *cmd = CDA_PLAY;
5592 }
5593 else if (strcmp(argv[i], "pause") == 0) {
5594 *cmd = CDA_PAUSE;
5595 }
5596 else if (strcmp(argv[i], "stop") == 0) {
5597 *cmd = CDA_STOP;
5598 }
5599 else if (strcmp(argv[i], "track") == 0) {
5600 /* <prev | next> */
5601 if (++i < argc) {
5602 if (strcmp(argv[i], "prev") == 0)
5603 arg[0] = 0;
5604 else if (strcmp(argv[i], "next") == 0)
5605 arg[0] = 1;
5606 else {
5607 /* Wrong arg */
5608 usage();
5609 return FALSE;
5610 }
5611 }
5612 else {
5613 /* Missing arg */
5614 usage();
5615 return FALSE;
5616 }
5617 *cmd = CDA_TRACK;
5618 }
5619 else if (strcmp(argv[i], "index") == 0) {
5620 /* <prev | next> */
5621 if (++i < argc) {
5622 if (strcmp(argv[i], "prev") == 0)
5623 arg[0] = 0;
5624 else if (strcmp(argv[i], "next") == 0)
5625 arg[0] = 1;
5626 else {
5627 /* Wrong arg */
5628 usage();
5629 return FALSE;
5630 }
5631 }
5632 else {
5633 /* Missing arg */
5634 usage();
5635 return FALSE;
5636 }
5637 *cmd = CDA_INDEX;
5638 }
5639 else if (strcmp(argv[i], "program") == 0) {
5640 /* [clear | save | track# ...] */
5641 arg[0] = 1;
5642
5643 if ((i+1) < argc) {
5644 if (strcmp(argv[i+1], "clear") == 0) {
5645 i++;
5646 arg[0] = 0;
5647 }
5648 else if (strcmp(argv[i+1], "save") == 0) {
5649 i++;
5650 arg[0] = 2;
5651 }
5652 else {
5653 j = 0;
5654 while ((i+1) < argc &&
5655 isdigit((int) argv[i+1][0]) &&
5656 j < (CDA_NARGS-2)) {
5657 arg[++j] = atoi(argv[++i]);
5658 }
5659 if (j > 0)
5660 arg[0] = (word32_t) -j;
5661 }
5662 }
5663 *cmd = CDA_PROGRAM;
5664 }
5665 else if (strcmp(argv[i], "shuffle") == 0) {
5666 /* <on | off> */
5667 if (++i < argc) {
5668 if (strcmp(argv[i], "off") == 0)
5669 arg[0] = 0;
5670 else if (strcmp(argv[i], "on") == 0)
5671 arg[0] = 1;
5672 else {
5673 /* Wrong arg */
5674 usage();
5675 return FALSE;
5676 }
5677 }
5678 else {
5679 /* Missing arg */
5680 usage();
5681 return FALSE;
5682 }
5683 *cmd = CDA_SHUFFLE;
5684 }
5685 else if (strcmp(argv[i], "repeat") == 0) {
5686 /* <on | off> */
5687 if (++i < argc) {
5688 if (strcmp(argv[i], "off") == 0)
5689 arg[0] = 0;
5690 else if (strcmp(argv[i], "on") == 0)
5691 arg[0] = 1;
5692 else {
5693 /* Wrong arg */
5694 usage();
5695 return FALSE;
5696 }
5697 }
5698 else {
5699 /* Missing arg */
5700 usage();
5701 return FALSE;
5702 }
5703 *cmd = CDA_REPEAT;
5704 }
5705 else if (strcmp(argv[i], "volume") == 0) {
5706 /* [value# | linear | square | invsqr] */
5707 if ((i+1) >= argc) {
5708 /* Query */
5709 arg[0] = 0;
5710 }
5711 else if (strcmp(argv[i+1], "linear") == 0) {
5712 arg[0] = 2;
5713 arg[2] = VOLTAPER_LINEAR;
5714 i++;
5715 }
5716 else if (strcmp(argv[i+1], "square") == 0) {
5717 arg[0] = 2;
5718 arg[2] = VOLTAPER_SQR;
5719 i++;
5720 }
5721 else if (strcmp(argv[i+1], "invsqr") == 0) {
5722 arg[0] = 2;
5723 arg[2] = VOLTAPER_INVSQR;
5724 i++;
5725 }
5726 else if (isdigit((int) argv[i+1][0])) {
5727 /* Set volume level */
5728 arg[0] = 1;
5729 arg[1] = (word32_t) atoi(argv[++i]);
5730 }
5731 else {
5732 /* Wrong arg */
5733 usage();
5734 return FALSE;
5735 }
5736 *cmd = CDA_VOLUME;
5737 }
5738 else if (strcmp(argv[i], "balance") == 0) {
5739 /* [value#] */
5740 if ((i+1) >= argc || !isdigit((int) argv[i+1][0]))
5741 /* Query */
5742 arg[0] = 0;
5743 else {
5744 /* Set */
5745 arg[0] = 1;
5746 arg[1] = (word32_t) atoi(argv[++i]);
5747 }
5748 *cmd = CDA_BALANCE;
5749 }
5750 else if (strcmp(argv[i], "route") == 0) {
5751 /* [stereo | reverse | mono-l | mono-r |
5752 * mono | value#]
5753 */
5754 if ((i+1) >= argc) {
5755 /* Query */
5756 arg[0] = 0;
5757 }
5758 else if (strcmp(argv[i+1], "stereo") == 0) {
5759 arg[0] = 1;
5760 arg[1] = CHROUTE_NORMAL;
5761 i++;
5762 }
5763 else if (strcmp(argv[i+1], "reverse") == 0) {
5764 arg[0] = 1;
5765 arg[1] = CHROUTE_REVERSE;
5766 i++;
5767 }
5768 else if (strcmp(argv[i+1], "mono-l") == 0) {
5769 arg[0] = 1;
5770 arg[1] = CHROUTE_L_MONO;
5771 i++;
5772 }
5773 else if (strcmp(argv[i+1], "mono-r") == 0) {
5774 arg[0] = 1;
5775 arg[1] = CHROUTE_R_MONO;
5776 i++;
5777 }
5778 else if (strcmp(argv[i+1], "mono") == 0) {
5779 arg[0] = 1;
5780 arg[1] = CHROUTE_MONO;
5781 i++;
5782 }
5783 else if (isdigit((int) argv[i+1][0])) {
5784 /* value# */
5785 arg[0] = 1;
5786 arg[1] = (word32_t) atoi(argv[++i]);
5787 }
5788 else {
5789 /* Wrong arg */
5790 usage();
5791 return FALSE;
5792 }
5793 *cmd = CDA_ROUTE;
5794 }
5795 else if (strcmp(argv[i], "outport") == 0) {
5796 /* [speaker | headphone | line-out | value#] */
5797 if ((i+1) >= argc) {
5798 /* Query */
5799 arg[0] = 0;
5800 }
5801 else if (strcmp(argv[i+1], "speaker") == 0) {
5802 arg[0] = 1;
5803 arg[1] = CDDA_OUT_SPEAKER;
5804 i++;
5805 }
5806 else if (strcmp(argv[i+1], "headphone") == 0) {
5807 arg[0] = 1;
5808 arg[1] = CDDA_OUT_HEADPHONE;
5809 i++;
5810 }
5811 else if (strcmp(argv[i+1], "line-out") == 0) {
5812 arg[0] = 1;
5813 arg[1] = CDDA_OUT_LINEOUT;
5814 i++;
5815 }
5816 else if (isdigit((int) argv[i+1][0])) {
5817 /* value# */
5818 arg[0] = 2;
5819 arg[1] = (word32_t) atoi(argv[++i]);
5820 }
5821 else {
5822 /* Wrong arg */
5823 usage();
5824 return FALSE;
5825 }
5826 *cmd = CDA_OUTPORT;
5827 }
5828 else if (strcmp(argv[i], "cdda-att") == 0) {
5829 /* [value#] */
5830 if ((i+1) >= argc || !isdigit((int) argv[i+1][0]))
5831 /* Query */
5832 arg[0] = 0;
5833 else {
5834 /* Set */
5835 arg[0] = 1;
5836 arg[1] = (word32_t) atoi(argv[++i]);
5837 }
5838 *cmd = CDA_CDDA_ATT;
5839 }
5840 else if (strcmp(argv[i], "status") == 0) {
5841 /* [cont [secs#]] */
5842 if ((i+1) >= argc || strcmp(argv[i+1], "cont") != 0)
5843 stat_cont = FALSE;
5844 else {
5845 i++;
5846 stat_cont = TRUE;
5847 if ((i+1) < argc &&
5848 isdigit((int) argv[i+1][0]))
5849 cont_delay = atoi(argv[++i]);
5850 }
5851 *cmd = CDA_STATUS;
5852 }
5853 else if (strcmp(argv[i], "toc") == 0) {
5854 /* [offsets] */
5855 if ((i+1) >= argc || strcmp(argv[i+1], "offsets") != 0)
5856 arg[0] = 0;
5857 else {
5858 i++;
5859 arg[0] = 1;
5860 }
5861 *cmd = CDA_TOC;
5862 }
5863 else if (strcmp(argv[i], "extinfo") == 0) {
5864 /* [track#] */
5865 arg[0] = 0;
5866 if ((i+1) >= argc || !isdigit((int) argv[i+1][0]))
5867 arg[1] = (word32_t) -1;
5868 else
5869 arg[1] = atoi(argv[++i]);
5870
5871 *cmd = CDA_EXTINFO;
5872 }
5873 else if (strcmp(argv[i], "notes") == 0) {
5874 /* [track#] */
5875 arg[0] = 0;
5876 if ((i+1) >= argc || !isdigit((int) argv[i+1][0]))
5877 arg[1] = (word32_t) -1;
5878 else
5879 arg[1] = atoi(argv[++i]);
5880
5881 *cmd = CDA_NOTES;
5882 }
5883 else if (strcmp(argv[i], "on-load") == 0) {
5884 /* [none | spindown | autoplay | autolock | noautolock]
5885 */
5886 if ((i+1) >= argc) {
5887 /* Query */
5888 arg[0] = 0;
5889 }
5890 else if (strcmp(argv[i+1], "none") == 0) {
5891 arg[0] = 1;
5892 arg[1] = 0;
5893 i++;
5894 }
5895 else if (strcmp(argv[i+1], "spindown") == 0) {
5896 arg[0] = 1;
5897 arg[1] = 1;
5898 i++;
5899 }
5900 else if (strcmp(argv[i+1], "autoplay") == 0) {
5901 arg[0] = 1;
5902 arg[1] = 2;
5903 i++;
5904 }
5905 else if (strcmp(argv[i+1], "noautolock") == 0) {
5906 arg[0] = 1;
5907 arg[1] = 3;
5908 i++;
5909 }
5910 else if (strcmp(argv[i+1], "autolock") == 0) {
5911 arg[0] = 1;
5912 arg[1] = 4;
5913 i++;
5914 }
5915 else {
5916 /* Wrong arg */
5917 usage();
5918 return FALSE;
5919 }
5920 *cmd = CDA_ON_LOAD;
5921 }
5922 else if (strcmp(argv[i], "on-exit") == 0) {
5923 /* [none | autostop | autoeject] */
5924 if ((i+1) >= argc) {
5925 /* Query */
5926 arg[0] = 0;
5927 }
5928 else if (strcmp(argv[i+1], "none") == 0) {
5929 arg[0] = 1;
5930 arg[1] = 0;
5931 i++;
5932 }
5933 else if (strcmp(argv[i+1], "autostop") == 0) {
5934 arg[0] = 1;
5935 arg[1] = 1;
5936 i++;
5937 }
5938 else if (strcmp(argv[i+1], "autoeject") == 0) {
5939 arg[0] = 1;
5940 arg[1] = 2;
5941 i++;
5942 }
5943 else {
5944 /* Wrong arg */
5945 usage();
5946 return FALSE;
5947 }
5948 *cmd = CDA_ON_EXIT;
5949 }
5950 else if (strcmp(argv[i], "on-done") == 0) {
5951 /* [autoeject | noautoeject | autoexit | noautoexit]
5952 */
5953 if ((i+1) >= argc) {
5954 /* Query */
5955 arg[0] = 0;
5956 }
5957 else if (strcmp(argv[i+1], "noautoeject") == 0) {
5958 arg[0] = 1;
5959 arg[1] = 0;
5960 i++;
5961 }
5962 else if (strcmp(argv[i+1], "autoeject") == 0) {
5963 arg[0] = 1;
5964 arg[1] = 1;
5965 i++;
5966 }
5967 else if (strcmp(argv[i+1], "noautoexit") == 0) {
5968 arg[0] = 1;
5969 arg[1] = 2;
5970 i++;
5971 }
5972 else if (strcmp(argv[i+1], "autoexit") == 0) {
5973 arg[0] = 1;
5974 arg[1] = 3;
5975 i++;
5976 }
5977 else {
5978 /* Wrong arg */
5979 usage();
5980 return FALSE;
5981 }
5982 *cmd = CDA_ON_DONE;
5983 }
5984 else if (strcmp(argv[i], "on-eject") == 0) {
5985 /* [autoexit | noautoexit] */
5986 if ((i+1) >= argc) {
5987 /* Query */
5988 arg[0] = 0;
5989 }
5990 else if (strcmp(argv[i+1], "noautoexit") == 0) {
5991 arg[0] = 1;
5992 arg[1] = 0;
5993 i++;
5994 }
5995 else if (strcmp(argv[i+1], "autoexit") == 0) {
5996 arg[0] = 1;
5997 arg[1] = 1;
5998 i++;
5999 }
6000 else {
6001 /* Wrong arg */
6002 usage();
6003 return FALSE;
6004 }
6005 *cmd = CDA_ON_EJECT;
6006 }
6007 else if (strcmp(argv[i], "changer") == 0) {
6008 /* [multiplay | nomultiplay | reverse | noreverse]
6009 */
6010 if ((i+1) >= argc) {
6011 /* Query */
6012 arg[0] = 0;
6013 }
6014 else if (strcmp(argv[i+1], "nomultiplay") == 0) {
6015 arg[0] = 1;
6016 arg[1] = 0;
6017 i++;
6018 }
6019 else if (strcmp(argv[i+1], "multiplay") == 0) {
6020 arg[0] = 1;
6021 arg[1] = 1;
6022 i++;
6023 }
6024 else if (strcmp(argv[i+1], "noreverse") == 0) {
6025 arg[0] = 1;
6026 arg[1] = 2;
6027 i++;
6028 }
6029 else if (strcmp(argv[i+1], "reverse") == 0) {
6030 arg[0] = 1;
6031 arg[1] = 3;
6032 i++;
6033 }
6034 else {
6035 /* Wrong arg */
6036 usage();
6037 return FALSE;
6038 }
6039 *cmd = CDA_CHGR;
6040 }
6041 else if (strcmp(argv[i], "mode") == 0) {
6042 /* [standard | cdda-play | cdda-save | cdda-save-play]
6043 */
6044 if ((i+1) >= argc) {
6045 /* Query */
6046 arg[0] = 0;
6047 }
6048 else if (strcmp(argv[i+1], "standard") == 0) {
6049 arg[0] = 1;
6050 arg[1] = PLAYMODE_STD;
6051 i++;
6052 }
6053 else if (strcmp(argv[i+1], "cdda-play") == 0) {
6054 arg[0] = 1;
6055 arg[1] = PLAYMODE_CDDA;
6056 i++;
6057 }
6058 else if (strcmp(argv[i+1], "cdda-save") == 0) {
6059 arg[0] = 1;
6060 arg[1] = PLAYMODE_FILE;
6061 i++;
6062 }
6063 else if (strcmp(argv[i+1], "cdda-pipe") == 0) {
6064 arg[0] = 1;
6065 arg[1] = PLAYMODE_PIPE;
6066 i++;
6067 }
6068 else {
6069 /* Wrong arg */
6070 usage();
6071 return FALSE;
6072 }
6073 *cmd = CDA_MODE;
6074 }
6075 else if (strcmp(argv[i], "jittercorr") == 0) {
6076 /* [on | off] */
6077 if ((i+1) >= argc) {
6078 /* Query */
6079 arg[0] = 0;
6080 }
6081 else if (strcmp(argv[i+1], "on") == 0) {
6082 arg[0] = 1;
6083 arg[1] = 1;
6084 i++;
6085 }
6086 else if (strcmp(argv[i+1], "off") == 0) {
6087 arg[0] = 1;
6088 arg[1] = 0;
6089 i++;
6090 }
6091 else {
6092 /* Wrong arg */
6093 usage();
6094 return FALSE;
6095 }
6096 *cmd = CDA_JITTER;
6097 }
6098 else if (strcmp(argv[i], "trackfile") == 0) {
6099 /* [on | off] */
6100 if ((i+1) >= argc) {
6101 /* Query */
6102 arg[0] = 0;
6103 }
6104 else if (strcmp(argv[i+1], "on") == 0) {
6105 arg[0] = 1;
6106 arg[1] = 1;
6107 i++;
6108 }
6109 else if (strcmp(argv[i+1], "off") == 0) {
6110 arg[0] = 1;
6111 arg[1] = 0;
6112 i++;
6113 }
6114 else {
6115 /* Wrong arg */
6116 usage();
6117 return FALSE;
6118 }
6119 *cmd = CDA_TRKFILE;
6120 }
6121 else if (strcmp(argv[i], "subst") == 0) {
6122 /* [on | off] */
6123 if ((i+1) >= argc) {
6124 /* Query */
6125 arg[0] = 0;
6126 }
6127 else if (strcmp(argv[i+1], "on") == 0) {
6128 arg[0] = 1;
6129 arg[1] = 1;
6130 i++;
6131 }
6132 else if (strcmp(argv[i+1], "off") == 0) {
6133 arg[0] = 1;
6134 arg[1] = 0;
6135 i++;
6136 }
6137 else {
6138 /* Wrong arg */
6139 usage();
6140 return FALSE;
6141 }
6142 *cmd = CDA_SUBST;
6143 }
6144 else if (strcmp(argv[i], "filefmt") == 0) {
6145 /* [ raw | au | wav | aiff | aiff-c |
6146 * mp3 | ogg | flac | aac | mp4 ]
6147 */
6148 if ((i+1) >= argc) {
6149 /* Query */
6150 arg[0] = 0;
6151 }
6152 else if (util_strcasecmp(argv[i+1], "raw") == 0) {
6153 arg[0] = 1;
6154 arg[1] = FILEFMT_RAW;
6155 i++;
6156 }
6157 else if (util_strcasecmp(argv[i+1], "au") == 0) {
6158 arg[0] = 1;
6159 arg[1] = FILEFMT_AU;
6160 i++;
6161 }
6162 else if (util_strcasecmp(argv[i+1], "wav") == 0 ||
6163 util_strcasecmp(argv[i+1], "wave") == 0) {
6164 arg[0] = 1;
6165 arg[1] = FILEFMT_WAV;
6166 i++;
6167 }
6168 else if (util_strcasecmp(argv[i+1], "aiff") == 0) {
6169 arg[0] = 1;
6170 arg[1] = FILEFMT_AIFF;
6171 i++;
6172 }
6173 else if (util_strcasecmp(argv[i+1], "aiff-c") == 0 ||
6174 util_strcasecmp(argv[i+1], "aifc") == 0) {
6175 arg[0] = 1;
6176 arg[1] = FILEFMT_AIFC;
6177 i++;
6178 }
6179 else if (util_strcasecmp(argv[i+1], "mp3") == 0) {
6180 arg[0] = 1;
6181 arg[1] = FILEFMT_MP3;
6182 i++;
6183 }
6184 else if (util_strcasecmp(argv[i+1], "ogg") == 0 ||
6185 util_strcasecmp(argv[i+1], "vorbis") == 0) {
6186 arg[0] = 1;
6187 arg[1] = FILEFMT_OGG;
6188 i++;
6189 }
6190 else if (util_strcasecmp(argv[i+1], "flac") == 0) {
6191 arg[0] = 1;
6192 arg[1] = FILEFMT_FLAC;
6193 i++;
6194 }
6195 else if (util_strcasecmp(argv[i+1], "aac") == 0) {
6196 arg[0] = 1;
6197 arg[1] = FILEFMT_AAC;
6198 i++;
6199 }
6200 else if (util_strcasecmp(argv[i+1], "mp4") == 0) {
6201 arg[0] = 1;
6202 arg[1] = FILEFMT_MP4;
6203 i++;
6204 }
6205 else {
6206 /* Wrong arg */
6207 usage();
6208 return FALSE;
6209 }
6210 *cmd = CDA_FILEFMT;
6211 }
6212 else if (strcmp(argv[i], "outfile") == 0) {
6213 if (++i < argc) {
6214 /* Set */
6215 struct stat stbuf;
6216
6217 arg[0] = 1;
6218 if (strlen(argv[i]) >=
6219 ((CDA_NARGS - 1) * sizeof(word32_t))) {
6220 (void) fprintf(errfp,
6221 "%s: file name too long.\n",
6222 argv[i]);
6223 return FALSE;
6224 }
6225 if (LSTAT(argv[i], &stbuf) == 0 &&
6226 !S_ISREG(stbuf.st_mode)) {
6227 (void) fprintf(errfp, "%s: %s\n",
6228 argv[i],
6229 app_data.str_notregfile);
6230 return FALSE;
6231 }
6232 (void) strcpy((char *) &arg[1], argv[i]);
6233 }
6234 else {
6235 /* Query */
6236 arg[0] = 0;
6237 }
6238 *cmd = CDA_FILE;
6239 }
6240 else if (strcmp(argv[i], "pipeprog") == 0) {
6241 if (++i < argc) {
6242 /* Set */
6243 arg[0] = 1;
6244 if (strlen(argv[i]) >=
6245 ((CDA_NARGS - 1) * sizeof(word32_t))) {
6246 (void) fprintf(errfp,
6247 "%s: file name too long.\n",
6248 argv[i]);
6249 return FALSE;
6250 }
6251 (void) strcpy((char *) &arg[1], argv[i]);
6252 }
6253 else {
6254 /* Query */
6255 arg[0] = 0;
6256 }
6257 *cmd = CDA_PIPEPROG;
6258 }
6259 else if (strcmp(argv[i], "compress") == 0) {
6260 /* [<0|3> [bitrate#] | <1|2> [qual#]] */
6261 if ((i+1) >= argc) {
6262 /* Query */
6263 arg[0] = 0;
6264 }
6265 else if (strcmp(argv[i+1], "0") == 0 ||
6266 strcmp(argv[i+1], "cbr") == 0) {
6267 arg[0] = 1;
6268 arg[1] = COMPMODE_0;
6269 i++;
6270 if ((i+1) >= argc ||
6271 !isdigit((int) argv[i+1][0]))
6272 arg[2] = (word32_t) -1;
6273 else
6274 arg[2] = atoi(argv[++i]);
6275 }
6276 else if (strcmp(argv[i+1], "3") == 0 ||
6277 strcmp(argv[i+1], "abr") == 0) {
6278 arg[0] = 1;
6279 arg[1] = COMPMODE_3;
6280 i++;
6281 if ((i+1) >= argc ||
6282 !isdigit((int) argv[i+1][0]))
6283 arg[2] = (word32_t) -1;
6284 else
6285 arg[2] = atoi(argv[++i]);
6286 }
6287 else if (strcmp(argv[i+1], "1") == 0 ||
6288 strcmp(argv[i+1], "vbr") == 0) {
6289 arg[0] = 1;
6290 arg[1] = COMPMODE_1;
6291 i++;
6292 if ((i+1) >= argc ||
6293 !isdigit((int) argv[i+1][0]))
6294 arg[2] = (word32_t) -1;
6295 else
6296 arg[2] = atoi(argv[++i]);
6297 }
6298 else if (strcmp(argv[i+1], "2") == 0 ||
6299 strcmp(argv[i+1], "vbr2") == 0) {
6300 arg[0] = 1;
6301 arg[1] = COMPMODE_2;
6302 i++;
6303 if ((i+1) >= argc ||
6304 !isdigit((int) argv[i+1][0]))
6305 arg[2] = (word32_t) -1;
6306 else
6307 arg[2] = atoi(argv[++i]);
6308 }
6309 else {
6310 /* Wrong arg */
6311 usage();
6312 return FALSE;
6313 }
6314 *cmd = CDA_COMPRESS;
6315 }
6316 else if (strcmp(argv[i], "min-brate") == 0) {
6317 /* [brate#] */
6318 if ((i+1) >= argc) {
6319 /* Query */
6320 arg[0] = 0;
6321 arg[1] = 1;
6322 }
6323 else if (isdigit((int) argv[i+1][0])) {
6324 arg[0] = 1;
6325 arg[2] = atoi(argv[i+1]);
6326 }
6327 else {
6328 /* Wrong arg */
6329 usage();
6330 return FALSE;
6331 }
6332 *cmd = CDA_BRATE;
6333 }
6334 else if (strcmp(argv[i], "max-brate") == 0) {
6335 /* [brate#] */
6336 if ((i+1) >= argc) {
6337 /* Query */
6338 arg[0] = 0;
6339 arg[1] = 2;
6340 }
6341 else if (isdigit((int) argv[i+1][0])) {
6342 arg[0] = 1;
6343 arg[2] = atoi(argv[i+1]);
6344 }
6345 else {
6346 /* Wrong arg */
6347 usage();
6348 return FALSE;
6349 }
6350 *cmd = CDA_BRATE;
6351 }
6352 else if (strcmp(argv[i], "lowpass") == 0) {
6353 /* [off | auto | freq# [width#]] */
6354 arg[2] = 1;
6355 if ((i+1) >= argc) {
6356 /* Query */
6357 arg[0] = 0;
6358 }
6359 else if (strcmp(argv[i+1], "off") == 0) {
6360 arg[0] = 1;
6361 arg[1] = FILTER_OFF;
6362 i++;
6363 }
6364 else if (strcmp(argv[i+1], "auto") == 0) {
6365 arg[0] = 1;
6366 arg[1] = FILTER_AUTO;
6367 i++;
6368 }
6369 else if (isdigit((int) argv[i+1][0])) {
6370 arg[0] = 1;
6371 arg[1] = FILTER_MANUAL;
6372 arg[3] = (word32_t) atoi(argv[i+1]);
6373 i++;
6374 if ((i+1) >= argc ||
6375 !isdigit((int) argv[i+1][0]))
6376 arg[4] = (word32_t) -1;
6377 else {
6378 if ((j = atoi(argv[++i])) < 0) {
6379 usage();
6380 return FALSE;
6381 }
6382 arg[4] = (word32_t) j;
6383 }
6384 }
6385 else {
6386 /* Wrong arg */
6387 usage();
6388 return FALSE;
6389 }
6390 *cmd = CDA_FILTER;
6391 }
6392 else if (strcmp(argv[i], "highpass") == 0) {
6393 /* [off | auto | freq# [width#]] */
6394 arg[2] = 2;
6395 if ((i+1) >= argc) {
6396 /* Query */
6397 arg[0] = 0;
6398 }
6399 else if (strcmp(argv[i+1], "off") == 0) {
6400 arg[0] = 1;
6401 arg[1] = FILTER_OFF;
6402 i++;
6403 }
6404 else if (strcmp(argv[i+1], "auto") == 0) {
6405 arg[0] = 1;
6406 arg[1] = FILTER_AUTO;
6407 i++;
6408 }
6409 else if (isdigit((int) argv[i+1][0])) {
6410 arg[0] = 1;
6411 arg[1] = FILTER_MANUAL;
6412 arg[3] = (word32_t) atoi(argv[i+1]);
6413 i++;
6414 if ((i+1) >= argc ||
6415 !isdigit((int) argv[i+1][0]))
6416 arg[4] = (word32_t) -1;
6417 else {
6418 if ((j = atoi(argv[++i])) < 0) {
6419 usage();
6420 return FALSE;
6421 }
6422 arg[4] = (word32_t) j;
6423 }
6424 }
6425 else {
6426 /* Wrong arg */
6427 usage();
6428 return FALSE;
6429 }
6430 *cmd = CDA_FILTER;
6431 }
6432 else if (strcmp(argv[i], "coding") == 0) {
6433 /* [stereo | j-stereo | force-ms | mono | algo#] */
6434 if ((i+1) >= argc) {
6435 /* Query */
6436 arg[0] = 0;
6437 }
6438 else if (strcmp(argv[i+1], "stereo") == 0) {
6439 arg[0] = 1;
6440 arg[1] = CH_STEREO;
6441 i++;
6442 }
6443 else if (strcmp(argv[i+1], "j-stereo") == 0) {
6444 arg[0] = 1;
6445 arg[1] = CH_JSTEREO;
6446 i++;
6447 }
6448 else if (strcmp(argv[i+1], "force-ms") == 0) {
6449 arg[0] = 1;
6450 arg[1] = CH_FORCEMS;
6451 i++;
6452 }
6453 else if (strcmp(argv[i+1], "mono") == 0) {
6454 arg[0] = 1;
6455 arg[1] = CH_MONO;
6456 i++;
6457 }
6458 else if (isdigit((int) argv[i+1][0])) {
6459 arg[0] = 2;
6460 arg[2] = (word32_t) atoi(argv[i+1]);
6461 i++;
6462 }
6463 else {
6464 /* Wrong arg */
6465 usage();
6466 return FALSE;
6467 }
6468 *cmd = CDA_CODING;
6469 }
6470 else if (strcmp(argv[i], "flags") == 0) {
6471 /* [C|c][O|o][N|n][E|e][I|i] */
6472 if ((i+1) >= argc) {
6473 /* Query */
6474 arg[0] = 0;
6475 }
6476 else {
6477 arg[0] = 1;
6478 arg[1] = (word32_t) -1;
6479 arg[2] = (word32_t) -1;
6480 arg[3] = (word32_t) -1;
6481 arg[4] = (word32_t) -1;
6482 arg[5] = (word32_t) -1;
6483
6484 if (strchr(argv[i+1], 'C') != 0)
6485 arg[1] = 1;
6486 else if (strchr(argv[i+1], 'c') != 0)
6487 arg[1] = 0;
6488
6489 if (strchr(argv[i+1], 'O') != 0)
6490 arg[2] = 1;
6491 else if (strchr(argv[i+1], 'o') != 0)
6492 arg[2] = 0;
6493
6494 if (strchr(argv[i+1], 'N') != 0)
6495 arg[3] = 1;
6496 else if (strchr(argv[i+1], 'n') != 0)
6497 arg[3] = 0;
6498
6499 if (strchr(argv[i+1], 'E') != 0)
6500 arg[4] = 1;
6501 else if (strchr(argv[i+1], 'e') != 0)
6502 arg[4] = 0;
6503
6504 if (strchr(argv[i+1], 'I') != 0)
6505 arg[5] = 1;
6506 else if (strchr(argv[i+1], 'i') != 0)
6507 arg[5] = 0;
6508 }
6509 *cmd = CDA_FLAGS;
6510 }
6511 else if (strcmp(argv[i], "lameopts") == 0) {
6512 /* [<disable | insert | append | replace> options] */
6513 if (++i < argc) {
6514 /* Set */
6515 arg[0] = 1;
6516 if (strcmp(argv[i], "disable") == 0)
6517 arg[1] = LAMEOPTS_DISABLE;
6518 else if (strcmp(argv[i], "insert") == 0)
6519 arg[1] = LAMEOPTS_INSERT;
6520 else if (strcmp(argv[i], "append") == 0)
6521 arg[1] = LAMEOPTS_APPEND;
6522 else if (strcmp(argv[i], "replace") == 0)
6523 arg[1] = LAMEOPTS_REPLACE;
6524 else
6525 return FALSE;
6526
6527 if (++i >= argc) {
6528 (void) strcpy((char *) &arg[2], ".");
6529 }
6530 else {
6531 if (strlen(argv[i]) >=
6532 ((CDA_NARGS - 2) * sizeof(word32_t))) {
6533 (void) fprintf(errfp,
6534 "%s: lame options too long.\n",
6535 argv[i]);
6536 return FALSE;
6537 }
6538 (void) strcpy((char *) &arg[2], argv[i]);
6539 }
6540 }
6541 else {
6542 /* Query */
6543 arg[0] = 0;
6544 }
6545 *cmd = CDA_LAMEOPTS;
6546 }
6547 else if (strcmp(argv[i], "tag") == 0) {
6548 /* [off | v1 | v2 | both] */
6549 if ((i+1) >= argc) {
6550 /* Query */
6551 arg[0] = 0;
6552 }
6553 else if (strcmp(argv[i+1], "off") == 0) {
6554 arg[0] = 1;
6555 arg[1] = 0;
6556 i++;
6557 }
6558 else if (strcmp(argv[i+1], "v1") == 0) {
6559 arg[0] = 1;
6560 arg[1] = ID3TAG_V1;
6561 i++;
6562 }
6563 else if (strcmp(argv[i+1], "v2") == 0) {
6564 arg[0] = 1;
6565 arg[1] = ID3TAG_V2;
6566 i++;
6567 }
6568 else if (strcmp(argv[i+1], "both") == 0) {
6569 arg[0] = 1;
6570 arg[1] = ID3TAG_BOTH;
6571 i++;
6572 }
6573 else {
6574 /* Wrong arg */
6575 usage();
6576 return FALSE;
6577 }
6578 *cmd = CDA_TAG;
6579 }
6580 else if (strcmp(argv[i], "motd") == 0) {
6581 *cmd = CDA_MOTD;
6582 }
6583 else if (strcmp(argv[i], "device") == 0) {
6584 *cmd = CDA_DEVICE;
6585 }
6586 else if (strcmp(argv[i], "version") == 0) {
6587 *cmd = CDA_VERSION;
6588 }
6589 else if (strcmp(argv[i], "cddbreg") == 0) {
6590 *cmd = CDA_CDDBREG;
6591 }
6592 else if (strcmp(argv[i], "cddbhint") == 0) {
6593 *cmd = CDA_CDDBHINT;
6594 }
6595 else if (strcmp(argv[i], "debug") == 0) {
6596 /* [level] */
6597 if ((i+1) < argc && isdigit((int) argv[i+1][0])) {
6598 /* Set debug level */
6599 arg[0] = 1;
6600 arg[1] = (word32_t) atoi(argv[++i]);
6601 }
6602 else {
6603 /* Query */
6604 arg[0] = 0;
6605 }
6606 *cmd = CDA_DEBUG;
6607 }
6608 else if (strcmp(argv[i], "visual") == 0) {
6609 #ifdef NOVISUAL
6610 (void) fprintf(errfp, "%s %s\n",
6611 "Cannot start visual mode:",
6612 "curses support is not compiled in!");
6613 return FALSE;
6614 #else
6615 visual = TRUE;
6616 *cmd = CDA_STATUS;
6617 /* Make sure simulator/debug output is redirectable */
6618 ttyfp = stderr;
6619 #endif
6620 }
6621 else {
6622 usage();
6623 return FALSE;
6624 }
6625 }
6626
6627 if (*cmd == 0) {
6628 /* User did not specify a command */
6629 usage();
6630 return FALSE;
6631 }
6632
6633 return TRUE;
6634 }
6635
6636
6637 /*
6638 * cda_match_select
6639 * Ask the user to select from a list of fuzzy CDDB matches.
6640 *
6641 * Args:
6642 * matchlist - List of fuzzy matches
6643 *
6644 * Return:
6645 * User selection number, or 0 for no selection.
6646 */
6647 STATIC int
cda_match_select(cdinfo_match_t * matchlist)6648 cda_match_select(cdinfo_match_t *matchlist)
6649 {
6650 int i,
6651 n;
6652 cdinfo_match_t *p;
6653 chset_conv_t *up;
6654 char *p1,
6655 *p2,
6656 *q1,
6657 *q2,
6658 input[64];
6659
6660 for (p = matchlist, i = 0; p != NULL; p = p->next, i++)
6661 ;
6662
6663 if (batch || (i == 1 && app_data.single_fuzzy)) {
6664 (void) fprintf(ttyfp, "\n\n%s\n%s\n\n",
6665 "CDDB has found an inexact match. If this is not the",
6666 "correct album, please submit corrections using xmcd.");
6667 (void) fflush(ttyfp);
6668 return 1;
6669 }
6670
6671 /* Convert CD info from UTF-8 to local charset if possible */
6672 if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
6673 return 0;
6674
6675 (void) fprintf(ttyfp, "\n\n%s\n%s\n\n",
6676 "CDDB has found the following potential matches for the album",
6677 "that you have. Please choose an appropriate entry:");
6678
6679 /* Display list */
6680 for (p = matchlist, i = 1; p != NULL; p = p->next, i++) {
6681 q1 = (p->artist == NULL) ? "(unknown artist)" : p->artist;
6682 q2 = (p->title == NULL) ? "(unknown title)" : p->title;
6683 p1 = p2 = NULL;
6684 if (!util_chset_conv(up, q1, &p1, FALSE) &&
6685 !util_newstr(&p1, q1)) {
6686 util_chset_close(up);
6687 return 0;
6688 }
6689 if (!util_chset_conv(up, q2, &p2, FALSE) &&
6690 !util_newstr(&p2, q2)) {
6691 util_chset_close(up);
6692 return 0;
6693 }
6694 (void) fprintf(ttyfp, " %2d. %.30s / %-40s\n", i,
6695 p1 == NULL ? "" : p1,
6696 p2 == NULL ? "" : p2);
6697 if (p1 != NULL)
6698 MEM_FREE(p1);
6699 if (p2 != NULL)
6700 MEM_FREE(p2);
6701 }
6702
6703 (void) fprintf(ttyfp, " %2d. None of the above\n\n", i);
6704
6705 util_chset_close(up);
6706
6707 for (;;) {
6708 (void) fprintf(ttyfp, "Choose one (1-%d): ", i);
6709 if (fgets(input, 63, stdin) == NULL) {
6710 (void) fprintf(ttyfp, "\n");
6711 (void) fflush(ttyfp);
6712 return 0;
6713 }
6714
6715 input[strlen(input)-1] = '\0';
6716
6717 n = atoi(input);
6718 if (n > 0 && n <= i)
6719 break;
6720 }
6721
6722 (void) fprintf(ttyfp, "\n");
6723 (void) fflush(ttyfp);
6724 return ((n == i) ? 0 : n);
6725 }
6726
6727
6728 /*
6729 * cda_auth
6730 * Ask the user to enter user and password for proxy authorization.
6731 *
6732 * Args:
6733 * retrycnt - How many times has the user retried this
6734 *
6735 * Return:
6736 * 0 for failure
6737 * 1 for success
6738 */
6739 STATIC int
cda_auth(int retrycnt)6740 cda_auth(int retrycnt)
6741 {
6742 word32_t arg[CDA_NARGS];
6743 char input[64];
6744 size_t maxsz = (CDA_NARGS - 1) * sizeof(word32_t);
6745 int retcode;
6746
6747 if (batch)
6748 return 0;
6749
6750 if (retrycnt == 0)
6751 (void) fprintf(ttyfp, "Proxy Authorization is required.\n");
6752 else {
6753 (void) fprintf(ttyfp, "Proxy Authorization failed.");
6754
6755 if (retrycnt < 3)
6756 (void) fprintf(ttyfp, " Try again.\n");
6757 else {
6758 (void) fprintf(ttyfp, " Too many tries.\n\n");
6759 (void) fflush(ttyfp);
6760 return 0;
6761 }
6762 }
6763
6764 (void) fprintf(ttyfp, "%s\n\n",
6765 "Please enter your proxy user name and password.");
6766
6767 /* Get user name */
6768 (void) fprintf(ttyfp, "Username: ");
6769 (void) fflush(ttyfp);
6770 if (fgets(input, 63, stdin) == NULL) {
6771 (void) fprintf(ttyfp, "\n");
6772 (void) fflush(ttyfp);
6773 return 0;
6774 }
6775 input[strlen(input)-1] = '\0';
6776 if (input[0] == '\0')
6777 return 0;
6778
6779 if (!util_newstr(&dbp->proxy_user, input)) {
6780 CDA_FATAL(app_data.str_nomemory);
6781 return 0;
6782 }
6783
6784 /* Get password */
6785 (void) fprintf(ttyfp, "Password: ");
6786 (void) fflush(ttyfp);
6787 cda_echo(ttyfp, FALSE);
6788 if (fgets(input, 63, stdin) == NULL) {
6789 cda_echo(ttyfp, TRUE);
6790 (void) fprintf(ttyfp, "\n");
6791 (void) fflush(ttyfp);
6792 return 0;
6793 }
6794 cda_echo(ttyfp, TRUE);
6795 input[strlen(input)-1] = '\0';
6796
6797 if (!util_newstr(&dbp->proxy_passwd, input)) {
6798 CDA_FATAL(app_data.str_nomemory);
6799 return 0;
6800 }
6801
6802 (void) fprintf(ttyfp, "\n");
6803
6804 /* Pass the proxy user name and password to the daemon */
6805
6806 (void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
6807 (void) strcpy((char *) &arg[0], "user");
6808 (void) strncpy((char *) &arg[1], dbp->proxy_user, maxsz - 1);
6809 if (!cda_sendcmd(CDA_AUTHUSER, arg, &retcode)) {
6810 (void) fprintf(ttyfp, "%s:\n%s\n",
6811 "Failed sending proxy user name to the cda daemon",
6812 emsgp
6813 );
6814 util_delayms(2000);
6815 }
6816
6817 (void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
6818 (void) strcpy((char *) &arg[0], "pass");
6819 (void) strncpy((char *) &arg[1], dbp->proxy_passwd, maxsz - 1);
6820 if (!cda_sendcmd(CDA_AUTHPASS, arg, &retcode)) {
6821 (void) fprintf(ttyfp, "%s:\n%s\n",
6822 "Failed sending proxy password to the cda daemon",
6823 emsgp
6824 );
6825 util_delayms(2000);
6826 }
6827
6828 (void) fflush(ttyfp);
6829 return 1;
6830 }
6831
6832
6833 /***********************
6834 * public routines *
6835 ***********************/
6836
6837
6838 /*
6839 * cda_echo
6840 * Turn off/on echo mode. It is assumed that the program starts
6841 * with echo mode enabled.
6842 *
6843 * Args:
6844 * fp - Opened file stream to the user's terminal
6845 * doecho - Whether echo should be enabled
6846 *
6847 * Return:
6848 * Nothing.
6849 */
6850 void
cda_echo(FILE * fp,bool_t doecho)6851 cda_echo(FILE *fp, bool_t doecho)
6852 {
6853 int fd = fileno(fp);
6854 #ifdef USE_TERMIOS
6855 struct termios tio;
6856
6857 (void) tcgetattr(fd, &tio);
6858
6859 if (doecho)
6860 tio.c_lflag |= ECHO;
6861 else
6862 tio.c_lflag &= ~ECHO;
6863
6864 (void) tcsetattr(fd, TCSADRAIN, &tio);
6865 #endif /* USE_TERMIOS */
6866
6867 #ifdef USE_TERMIO
6868 struct termio tio;
6869
6870 (void) ioctl(fd, TCGETA, &tio);
6871
6872 if (doecho)
6873 tio.c_lflag |= ECHO;
6874 else
6875 tio.c_lflag &= ~ECHO;
6876
6877 (void) ioctl(fd, TCSETAW, &tio);
6878 #endif /* USE_TERMIO */
6879
6880 #ifdef USE_SGTTY
6881 struct sgttyb tio;
6882
6883 (void) ioctl(fd, TIOCGETP, &tio);
6884
6885 if (doecho)
6886 tio.sg_flags |= ECHO;
6887 else
6888 tio.sg_flags &= ~ECHO;
6889
6890 (void) ioctl(fd, TIOCSETP, &tio);
6891 #endif /* USE_SGTTY */
6892 }
6893
6894
6895 /*
6896 * curstat_addr
6897 * Return the address of the curstat_t structure.
6898 *
6899 * Args:
6900 * Nothing.
6901 *
6902 * Return:
6903 * Nothing.
6904 */
6905 curstat_t *
curstat_addr(void)6906 curstat_addr(void)
6907 {
6908 return (&status);
6909 }
6910
6911
6912 /*
6913 * cda_quit
6914 * Shut down and exit
6915 *
6916 * Args:
6917 * s - Pointer to the curstat_t structure.
6918 *
6919 * Return:
6920 * No return: program exits at end of function
6921 */
6922 void
cda_quit(curstat_t * s)6923 cda_quit(curstat_t *s)
6924 {
6925 if (isdaemon) {
6926 /* Send response packet to prevent client hang */
6927 if (curr_pkt != NULL) {
6928 curr_pkt->retcode = CDA_DAEMON_EXIT;
6929 (void) cda_sendpkt("CD audio daemon",
6930 cda_rfd[0], curr_pkt);
6931 }
6932
6933 /* Remove lock */
6934 if (dlock[0] != '\0')
6935 (void) UNLINK(dlock);
6936
6937 /* Close FIFOs - daemon side */
6938 if (cda_sfd[0] >= 0)
6939 (void) close(cda_sfd[0]);
6940 if (cda_rfd[0] >= 0)
6941 (void) close(cda_rfd[0]);
6942
6943 /* Remove FIFOs */
6944 if (spipe[0] != '\0')
6945 (void) UNLINK(spipe);
6946 if (rpipe[0] != '\0')
6947 (void) UNLINK(rpipe);
6948
6949 /* Shut down CD interface subsystem */
6950 di_halt(s);
6951 }
6952 #ifndef NOVISUAL
6953 else {
6954 cda_vtidy();
6955 }
6956 #endif
6957
6958 exit(exit_status);
6959 }
6960
6961
6962 /*
6963 * cda_warning_msg
6964 * Print warning message.
6965 *
6966 * Args:
6967 * title - Not used.
6968 * msg - The warning message text string.
6969 *
6970 * Return:
6971 * Nothing.
6972 */
6973 /*ARGSUSED*/
6974 void
cda_warning_msg(char * title,char * msg)6975 cda_warning_msg(char *title, char *msg)
6976 {
6977 (void) fprintf(errfp, "CD audio Warning: %s\n", msg);
6978 }
6979
6980
6981 /*
6982 * cda_info_msg
6983 * Print info message.
6984 *
6985 * Args:
6986 * title - Not used.
6987 * msg - The info message text string.
6988 *
6989 * Return:
6990 * Nothing.
6991 */
6992 /*ARGSUSED*/
6993 void
cda_info_msg(char * title,char * msg)6994 cda_info_msg(char *title, char *msg)
6995 {
6996 (void) fprintf(errfp, "%s\n", msg);
6997 }
6998
6999
7000 /*
7001 * cda_fatal_msg
7002 * Print fatal error message.
7003 *
7004 * Args:
7005 * title - Not used..
7006 * msg - The fatal error message text string.
7007 *
7008 * Return:
7009 * Nothing.
7010 */
7011 /*ARGSUSED*/
7012 void
cda_fatal_msg(char * title,char * msg)7013 cda_fatal_msg(char *title, char *msg)
7014 {
7015 (void) fprintf(errfp, "CD audio Fatal Error:\n%s\n", msg);
7016 exit_status = 6;
7017 cda_quit(&status);
7018 /*NOTREACHED*/
7019 }
7020
7021
7022 /*
7023 * cda_parse_devlist
7024 * Parse the app_data.devlist string and create the cda_devlist array.
7025 *
7026 * Args:
7027 * s - Pointer to the curstat_t structure.
7028 *
7029 * Return:
7030 * Nothing.
7031 */
7032 void
cda_parse_devlist(curstat_t * s)7033 cda_parse_devlist(curstat_t *s)
7034 {
7035 char *p,
7036 *q;
7037 int i,
7038 n,
7039 listsz;
7040
7041 if (app_data.chg_method < 0 || app_data.chg_method >= MAX_CHG_METHODS)
7042 /* Fix-up in case of mis-configuration */
7043 app_data.chg_method = CHG_NONE;
7044
7045 n = app_data.numdiscs;
7046
7047 switch (app_data.chg_method) {
7048 case CHG_SCSI_MEDCHG:
7049 n = 2;
7050 /*FALLTHROUGH*/
7051
7052 case CHG_SCSI_LUN:
7053 /* SCSI LUN addressing method */
7054 listsz = n * sizeof(char *);
7055
7056 cda_devlist = (char **) MEM_ALLOC("cda_devlist", listsz);
7057 if (cda_devlist == NULL) {
7058 CDA_FATAL(app_data.str_nomemory);
7059 return;
7060 }
7061 (void) memset(cda_devlist, 0, listsz);
7062
7063 p = q = app_data.devlist;
7064 if (p == NULL || *p == '\0') {
7065 CDA_FATAL(app_data.str_devlist_undef);
7066 return;
7067 }
7068
7069 for (i = 0; i < n; i++) {
7070 q = strchr(p, ';');
7071
7072 if (q == NULL && i < (n - 1)) {
7073 CDA_FATAL(app_data.str_devlist_count);
7074 return;
7075 }
7076
7077 if (q != NULL)
7078 *q = '\0';
7079
7080 cda_devlist[i] = NULL;
7081 if (!util_newstr(&cda_devlist[i], p)) {
7082 CDA_FATAL(app_data.str_nomemory);
7083 return;
7084 }
7085
7086 if (q != NULL)
7087 *q = ';';
7088
7089 p = q + 1;
7090 }
7091 break;
7092
7093 case CHG_OS_IOCTL:
7094 case CHG_NONE:
7095 default:
7096 if (app_data.devlist == NULL) {
7097 if (!util_newstr(&app_data.devlist, app_data.device)) {
7098 CDA_FATAL(app_data.str_nomemory);
7099 return;
7100 }
7101 }
7102 else if (strcmp(app_data.devlist, app_data.device) != 0) {
7103 MEM_FREE(app_data.devlist);
7104 if (!util_newstr(&app_data.devlist, app_data.device)) {
7105 CDA_FATAL(app_data.str_nomemory);
7106 return;
7107 }
7108 }
7109
7110 cda_devlist = (char **) MEM_ALLOC(
7111 "cda_devlist",
7112 sizeof(char *)
7113 );
7114 if (cda_devlist == NULL) {
7115 CDA_FATAL(app_data.str_nomemory);
7116 return;
7117 }
7118
7119 cda_devlist[0] = NULL;
7120 if (!util_newstr(&cda_devlist[0], app_data.devlist)) {
7121 CDA_FATAL(app_data.str_nomemory);
7122 return;
7123 }
7124 break;
7125 }
7126
7127 /* Initialize to the first device */
7128 s->curdev = cda_devlist[0];
7129 }
7130
7131
7132 /*
7133 * cda_dbclear
7134 * Clear in-core CD information.
7135 *
7136 * Args:
7137 * s - Pointer to the curstat_t structure.
7138 * reload - Whether we are going to be re-loading the CD info entry.
7139 *
7140 * Return:
7141 * Nothing.
7142 */
7143 void
cda_dbclear(curstat_t * s,bool_t reload)7144 cda_dbclear(curstat_t *s, bool_t reload)
7145 {
7146 /* Clear in-core CD information.
7147 * Note that in cda, QMODE_ERR means CD info is not loaded.
7148 * This is different than in xmcd.
7149 */
7150 cdinfo_clear(reload);
7151 s->qmode = QMODE_ERR;
7152 }
7153
7154
7155 /*
7156 * cda_dbload
7157 * Load the CD information pertaining to the current disc, if available.
7158 * This is for use by the cda client.
7159 *
7160 * Args:
7161 * id - The xmcd disc ID
7162 * ifunc - Function pointer to a fuzzy CD info match handling function.
7163 * pfunc - Function pointer to a proxy authorization handling function.
7164 * depth - recursion depth
7165 *
7166 * Return:
7167 * TRUE = success
7168 * FALSE = failure
7169 */
7170 bool_t
cda_dbload(word32_t id,int (* ifunc)(cdinfo_match_t *),int (* pfunc)(int),int depth)7171 cda_dbload(
7172 word32_t id,
7173 int (*ifunc)(cdinfo_match_t *),
7174 int (*pfunc)(int),
7175 int depth
7176 )
7177 {
7178 int i,
7179 n,
7180 retcode,
7181 ret;
7182 curstat_t *s;
7183 word32_t arg[CDA_NARGS];
7184
7185 s = &status;
7186 (void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
7187
7188 if (!cda_sendcmd(CDA_TOC2, arg, &retcode)) {
7189 (void) printf("\n%s\n", emsgp);
7190 return FALSE;
7191 }
7192
7193 s->tot_trks = id & 0xff;
7194
7195 /* Update curstat with essential info */
7196 for (i = 0; i < (int) s->tot_trks; i++) {
7197 RD_ARG_TOC2(arg[i+1], s->trkinfo[i].trkno,
7198 s->trkinfo[i].min,
7199 s->trkinfo[i].sec,
7200 s->trkinfo[i].frame
7201 );
7202
7203 util_msftoblk(
7204 s->trkinfo[i].min,
7205 s->trkinfo[i].sec,
7206 s->trkinfo[i].frame,
7207 &s->trkinfo[i].addr,
7208 MSF_OFFSET
7209 );
7210 }
7211
7212 /* Lead-out track */
7213 RD_ARG_TOC2(arg[i+1], s->trkinfo[i].trkno,
7214 s->trkinfo[i].min,
7215 s->trkinfo[i].sec,
7216 s->trkinfo[i].frame
7217 );
7218 util_msftoblk(
7219 s->trkinfo[i].min,
7220 s->trkinfo[i].sec,
7221 s->trkinfo[i].frame,
7222 &s->trkinfo[i].addr,
7223 MSF_OFFSET
7224 );
7225
7226 /* Set appropriate offline mode */
7227 if ((ret = cdinfo_offline(s)) != 0) {
7228 DBGPRN(DBG_CDI)(errfp,
7229 "cdinfo_offline: status=%d arg=%d\n",
7230 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
7231 }
7232
7233 /* Load CD information */
7234 if ((ret = cdinfo_load(s)) != 0) {
7235 /* Failed to load */
7236 DBGPRN(DBG_CDI)(errfp, "\ncdinfo_load: status=%d arg=%d\n",
7237 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
7238
7239 switch (CDINFO_GET_STAT(ret)) {
7240 case AUTH_ERR:
7241 if (pfunc == NULL || (n = (*pfunc)(depth)) == 0)
7242 return FALSE;
7243
7244 /* Recursion */
7245 return (cda_dbload(id, ifunc, pfunc, depth + 1));
7246
7247 default:
7248 return FALSE;
7249 }
7250 }
7251
7252 if (dbp->matchlist != NULL) {
7253 /* Got a list of "fuzzy matches": Prompt user
7254 * for a selection.
7255 */
7256 if (ifunc == NULL || (n = (*ifunc)(dbp->matchlist)) == 0)
7257 return TRUE;
7258
7259 dbp->match_tag = (long) n;
7260
7261 /* Load CD information */
7262 if ((ret = cdinfo_load_matchsel(s)) != 0) {
7263 /* Failed to load */
7264 DBGPRN(DBG_CDI)(errfp,
7265 "\ncdinfo_load_matchsel: status=%d arg=%d\n",
7266 CDINFO_GET_STAT(ret), CDINFO_GET_ARG(ret));
7267 return FALSE;
7268 }
7269 else if ((dbp->flags & CDINFO_MATCH) != 0) {
7270 /* Exact match */
7271 if (app_data.discog_mode & DISCOG_GEN_CDINFO) {
7272 /* Generate new local discography */
7273 if ((ret = cdinfo_gen_discog(s)) != 0) {
7274 DBGPRN(DBG_CDI)(errfp,
7275 "cdinfo_gen_discog: status=%d arg=%d\n",
7276 CDINFO_GET_STAT(ret),
7277 CDINFO_GET_ARG(ret));
7278 }
7279 }
7280
7281 return TRUE;
7282 }
7283 else
7284 return FALSE;
7285 }
7286 else if ((dbp->flags & CDINFO_NEEDREG) != 0) {
7287 /* User registration required */
7288 (void) fprintf(ttyfp,
7289 "\n\nYou must register with CDDB to access the CDDB2 "
7290 "service.\nTo do that, run the \"cda cddbreg\" command.");
7291 return FALSE;
7292 }
7293 else if ((dbp->flags & CDINFO_MATCH) != 0) {
7294 /* Exact match */
7295 if (app_data.discog_mode & DISCOG_GEN_CDINFO) {
7296 /* Generate new local discography */
7297 if ((ret = cdinfo_gen_discog(s)) != 0) {
7298 DBGPRN(DBG_CDI)(errfp,
7299 "cdinfo_gen_discog: status=%d arg=%d\n",
7300 CDINFO_GET_STAT(ret),
7301 CDINFO_GET_ARG(ret));
7302 }
7303 }
7304
7305 return TRUE;
7306 }
7307
7308 return TRUE;
7309 }
7310
7311
7312 /*
7313 * cda_sendcmd
7314 * Send command down the pipe and handle response.
7315 *
7316 * Args:
7317 * cmd - The command code
7318 * arg - Command arguments
7319 * retcode - Return code
7320 *
7321 * Return:
7322 * Status code
7323 */
7324 bool_t
cda_sendcmd(word32_t cmd,word32_t arg[],int * retcode)7325 cda_sendcmd(word32_t cmd, word32_t arg[], int *retcode)
7326 {
7327 int i;
7328 cdapkt_t p,
7329 r;
7330
7331 /* Fill in command packet */
7332 (void) memset(&p, 0, sizeof(cdapkt_t));
7333 p.pktid = getpid();
7334 p.cmd = cmd;
7335 (void) memcpy(p.arg, arg, CDA_NARGS * sizeof(word32_t));
7336
7337 /* Send command packet */
7338 if (!cda_sendpkt("CD audio", cda_sfd[1], &p)) {
7339 emsgp = "\nCannot send packet to CD audio daemon.";
7340 *retcode = -1;
7341 return FALSE;
7342 }
7343
7344 for (i = 0; ; i++) {
7345 /* Get response packet */
7346 if (!cda_getpkt("CD audio", cda_rfd[1], &r)) {
7347 emsgp = "Cannot get packet from CD audio daemon.";
7348 *retcode = -1;
7349 return FALSE;
7350 }
7351
7352 /* Sanity check */
7353 if (p.pktid == r.pktid)
7354 break;
7355
7356 /* This is not our packet */
7357
7358 if (i >= MAX_PKT_RETRIES) {
7359 emsgp =
7360 "Retry overflow reading packet from CD audio daemon.";
7361 *retcode = -1;
7362 return FALSE;
7363 }
7364
7365 /* Increment packet reject count */
7366 r.rejcnt++;
7367
7368 /* If packet has not reached reject limit, put it back
7369 * into the pipe.
7370 */
7371 if (r.rejcnt < MAX_PKT_RETRIES &&
7372 !cda_sendpkt("CD audio", cda_rfd[0], &r)) {
7373 emsgp = "Cannot send packet to CD audio daemon.";
7374 *retcode = -1;
7375 return FALSE;
7376 }
7377
7378 /* Introduce some randomness to shuffle
7379 * the order of packets in the pipe
7380 */
7381 if ((rand() & 0x80) != 0)
7382 util_delayms(100);
7383 }
7384
7385 /* Return args */
7386 (void) memcpy(arg, r.arg, CDA_NARGS * sizeof(word32_t));
7387
7388 *retcode = r.retcode;
7389
7390 /* Check return code */
7391 switch (r.retcode) {
7392 case CDA_OK:
7393 return TRUE;
7394 case CDA_INVALID:
7395 emsgp = "This command is not valid in the current state.";
7396 return FALSE;
7397 case CDA_PARMERR:
7398 emsgp = "Command argument error.";
7399 return FALSE;
7400 case CDA_FAILED:
7401 emsgp = "This command failed.";
7402 return FALSE;
7403 case CDA_REFUSED:
7404 emsgp = "The CD audio daemon does not accept this command.";
7405 return FALSE;
7406 case CDA_DAEMON_EXIT:
7407 emsgp = "CD audio daemon exited.";
7408 return FALSE;
7409 default:
7410 emsgp = "The CD audio daemon returned an invalid status.";
7411 return FALSE;
7412 }
7413 /*NOTREACHED*/
7414 }
7415
7416
7417 /*
7418 * cda_daemon_alive
7419 * Check if the cda daemon is running.
7420 *
7421 * Args:
7422 * None.
7423 *
7424 * Return:
7425 * TRUE - daemon is alive
7426 * FALSE - daemon is dead
7427 */
7428 bool_t
cda_daemon_alive(void)7429 cda_daemon_alive(void)
7430 {
7431 int fd;
7432 pid_t pid;
7433 char dlock[FILE_PATH_SZ],
7434 buf[32];
7435
7436 (void) sprintf(dlock, "%s/cdad.%x", TEMP_DIR, (int) cd_rdev);
7437
7438 fd = open(dlock, O_RDONLY
7439 #ifdef O_NOFOLLOW
7440 | O_NOFOLLOW
7441 #endif
7442 );
7443 if (fd < 0)
7444 return FALSE;
7445
7446 if (read(fd, buf, 32) < 0) {
7447 (void) close(fd);
7448 return FALSE;
7449 }
7450 (void) close(fd);
7451
7452 if (strncmp(buf, "cdad ", 5) != 0)
7453 return FALSE;
7454
7455 pid = (pid_t) atoi(buf + 5);
7456
7457 if (kill(pid, 0) < 0)
7458 return FALSE;
7459
7460 daemon_pid = pid;
7461
7462 return TRUE;
7463 }
7464
7465
7466 /*
7467 * cda_daemon
7468 * CD audio daemon main loop function.
7469 *
7470 * Args:
7471 * s - Pointer to the curstat_t structure.
7472 *
7473 * Return:
7474 * Visual mode: FALSE = failure, TRUE = success
7475 * Command line mode: No return
7476 */
7477 bool_t
cda_daemon(curstat_t * s)7478 cda_daemon(curstat_t *s)
7479 {
7480 int i;
7481 cdapkt_t p;
7482 bool_t done = FALSE;
7483
7484 if (cda_daemon_alive()) {
7485 /* Daemon already running */
7486 #ifndef NOVISUAL
7487 if (visual)
7488 return TRUE;
7489 #endif
7490 cda_quit(s);
7491 /*NOTREACHED*/
7492 }
7493
7494 /* Make some needed directories */
7495 cda_mkdirs();
7496
7497 /* Become a daemon process */
7498 switch (FORK()) {
7499 case -1:
7500 #ifndef NOVISUAL
7501 if (visual)
7502 return FALSE;
7503 #endif
7504 perror("Cannot fork CD audio daemon");
7505 cda_quit(s);
7506 /*NOTREACHED*/
7507 case 0:
7508 /* Child process: the daemon */
7509 break;
7510 default:
7511 /* Parent process */
7512 /* Make sure the daemon is running */
7513 for (i = 0; i < 5; i++) {
7514 if (cda_daemon_alive())
7515 break;
7516 util_delayms(1000);
7517 }
7518 if (i >= 5) {
7519 CDA_FATAL("CD audio daemon not started.");
7520 }
7521
7522 #ifndef NOVISUAL
7523 if (visual)
7524 return TRUE;
7525 #endif
7526 exit(0);
7527 }
7528
7529 /* Check for duplicate daemon processes */
7530 if (!cda_daemonlock()) {
7531 (void) fprintf(errfp, "CD audio daemon already running.\n");
7532 exit(1);
7533 }
7534
7535 /* Set daemon flag */
7536 isdaemon = TRUE;
7537
7538 /* Create FIFOs */
7539 if (MKFIFO(spipe, 0600) < 0) {
7540 if (errno != EEXIST) {
7541 perror("CD audio: Cannot create send pipe");
7542 exit_status = 4;
7543 cda_quit(s);
7544 /*NOTREACHED*/
7545 }
7546
7547 /* Try removing and re-creating the FIFO */
7548 if (UNLINK(spipe) < 0) {
7549 perror("CD audio: Cannot unlink old send pipe");
7550 exit_status = 4;
7551 cda_quit(s);
7552 /*NOTREACHED*/
7553 }
7554 if (MKFIFO(spipe, 0600) < 0) {
7555 perror("CD audio: Cannot create send pipe");
7556 exit_status = 4;
7557 cda_quit(s);
7558 /*NOTREACHED*/
7559 }
7560 }
7561 if (MKFIFO(rpipe, 0600) < 0) {
7562 if (errno != EEXIST) {
7563 perror("CD audio: Cannot create recv pipe");
7564 exit_status = 4;
7565 cda_quit(s);
7566 /*NOTREACHED*/
7567 }
7568
7569 /* Try removing and re-creating the FIFO */
7570 if (UNLINK(rpipe) < 0) {
7571 perror("CD audio: Cannot unlink old recv pipe");
7572 exit_status = 4;
7573 cda_quit(s);
7574 /*NOTREACHED*/
7575 }
7576 if (MKFIFO(rpipe, 0600) < 0) {
7577 perror("CD audio: Cannot create recv pipe");
7578 exit_status = 4;
7579 cda_quit(s);
7580 /*NOTREACHED*/
7581 }
7582 }
7583
7584 /* Initialize and start drive interfaces */
7585 cda_init(s);
7586 cda_start(s);
7587
7588 /* Handle some signals */
7589 if (util_signal(SIGHUP, onsig0) == SIG_IGN)
7590 (void) util_signal(SIGHUP, SIG_IGN);
7591 (void) util_signal(SIGTERM, SIG_IGN);
7592 (void) util_signal(SIGINT, SIG_IGN);
7593 (void) util_signal(SIGQUIT, SIG_IGN);
7594 #if defined(SIGTSTP) && defined(SIGCONT)
7595 (void) util_signal(SIGTSTP, SIG_IGN);
7596 (void) util_signal(SIGCONT, SIG_IGN);
7597 #endif
7598
7599 /* Main command handling loop */
7600 while (!done) {
7601 /* Get command packet */
7602 if (!cda_getpkt("CD audio daemon", cda_sfd[0], &p)) {
7603 exit_status = 5;
7604 cda_quit(s);
7605 /*NOTREACHED*/
7606 }
7607
7608 /* HACK: Set current packet pointer. In case
7609 * cda_docmd() leads to cda_quit() due to exitOnEject
7610 * or exitOnDone, cda_quit() can send a response
7611 * packet back to the client before exiting.
7612 */
7613 curr_pkt = &p;
7614
7615 /* Interpret and carry out command */
7616 done = cda_docmd(s, &p);
7617
7618 /* Clear current packet pointer */
7619 curr_pkt = NULL;
7620
7621 /* Send response packet */
7622 if (!cda_sendpkt("CD audio daemon", cda_rfd[0], &p)) {
7623 exit_status = 5;
7624 cda_quit(s);
7625 /*NOTREACHED*/
7626 }
7627 }
7628
7629 /* Stop the drive */
7630 exit_status = 0;
7631 cda_quit(s);
7632
7633 /* This is to appease gcc -Wall even though we can't get here */
7634 return TRUE;
7635 }
7636
7637
7638 /*
7639 * main
7640 * The main function
7641 */
7642 int
main(int argc,char ** argv)7643 main(int argc, char **argv)
7644 {
7645 int i,
7646 retcode,
7647 ret;
7648 word32_t cmd,
7649 *arg1,
7650 *arg2;
7651 struct stat stbuf;
7652 char *ttypath,
7653 *cp,
7654 *hd,
7655 path[FILE_PATH_SZ * 2];
7656 bool_t loaddb = TRUE;
7657 curstat_t *s;
7658
7659 /* Program startup multi-threading initializations */
7660 cdda_preinit();
7661
7662 errfp = stderr;
7663 s = &status;
7664 (void) memset(s, 0, sizeof(curstat_t));
7665
7666 /* Hack: Aside from stdin, stdout, stderr, there shouldn't
7667 * be any other files open, so force-close them. This is
7668 * necessary in case xmcd was inheriting any open file
7669 * descriptors from a parent process which is for the
7670 * CD-ROM device, and violating exclusive-open requirements
7671 * on some platforms.
7672 */
7673 for (i = 3; i < 10; i++)
7674 (void) close(i);
7675
7676 #ifdef HAS_ICONV
7677 /* Set locale to be based on environment */
7678 (void) setlocale(LC_ALL, "");
7679 #endif
7680
7681 (void) util_signal(SIGCHLD, SIG_DFL);
7682
7683 /* Initialize */
7684 if ((ttypath = ttyname(2)) == NULL)
7685 ttypath = "/dev/tty";
7686 if ((ttyfp = fopen(ttypath, "w")) != NULL)
7687 setbuf(ttyfp, NULL);
7688 else
7689 ttyfp = stderr;
7690
7691 /* Initialize error messages */
7692 app_data.str_nomethod = STR_NOMETHOD;
7693 app_data.str_novu = STR_NOVU;
7694 app_data.str_unknartist = STR_UNKNARTIST;
7695 app_data.str_unkndisc = STR_UNKNDISC;
7696 app_data.str_unkntrk = STR_UNKNTRK;
7697 app_data.str_fatal = STR_FATAL;
7698 app_data.str_warning = STR_WARNING;
7699 app_data.str_info = STR_INFO;
7700 app_data.str_nomemory = STR_NOMEMORY;
7701 app_data.str_nocfg = STR_NOCFG;
7702 app_data.str_notrom = STR_NOTROM;
7703 app_data.str_notscsi2 = STR_NOTSCSI2;
7704 app_data.str_moderr = STR_MODERR;
7705 app_data.str_staterr = STR_STATERR;
7706 app_data.str_noderr = STR_NODERR;
7707 app_data.str_recoverr = STR_RECOVERR;
7708 app_data.str_maxerr = STR_MAXERR;
7709 app_data.str_tmpdirerr = STR_TMPDIRERR;
7710 app_data.str_libdirerr = STR_LIBDIRERR;
7711 app_data.str_seqfmterr = STR_SEQFMTERR;
7712 app_data.str_invpgmtrk = STR_INVPGMTRK;
7713 app_data.str_longpatherr = STR_LONGPATHERR;
7714 app_data.str_devlist_undef = STR_DEVLIST_UNDEF;
7715 app_data.str_devlist_count = STR_DEVLIST_COUNT;
7716 app_data.str_medchg_noinit = STR_MEDCHG_NOINIT;
7717 app_data.str_noaudiopath = STR_NOAUDIOPATH;
7718 app_data.str_audiopathexists = STR_AUDIOPATHEXISTS;
7719 app_data.str_cddainit_fail = STR_CDDAINIT_FAIL;
7720 app_data.str_notregfile = STR_NOTREGFILE;
7721 app_data.str_noprog = STR_NOPROG;
7722 app_data.str_overwrite = STR_OVERWRITE;
7723 app_data.str_nomsg = STR_NOMSG;
7724
7725 /* Initialize other app_data parameters */
7726 app_data.stat_interval = 260;
7727 app_data.ins_interval = 4000;
7728 app_data.startup_vol = -1;
7729 app_data.aux = (void *) &status;
7730
7731 /* Allocate arg arrays */
7732 arg1 = (word32_t *) MEM_ALLOC("arg1", CDA_NARGS * sizeof(word32_t));
7733 arg2 = (word32_t *) MEM_ALLOC("arg2", CDA_NARGS * sizeof(word32_t));
7734 if (arg1 == NULL || arg2 == NULL)
7735 CDA_FATAL(app_data.str_nomemory);
7736
7737 /* Parse command line args */
7738 if (!cda_parse_args(argc, argv, &cmd, arg1))
7739 exit(1);
7740
7741 /* Seed random number generator */
7742 srand((unsigned) time(NULL));
7743 for (i = 0, cp = PROGNAME; i < 4 && *cp != '\0'; i++, cp++)
7744 s->aux[i] = (byte_t) *cp ^ 0xff;
7745
7746 /* Debug mode start-up banner */
7747 DBGPRN(DBG_ALL)(errfp, "CDA %s.%s.%s DEBUG MODE\n\n",
7748 VERSION_MAJ, VERSION_MIN, VERSION_TEENY);
7749
7750 /* Initialize libutil */
7751 util_init();
7752
7753 /* Set library directory path */
7754 if ((cp = (char *) getenv("XMCD_LIBDIR")) == NULL)
7755 CDA_FATAL(app_data.str_libdirerr);
7756
7757 if (!util_newstr(&app_data.libdir, cp))
7758 CDA_FATAL(app_data.str_nomemory);
7759
7760 /* Paranoia: avoid overflowing buffers */
7761 if ((int) strlen(app_data.libdir) >= FILE_PATH_SZ)
7762 CDA_FATAL(app_data.str_longpatherr);
7763
7764 hd = util_homedir(util_get_ouid());
7765 if ((int) strlen(hd) >= FILE_PATH_SZ)
7766 CDA_FATAL(app_data.str_longpatherr);
7767
7768 /* Set up defaults */
7769 app_data.cdinfo_maxhist = 100;
7770
7771 /* Get system common configuration parameters */
7772 (void) sprintf(path, SYS_CMCFG_PATH, app_data.libdir);
7773 di_common_parmload(path, TRUE, FALSE);
7774
7775 /* Get user common configuration parameters */
7776 (void) sprintf(path, USR_CMCFG_PATH, hd);
7777 di_common_parmload(path, FALSE, FALSE);
7778
7779 if (app_data.device != NULL &&
7780 (int) strlen(app_data.device) >= FILE_PATH_SZ)
7781 CDA_FATAL(app_data.str_longpatherr);
7782
7783 if ((cp = util_basename(app_data.device)) == NULL)
7784 CDA_FATAL("Device path basename error");
7785
7786 if ((int) strlen(cp) >= FILE_BASE_SZ)
7787 CDA_FATAL(app_data.str_longpatherr);
7788
7789 MEM_FREE(cp);
7790
7791 /* Check validity of device */
7792 if (di_isdemo())
7793 cd_rdev = 0;
7794 else {
7795 if (stat(app_data.device, &stbuf) < 0) {
7796 (void) sprintf(errmsg, "Cannot stat %s.",
7797 app_data.device);
7798 CDA_FATAL(errmsg);
7799 }
7800 cd_rdev = stbuf.st_rdev;
7801 }
7802
7803 /* FIFO paths */
7804 (void) sprintf(spipe, "%s/send.%x", TEMP_DIR, (int) cd_rdev);
7805 (void) sprintf(rpipe, "%s/recv.%x", TEMP_DIR, (int) cd_rdev);
7806
7807 /* Initialize CD information services */
7808 if (inetoffln > 0)
7809 app_data.cdinfo_inetoffln = (inetoffln == 1) ? FALSE : TRUE;
7810 (void) strcpy(cdinfo_cldata.prog, PROGNAME);
7811 (void) strcpy(cdinfo_cldata.user, util_loginname());
7812 cdinfo_cldata.isdemo = di_isdemo;
7813 cdinfo_cldata.curstat_addr = curstat_addr;
7814 cdinfo_cldata.fatal_msg = cda_fatal_msg;
7815 cdinfo_cldata.warning_msg = cda_warning_msg;
7816 cdinfo_cldata.info_msg = cda_info_msg;
7817 cdinfo_init(&cdinfo_cldata);
7818
7819 /* Set up basic wwwWarp structure */
7820 cdinfo_wwwwarp_parmload();
7821
7822 #ifndef NOVISUAL
7823 if (visual) {
7824 /* Handle some signals */
7825 if (util_signal(SIGINT, onsig0) == SIG_IGN)
7826 (void) util_signal(SIGINT, SIG_IGN);
7827 if (util_signal(SIGQUIT, onsig0) == SIG_IGN)
7828 (void) util_signal(SIGQUIT, SIG_IGN);
7829 if (util_signal(SIGTERM, onsig0) == SIG_IGN)
7830 (void) util_signal(SIGTERM, SIG_IGN);
7831 (void) util_signal(SIGPIPE, onsig1);
7832
7833 /* Initialize subsystems */
7834 cda_init(s);
7835
7836 /* Start visual mode */
7837 cda_visual();
7838 }
7839 #endif
7840
7841 if (cmd == CDA_ON) {
7842 /* Start CDA daemon */
7843 (void) printf("Initializing...\n");
7844 if (!cda_daemon(&status))
7845 exit(1);
7846 }
7847
7848 /* Initialize subsystems */
7849 cda_init(s);
7850
7851 /* Create device list */
7852 cda_parse_devlist(s);
7853
7854 /* Open FIFOs - command side */
7855 cda_sfd[1] = open(spipe, O_WRONLY
7856 #ifdef O_NOFOLLOW
7857 | O_NOFOLLOW
7858 #endif
7859 );
7860 if (cda_sfd[1] < 0) {
7861 perror("CD audio: cannot open send pipe");
7862 CDA_FATAL(
7863 "Run \"cda on\" first to initialize CD audio daemon."
7864 );
7865 }
7866 cda_rfd[1] = open(rpipe, O_RDONLY
7867 #ifdef O_NOFOLLOW
7868 | O_NOFOLLOW
7869 #endif
7870 );
7871 if (cda_rfd[1] < 0) {
7872 perror("CD audio: cannot open recv pipe for reading");
7873 CDA_FATAL(
7874 "Run \"cda on\" first to initialize CD audio daemon."
7875 );
7876 }
7877 cda_rfd[0] = open(rpipe, O_WRONLY
7878 #ifdef O_NOFOLLOW
7879 | O_NOFOLLOW
7880 #endif
7881 );
7882 if (cda_rfd[0] < 0) {
7883 perror("CD audio: cannot open recv pipe for writing");
7884 CDA_FATAL(
7885 "Run \"cda on\" first to initialize CD audio daemon."
7886 );
7887 }
7888
7889 /* Check FIFOs */
7890 if (fstat(cda_sfd[1], &stbuf) < 0 || !S_ISFIFO(stbuf.st_mode))
7891 CDA_FATAL("Send pipe error: Not a FIFO");
7892 if (fstat(cda_rfd[1], &stbuf) < 0 || !S_ISFIFO(stbuf.st_mode))
7893 CDA_FATAL("Recv pipe error: Not a FIFO");
7894 if (fstat(cda_rfd[0], &stbuf) < 0 || !S_ISFIFO(stbuf.st_mode))
7895 CDA_FATAL("Recv pipe error: Not a FIFO");
7896
7897 /* Handle some signals */
7898 (void) util_signal(SIGINT, onintr);
7899 (void) util_signal(SIGQUIT, onintr);
7900 (void) util_signal(SIGTERM, onintr);
7901
7902 ret = 0;
7903 for (;;) {
7904 /* Send the command */
7905 switch (cmd) {
7906 case CDA_ON:
7907 /* Send command to cda daemon */
7908 if (!cda_sendcmd(cmd, arg1, &retcode)) {
7909 (void) printf("%s\n", emsgp);
7910 ret = 2;
7911 break;
7912 }
7913
7914 daemon_pid = (pid_t) arg1[0];
7915
7916 (void) fprintf(errfp,
7917 "CD audio daemon pid=%d dev=%s started.\n",
7918 (int) daemon_pid, app_data.device);
7919 break;
7920
7921 case CDA_OFF:
7922 /* Send command to cda daemon */
7923 if (!cda_sendcmd(cmd, arg1, &retcode)) {
7924 (void) printf("%s\n", emsgp);
7925 ret = 2;
7926 break;
7927 }
7928
7929 daemon_pid = (pid_t) arg1[0];
7930
7931 (void) fprintf(errfp,
7932 "CD audio daemon pid=%d dev=%s exited.\n",
7933 (int) daemon_pid, app_data.device);
7934 break;
7935
7936 case CDA_STATUS:
7937 /* Send command to cda daemon */
7938 if (!cda_sendcmd(cmd, arg1, &retcode)) {
7939 (void) printf("%s\n", emsgp);
7940 ret = 2;
7941 break;
7942 }
7943
7944 if (RD_ARG_MODE(arg1[0]) == MOD_NODISC)
7945 loaddb = TRUE;
7946
7947 dbp->discid = arg1[6];
7948 s->cur_disc = arg1[7];
7949
7950 switch (app_data.chg_method) {
7951 case CHG_SCSI_LUN:
7952 s->curdev = cda_devlist[s->cur_disc - 1];
7953 break;
7954 case CHG_SCSI_MEDCHG:
7955 case CHG_OS_IOCTL:
7956 case CHG_NONE:
7957 default:
7958 s->curdev = cda_devlist[0];
7959 break;
7960 }
7961
7962 /* Load CD information */
7963 if (loaddb &&
7964 RD_ARG_MODE(arg1[0]) != MOD_NODISC &&
7965 dbp->discid != 0) {
7966 if (cda_dbload(dbp->discid, NULL, NULL, 0))
7967 s->qmode = QMODE_MATCH;
7968 else
7969 s->qmode = QMODE_NONE;
7970 }
7971
7972 /* Print status */
7973 prn_stat(arg1);
7974 break;
7975
7976 default: /* All other commands */
7977 /* Check current status */
7978 if (!cda_sendcmd(CDA_STATUS, arg2, &retcode)) {
7979 (void) printf("%s\n", emsgp);
7980 ret = 2;
7981 break;
7982 }
7983
7984 if (RD_ARG_MODE(arg2[0]) == MOD_NODISC)
7985 loaddb = TRUE;
7986
7987 dbp->discid = arg2[6];
7988 s->cur_disc = arg2[7];
7989
7990 switch (app_data.chg_method) {
7991 case CHG_SCSI_LUN:
7992 s->curdev = cda_devlist[s->cur_disc - 1];
7993 break;
7994 case CHG_SCSI_MEDCHG:
7995 case CHG_OS_IOCTL:
7996 case CHG_NONE:
7997 default:
7998 s->curdev = cda_devlist[0];
7999 break;
8000 }
8001
8002 /* Load CD information */
8003 if (loaddb && RD_ARG_MODE(arg2[0]) != MOD_NODISC &&
8004 dbp->discid != 0) {
8005 if ((cmd == CDA_TOC || cmd == CDA_EXTINFO ||
8006 cmd == CDA_NOTES)) {
8007 (void) printf("Accessing CDDB... ");
8008 (void) fflush(stdout);
8009
8010 if (cda_dbload(dbp->discid,
8011 cda_match_select,
8012 cda_auth, 0))
8013 s->qmode = QMODE_MATCH;
8014 else
8015 s->qmode = QMODE_NONE;
8016
8017 (void) printf("\n\n");
8018 }
8019 else if (cmd != CDA_PLAY) {
8020 if (cda_dbload(dbp->discid,
8021 NULL, NULL, 0))
8022 s->qmode = QMODE_MATCH;
8023 else
8024 s->qmode = QMODE_NONE;
8025 }
8026 }
8027
8028 if (cmd == CDA_CDDBREG) {
8029 if (!cda_userreg(ttyfp))
8030 ret = 2;
8031 break;
8032 }
8033 else if (cmd == CDA_CDDBHINT) {
8034 if (!cda_cddbhint(ttyfp))
8035 ret = 2;
8036 break;
8037 }
8038
8039 /* Send command to cda daemon */
8040 if (!cda_sendcmd(cmd, arg1, &retcode) &&
8041 retcode != CDA_DAEMON_EXIT) {
8042 (void) printf("%s\n", emsgp);
8043 ret = 2;
8044 break;
8045 }
8046
8047 /* Map the command to its print function and
8048 * call it
8049 */
8050 for (i = 0; cmd_fmtab[i].cmd != 0; i++) {
8051 if (cmd == cmd_fmtab[i].cmd) {
8052 if (cmd_fmtab[i].prnfunc != NULL)
8053 (*cmd_fmtab[i].prnfunc)(arg1);
8054 break;
8055 }
8056 }
8057 break;
8058 }
8059
8060 (void) fflush(stdout);
8061
8062 if (!stat_cont)
8063 break;
8064
8065 if (cont_delay > 0)
8066 util_delayms(cont_delay * 1000);
8067 }
8068
8069 /* Close FIFOs - command side */
8070 if (cda_sfd[1] >= 0)
8071 (void) close(cda_sfd[1]);
8072 if (cda_rfd[1] >= 0)
8073 (void) close(cda_rfd[1]);
8074 if (cda_rfd[0] >= 0)
8075 (void) close(cda_rfd[0]);
8076
8077 exit(ret);
8078
8079 /*NOTREACHED*/
8080 }
8081
8082
8083