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