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 
23 /*
24  *   Visual mode support
25  *
26  *   Contributing author: Philip Le Riche
27  *   E-Mail: pleriche@uk03.bull.co.uk
28  */
29 
30 #ifndef lint
31 static char *_visual_c_ident_ = "@(#)visual.c	6.125 04/04/05";
32 #endif
33 
34 #include "common_d/appenv.h"
35 
36 #ifndef NOVISUAL
37 
38 #include "common_d/util.h"
39 #include "common_d/version.h"
40 #include "libdi_d/libdi.h"
41 #include "cdinfo_d/cdinfo.h"
42 
43 /* TRUE and FALSE is redefined in curses.h. */
44 #undef TRUE
45 #undef FALSE
46 
47 /* curses.h redefines SYSV - handle with care */
48 #ifdef SYSV
49 #undef SYSV
50 #include <curses.h>
51 #define SYSV
52 #else
53 #if defined(_ULTRIX)
54 #include <cursesX.h>
55 #else
56 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__bsdi__)
57 #include <ncurses.h>
58 #else
59 #include <curses.h>
60 #endif	/* __FreeBSD__ __NetBSD__ __bsdi__ */
61 #endif	/* ultrix */
62 #endif	/* SYSV */
63 
64 #ifdef _SCO_SVR3
65 #include <sys/stream.h>
66 #include <sys/ptem.h>
67 #endif
68 
69 #ifndef __QNX__
70 #include <term.h>
71 #endif
72 
73 #include "cda_d/cda.h"
74 #include "cda_d/visual.h"
75 
76 extern appdata_t	app_data;
77 extern cdinfo_incore_t	*dbp;
78 extern char		*emsgp,
79 			**cda_devlist,
80 			errmsg[],
81 			spipe[],
82 			rpipe[];
83 extern int		cda_sfd[],
84 			cda_rfd[],
85 			exit_status;
86 extern pid_t		daemon_pid;
87 
88 STATIC int		scr_lines = 0,		/* number of screen lines */
89 			scr_cols = 0,		/* number of screen cols */
90 			scroll_line,		/* 1st line of info window */
91 			scroll_length,		/* # of scrollable lines */
92 			route,			/* Stereo, Mono ... */
93 			old_route = 1,
94 			track = -2,		/* Current track no. */
95 			savch;			/* Saved ch for cda_ungetch */
96 STATIC unsigned int	jc_alarm = 0;		/* Saved alarm for job ctl */
97 STATIC bool_t		isvisual = FALSE,	/* Currently in visual mode */
98 			stat_on = FALSE,	/* Visual: cda is "on" */
99 			ostat_on = TRUE,	/* Previous value */
100 			refresh_fkeys = TRUE,	/* Refresh funct key labels */
101 			help = FALSE,		/* Display help in info_win? */
102 			old_help = TRUE,	/* Previous value */
103 			refresh_sts = FALSE,	/* Refresh status line */
104 			win_resize = FALSE,	/* Window resize */
105 			echflag = FALSE,	/* Own echo flag */
106 			savch_echo = FALSE,	/* Control echo for savch */
107 			loaddb = TRUE;		/* Load CD information */
108 STATIC word32_t		oastat0 = (word32_t)-1,	/* Previous status value */
109 			oastat1 = (word32_t)-1;
110 STATIC WINDOW		*info_win,		/* Scrolling window for info */
111 			*status_win;		/* Window for status */
112 
113 
114 /* Fatal error macro for visual mode */
115 #define CDA_V_FATAL(msg, code)	{	\
116 	emsgp = (msg);			\
117 	exit_status = (code);		\
118 	cda_quit(curstat_addr());	\
119 }
120 
121 /* Keyboard input to command function mapping table */
122 typedef struct {
123 	int	key;
124 	void	(*keyfunc)(int, word32_t[]);
125 } keytbl_t;
126 
127 
128 /***********************
129  *  internal routines  *
130  ***********************/
131 
132 
133 #if defined(SIGTSTP) && defined(SIGCONT)
134 /*
135  * ontstp
136  *	Handler for job control stop signal
137  *
138  * Args:
139  *	signo - The signal number
140  *
141  * Return:
142  *	Nothing.
143  */
144 /*ARGSUSED*/
145 STATIC void
ontstp(int signo)146 ontstp(int signo)
147 {
148 	/* Cancel alarms and save state */
149 	jc_alarm = alarm(0);
150 
151 	/* Put screen in sane state */
152 	(void) keypad(stdscr, FALSE);
153 	(void) putp(cursor_normal);
154 	(void) endwin();
155 
156 	/* Now stop the process */
157 	(void) util_signal(SIGTSTP, SIG_DFL);
158 	(void) kill((pid_t) getpid(), SIGTSTP);
159 }
160 
161 
162 /*
163  * oncont
164  *	Handler for job control continue signal
165  *
166  * Args:
167  *	signo - The signal number
168  *
169  * Return:
170  *	Nothing.
171  */
172 /*ARGSUSED*/
173 STATIC void
oncont(int signo)174 oncont(int signo)
175 {
176 	/* Use the window resize routine to restore visual attributes */
177 	win_resize = TRUE;
178 
179 	if (jc_alarm != 0) {
180 		/* Get back into the loop */
181 		(void) kill(getpid(), SIGALRM);
182 	}
183 }
184 #endif	/* SIGTSTP SIGCONT */
185 
186 
187 #ifdef SIGWINCH
188 /*
189  * onwinch
190  *	Handler for window size change signal
191  *
192  * Args:
193  *	signo - The signal number
194  *
195  * Return:
196  *	Nothing.
197  */
198 /*ARGSUSED*/
199 STATIC void
onwinch(int signo)200 onwinch(int signo)
201 {
202 	win_resize = TRUE;
203 }
204 #endif
205 
206 
207 /*
208  * cda_makewins
209  *	Create the main window areas on the display.
210  *
211  * Args:
212  *	None.
213  *
214  * Return:
215  *	Nothing.  Program exits on error.
216  */
217 STATIC void
cda_makewins(void)218 cda_makewins(void)
219 {
220 	WINDOW		*new_info_win,
221 			*new_status_win;
222 #ifdef TIOCGWINSZ
223 	int		fd;
224 	struct winsize	ws;
225 #endif
226 
227 	if (!isvisual) {
228 		/* Startup */
229 		if (initscr() == NULL)
230 		    CDA_FATAL("cda visual Error: Cannot initialize curses");
231 
232 		isvisual = TRUE;
233 	}
234 	else {
235 		/* Re-initialize due to resize */
236 		(void) endwin();
237 #ifdef __hpux
238 		if (initscr() == NULL)
239 #else
240 		if (newterm(NULL, stdout, stdin) == NULL)
241 #endif
242 		{
243 		    CDA_FATAL("cda visual Error: Cannot re-initialize curses");
244 		}
245 	}
246 
247 	scr_cols = scr_lines = 0;
248 
249 #ifdef TIOCGWINSZ
250 	fd = fileno(stdout);
251 	if (isatty(fd) && ioctl(fd, TIOCGWINSZ, &ws) >= 0) {
252 		scr_lines = (int) ws.ws_row;
253 		scr_cols = (int) ws.ws_col;
254 	}
255 #endif	/* TIOCGWINSZ */
256 
257 	if (scr_cols == 0 || scr_lines == 0) {
258 #ifdef getmaxyx
259 		getmaxyx(stdscr, scr_lines, scr_cols);
260 #else
261 		scr_lines = LINES;
262 		scr_cols = COLS;
263 #endif
264 	}
265 
266 	if (scr_lines < 9 || scr_cols < 40) {
267 		CDA_V_FATAL(
268 			"cda visual Error: "
269 			"The terminal screen size is too small.\n"
270 			"It must be at least 80 columns x 9 lines\n",
271 			6
272 		);
273 	}
274 
275 	LINES = scr_lines;
276 	COLS = scr_cols;
277 
278 #if defined(NCURSES_VERSION_MAJOR) && (NCURSES_VERSION_MAJOR >= 4)
279 	(void) resizeterm(LINES, COLS);
280 #endif
281 
282 #ifdef SIGWINCH
283 	/* Handle terminal screen size change */
284 	(void) util_signal(SIGWINCH, onwinch);
285 #endif
286 
287 #if defined(SIGTSTP) && defined(SIGCONT)
288 	/* Handle job control */
289 	(void) util_signal(SIGTSTP, ontstp);
290 	(void) util_signal(SIGCONT, oncont);
291 #endif
292 
293 	(void) noecho();
294 	(void) cbreak();
295 	(void) clear();
296 	(void) putp(cursor_invisible);
297 	(void) scrollok(stdscr, FALSE);
298 
299 	new_info_win = newpad(132, scr_cols);
300 	if (new_info_win == NULL) {
301 		CDA_V_FATAL(
302 			"cda visual Error: Cannot create information window.",
303 			6
304 		);
305 	}
306 
307 	new_status_win = newwin(7, scr_cols, scr_lines-8, 0);
308 	if (new_status_win == NULL) {
309 		CDA_V_FATAL(
310 			"cda visual Error: Cannot create status window.",
311 			6
312 		);
313 	}
314 
315 	if (info_win != NULL)
316 		(void) delwin(info_win);
317 	if (status_win != NULL)
318 		(void) delwin(status_win);
319 
320 	info_win = new_info_win;
321 	status_win = new_status_win;
322 
323 	(void) keypad(info_win, TRUE);
324 	(void) keypad(status_win, TRUE);
325 
326 	(void) wclear(info_win);
327 	(void) wclear(status_win);
328 	(void) wmove(status_win, 3, 0);
329 	(void) waddstr(status_win, STATUS_LINE0);
330 	(void) wmove(status_win, 4, 0);
331 	(void) waddstr(status_win, STATUS_LINE1);
332 	(void) wmove(status_win, 5, 0);
333 	(void) waddstr(status_win, STATUS_LINE2);
334 	(void) box(status_win, 0, 0);
335 
336 	(void) refresh();
337 }
338 
339 
340 /*
341  * cda_wgetch
342  *	Own version of curses wgetch. This interworks with cda_ungetch.
343  *
344  * Args:
345  *	None.
346  *
347  * Return:
348  *	Input character or function key token.
349  */
350 STATIC int
cda_wgetch(WINDOW * win)351 cda_wgetch(WINDOW *win)
352 {
353 	int	ch;
354 
355 	if (savch) {
356 		/* Echo character now if echo on but not yet echoed */
357 		if (!savch_echo && echflag &&
358 		    isprint(savch) && !iscntrl(savch)) {
359 			(void) waddch(win, savch);
360 			(void) wrefresh(win);
361 		}
362 		ch = savch;
363 		savch = 0;
364 		return (ch);
365 	}
366 
367 	ch = wgetch(win);
368 
369 	/* Do our own echoing because switching it on and off doesn't
370 	 * seem to work on some platforms.
371 	 */
372 	if (echflag && isprint(ch) && !iscntrl(ch)) {
373 		(void) waddch(win, ch);
374 		(void) wrefresh(win);
375 	}
376 
377 	return (ch);
378 }
379 
380 
381 /*
382  * cda_ungetch
383  *	Own version of ungetch, because some systems don't have it.
384  *	Also, we need to remember the echo status of the ungotten
385  *	character.
386  *
387  * Args:
388  *	Char or function key token to return.
389  *
390  * Return:
391  *	Nothing.
392  */
393 STATIC void
cda_ungetch(int ch)394 cda_ungetch(int ch)
395 {
396 	savch = ch;
397 	/* Remember whether the character has been echoed */
398 	savch_echo = echflag;
399 }
400 
401 
402 /*
403  * cda_wgetstr
404  *	Own version of wgetstr, using cda_wgetch and cda_ungetch.
405  *
406  * Args:
407  *	Buffer to be filled with input string.
408  *
409  * Return:
410  *	Nothing.
411  */
412 STATIC void
cda_wgetstr(WINDOW * win,char * inbuf,int max)413 cda_wgetstr(WINDOW *win, char *inbuf, int max)
414 {
415 	int	ch,
416 		n,
417 		x,
418 		y;
419 	char	*p;
420 	bool_t	eos = FALSE;
421 
422 	p = inbuf;
423 	n = 0;
424 
425 	while (!eos) {
426 		if (n > max) {
427 			beep();
428 			break;
429 		}
430 
431 		ch = cda_wgetch(win);
432 
433 		switch (ch) {
434 		case KEY_BACKSPACE:
435 		case KEY_LEFT:
436 		case '\010':
437 			if (n > 0) {
438 				p--;
439 				n--;
440 				/* Echo the effect of backspace */
441 				getyx(win, y, x);
442 				(void) wmove(win, y, x-1);
443 				(void) waddch(win, ' ');
444 				(void) wmove(win, y, x-1);
445 			}
446 			break;
447 
448 		case KEY_UP:
449 		case '\036':	/* ctrl-^ */
450 		case KEY_DOWN:
451 		case '\026':	/* ctrl-v */
452 		case KEY_PPAGE:
453 		case '\002':	/* ctrl-b */
454 		case '\025':	/* ctrl-u */
455 		case KEY_NPAGE:
456 		case '\006':	/* ctrl-f */
457 		case '\004':	/* ctrl-d */
458 			if (n > 0) {
459 				n--;
460 				getyx(win, y, x);
461 				(void) wmove(win, y, x-1);
462 				(void) waddch(win, ' ');
463 				(void) wmove(win, y, x-1);
464 			}
465 
466 			switch (ch) {
467 			case KEY_UP:
468 			case '\036':
469 				scroll_line--;
470 				break;
471 			case KEY_DOWN:
472 			case '\026':
473 				scroll_line++;
474 				break;
475 			case KEY_PPAGE:
476 			case '\002':
477 				scroll_line -= (scr_lines - 9);
478 				break;
479 			case '\025':
480 				scroll_line -= ((scr_lines - 9) / 2);
481 				break;
482 			case KEY_NPAGE:
483 			case '\006':
484 				scroll_line += (scr_lines - 9);
485 				break;
486 			case '\004':
487 				scroll_line += ((scr_lines - 9) / 2);
488 				break;
489 			}
490 
491 			if (scroll_line < 0)
492 				scroll_line = 0;
493 			else if (scroll_line > (scroll_length - 1))
494 				scroll_line = scroll_length - 1;
495 
496 			(void) prefresh(info_win, scroll_line, 0, 0, 0,
497 					scr_lines - 9, scr_cols - 1);
498 			break;
499 
500 		case '\n':
501 		case '\r':
502 			/* End-of-string */
503 			eos = TRUE;
504 			break;
505 
506 		default:
507 			if (!isprint(ch) || iscntrl(ch))
508 				beep();
509 			else {
510 				*p++ = (char) ch;
511 				n++;
512 			}
513 
514 			break;
515 		}
516 
517 		(void) wrefresh(win);
518 	}
519 
520 	*p = '\0';
521 }
522 
523 
524 /*
525  * cda_v_echo
526  *	Own versions of curses echo function.
527  *
528  * Args:
529  *	doecho - Whether to enable echo
530  *
531  * Return:
532  *	Nothing.
533  *
534  */
535 STATIC void
cda_v_echo(bool_t doecho)536 cda_v_echo(bool_t doecho)
537 {
538 	echflag = doecho;
539 }
540 
541 
542 /*
543  * cda_clrstatus
544  *	Clear the status line and position cursor at the beginning.
545  *
546  * Args:
547  *	None.
548  *
549  * Return:
550  *	Nothing.
551  */
552 STATIC void
cda_clrstatus(void)553 cda_clrstatus(void)
554 {
555 	int	i;
556 
557 	(void) wmove(status_win, 1, 1);
558 	for (i = 1; i < scr_cols-1; i++)
559 		(void) waddch(status_win, ' ');
560 	(void) wmove(status_win, 1, 1);
561 }
562 
563 
564 /*
565  * cda_v_errhandler
566  *	Handler function used when the daemon has exited.
567  *
568  * Args:
569  *	code - status code from cda_sendcmd
570  *
571  * Return:
572  *	Nothing.
573  */
574 STATIC void
cda_v_errhandler(int code)575 cda_v_errhandler(int code)
576 {
577 	if (code == CDA_OK || code == CDA_PARMERR)
578 		/* Ignore these codes */
579 		return;
580 
581 	/* Daemon is exiting or has exited */
582 	cda_clrstatus();
583 
584 	if (code < 0 || code == CDA_DAEMON_EXIT) {
585 		(void) wprintw(status_win,
586 			"CD audio daemon pid=%d dev=%s exited.",
587 			daemon_pid, app_data.device);
588 
589 		ostat_on = stat_on;
590 		stat_on = FALSE;
591 
592 		if (cda_sfd[1] >= 0) {
593 			(void) close(cda_sfd[1]);
594 			(void) close(cda_rfd[1]);
595 			(void) close(cda_rfd[0]);
596 			cda_sfd[1] = cda_rfd[1] = cda_rfd[0] = -1;
597 		}
598 
599 		loaddb = TRUE;	/* Force reload of CDDB */
600 		track = -2;	/* Force redisplay of version info */
601 		scroll_line = 0;
602 	}
603 	else
604 		(void) wprintw(status_win, "%s", emsgp);
605 
606 	(void) wrefresh(status_win);
607 }
608 
609 
610 /*
611  * cda_v_match_select
612  *	Ask the user to select from a list of fuzzy CDDB matches.
613  *
614  * Args:
615  *	matchlist - List of fuzzy matches
616  *
617  * Return:
618  *	User selection number, or 0 for no selection.
619  */
620 STATIC int
cda_v_match_select(cdinfo_match_t * matchlist)621 cda_v_match_select(cdinfo_match_t *matchlist)
622 {
623 	int		i,
624 			n,
625 			x;
626 	cdinfo_match_t	*p;
627 	chset_conv_t	*up;
628 	char		*p1,
629 			*p2,
630 			*q1,
631 			*q2,
632 			input[64];
633 
634 	(void) wclear(info_win);
635 	(void) wmove(info_win, 0, 0);
636 
637 	for (p = matchlist, i = 0; p != NULL; p = p->next, i++)
638 		;
639 
640 	if (i == 1 && app_data.single_fuzzy) {
641 		(void) wprintw(info_win, "\n\n%s\n%s\n\n",
642 		    "CDDB has found an inexact match.  If this is not the",
643 		    "correct album, please submit corrections using xmcd.");
644 		return 1;
645 	}
646 
647 	/* Convert CD info from UTF-8 to local charset if possible */
648 	if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
649 		return 0;
650 
651 	(void) wprintw(info_win, "\n%s\n%s\n\n",
652 		"CDDB has found the following potential matches for the album",
653 		"that you have.  Please choose an appropriate entry:");
654 
655 	for (p = matchlist, i = 1; p != NULL; p = p->next, i++) {
656 		q1 = (p->artist == NULL) ? "(unknown artist)" : p->artist;
657 		q2 = (p->title == NULL) ? "(unknown title)" : p->title;
658 		p1 = p2 = NULL;
659 		if (!util_chset_conv(up, q1, &p1, FALSE) &&
660 		    !util_newstr(&p1, q1)) {
661 			util_chset_close(up);
662 			return 0;
663 		}
664 		if (!util_chset_conv(up, q2, &p2, FALSE) &&
665 		    !util_newstr(&p2, q2)) {
666 			util_chset_close(up);
667 			return 0;
668 		}
669 		(void) wprintw(info_win, "  %2d. %.30s / %-40s\n", i,
670 				p1 == NULL ? "" : p1,
671 				p2 == NULL ? "" : p2);
672 		if (p1 != NULL)
673 			MEM_FREE(p1);
674 		if (p2 != NULL)
675 			MEM_FREE(p2);
676 	}
677 
678 	(void) wprintw(info_win, "  %2d. None of the above\n\n", i);
679 
680 	util_chset_close(up);
681 
682 	scroll_line = 0;
683 	(void) prefresh(info_win, scroll_line, 0, 0, 0,
684 			scr_lines - 9, scr_cols - 1);
685 	getyx(info_win, scroll_length, x);
686 #ifdef lint
687 	x = x;	/* Get rid of "set but not used" lint warning */
688 #endif
689 	--scroll_length;
690 
691 	for (;;) {
692 		cda_clrstatus();
693 		(void) wprintw(status_win, "Choose one (1-%d): ", i);
694 		cda_v_echo(TRUE);
695 		(void) putp(cursor_normal);
696 		(void) wrefresh(status_win);
697 		cda_wgetstr(status_win, input, 60);
698 
699 		n = atoi(input);
700 		if (n > 0 && n <= i)
701 			break;
702 
703 		beep();
704 	}
705 
706 	cda_v_echo(FALSE);
707 	(void) putp(cursor_invisible);
708 
709 	if (n == i)
710 		return 0;
711 
712 	cda_clrstatus();
713 	(void) wprintw(status_win, "Accessing CDDB...");
714 	(void) wrefresh(status_win);
715 	util_delayms(2000);
716 	return (n);
717 }
718 
719 
720 /*
721  * cda_v_auth
722  *	Ask the user to enter name and password for proxy authorization.
723  *
724  * Args:
725  *	retrycnt - Number of times the user tried
726  *
727  * Return:
728  *	0 - failure
729  *	1 - success
730  */
731 STATIC int
cda_v_auth(int retrycnt)732 cda_v_auth(int retrycnt)
733 {
734 	word32_t	arg[CDA_NARGS];
735 	char		input[64];
736 	size_t		maxsz = (CDA_NARGS - 1) * sizeof(word32_t);
737 	int		x,
738 			retcode;
739 
740 	(void) wclear(info_win);
741 	(void) wmove(info_win, 0, 0);
742 
743 	if (retrycnt == 0)
744 		(void) wprintw(info_win, "Proxy Authorization is required.\n");
745 	else {
746 		(void) wprintw(info_win, "Proxy Authorization failed.");
747 
748 		if (retrycnt < 3)
749 			(void) wprintw(info_win, "  Try again.\n");
750 		else {
751 			(void) wprintw(info_win, "  Too many tries.\n");
752 			scroll_line = 0;
753 			(void) prefresh(info_win, scroll_line, 0, 0, 0,
754 					scr_lines - 9, scr_cols - 1);
755 			getyx(info_win, scroll_length, x);
756 #ifdef lint
757 			x = x;	/* Get rid of "set but not used" lint warning */
758 #endif
759 			--scroll_length;
760 			util_delayms(2000);
761 			return 0;
762 		}
763 	}
764 
765 	(void) wprintw(info_win, "%s\n\n",
766 		"Please enter your proxy user name and password.");
767 
768 	scroll_line = 0;
769 	(void) prefresh(info_win, scroll_line, 0, 0, 0,
770 			scr_lines - 9, scr_cols - 1);
771 	getyx(info_win, scroll_length, x);
772 #ifdef lint
773 	x = x;	/* Get rid of "set but not used" lint warning */
774 #endif
775 	--scroll_length;
776 
777 	/* Get user name */
778 	input[0] = '\0';
779 	cda_clrstatus();
780 	(void) wprintw(status_win, "Username: ");
781 	cda_v_echo(TRUE);
782 	(void) putp(cursor_normal);
783 	(void) wrefresh(status_win);
784 	cda_wgetstr(status_win, input, 60);
785 	if (input[0] == '\0')
786 		return 0;
787 
788 	if (!util_newstr(&dbp->proxy_user, input))
789 		CDA_V_FATAL(app_data.str_nomemory, 6);
790 
791 	/* Get password */
792 	input[0] = '\0';
793 	cda_clrstatus();
794 	(void) wprintw(status_win, "Password: ");
795 	cda_v_echo(FALSE);
796 	(void) wrefresh(status_win);
797 	cda_wgetstr(status_win, input, 60);
798 
799 	if (!util_newstr(&dbp->proxy_passwd, input))
800 		CDA_V_FATAL(app_data.str_nomemory, 6);
801 
802 	/* Pass the proxy user name and password to the daemon */
803 
804 	(void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
805 	(void) strcpy((char *) &arg[0], "user");
806 	(void) strncpy((char *) &arg[1], dbp->proxy_user, maxsz - 1);
807 	if (!cda_sendcmd(CDA_AUTHUSER, arg, &retcode)) {
808 		(void) wprintw(status_win, "%s:\n%s\n"
809 			"Failed sending proxy user name to the cda daemon.",
810 			emsgp
811 		);
812 		util_delayms(2000);
813 	}
814 
815 	(void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
816 	(void) strcpy((char *) &arg[0], "pass");
817 	(void) strncpy((char *) &arg[1], dbp->proxy_passwd, maxsz - 1);
818 	if (!cda_sendcmd(CDA_AUTHPASS, arg, &retcode)) {
819 		(void) wprintw(status_win, "%s:\n%s\n"
820 			"Failed sending proxy password to the cda daemon.",
821 			emsgp
822 		);
823 		util_delayms(2000);
824 	}
825 
826 	return 1;
827 }
828 
829 
830 /*
831  * cda_screen
832  *	Paints the screen in visual mode, geting status and extinfo
833  *	as required.
834  *
835  * Args:
836  *	None.
837  *
838  * Return:
839  *	Nothing.
840  */
841 /*ARGSUSED*/
842 STATIC void
cda_screen(int signo)843 cda_screen(int signo)
844 {
845 	word32_t	arg[CDA_NARGS],
846 			discid,
847 			astat0 = 0,
848 			astat1 = 0;
849 	int		x,
850 			y,
851 			i,
852 			retcode,
853 			vol,
854 			bal,
855 			rptcnt;
856 	unsigned int	discnum = 0,
857 			trk,
858 			idx,
859 			min,
860 			sec;
861 	char		*p,
862 			*p1,
863 			*p2,
864 			*q1,
865 			*q2,
866 			*ctrlver;
867 	curstat_t	*s = curstat_addr();
868 	bool_t		playing;
869 	struct stat	stbuf;
870 	static unsigned int odiscnum;
871 	static chset_conv_t *up = NULL;
872 
873 	/* Convert CD info from UTF-8 to local charset if possible */
874 	if (up == NULL && (up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
875 		return;
876 
877 	/* Window resize event? */
878 	if (win_resize) {
879 		win_resize = FALSE;
880 
881 		/* Re-init curses */
882 		cda_makewins();
883 
884 		/* Force a repaint */
885 		wclear(info_win);
886 		wclear(status_win);
887 		oastat0 = oastat1 = (word32_t) -1;
888 		ostat_on = !stat_on;
889 		old_help = !help;
890 		old_route = !route;
891 		refresh_fkeys = TRUE;
892 	}
893 
894 	/* Need to refresh function key labels? */
895 	if (refresh_fkeys) {
896 		refresh_fkeys = FALSE;
897 		(void) wmove(status_win, 3, 0);
898 		(void) waddstr(status_win, STATUS_LINE0);
899 		(void) wmove(status_win, 4, 0);
900 		(void) waddstr(status_win, STATUS_LINE1);
901 		(void) wmove(status_win, 5, 0);
902 		(void) waddstr(status_win, STATUS_LINE2);
903 		(void) wrefresh(status_win);
904 
905 		(void) box(status_win, 0, 0);
906 	}
907 
908 	/* If daemon running, get status and update display */
909 	switch ((int) stat_on) {
910 	case FALSE:
911 		/* Daemon not running - just update display to "off" */
912 		if (stat_on != ostat_on) {
913 			(void) wmove(status_win, ON_Y, ON_X);
914 			(void) waddstr(status_win, "On");
915 
916 			(void) wmove(status_win, OFF_Y, OFF_X);
917 			(void) wattron(status_win, A_STANDOUT);
918 			(void) waddstr(status_win, "Off");
919 			(void) wattroff(status_win, A_STANDOUT);
920 
921 			(void) wmove(status_win, LOAD_Y, LOAD_X);
922 			(void) waddstr(status_win, "Load");
923 
924 			(void) wmove(status_win, EJECT_Y, EJECT_X);
925 			(void) waddstr(status_win, "Eject");
926 
927 			(void) wmove(status_win, PLAY_Y, PLAY_X);
928 			(void) waddstr(status_win, "Play");
929 
930 			(void) wmove(status_win, PAUSE_Y, PAUSE_X);
931 			(void) waddstr(status_win, "Pause");
932 
933 			(void) wmove(status_win, STOP_Y, STOP_X);
934 			(void) waddstr(status_win, "Stop");
935 
936 			(void) wmove(status_win, LOCK_Y, LOCK_X);
937 			(void) waddstr(status_win, "Lock");
938 
939 			(void) wmove(status_win, UNLOCK_Y, UNLOCK_X);
940 			(void) waddstr(status_win, "Unlock");
941 
942 			(void) wmove(status_win, SHUFFLE_Y, SHUFFLE_X);
943 			(void) waddstr(status_win, "Shuffle");
944 
945 			(void) wmove(status_win, PROGRAM_Y, PROGRAM_X);
946 			(void) waddstr(status_win, "Program");
947 
948 			(void) wmove(status_win, REPEAT_ON_Y, REPEAT_ON_X);
949 			(void) waddstr(status_win, "On");
950 
951 			(void) wmove(status_win, REPEAT_OFF_Y, REPEAT_OFF_X);
952 			(void) waddstr(status_win, "Off");
953 		}
954 
955 		/* Check to see if daemon started */
956 		if (cda_daemon_alive()) {
957 			/* Let the mkfifo complete */
958 			util_delayms(1000);
959 
960 			/* Daemon is alive: open FIFOs - command side */
961 			if (cda_sfd[1] < 0)
962 				cda_sfd[1] = open(spipe, O_WRONLY
963 #ifdef O_NOFOLLOW
964 							 | O_NOFOLLOW
965 #endif
966 				);
967 
968 			if (cda_sfd[1] >= 0) {
969 				stat_on = TRUE;
970 
971 				cda_rfd[1] = open(rpipe, O_RDONLY
972 #ifdef O_NOFOLLOW
973 							 | O_NOFOLLOW
974 #endif
975 				);
976 				if (cda_rfd[1] < 0) {
977 				    CDA_V_FATAL(
978 					"Cannot open recv pipe for reading", 6
979 				    );
980 				}
981 				cda_rfd[0] = open(rpipe, O_WRONLY
982 #ifdef O_NOFOLLOW
983 							 | O_NOFOLLOW
984 #endif
985 				);
986 				if (cda_rfd[0] < 0) {
987 				    CDA_V_FATAL(
988 					"Cannot open recv pipe for writing", 6
989 				    );
990 				}
991 			}
992 			else {
993 				CDA_V_FATAL(
994 				    "Cannot open send pipe for writing", 6
995 				);
996 			}
997 
998 			/* Check FIFOs */
999 			if (fstat(cda_sfd[1], &stbuf) < 0 ||
1000 			    !S_ISFIFO(stbuf.st_mode))
1001 				CDA_V_FATAL("Send pipe error: Not a FIFO", 6);
1002 
1003 			if (fstat(cda_rfd[1], &stbuf) < 0 ||
1004 			    !S_ISFIFO(stbuf.st_mode))
1005 				CDA_V_FATAL("Recv pipe error: Not a FIFO", 6);
1006 
1007 			if (fstat(cda_rfd[0], &stbuf) < 0 ||
1008 			    !S_ISFIFO(stbuf.st_mode))
1009 				CDA_V_FATAL("Recv pipe error: Not a FIFO", 6);
1010 		}
1011 
1012 		if (!stat_on)
1013 			break;
1014 
1015 		arg[0] = 0;
1016 		if (!cda_sendcmd(CDA_ON, arg, &retcode))
1017 			cda_v_errhandler(retcode);
1018 		else {
1019 			daemon_pid = (pid_t) arg[0];
1020 			cda_clrstatus();
1021 			(void) wprintw(status_win,
1022 				"CD audio daemon pid=%d dev=%s started.",
1023 				daemon_pid, app_data.device);
1024 			(void) wrefresh(status_win);
1025 		}
1026 
1027 		/*FALLTHROUGH*/
1028 
1029 	case TRUE:
1030 		/* Daemon running - get status and update display */
1031 		(void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
1032 		if (!cda_sendcmd(CDA_STATUS, arg, &retcode)) {
1033 			cda_v_errhandler(retcode);
1034 
1035 			oastat0 = astat0;
1036 			oastat1 = astat1;
1037 
1038 			/* Come back in 1 sec */
1039 			(void) util_signal(SIGALRM, cda_screen);
1040 			(void) alarm(1);
1041 			return;
1042 		}
1043 
1044 		(void) wmove(status_win, ON_Y, ON_X);
1045 		(void) wattron(status_win, A_STANDOUT);
1046 		(void) waddstr(status_win, "On");
1047 		(void) wattroff(status_win, A_STANDOUT);
1048 
1049 		(void) wmove(status_win, OFF_Y, OFF_X);
1050 		(void) waddstr(status_win, "Off");
1051 
1052 		astat0 = arg[0];
1053 		astat1 = arg[1];
1054 		rptcnt = (int) arg[2];
1055 		vol = (int) arg[3];
1056 		bal = (int) arg[4];
1057 		route = (int) arg[5];
1058 		discid = arg[6];
1059 		discnum = (unsigned int) arg[7];
1060 		s->cur_disc = arg[7];
1061 
1062 		switch (app_data.chg_method) {
1063 		case CHG_SCSI_LUN:
1064 			s->curdev = cda_devlist[s->cur_disc - 1];
1065 			break;
1066 		case CHG_SCSI_MEDCHG:
1067 		case CHG_OS_IOCTL:
1068 		case CHG_NONE:
1069 		default:
1070 			s->curdev = cda_devlist[0];
1071 			break;
1072 		}
1073 
1074 		if (RD_ARG_MODE(astat0) == MOD_NODISC || odiscnum != discnum)
1075 			loaddb = TRUE;
1076 
1077 		if (discid != 0 && loaddb) {
1078 			/* Load CD information */
1079 			loaddb = FALSE;
1080 			cda_clrstatus();
1081 			(void) wprintw(status_win, "Accessing CDDB...");
1082 			(void) wrefresh(status_win);
1083 			util_delayms(2000);
1084 
1085 			cda_dbclear(s, TRUE);
1086 			dbp->discid = discid;
1087 			if (cda_dbload(dbp->discid, cda_v_match_select,
1088 				       cda_v_auth, 0))
1089 				s->qmode = QMODE_MATCH;
1090 			else
1091 				s->qmode = QMODE_NONE;
1092 
1093 			cda_clrstatus();
1094 		}
1095 
1096 		if (astat0 != oastat0 || astat1 != oastat1) {
1097 			if (RD_ARG_MODE(astat0) == MOD_STOP &&
1098 			    RD_ARG_MODE(oastat0) != MOD_NODISC) {
1099 				/* CD no longer busy or playing:
1100 				 * clear status line.
1101 				 */
1102 				cda_clrstatus();
1103 				refresh_sts = TRUE;
1104 			}
1105 
1106 			switch (RD_ARG_MODE(astat0)) {
1107 			case MOD_BUSY:
1108 			case MOD_NODISC:
1109 				(void) wmove(status_win, LOAD_Y, LOAD_X);
1110 				(void) waddstr(status_win, "Load");
1111 
1112 				(void) wmove(status_win, EJECT_Y, EJECT_X);
1113 				(void) wattron(status_win, A_STANDOUT);
1114 				(void) waddstr(status_win, "Eject");
1115 				(void) wattroff(status_win, A_STANDOUT);
1116 
1117 				(void) wmove(status_win, PLAY_Y, PLAY_X);
1118 				(void) waddstr(status_win, "Play");
1119 
1120 				(void) wmove(status_win, PAUSE_Y, PAUSE_X);
1121 				(void) waddstr(status_win, "Pause");
1122 
1123 				(void) wmove(status_win, STOP_Y, STOP_X);
1124 				(void) wattron(status_win, A_STANDOUT);
1125 				(void) waddstr(status_win, "Stop");
1126 				(void) wattroff(status_win, A_STANDOUT);
1127 
1128 				break;
1129 
1130 			case MOD_STOP:
1131 				(void) wmove(status_win, LOAD_Y, LOAD_X);
1132 				(void) wattron(status_win, A_STANDOUT);
1133 				(void) waddstr(status_win, "Load");
1134 				(void) wattroff(status_win, A_STANDOUT);
1135 
1136 				(void) wmove(status_win, EJECT_Y, EJECT_X);
1137 				(void) waddstr(status_win, "Eject");
1138 
1139 				(void) wmove(status_win, PLAY_Y, PLAY_X);
1140 				(void) waddstr(status_win, "Play");
1141 
1142 				(void) wmove(status_win, PAUSE_Y, PAUSE_X);
1143 				(void) waddstr(status_win, "Pause");
1144 
1145 				(void) wmove(status_win, STOP_Y, STOP_X);
1146 				(void) wattron(status_win, A_STANDOUT);
1147 				(void) waddstr(status_win, "Stop");
1148 				(void) wattroff(status_win, A_STANDOUT);
1149 
1150 				break;
1151 
1152 			case MOD_PLAY:
1153 				(void) wmove(status_win, LOAD_Y, LOAD_X);
1154 				(void) wattron(status_win, A_STANDOUT);
1155 				(void) waddstr(status_win, "Load");
1156 				(void) wattroff(status_win, A_STANDOUT);
1157 
1158 				(void) wmove(status_win, EJECT_Y, EJECT_X);
1159 				(void) waddstr(status_win, "Eject");
1160 
1161 				(void) wmove(status_win, PLAY_Y, PLAY_X);
1162 				(void) wattron(status_win, A_STANDOUT);
1163 				(void) waddstr(status_win, "Play");
1164 				(void) wattroff(status_win, A_STANDOUT);
1165 
1166 				(void) wmove(status_win, PAUSE_Y, PAUSE_X);
1167 				(void) waddstr(status_win, "Pause");
1168 
1169 				(void) wmove(status_win, STOP_Y, STOP_X);
1170 				(void) waddstr(status_win, "Stop");
1171 
1172 				break;
1173 
1174 			case MOD_PAUSE:
1175 				(void) wmove(status_win, LOAD_Y, LOAD_X);
1176 				(void) wattron(status_win, A_STANDOUT);
1177 				(void) waddstr(status_win, "Load");
1178 				(void) wattroff(status_win, A_STANDOUT);
1179 
1180 				(void) wmove(status_win, EJECT_Y, EJECT_X);
1181 				(void) waddstr(status_win, "Eject");
1182 
1183 				(void) wmove(status_win, PLAY_Y, PLAY_X);
1184 				(void) waddstr(status_win, "Play");
1185 
1186 				(void) wmove(status_win, PAUSE_Y, PAUSE_X);
1187 				(void) wattron(status_win, A_STANDOUT);
1188 				(void) waddstr(status_win, "Pause");
1189 				(void) wattroff(status_win, A_STANDOUT);
1190 
1191 				(void) wmove(status_win, STOP_Y, STOP_X);
1192 				(void) waddstr(status_win, "Stop");
1193 
1194 				break;
1195 			}
1196 
1197 			(void) wmove(status_win, LOCK_Y, LOCK_X);
1198 			if (RD_ARG_LOCK(astat0))
1199 				(void) wattron(status_win, A_STANDOUT);
1200 			(void) waddstr(status_win, "Lock");
1201 			(void) wattroff(status_win, A_STANDOUT);
1202 
1203 			(void) wmove(status_win, UNLOCK_Y, UNLOCK_X);
1204 			if (!RD_ARG_LOCK(astat0))
1205 				(void) wattron(status_win, A_STANDOUT);
1206 			(void) waddstr(status_win, "Unlock");
1207 			(void) wattroff(status_win, A_STANDOUT);
1208 
1209 			(void) wmove(status_win, SHUFFLE_Y, SHUFFLE_X);
1210 			if (RD_ARG_SHUF(astat0))
1211 				(void) wattron(status_win, A_STANDOUT);
1212 			(void) waddstr(status_win, "Shuffle");
1213 			(void) wattroff(status_win, A_STANDOUT);
1214 
1215 			(void) wmove(status_win, PROGRAM_Y, PROGRAM_X);
1216 			if (stat_on &&
1217 			    RD_ARG_MODE(astat0) != MOD_BUSY &&
1218 			    RD_ARG_MODE(astat0) != MOD_NODISC &&
1219 			    !RD_ARG_SHUF(astat0)) {
1220 				arg[0] = 1;
1221 				if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
1222 					cda_v_errhandler(retcode);
1223 
1224 					oastat0 = astat0;
1225 					oastat1 = astat1;
1226 
1227 					/* Come back in 1 sec */
1228 					(void) util_signal(SIGALRM,
1229 							   cda_screen);
1230 					(void) alarm(1);
1231 					return;
1232 				}
1233 
1234 				if (arg[0] != 0)
1235 					wattron(status_win, A_STANDOUT);
1236 			}
1237 			(void) waddstr(status_win, "Program");
1238 			(void) wattroff(status_win, A_STANDOUT);
1239 
1240 			(void) wmove(status_win, REPEAT_ON_Y, REPEAT_ON_X);
1241 			if (RD_ARG_REPT(astat0))
1242 				(void) wattron(status_win, A_STANDOUT);
1243 			(void) waddstr(status_win, "On");
1244 			(void) wattroff(status_win, A_STANDOUT);
1245 
1246 			(void) wmove(status_win, REPEAT_OFF_Y, REPEAT_OFF_X);
1247 			if (!RD_ARG_REPT(astat0))
1248 				(void) wattron(status_win, A_STANDOUT);
1249 			(void) waddstr(status_win, "Off");
1250 			(void) wattroff(status_win, A_STANDOUT);
1251 		}
1252 
1253 		wmove(status_win, 1, 1);
1254 		if (RD_ARG_MODE(astat0) == MOD_BUSY) {
1255 			cda_clrstatus();
1256 			(void) wprintw(status_win, "CD Busy");
1257 		}
1258 		else if (RD_ARG_MODE(astat0) == MOD_PLAY ||
1259 			 RD_ARG_MODE(astat0) == MOD_PAUSE ||
1260 			 RD_ARG_MODE(astat0) == MOD_NODISC) {
1261 
1262 			if (RD_ARG_MODE(astat0) == MOD_NODISC) {
1263 				track = -2;	/* Force redisplay of
1264 						 * version/device
1265 						 */
1266 				(void) wprintw(status_win, "No Disc   ");
1267 			}
1268 			else {
1269 				trk = (unsigned int) RD_ARG_TRK(astat1);
1270 				idx = (unsigned int) RD_ARG_IDX(astat1);
1271 				min = (unsigned int) RD_ARG_MIN(astat1);
1272 				sec = (unsigned int) RD_ARG_SEC(astat1);
1273 
1274 				if (idx == 0 && app_data.subq_lba) {
1275 					/* In LBA mode the time information
1276 					 * isn't meaningful when in the
1277 					 * lead-in, so just set to zero.
1278 					 */
1279 					min = sec = 0;
1280 				}
1281 
1282 				(void) wprintw(status_win,
1283 					"Disc %u Track %02u Index %02u "
1284 					"%s%02u:%02u  ",
1285 					discnum, trk, idx,
1286 					(idx == 0) ? "-" : "+", min, sec);
1287 			}
1288 
1289 			(void) wprintw(status_win,
1290 				"Volume %3u%% Balance %3u%% ", vol, bal);
1291 
1292 			switch (route) {
1293 			case 0:
1294 				(void) wprintw(status_win, "Stereo    ");
1295 				break;
1296 			case 1:
1297 				(void) wprintw(status_win, "Reverse   ");
1298 				break;
1299 			case 2:
1300 				(void) wprintw(status_win, "Mono-L    ");
1301 				break;
1302 			case 3:
1303 				(void) wprintw(status_win, "Mono-R    ");
1304 				break;
1305 			case 4:
1306 				(void) wprintw(status_win, "Mono-L+R  ");
1307 				break;
1308 			}
1309 
1310 			if (rptcnt >= 0)
1311 				(void) wprintw(status_win, "Count %u", rptcnt);
1312 
1313 			getyx(status_win, y, x);
1314 #ifdef lint
1315 			y = y;	/* Get rid of "set but not used" lint warning */
1316 #endif
1317 			for (i = x; i < scr_cols-1; i++)
1318 				(void) waddch(status_win, ' ');
1319 
1320 			(void) wmove(status_win, 1, 1);
1321 		}
1322 		else if (refresh_sts) {
1323 			refresh_sts = FALSE;
1324 			cda_clrstatus();
1325 
1326 			if (stat_on &&
1327 			    RD_ARG_MODE(astat0) != MOD_BUSY &&
1328 			    RD_ARG_MODE(astat0) != MOD_NODISC &&
1329 			    !RD_ARG_SHUF(astat0)) {
1330 				arg[0] = 1;
1331 				if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
1332 					cda_v_errhandler(retcode);
1333 
1334 					oastat0 = astat0;
1335 					oastat1 = astat1;
1336 
1337 					/* Come back in 1 sec */
1338 					(void) util_signal(SIGALRM,
1339 							   cda_screen);
1340 					(void) alarm(1);
1341 					return;
1342 				}
1343 
1344 				if (arg[0] != 0) {
1345 					(void) wprintw(status_win,
1346 							"Program: ");
1347 					for (i = 1; i <= arg[0]; i++) {
1348 						(void) wprintw(status_win,
1349 							" %02u", arg[i+1]);
1350 					}
1351 				}
1352 			}
1353 		}
1354 		break;
1355 	}
1356 
1357 	(void) wrefresh(status_win);
1358 
1359 	/* See if we want to display help info */
1360 	if (help) {
1361 		if (!old_help) {
1362 			(void) wclear(info_win);
1363 			(void) wmove(info_win, 0, 0);
1364 			(void) wprintw(info_win, HELP_INFO);
1365 			scroll_line = 0;
1366 			(void) prefresh(info_win, scroll_line, 0, 0, 0,
1367 					scr_lines - 9, scr_cols - 1);
1368 			old_help = help;
1369 		}
1370 		odiscnum = discnum;
1371 		(void) util_signal(SIGALRM, cda_screen);
1372 		(void) alarm(1);
1373 		return;
1374 	}
1375 	else if (old_help) {
1376 		(void) wclear(info_win);
1377 		scroll_line = 0;
1378 		track = -2;	/* Force display of version/device */
1379 	}
1380 
1381 	/* Disc changed: redisplay */
1382 	if (odiscnum != discnum) {
1383 		(void) wclear(info_win);
1384 		scroll_line = 0;
1385 		track = -2;
1386 	}
1387 
1388 	/* If state is unchanged since last time, no more to do */
1389 	if (stat_on == ostat_on && old_help == help && old_route == route &&
1390 	    RD_ARG_MODE(astat0) == RD_ARG_MODE(oastat0) &&
1391 	    RD_ARG_TRK(astat1) == RD_ARG_TRK(oastat1) &&
1392 	    RD_ARG_IDX(astat1) == RD_ARG_IDX(oastat1) &&
1393 	    odiscnum == discnum) {
1394 		oastat0 = astat0;
1395 		oastat1 = astat1;
1396 
1397 		/* Call us again - nothing is too much trouble! */
1398 		(void) util_signal(SIGALRM, cda_screen);
1399 		(void) alarm(1);
1400 		return;
1401 	}
1402 
1403 	old_help = help;
1404 	old_route = route;
1405 	odiscnum = discnum;
1406 	ostat_on = stat_on;
1407 	oastat0 = astat0;
1408 	oastat1 = astat1;
1409 
1410 	/* Now display data, according to our state: */
1411 
1412 	/* Off, busy or no disc, display version and device */
1413 	if (!stat_on ||
1414 	    RD_ARG_MODE(astat0) == MOD_BUSY ||
1415 	    RD_ARG_MODE(astat0) == MOD_NODISC) {
1416 		if (track != -1) {
1417 			track = -1;
1418 			(void) wclear(info_win);
1419 			(void) wmove(info_win, 0,0);
1420 			(void) wprintw(info_win,
1421 				"CDA - Command Line CD Audio Player/Ripper");
1422 			(void) wmove(info_win, 0, scr_cols-18);
1423 			(void) wprintw(info_win, "Press ");
1424 			(void) wattron(info_win, A_STANDOUT);
1425 			(void) wprintw(info_win, "?");
1426 			(void) wattroff(info_win, A_STANDOUT);
1427 			(void) wprintw(info_win, " for help.\n\n");
1428 
1429 			(void) wprintw(info_win, "CD audio        %s.%s.%s\n",
1430 				VERSION_MAJ, VERSION_MIN, VERSION_TEENY);
1431 
1432 			switch ((int) stat_on) {
1433 			case TRUE:
1434 				if (cda_sendcmd(CDA_VERSION, arg, &retcode)) {
1435 					(void) wprintw(info_win,
1436 						"CD audio daemon %s\n",
1437 						(char *) arg);
1438 					break;
1439 				}
1440 				cda_v_errhandler(retcode);
1441 				/*FALLTHROUGH*/
1442 
1443 			case FALSE:
1444 				(void) sprintf((char *) arg, "%s.%s.%s\n%s\n",
1445 					       VERSION_MAJ, VERSION_MIN,
1446 					       VERSION_TEENY, di_methodstr());
1447 				break;
1448 			}
1449 			(void) wprintw(info_win, "\nDevice: %s\n",
1450 					app_data.device);
1451 			if (stat_on) {
1452 				if (!cda_sendcmd(CDA_DEVICE, arg, &retcode)) {
1453 					cda_v_errhandler(retcode);
1454 
1455 					oastat0 = astat0;
1456 					oastat1 = astat1;
1457 
1458 					/* Come back in 1 sec */
1459 					(void) util_signal(SIGALRM,
1460 							   cda_screen);
1461 					(void) alarm(1);
1462 					return;
1463 				}
1464 				(void) wprintw(info_win, "%s", (char *) arg);
1465 			}
1466 			(void) wprintw(info_win,
1467 				"\n%s\nURL: %s\nE-mail: %s\n\n%s\n\n",
1468 				COPYRIGHT, XMCD_URL, EMAIL, GNU_BANNER);
1469 
1470 			ctrlver = cdinfo_cddbctrl_ver();
1471 			(void) wprintw(info_win, "CDDB%s service%s%s\n%s\n",
1472 				(cdinfo_cddb_ver() == 2) ?
1473 					"\262" : " \"classic\"",
1474 				(ctrlver[0] == '\0') ? "" : ": ",
1475 				(ctrlver[0] == '\0') ? "\n" : ctrlver,
1476 				CDDB_BANNER);
1477 
1478 			(void) prefresh(info_win, scroll_line, 0, 0, 0,
1479 					scr_lines - 9, scr_cols - 1);
1480 			getyx(info_win, scroll_length, i);
1481 			--scroll_length;
1482 		}
1483 	}
1484 	else if (track != RD_ARG_TRK(astat1)) {
1485 		/* If disc loaded, display extinfo */
1486 
1487 		(void) wclear(info_win);
1488 		(void) wmove(info_win, 0, 0);
1489 
1490 		/* Get CD information */
1491 		if (RD_ARG_MODE(astat0) == MOD_PLAY ||
1492 		    RD_ARG_MODE(astat0) == MOD_PAUSE) {
1493 			track = RD_ARG_TRK(astat1);
1494 		}
1495 		else
1496 			track = -1;
1497 
1498 		arg[0] = 0;
1499 		arg[1] = track;
1500 		if (!cda_sendcmd(CDA_EXTINFO, arg, &retcode)) {
1501 			cda_v_errhandler(retcode);
1502 
1503 			oastat0 = astat0;
1504 			oastat1 = astat1;
1505 
1506 			/* Come back in 1 sec */
1507 			(void) util_signal(SIGALRM, cda_screen);
1508 			(void) alarm(1);
1509 			return;
1510 		}
1511 
1512 		if (s->qmode != QMODE_MATCH ||
1513 		    RD_ARG_MODE(astat0) == MOD_STOP) {
1514 			int	ntrks;
1515 
1516 			/* No CD information */
1517 			arg[0] = 0;
1518 			if (!cda_sendcmd(CDA_TOC, arg, &retcode)) {
1519 				cda_v_errhandler(retcode);
1520 
1521 				oastat0 = astat0;
1522 				oastat1 = astat1;
1523 
1524 				/* Come back in 1 sec */
1525 				(void) util_signal(SIGALRM, cda_screen);
1526 				(void) alarm(1);
1527 				return;
1528 			}
1529 
1530 			dbp->discid = arg[0];
1531 			ntrks = (int) (arg[0] & 0xff);
1532 
1533 			q1 = (dbp->disc.genre == NULL) ?
1534 				"(unknown genre)" :
1535 				cdinfo_genre_name(dbp->disc.genre);
1536 			p1 = NULL;
1537 			if (!util_chset_conv(up, q1, &p1, FALSE) &&
1538 			    !util_newstr(&p1, q1)) {
1539 				util_chset_close(up);
1540 				return;
1541 			}
1542 			(void) wprintw(info_win, "Disc %u  Genre: %s %s",
1543 				discnum,
1544 				p1 == NULL ? "" : p1,
1545 				(dbp->disc.notes != NULL ||
1546 				 dbp->disc.credit_list != NULL) ?
1547 					 "*" : "");
1548 			if (p1 != NULL)
1549 				MEM_FREE(p1);
1550 
1551 			(void) wmove(info_win, 0, scr_cols-18);
1552 			(void) wprintw(info_win, "Press ");
1553 			(void) wattron(info_win, A_STANDOUT);
1554 			(void) wprintw(info_win, "?");
1555 			(void) wattroff(info_win, A_STANDOUT);
1556 			(void) wprintw(info_win, " for help.\n\n");
1557 
1558 			q1 = (dbp->disc.artist == NULL) ?
1559 				"" : dbp->disc.artist;
1560 			q2 = (dbp->disc.title == NULL) ?
1561 				"(unknown title)" : dbp->disc.title;
1562 			p1 = p2 = NULL;
1563 			if (!util_chset_conv(up, q1, &p1, FALSE) &&
1564 			    !util_newstr(&p1, q1)) {
1565 				util_chset_close(up);
1566 				return;
1567 			}
1568 			if (!util_chset_conv(up, q2, &p2, FALSE) &&
1569 			    !util_newstr(&p1, q1)) {
1570 				util_chset_close(up);
1571 				return;
1572 			}
1573 			(void) wprintw(info_win, "%s%s%s\n\n",
1574 				p1 == NULL ? "" : p1,
1575 				(dbp->disc.artist != NULL &&
1576 					dbp->disc.title != NULL) ? " / " : "",
1577 				p2 == NULL ? "" : p2);
1578 			if (p1 != NULL)
1579 				MEM_FREE(p1);
1580 			MEM_FREE(p2);
1581 
1582 			for (i = 0; i < (int) ntrks; i++) {
1583 				RD_ARG_TOC(arg[i+1], trk, playing, min, sec);
1584 				(void) wprintw(info_win, "%s%02u %02u:%02u  ",
1585 					playing ? ">" : " ",
1586 					trk, min, sec);
1587 
1588 				if (dbp->track[i].title != NULL) {
1589 				    q1 = dbp->track[i].title;
1590 				    p1 = NULL;
1591 				    if (!util_chset_conv(up, q1, &p1, FALSE) &&
1592 					!util_newstr(&p1, q1)) {
1593 					    util_chset_close(up);
1594 					    return;
1595 				    }
1596 				    (void) wprintw(info_win, "%s%s\n",
1597 					p1 == NULL ? "" : p1,
1598 					(dbp->track[i].notes != NULL ||
1599 					 dbp->track[i].credit_list != NULL) ?
1600 					    "*" : "");
1601 				    if (p1 != NULL)
1602 					    MEM_FREE(p1);
1603 				}
1604 				else {
1605 				    (void) wprintw(info_win, "??%s\n",
1606 					(dbp->track[i].notes != NULL ||
1607 					 dbp->track[i].credit_list != NULL) ?
1608 					    "*" : "");
1609 				}
1610 			}
1611 
1612 			RD_ARG_TOC(arg[i+1], trk, playing, min, sec);
1613 			(void) wprintw(info_win, "\nTotal Time: %02u:%02u\n",
1614 				min, sec);
1615 		}
1616 		else {
1617 			/* Got CD information */
1618 			if (dbp->disc.notes == NULL) {
1619 				(void) wprintw(info_win,
1620 				    "No Disc Notes for this CD.\n");
1621 			}
1622 			else {
1623 				q1 = (dbp->disc.artist == NULL) ?
1624 					"" : dbp->disc.artist;
1625 				q2 = (dbp->disc.title == NULL) ?
1626 					"(unknown title)" : dbp->disc.title;
1627 				p1 = p2 = NULL;
1628 				if (!util_chset_conv(up, q1, &p1, FALSE) &&
1629 				    !util_newstr(&p1, q1)) {
1630 					util_chset_close(up);
1631 					return;
1632 				}
1633 				if (!util_chset_conv(up, q2, &p2, FALSE) &&
1634 				    !util_newstr(&p2, q2)) {
1635 					util_chset_close(up);
1636 					return;
1637 				}
1638 				(void) wprintw(info_win, "%s%s%s\n\n",
1639 				    p1 == NULL ? "" : p1,
1640 				    (dbp->disc.artist != NULL &&
1641 					dbp->disc.title != NULL) ? " / " : "",
1642 				    p2 == NULL ? "" : p2);
1643 
1644 				q1 = dbp->disc.notes;
1645 				p1 = NULL;
1646 				if (!util_chset_conv(up, q1, &p1, FALSE) &&
1647 				    !util_newstr(&p1, q1)) {
1648 					util_chset_close(up);
1649 					return;
1650 				}
1651 				/* Not using wprintw here to avoid a bug
1652 				 * with very long strings
1653 				 */
1654 				for (p = p1; p != NULL && *p != '\0'; p++)
1655 				       (void) waddch(info_win, *p);
1656 				(void) waddch(info_win, '\n');
1657 				if (p1 != NULL)
1658 					MEM_FREE(p1);
1659 			}
1660 
1661 			for (i = 0; i < scr_cols-1; i++)
1662 				(void) waddch(info_win, ACS_HLINE);
1663 			(void) waddch(info_win, '\n');
1664 
1665 			/* If Play or Pause, display track info */
1666 			if (RD_ARG_MODE(astat0) == MOD_PLAY ||
1667 			    RD_ARG_MODE(astat0) == MOD_PAUSE) {
1668 				if (dbp->track[arg[2]].title == NULL) {
1669 					(void) wprintw(info_win,
1670 						"(No title for track %02u.)\n",
1671 						arg[1]);
1672 				}
1673 				else {
1674 					q1 = dbp->track[arg[2]].title;
1675 					p1 = NULL;
1676 					if (!util_chset_conv(up, q1,
1677 							     &p1, FALSE) &&
1678 					    !util_newstr(&p1, q1)) {
1679 						util_chset_close(up);
1680 						return;
1681 					}
1682 					(void) wprintw(info_win, "%s\n",
1683 						       p1 == NULL ? "" : p1);
1684 					if (p1 != NULL)
1685 						MEM_FREE(p1);
1686 
1687 					if (dbp->track[arg[2]].notes != NULL) {
1688 						q1 = dbp->track[arg[2]].notes;
1689 						p1 = NULL;
1690 						if (!util_chset_conv(up,
1691 							    q1, &p1, FALSE) &&
1692 						    !util_newstr(&p1, q1)) {
1693 							util_chset_close(up);
1694 							return;
1695 						}
1696 						/* Not using wprintw here
1697 						 * to avoid a bug with very
1698 						 * long strings
1699 						 */
1700 						(void) waddch(info_win, '\n');
1701 						for (p = p1;
1702 						     p != NULL && *p != '\0';
1703 						     p++)
1704 						       (void) waddch(info_win,
1705 								     *p);
1706 						(void) waddch(info_win, '\n');
1707 						if (p1 != NULL)
1708 							MEM_FREE(p1);
1709 					}
1710 				}
1711 			}
1712 		}
1713 		scroll_line = 0;
1714 		getyx(info_win, scroll_length, i);
1715 		(void) prefresh(info_win, scroll_line, 0, 0, 0,
1716 				scr_lines - 9, scr_cols - 1);
1717 	}
1718 	oastat0 = astat0;
1719 	oastat1 = astat1;
1720 
1721 	/* Come back in 1 sec */
1722 	(void) util_signal(SIGALRM, cda_screen);
1723 	(void) alarm(1);
1724 }
1725 
1726 
1727 /*
1728  * cda_v_on_off
1729  *	Handler function for the visual mode on/off control.
1730  *
1731  * Args:
1732  *	inp - command character
1733  *	arg - command arguments
1734  *
1735  * Return:
1736  *	Nothing.
1737  */
1738 /*ARGSUSED*/
1739 STATIC void
cda_v_on_off(int inp,word32_t arg[])1740 cda_v_on_off(int inp, word32_t arg[])
1741 {
1742 	int		retcode;
1743 	curstat_t	*s = curstat_addr();
1744 
1745 	cda_clrstatus();
1746 
1747 	if (!stat_on) {
1748 		if (cda_daemon_alive()) {
1749 			(void) wprintw(status_win,
1750 				"CD audio daemon already running.");
1751 			(void) wrefresh(status_win);
1752 			beep();
1753 		}
1754 		else {
1755 			/* Start CDA daemon */
1756 			loaddb = TRUE;
1757 			(void) wprintw(status_win, "Initializing...");
1758 			(void) wrefresh(status_win);
1759 
1760 			if (!cda_daemon(s)) {
1761 				cda_clrstatus();
1762 				(void) wprintw(status_win,
1763 					"Cannot start CD audio daemon.");
1764 				(void) wrefresh(status_win);
1765 			}
1766 		}
1767 	}
1768 	else {
1769 		if (cda_sendcmd(CDA_OFF, arg, &retcode)) {
1770 			(void) wprintw(status_win,
1771 					"Stopping CD audio daemon...");
1772 			(void) wrefresh(status_win);
1773 
1774 			/* Wait for daemon to die */
1775 			do {
1776 				util_delayms(1000);
1777 			} while (cda_daemon_alive());
1778 		}
1779 
1780 		cda_v_errhandler(retcode);
1781 	}
1782 }
1783 
1784 
1785 /*
1786  * cda_v_load_eject
1787  *	Handler function for the visual mode load/eject control.
1788  *
1789  * Args:
1790  *	inp - command character
1791  *	arg - command arguments
1792  *
1793  * Return:
1794  *	Nothing.
1795  */
1796 /*ARGSUSED*/
1797 STATIC void
cda_v_load_eject(int inp,word32_t arg[])1798 cda_v_load_eject(int inp, word32_t arg[])
1799 {
1800 	int	retcode;
1801 
1802 	if (!stat_on || RD_ARG_MODE(arg[0]) == MOD_BUSY) {
1803 		beep();
1804 		return;
1805 	}
1806 
1807 	arg[0] = (RD_ARG_MODE(arg[0]) == MOD_NODISC) ? 0 : 1;
1808 
1809 	if (!cda_sendcmd(CDA_DISC, arg, &retcode))
1810 		cda_v_errhandler(retcode);
1811 
1812 	loaddb = TRUE;	/* Force reload of CDDB */
1813 	refresh_sts = TRUE;
1814 }
1815 
1816 
1817 /*
1818  * cda_v_play_pause
1819  *	Handler function for the visual mode play/pause control.
1820  *
1821  * Args:
1822  *	inp - command character
1823  *	arg - command arguments
1824  *
1825  * Return:
1826  *	Nothing.
1827  */
1828 /*ARGSUSED*/
1829 STATIC void
cda_v_play_pause(int inp,word32_t arg[])1830 cda_v_play_pause(int inp, word32_t arg[])
1831 {
1832 	word32_t	cmd;
1833 	int		retcode;
1834 
1835 	if (!stat_on ||
1836 	    RD_ARG_MODE(arg[0]) == MOD_BUSY ||
1837 	    RD_ARG_MODE(arg[0]) == MOD_NODISC) {
1838 		beep();
1839 		return;
1840 	}
1841 
1842 	if (RD_ARG_MODE(arg[0]) == MOD_PLAY)
1843 		cmd = CDA_PAUSE;
1844 	else
1845 		cmd = CDA_PLAY;
1846 
1847 	arg[0] = 0;
1848 	if (!cda_sendcmd(cmd, arg, &retcode))
1849 		cda_v_errhandler(retcode);
1850 }
1851 
1852 
1853 /*
1854  * cda_v_stop
1855  *	Handler function for the visual mode stop control.
1856  *
1857  * Args:
1858  *	inp - command character
1859  *	arg - command arguments
1860  *
1861  * Return:
1862  *	Nothing.
1863  */
1864 /*ARGSUSED*/
1865 STATIC void
cda_v_stop(int inp,word32_t arg[])1866 cda_v_stop(int inp, word32_t arg[])
1867 {
1868 	int	retcode;
1869 
1870 	if (!stat_on ||
1871 	    RD_ARG_MODE(arg[0]) == MOD_BUSY ||
1872 	    RD_ARG_MODE(arg[0]) == MOD_NODISC) {
1873 		beep();
1874 		return;
1875 	}
1876 
1877 	if (RD_ARG_MODE(arg[0]) != MOD_PLAY &&
1878 	    RD_ARG_MODE(arg[0]) != MOD_PAUSE) {
1879 		beep();
1880 		return;
1881 	}
1882 
1883 	if (!cda_sendcmd(CDA_STOP, arg, &retcode))
1884 		cda_v_errhandler(retcode);
1885 
1886 	refresh_sts = TRUE;
1887 }
1888 
1889 
1890 /*
1891  * cda_v_lock
1892  *	Handler function for the visual mode lock/unlock control.
1893  *
1894  * Args:
1895  *	inp - command character
1896  *	arg - command arguments
1897  *
1898  * Return:
1899  *	Nothing.
1900  */
1901 /*ARGSUSED*/
1902 STATIC void
cda_v_lock(int inp,word32_t arg[])1903 cda_v_lock(int inp, word32_t arg[])
1904 {
1905 	int	retcode;
1906 
1907 	if (!stat_on ||
1908 	    RD_ARG_MODE(arg[0]) == MOD_BUSY ||
1909 	    RD_ARG_MODE(arg[0]) == MOD_NODISC) {
1910 		beep();
1911 		return;
1912 	}
1913 
1914 	arg[0] = RD_ARG_LOCK(arg[0]) ? 0 : 1;
1915 
1916 	if (!cda_sendcmd(CDA_LOCK, arg, &retcode))
1917 		cda_v_errhandler(retcode);
1918 }
1919 
1920 
1921 /*
1922  * cda_v_shufprog
1923  *	Handler function for the visual mode shuffle/program control.
1924  *
1925  * Args:
1926  *	inp - command character
1927  *	arg - command arguments
1928  *
1929  * Return:
1930  *	Nothing.
1931  */
1932 /*ARGSUSED*/
1933 STATIC void
cda_v_shufprog(int inp,word32_t arg[])1934 cda_v_shufprog(int inp, word32_t arg[])
1935 {
1936 	word32_t	astat0 = arg[0];
1937 	int		i,
1938 			j,
1939 			retcode;
1940 	char		inbuf[80],
1941 			*p;
1942 
1943 	if (!stat_on ||
1944 	    RD_ARG_MODE(astat0) == MOD_BUSY ||
1945 	    RD_ARG_MODE(astat0) == MOD_NODISC) {
1946 		beep();
1947 		return;
1948 	}
1949 
1950 	/* Not allowed if play or pause */
1951 	if (RD_ARG_MODE(astat0) == MOD_PLAY ||
1952 	    RD_ARG_MODE(astat0) == MOD_PAUSE) {
1953 		beep();
1954 		return;
1955 	}
1956 
1957 	/* See if program on */
1958 	arg[0] = 1;
1959 	if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
1960 		cda_v_errhandler(retcode);
1961 		return;
1962 	}
1963 
1964 	/* If neither program nor shuffle, set shuffle */
1965 	if (!RD_ARG_SHUF(astat0) && arg[0] == 0) {
1966 		arg[0] = 1;
1967 		if (!cda_sendcmd(CDA_SHUFFLE, arg, &retcode)) {
1968 			cda_v_errhandler(retcode);
1969 			return;
1970 		}
1971 	}
1972 	else if (RD_ARG_SHUF(astat0)) {
1973 		/* If shuffle, turn it off and prompt for program */
1974 		arg[0] = 0;
1975 		if (!cda_sendcmd(CDA_SHUFFLE, arg, &retcode)) {
1976 			cda_v_errhandler(retcode);
1977 			return;
1978 		}
1979 
1980 		(void) wmove(status_win, SHUFFLE_Y, SHUFFLE_X);
1981 		(void) waddstr(status_win, "Shuffle");
1982 
1983 		(void) wmove(status_win, PROGRAM_Y, PROGRAM_X);
1984 		(void) wattron(status_win, A_STANDOUT);
1985 		(void) waddstr(status_win, "Program");
1986 		(void) wattroff(status_win, A_STANDOUT);
1987 
1988 		cda_clrstatus();
1989 		(void) wprintw(status_win, "Program: ");
1990 		cda_v_echo(TRUE);
1991 		(void) putp(cursor_normal);
1992 		(void) wrefresh(status_win);
1993 
1994 		/* Before reading the program list, check for
1995 		 * F6 or "u", and dismiss prog mode if found
1996 		 */
1997 		i = cda_wgetch(status_win);
1998 		if (i != KEY_F(6) && i != 'u') {
1999 			cda_ungetch(i);
2000 
2001 			cda_wgetstr(status_win, inbuf, scr_cols-12);
2002 
2003 			/* Is the string just read was
2004 			 * terminated by F6, it will
2005 			 * have been ungotten, but must be
2006 			 * thrown away or it will cause
2007 			 * return to shuffle mode.
2008 			 */
2009 			if (savch == KEY_F(6))
2010 				savch = 0;
2011 
2012 			j = 0;
2013 			p = inbuf;
2014 			while ((i = strtol(p, &p, 10)) != 0) {
2015 				arg[++j] = i;
2016 
2017 				if (p == NULL)
2018 					break;
2019 
2020 				while (*p != '\0' && !isdigit((int) *p))
2021 					p++;
2022 			}
2023 
2024 			arg[0] = (word32_t) -j;
2025 			if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
2026 				emsgp = NULL;
2027 				beep();
2028 			}
2029 		}
2030 
2031 		cda_v_echo(FALSE);
2032 		(void) putp(cursor_invisible);
2033 
2034 		refresh_sts = TRUE;
2035 	}
2036 	else {
2037 		/* Program is on - reset it */
2038 		arg[0] = 0;
2039 		if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
2040 			cda_v_errhandler(retcode);
2041 			return;
2042 		}
2043 		refresh_sts = TRUE;
2044 	}
2045 }
2046 
2047 
2048 /*
2049  * cda_v_repeat
2050  *	Handler function for the visual mode repeat control.
2051  *
2052  * Args:
2053  *	inp - command character
2054  *	arg - command arguments
2055  *
2056  * Return:
2057  *	Nothing.
2058  */
2059 /*ARGSUSED*/
2060 STATIC void
cda_v_repeat(int inp,word32_t arg[])2061 cda_v_repeat(int inp, word32_t arg[])
2062 {
2063 	int	retcode;
2064 
2065 	if (!stat_on) {
2066 		beep();
2067 		return;
2068 	}
2069 
2070 	arg[0] = RD_ARG_REPT(arg[0]) ? 0 : 1;
2071 	if (!cda_sendcmd(CDA_REPEAT, arg, &retcode))
2072 		cda_v_errhandler(retcode);
2073 }
2074 
2075 
2076 /*
2077  * cda_v_disc
2078  *	Handler function for the visual mode disc change control.
2079  *
2080  * Args:
2081  *	inp - command character
2082  *	arg - command arguments
2083  *
2084  * Return:
2085  *	Nothing.
2086  */
2087 STATIC void
cda_v_disc(int inp,word32_t arg[])2088 cda_v_disc(int inp, word32_t arg[])
2089 {
2090 	int	retcode;
2091 
2092 	if (!stat_on) {
2093 		beep();
2094 		return;
2095 	}
2096 
2097 	arg[0] = (inp == 'd') ? 2 : 3;
2098 	if (!cda_sendcmd(CDA_DISC, arg, &retcode))
2099 		cda_v_errhandler(retcode);
2100 
2101 	loaddb = TRUE;
2102 }
2103 
2104 
2105 /*
2106  * cda_v_track
2107  *	Handler function for the visual mode track change control.
2108  *
2109  * Args:
2110  *	inp - command character
2111  *	arg - command arguments
2112  *
2113  * Return:
2114  *	Nothing.
2115  */
2116 STATIC void
cda_v_track(int inp,word32_t arg[])2117 cda_v_track(int inp, word32_t arg[])
2118 {
2119 	int	retcode;
2120 
2121 	if (!stat_on ||
2122 	    RD_ARG_MODE(arg[0]) == MOD_BUSY ||
2123 	    RD_ARG_MODE(arg[0]) == MOD_NODISC ||
2124 	    RD_ARG_MODE(arg[0]) != MOD_PLAY) {
2125 		beep();
2126 		return;
2127 	}
2128 
2129 	arg[0] = (inp == KEY_LEFT || inp == 'C') ? 0 : 1;
2130 	if (!cda_sendcmd(CDA_TRACK, arg, &retcode))
2131 		cda_v_errhandler(retcode);
2132 }
2133 
2134 
2135 /*
2136  * cda_v_index
2137  *	Handler function for the visual mode index change control.
2138  *
2139  * Args:
2140  *	inp - command character
2141  *	arg - command arguments
2142  *
2143  * Return:
2144  *	Nothing.
2145  */
2146 STATIC void
cda_v_index(int inp,word32_t arg[])2147 cda_v_index(int inp, word32_t arg[])
2148 {
2149 	int	retcode;
2150 
2151 	if (!stat_on ||
2152 	    RD_ARG_MODE(arg[0]) == MOD_BUSY ||
2153 	    RD_ARG_MODE(arg[0]) == MOD_NODISC) {
2154 		beep();
2155 		return;
2156 	}
2157 
2158 	arg[0] = (inp == '<') ? 0 : 1;
2159 	if (!cda_sendcmd(CDA_INDEX, arg, &retcode))
2160 		cda_v_errhandler(retcode);
2161 }
2162 
2163 
2164 /*
2165  * cda_v_volume
2166  *	Handler function for the visual mode volume control.
2167  *
2168  * Args:
2169  *	inp - command character
2170  *	arg - command arguments
2171  *
2172  * Return:
2173  *	Nothing.
2174  */
2175 STATIC void
cda_v_volume(int inp,word32_t arg[])2176 cda_v_volume(int inp, word32_t arg[])
2177 {
2178 	int	retcode;
2179 
2180 	if (!stat_on) {
2181 		beep();
2182 		return;
2183 	}
2184 
2185 	arg[0] = 0;
2186 	if (!cda_sendcmd(CDA_VOLUME, arg, &retcode)) {
2187 		cda_v_errhandler(retcode);
2188 		return;
2189 	}
2190 
2191 	if (inp == '+') {
2192 		if (arg[1] <= 95)
2193 			arg[1] += 5;
2194 		else
2195 			arg[1] = 100;
2196 	}
2197 	else {
2198 		if (arg[1] >= 5)
2199 			arg[1] -= 5;
2200 		else
2201 			arg[1] = 0;
2202 	}
2203 
2204 	arg[0] = 1;
2205 	if (!cda_sendcmd(CDA_VOLUME, arg, &retcode)) {
2206 		cda_v_errhandler(retcode);
2207 		return;
2208 	}
2209 }
2210 
2211 
2212 /*
2213  * cda_v_balance
2214  *	Handler function for the visual mode balance control.
2215  *
2216  * Args:
2217  *	inp - command character
2218  *	arg - command arguments
2219  *
2220  * Return:
2221  *	Nothing.
2222  */
2223 STATIC void
cda_v_balance(int inp,word32_t arg[])2224 cda_v_balance(int inp, word32_t arg[])
2225 {
2226 	int	retcode;
2227 
2228 	if (!stat_on) {
2229 		beep();
2230 		return;
2231 	}
2232 
2233 	arg[0] = 0;
2234 	if (!cda_sendcmd(CDA_BALANCE, arg, &retcode)) {
2235 		cda_v_errhandler(retcode);
2236 		return;
2237 	}
2238 
2239 	if (inp == 'r' || inp == 'R') {
2240 		if (arg[1] <= 95)
2241 			arg[1] += 5;
2242 		else
2243 			arg[1] = 100;
2244 	}
2245 	else {
2246 		if (arg[1] >= 5)
2247 			arg[1] -= 5;
2248 		else
2249 			arg[1] = 0;
2250 	}
2251 
2252 	arg[0] = 1;
2253 	if (!cda_sendcmd(CDA_BALANCE, arg, &retcode))
2254 		cda_v_errhandler(retcode);
2255 }
2256 
2257 
2258 /*
2259  * cda_v_route
2260  *	Handler function for the visual mode channel routing control.
2261  *
2262  * Args:
2263  *	inp - command character
2264  *	arg - command arguments
2265  *
2266  * Return:
2267  *	Nothing.
2268  */
2269 /*ARGSUSED*/
2270 STATIC void
cda_v_route(int inp,word32_t arg[])2271 cda_v_route(int inp, word32_t arg[])
2272 {
2273 	int	retcode;
2274 
2275 	if (!stat_on) {
2276 		beep();
2277 		return;
2278 	}
2279 
2280 	if (--route < 0)
2281 		route = 4;
2282 
2283 	arg[0] = 1;
2284 	arg[1] = route;
2285 	if (!cda_sendcmd(CDA_ROUTE, arg, &retcode))
2286 		cda_v_errhandler(retcode);
2287 }
2288 
2289 
2290 /*
2291  * cda_v_help
2292  *	Handler function for the visual mode help control.
2293  *
2294  * Args:
2295  *	inp - command character
2296  *	arg - command arguments
2297  *
2298  * Return:
2299  *	Nothing.
2300  */
2301 /*ARGSUSED*/
2302 STATIC void
cda_v_help(int inp,word32_t arg[])2303 cda_v_help(int inp, word32_t arg[])
2304 {
2305 	if (inp == '?') {
2306 		help = TRUE;
2307 		scroll_length = HELP_SCROLL_LENGTH;
2308 	}
2309 	else
2310 		help = FALSE;
2311 }
2312 
2313 
2314 /*
2315  * cda_v_repaint
2316  *	Handler function for the visual mode repaint control.
2317  *
2318  * Args:
2319  *	inp - command character
2320  *	arg - command arguments
2321  *
2322  * Return:
2323  *	Nothing.
2324  */
2325 /*ARGSUSED*/
2326 STATIC void
cda_v_repaint(int inp,word32_t arg[])2327 cda_v_repaint(int inp, word32_t arg[])
2328 {
2329 	wclear(info_win);
2330 	wclear(status_win);
2331 	oastat0 = oastat1 = (word32_t) -1;
2332 	ostat_on = !stat_on;
2333 	old_help = !help;
2334 	old_route = !route;
2335 	refresh_fkeys = TRUE;
2336 }
2337 
2338 
2339 /*
2340  * cda_v_scroll
2341  *	Handler function for the visual mode scroll control.
2342  *
2343  * Args:
2344  *	inp - command character
2345  *	arg - command arguments
2346  *
2347  * Return:
2348  *	Nothing.
2349  */
2350 /*ARGSUSED*/
2351 STATIC void
cda_v_scroll(int inp,word32_t arg[])2352 cda_v_scroll(int inp, word32_t arg[])
2353 {
2354 	switch (inp) {
2355 	case KEY_UP:
2356 	case '^':
2357 	case '\036':	/* ctrl-^ */
2358 		scroll_line--;
2359 		break;
2360 	case KEY_DOWN:
2361 	case 'v':
2362 	case 'V':
2363 	case '\026':	/* ctrl-v */
2364 		scroll_line++;
2365 		break;
2366 	case KEY_PPAGE:
2367 	case '\002':	/* ctrl-b */
2368 		scroll_line -= (scr_lines - 9);
2369 		break;
2370 	case '\025':	/* ctrl-u */
2371 		scroll_line -= ((scr_lines - 9) / 2);
2372 		break;
2373 	case KEY_NPAGE:
2374 	case '\006':	/* ctrl-f */
2375 		scroll_line += (scr_lines - 9);
2376 		break;
2377 	case '\004':	/* ctrl-d */
2378 		scroll_line += ((scr_lines - 9) / 2);
2379 		break;
2380 	default:
2381 		return;
2382 	}
2383 
2384 	if (scroll_line < 0)
2385 		scroll_line = 0;
2386 	else if (scroll_line > (scroll_length - 1))
2387 		scroll_line = scroll_length - 1;
2388 
2389 	(void) prefresh(info_win, scroll_line, 0, 0, 0,
2390 			scr_lines - 9, scr_cols - 1);
2391 }
2392 
2393 
2394 /*
2395  * cda_v_debug
2396  *	Handler function for the debug command - toggle debug mode.
2397  *
2398  * Args:
2399  *	inp - command character
2400  *	arg - command arguments
2401  *
2402  * Return:
2403  *	Nothing.
2404  */
2405 /*ARGSUSED*/
2406 STATIC void
cda_v_debug(int inp,word32_t arg[])2407 cda_v_debug(int inp, word32_t arg[])
2408 {
2409 	int	retcode;
2410 
2411 	if (!stat_on) {
2412 		beep();
2413 		return;
2414 	}
2415 
2416 	/* Query current debug mode */
2417 	arg[0] = 0;
2418 	if (!cda_sendcmd(CDA_DEBUG, arg, &retcode)) {
2419 		beep();
2420 		cda_v_errhandler(retcode);
2421 		return;
2422 	}
2423 
2424 	/* Toggle debug mode */
2425 	arg[0] = 1;
2426 	if (arg[1] == 0)
2427 		arg[1] = 1;
2428 	else
2429 		arg[1] = 0;
2430 
2431 	if (!cda_sendcmd(CDA_DEBUG, arg, &retcode)) {
2432 		beep();
2433 		cda_v_errhandler(retcode);
2434 		return;
2435 	}
2436 
2437 	cda_clrstatus();
2438 	(void) wprintw(status_win, "Debug mode is now %s.",
2439 		(arg[1] == 0) ? "off" : "on");
2440 	(void) wrefresh(status_win);
2441 }
2442 
2443 
2444 /*
2445  * cda_v_dirtrk
2446  *	Handler function for the visual mode direct track access.
2447  *
2448  * Args:
2449  *	inp - command character
2450  *	arg - command arguments
2451  *
2452  * Return:
2453  *	Nothing.
2454  */
2455 STATIC void
cda_v_dirtrk(int inp,word32_t arg[])2456 cda_v_dirtrk(int inp, word32_t arg[])
2457 {
2458 	int	i,
2459 		mins,
2460 		secs,
2461 		retcode;
2462 	char	inbuf[80],
2463 		*p;
2464 
2465 	if (!isdigit(inp))
2466 		return;
2467 
2468 	if (!stat_on) {
2469 		beep();
2470 		return;
2471 	}
2472 
2473 	cda_ungetch(inp);
2474 
2475 	cda_clrstatus();
2476 	(void) wprintw(status_win, "Track n [mins secs] : ");
2477 	cda_v_echo(TRUE);
2478 	(void) putp(cursor_normal);
2479 	(void) wrefresh(status_win);
2480 
2481 	cda_wgetstr(status_win, inbuf, 20);
2482 	if ((i = strtol(inbuf, &p, 10)) == 0)
2483 		beep();
2484 	else {
2485 		mins = secs = 0;
2486 		if (p != NULL) {
2487 			while (*p != '\0' && !isdigit((int) *p))
2488 				p++;
2489 
2490 			mins = strtol(p, &p, 10);
2491 
2492 			if (p != NULL) {
2493 				while (*p != '\0' && !isdigit((int) *p))
2494 					p++;
2495 
2496 				secs = strtol(p, &p, 10);
2497 			}
2498 		}
2499 
2500 		arg[0] = i;
2501 		arg[1] = mins;
2502 		arg[2] = secs;
2503 		if (!cda_sendcmd(CDA_PLAY, arg, &retcode))
2504 			cda_v_errhandler(retcode);
2505 	}
2506 
2507 	cda_v_echo(FALSE);
2508 	(void) putp(cursor_invisible);
2509 	refresh_sts = TRUE;
2510 }
2511 
2512 
2513 #ifdef KEY_RESIZE
2514 /*
2515  * cda_v_resize
2516  *	Handler function when KEY_RESIZE is received.  This is to support
2517  *	ncurses library configured with its own SIGWINCH handler.
2518  *
2519  * Args:
2520  *	inp - command character
2521  *	arg - command arguments
2522  *
2523  * Return:
2524  *	Nothing.
2525  */
2526 /*ARGSUSED*/
2527 STATIC void
cda_v_resize(int inp,word32_t arg[])2528 cda_v_resize(int inp, word32_t arg[])
2529 {
2530 	win_resize = TRUE;
2531 }
2532 #endif
2533 
2534 
2535 /***********************
2536  *   public routines   *
2537  ***********************/
2538 
2539 
2540 /*
2541  * cda_vtidy
2542  *	Tidy up and go home for visual mode.
2543  *
2544  * Args:
2545  *	None.
2546  *
2547  * Return:
2548  *	Nothing.
2549  */
2550 void
cda_vtidy(void)2551 cda_vtidy(void)
2552 {
2553 	if (isvisual) {
2554 		(void) keypad(stdscr, FALSE);
2555 		(void) putp(cursor_normal);
2556 		(void) endwin();
2557 		isvisual = FALSE;
2558 	}
2559 
2560 	(void) printf("%s\n", emsgp != NULL ? emsgp : "Goodbye!");
2561 }
2562 
2563 
2564 /* Keyboard input to command function mapping table */
2565 STATIC keytbl_t	ktbl[] = {
2566 	{ KEY_F(1),	cda_v_on_off		},
2567 	{ 'o',		cda_v_on_off		},
2568 	{ 'O',		cda_v_on_off		},
2569 	{ KEY_F(2),	cda_v_load_eject	},
2570 	{ 'j',		cda_v_load_eject	},
2571 	{ 'J',		cda_v_load_eject	},
2572 	{ KEY_F(3),	cda_v_play_pause	},
2573 	{ 'p',		cda_v_play_pause	},
2574 	{ 'P',		cda_v_play_pause	},
2575 	{ KEY_F(4),	cda_v_stop		},
2576 	{ 's',		cda_v_stop		},
2577 	{ 'S',		cda_v_stop		},
2578 	{ KEY_F(5),	cda_v_lock		},
2579 	{ 'k',		cda_v_lock		},
2580 	{ 'K',		cda_v_lock		},
2581 	{ KEY_F(6),	cda_v_shufprog		},
2582 	{ 'u',		cda_v_shufprog		},
2583 	{ 'U',		cda_v_shufprog		},
2584 	{ KEY_F(7),	cda_v_repeat		},
2585 	{ 'e',		cda_v_repeat		},
2586 	{ 'E',		cda_v_repeat		},
2587 	{ 'd',		cda_v_disc		},
2588 	{ 'D',		cda_v_disc		},
2589 	{ KEY_LEFT,	cda_v_track		},
2590 	{ KEY_RIGHT,	cda_v_track		},
2591 	{ 'C',		cda_v_track		},
2592 	{ 'c',		cda_v_track		},
2593 	{ '<',		cda_v_index		},
2594 	{ '>',		cda_v_index		},
2595 	{ '+',		cda_v_volume		},
2596 	{ '-',		cda_v_volume		},
2597 	{ 'l',		cda_v_balance		},
2598 	{ 'L',		cda_v_balance		},
2599 	{ 'r',		cda_v_balance		},
2600 	{ 'R',		cda_v_balance		},
2601 	{ '\011',	cda_v_route		},
2602 	{ '?',		cda_v_help		},
2603 	{ ' ',		cda_v_help		},
2604 	{ '\014',	cda_v_repaint		},
2605 	{ '\022',	cda_v_repaint		},
2606 	{ KEY_UP,	cda_v_scroll		},
2607 	{ KEY_DOWN,	cda_v_scroll		},
2608 	{ '^',		cda_v_scroll		},
2609 	{ 'v',		cda_v_scroll		},
2610 	{ '\036',	cda_v_scroll		},
2611 	{ '\026',	cda_v_scroll		},
2612 	{ 'V',		cda_v_scroll		},
2613 	{ KEY_PPAGE,	cda_v_scroll		},
2614 	{ KEY_NPAGE,	cda_v_scroll		},
2615 	{ '\002',	cda_v_scroll		},
2616 	{ '\025',	cda_v_scroll		},
2617 	{ '\006',	cda_v_scroll		},
2618 	{ '\004',	cda_v_scroll		},
2619 	{ 'd',		cda_v_debug		},
2620 	{ '\0',		cda_v_dirtrk		},
2621 #ifdef KEY_RESIZE
2622 	{ KEY_RESIZE,	cda_v_resize		},
2623 #endif
2624 	{ '\0',		NULL			}
2625 };
2626 
2627 
2628 /*
2629  * cda_visual
2630  *	Visual (curses mode) interface.
2631  *
2632  * Args:
2633  *	None.
2634  *
2635  * Return:
2636  *	Nothing.
2637  */
2638 void
cda_visual(void)2639 cda_visual(void)
2640 {
2641 	word32_t	arg[CDA_NARGS];
2642 	int		inp,
2643 			i,
2644 			retcode;
2645 	curstat_t	*s = curstat_addr();
2646 
2647 	/* Set up device list */
2648 	cda_parse_devlist(s);
2649 
2650 	/* Initialize curses */
2651 	cda_makewins();
2652 
2653 	/* Paint the screen every second */
2654 	cda_screen(0);
2655 
2656 	/* Main processing loop */
2657 	for (;;) {
2658 		inp = cda_wgetch(status_win);
2659 
2660 		if (inp == KEY_F(8) || inp == 'q' || inp == 'Q')
2661 			break;
2662 
2663 		/* Cancel alarm so we don't nest */
2664 		(void) alarm(0);
2665 
2666 		/* Get current status */
2667 		if (stat_on && !cda_sendcmd(CDA_STATUS, arg, &retcode))
2668 			cda_v_errhandler(retcode);
2669 
2670 		/* Execute command function */
2671 		for (i = 0; ktbl[i].keyfunc != NULL; i++) {
2672 			if (ktbl[i].key == inp || ktbl[i].key == '\0') {
2673 				(*ktbl[i].keyfunc)(inp, arg);
2674 				break;
2675 			}
2676 		}
2677 
2678 		/* Repaint screen */
2679 		cda_screen(0);
2680 	}
2681 
2682 	/* Tidy up and go home */
2683 	(void) alarm(0);
2684 	emsgp = NULL;
2685 
2686 	if (cda_daemon_alive()) {
2687 		(void) sprintf(errmsg, "CD audio daemon pid=%d still running.",
2688 			       (int) daemon_pid);
2689 		emsgp = errmsg;
2690 	}
2691 
2692 	exit_status = 0;
2693 	cda_quit(s);
2694 	/*NOTREACHED*/
2695 }
2696 
2697 
2698 #endif	/* NOVISUAL */
2699 
2700