1 /*
2  *   cdinfo - CD Information Management Library
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 *_cdinfo_x_c_ident_ = "@(#)cdinfo_x.c	7.176 04/04/20";
24 #endif
25 
26 #ifdef __VMS
27 typedef char *	caddr_t;
28 #endif
29 
30 #define _CDINFO_INTERN	/* Expose internal function protos in cdinfo.h */
31 
32 #include "common_d/appenv.h"
33 #include "common_d/version.h"
34 #include "common_d/util.h"
35 #include "libdi_d/libdi.h"
36 #include "cdinfo_d/cdinfo.h"
37 
38 
39 #define MAX_ENV_LEN	(STR_BUF_SZ * 16)
40 
41 extern appdata_t	app_data;
42 extern FILE		*errfp;
43 extern cdinfo_client_t	*cdinfo_clinfo;		/* Client info */
44 extern cdinfo_incore_t	*cdinfo_dbp;		/* Incore CD info structure */
45 extern cdinfo_cddb_t	*cdinfo_cddbp;		/* Opened CDDB handle */
46 extern w_ent_t		*cdinfo_discog,		/* Local discog menu ptr */
47 			*cdinfo_scddb;		/* Search CDDB menu ptr */
48 extern void		*cdinfo_lconv_desc;	/* Charset conv descriptor */
49 extern bool_t		cdinfo_ischild;		/* Is a child process */
50 
51 STATIC pid_t		child_pid = 0;		/* Child pid */
52 STATIC char		curfile[FILE_PATH_SZ] = { '\0' };
53 						/* Current disc info file */
54 
55 
56 /***********************
57  *   private routines  *
58  ***********************/
59 
60 
61 /*
62  * cdinfo_mkpath
63  *	Convert a string to replace all occurrences of non-alpha and
64  *	non-digit characters with an underscore.  Multiple such characters
65  *	are only substituted with one underscore.  This is used to create
66  *	names suitable for use as a path name.  This function allocates
67  *	memory which must be freed by the caller via MEM_FREE when done.
68  *
69  * Args:
70  *	str - Input string
71  *	len - Max output string length including terminating null character
72  *
73  * Return:
74  *	Converted string, or NULL on failure.
75  */
76 STATIC char *
cdinfo_mkpath(char * str,int len)77 cdinfo_mkpath(char *str, int len)
78 {
79 	int	i;
80 	char	*buf,
81 		*cp,
82 		*cp2;
83 
84 	if ((buf = (char *) MEM_ALLOC("cdinfo_mkpath", len)) == NULL)
85 		return NULL;
86 
87 	cp = buf;
88 	cp2 = str;
89 	for (i = 0; *cp2 != '\0' && i < (len - 1); i++) {
90 		/* Replace non-alpha and non-digit chars with an underscore */
91 
92 		if (isalnum((int) *cp2))
93 			*cp++ = *cp2;
94 		else if (cp > buf && *(cp-1) != '_')
95 			*cp++ = '_';
96 		else
97 			i--;
98 
99 		cp2++;
100 	}
101 	*cp = '\0';
102 
103 	return (buf);
104 }
105 
106 
107 /*
108  * cdinfo_forkwait
109  *	Function that forks a child and sets the uid and gid to the original
110  *	invoking user.  The parent then waits for the child to do some
111  *	work and exit, and return an exit code in the location pointed to
112  *	by retcode.  The caller can check the return of this function to
113  *	determine whether it's the parent or child.  If SYNCHRONOUS is
114  *	defined, this function always returns FALSE and no child is forked.
115  *
116  * Args:
117  *	retcode - Location that the parent is to return an status code of
118  *		  the child.
119  *
120  * Return:
121  *	TRUE - Parent process: child has exited and the status code is
122  *	       returned in retcode.
123  *	FALSE - Child process: Now running with original uid/gid.
124  */
125 bool_t
cdinfo_forkwait(cdinfo_ret_t * retcode)126 cdinfo_forkwait(cdinfo_ret_t *retcode)
127 {
128 #ifdef SYNCHRONOUS
129 	*retcode = 0;
130 	return FALSE;
131 #else
132 	int		ret;
133 	pid_t		cpid;
134 	waitret_t	wstat;
135 
136 	/* Fork child and make child do actual work */
137 	switch (cpid = FORK()) {
138 	case 0:
139 		/* Child process */
140 		cdinfo_ischild = TRUE;
141 
142 		(void) util_signal(SIGTERM, cdinfo_onterm);
143 		(void) util_signal(SIGPIPE, SIG_IGN);
144 
145 		/* Force uid and gid to original setting */
146 		if (!util_set_ougid())
147 			_exit(SETUID_ERR);
148 
149 		return FALSE;
150 
151 	case -1:
152 		*retcode = CDINFO_SET_CODE(FORK_ERR, errno);
153 		return TRUE;
154 
155 	default:
156 		/* Parent process: wait for child to exit */
157 		child_pid = cpid;
158 
159 		ret = util_waitchild(cpid, app_data.srv_timeout + 5,
160 				     cdinfo_clinfo->workproc,
161 				     cdinfo_clinfo->arg,
162 				     TRUE, &wstat);
163 		child_pid = 0;
164 
165 		if (ret < 0) {
166 			*retcode = CDINFO_SET_CODE(WAIT_ERR, errno);
167 		}
168 		else if (WIFEXITED(wstat)) {
169 			if (WEXITSTATUS(wstat) == 0)
170 				*retcode = 0;
171 			else
172 				*retcode = CDINFO_SET_CODE(
173 					WEXITSTATUS(wstat), 0
174 				);
175 		}
176 		else if (WIFSIGNALED(wstat)) {
177 			*retcode = CDINFO_SET_CODE(
178 				KILLED_ERR, WTERMSIG(wstat)
179 			);
180 		}
181 		else
182 			*retcode = 0;
183 
184 		return TRUE;
185 	}
186 
187 	/*NOTREACHED*/
188 #endif	/* SYNCHRONOUS */
189 }
190 
191 
192 /***********************
193  *   public routines   *
194  ***********************/
195 
196 
197 /*
198  * cdinfo_addr
199  *	Return a pointer to the incore CD information structure.
200  *
201  * Args:
202  *	None.
203  *
204  * Return:
205  *	Pointer to the incore CD info structure.
206  */
207 cdinfo_incore_t *
cdinfo_addr(void)208 cdinfo_addr(void)
209 {
210 	return (cdinfo_dbp);
211 }
212 
213 
214 /*
215  * cdinfo_discid
216  *      Compute a quasi-unique integer disc identifier based on the
217  *	number of tracks, the length of each track, and a checksum of
218  *	the string that represents the offset of each track.
219  *
220  * Args:
221  *      s - Pointer to the curstat_t structure.
222  *
223  * Return:
224  *      The integer disc ID.
225  */
226 word32_t
cdinfo_discid(curstat_t * s)227 cdinfo_discid(curstat_t *s)
228 {
229 	int	i,
230 		a,
231 		t = 0,
232 		n = 0;
233 
234 	/* For backward compatibility this algorithm must not change */
235 	a = (int) s->tot_trks;
236 
237 	for (i = 0; i < a; i++)
238 		n += cdinfo_sum((s->trkinfo[i].min * 60) + s->trkinfo[i].sec);
239 
240 	t = ((s->trkinfo[a].min * 60) + s->trkinfo[a].sec) -
241 	     ((s->trkinfo[0].min * 60) + s->trkinfo[0].sec);
242 
243 	return ((n % 0xff) << 24 | t << 8 | s->tot_trks);
244 }
245 
246 
247 /*
248  * cdinfo_txtreduce
249  *	Transform a text string into a CGI search string
250  *
251  * Args:
252  *	text - Input text string
253  *      reduce - Whether to reduce text (remove punctuations,
254  *		 exclude words  from the excludeWords list).
255  *
256  * Return:
257  *	Output string.  The storage is internally allocated per call,
258  *	and should be freed by the caller with MEM_FREE() after the
259  *	buffer is no longer needed.  If there is no sufficient memory
260  *	to allocate the buffer, NULL is returned.
261  */
262 char *
cdinfo_txtreduce(char * text,bool_t reduce)263 cdinfo_txtreduce(char *text, bool_t reduce)
264 {
265 	char	*p,
266 		*q;
267 
268 	if (reduce) {
269 		if ((p = util_text_reduce(text)) == NULL)
270 		return NULL;
271 	}
272 	else
273 		p = text;
274 
275 	if ((q = util_cgi_xlate(p)) == NULL)
276 		return NULL;
277 
278 	if (reduce)
279 		MEM_FREE(p);
280 
281 	return (q);
282 }
283 
284 
285 /*
286  * cdinfo_tmpl_to_url
287  *	Make a URL string from a template.
288  *
289  * Args:
290  *	s - Pointer to the curstat_t structure
291  *	tmpl - Template string
292  *	url - Buffer to store resultant URL string
293  *	trkpos - The track position for %T or %t
294  *
295  * Return:
296  *	Nothing.
297  */
298 void
cdinfo_tmpl_to_url(curstat_t * s,char * tmpl,char * url,int trkpos)299 cdinfo_tmpl_to_url(curstat_t *s, char *tmpl, char *url, int trkpos)
300 {
301 	char	*q,
302 		*r,
303 		*a;
304 	char	dtitle[(STR_BUF_SZ * 4) + 4];
305 
306 	r = url;
307 	for (a = tmpl; *a != '\0'; a++) {
308 		switch (*a) {
309 		case '%':
310 			switch (*(++a)) {
311 			case 'X':
312 				(void) strcpy(r, cdinfo_clinfo->prog);
313 				r += strlen(cdinfo_clinfo->prog);
314 				break;
315 
316 			case 'V':
317 				(void) sprintf(r, "%s.%s",
318 					       VERSION_MAJ, VERSION_MIN);
319 				r += (strlen(VERSION_MAJ) +
320 				      strlen(VERSION_MIN) + 1);
321 				break;
322 
323 			case 'N':
324 				q = util_loginname();
325 				(void) strcpy(r, q);
326 				r += strlen(q);
327 				break;
328 
329 			case '~':
330 				q = util_homedir(util_get_ouid());
331 				(void) strcpy(r, q);
332 				r += strlen(q);
333 				break;
334 
335 			case 'H':
336 				(void) strcpy(r, cdinfo_clinfo->host);
337 				r += strlen(cdinfo_clinfo->host);
338 				break;
339 
340 			case 'L':
341 				(void) strcpy(r, app_data.libdir);
342 				r += strlen(app_data.libdir);
343 				break;
344 
345 			case 'S':
346 				(void) strcpy(r, app_data.libdir);
347 #ifdef __VMS
348 				(void) strcat(r, ".discog");
349 #else
350 				(void) strcat(r, "/discog");
351 #endif
352 				r += strlen(app_data.libdir) +
353 				     strlen("discog") + 1;
354 				break;
355 
356 			case 'C':
357 				q = cdinfo_genre_path(cdinfo_dbp->disc.genre);
358 				(void) strcpy(r, q);
359 				r += strlen(q);
360 				break;
361 
362 			case 'I':
363 				(void) sprintf(r, "%08x", cdinfo_dbp->discid);
364 				r += 8;
365 				break;
366 
367 			case 'A':
368 			case 'a':
369 				/* Translate disk artist into a
370 				 * keyword string in CGI form
371 				 */
372 				if (cdinfo_dbp->disc.artist == NULL)
373 					break;
374 
375 				q = cdinfo_txtreduce(
376 					cdinfo_dbp->disc.artist,
377 					(bool_t) (*a == 'a')
378 				);
379 				if (q == NULL) {
380 					CDINFO_FATAL(app_data.str_nomemory);
381 					return;
382 				}
383 
384 				(void) strcpy(r, q);
385 				r += strlen(q);
386 				MEM_FREE(q);
387 				break;
388 
389 			case 'R':
390 			case 'r':
391 				/* Translate track artist into a
392 				 * keyword string in CGI form
393 				 */
394 				if (trkpos >= 0 &&
395 				    cdinfo_dbp->track[trkpos].artist != NULL) {
396 					q = cdinfo_txtreduce(
397 					    cdinfo_dbp->track[trkpos].artist,
398 					    (bool_t) (*a == 'r')
399 					);
400 					if (q == NULL) {
401 						CDINFO_FATAL(
402 							app_data.str_nomemory
403 						);
404 						return;
405 					}
406 					strcpy(r, q);
407 					r += strlen(q);
408 					MEM_FREE(q);
409 					break;
410 				}
411 
412 				if (cdinfo_dbp->disc.artist == NULL)
413 					break;
414 
415 				/* Can't do track, Use disc artist instead */
416 				q = cdinfo_txtreduce(cdinfo_dbp->disc.artist,
417 						      (bool_t) (*a == 'r'));
418 				if (q == NULL) {
419 					CDINFO_FATAL(app_data.str_nomemory);
420 					return;
421 				}
422 
423 				(void) strcpy(r, q);
424 				r += strlen(q);
425 				MEM_FREE(q);
426 				break;
427 
428 			case 'T':
429 			case 't':
430 				/* Translate track title into a
431 				 * keyword string in CGI form
432 				 */
433 				if (trkpos >= 0 &&
434 				    cdinfo_dbp->track[trkpos].title != NULL) {
435 					q = cdinfo_txtreduce(
436 						cdinfo_dbp->track[trkpos].title,
437 						(bool_t) (*a == 't')
438 					);
439 					if (q == NULL) {
440 						CDINFO_FATAL(
441 							app_data.str_nomemory
442 						);
443 						return;
444 					}
445 					strcpy(r, q);
446 					r += strlen(q);
447 					MEM_FREE(q);
448 					break;
449 				}
450 
451 				if (cdinfo_dbp->disc.title == NULL)
452 					break;
453 
454 				/* Can't do track, Use disc title instead */
455 				q = cdinfo_txtreduce(cdinfo_dbp->disc.title,
456 						     (bool_t) (*a == 't'));
457 				if (q == NULL) {
458 					CDINFO_FATAL(app_data.str_nomemory);
459 					return;
460 				}
461 
462 				(void) strcpy(r, q);
463 				r += strlen(q);
464 				MEM_FREE(q);
465 				break;
466 
467 			case 'D':
468 			case 'd':
469 				/* Translate disc title into a
470 				 * keyword string in CGI form
471 				 */
472 				if (cdinfo_dbp->disc.title == NULL)
473 					break;
474 
475 				q = cdinfo_txtreduce(cdinfo_dbp->disc.title,
476 						      (bool_t) (*a == 'd'));
477 				if (q == NULL) {
478 					CDINFO_FATAL(app_data.str_nomemory);
479 					return;
480 				}
481 
482 				(void) strcpy(r, q);
483 				r += strlen(q);
484 				MEM_FREE(q);
485 				break;
486 
487 			case 'B':
488 			case 'b':
489 				/* Translate artist and disc title into a
490 				 * keyword string in CGI form
491 				 */
492 				if (cdinfo_dbp->disc.artist == NULL &&
493 				    cdinfo_dbp->disc.title == NULL)
494 					break;
495 
496 				(void) sprintf(dtitle, "%.127s%s%.127s",
497 					(cdinfo_dbp->disc.artist == NULL) ?
498 						"" : cdinfo_dbp->disc.artist,
499 					(cdinfo_dbp->disc.artist != NULL &&
500 					 cdinfo_dbp->disc.title != NULL) ?
501 						" / " : "",
502 					(cdinfo_dbp->disc.title == NULL) ?
503 						"" : cdinfo_dbp->disc.title);
504 
505 				q = cdinfo_txtreduce(
506 					dtitle,
507 					(bool_t) (*a == 'b')
508 				);
509 				if (q == NULL) {
510 					CDINFO_FATAL(app_data.str_nomemory);
511 					return;
512 				}
513 
514 				(void) strcpy(r, q);
515 				r += strlen(q);
516 				MEM_FREE(q);
517 				break;
518 
519 			case '#':
520 				if (trkpos >= 0) {
521 					(void) sprintf(r, "%02d",
522 						    s->trkinfo[trkpos].trkno);
523 					r += 2;
524 				}
525 				break;
526 
527 			case '%':
528 				*r++ = '%';
529 				break;
530 
531 			default:
532 				*r++ = '%';
533 				*r++ = *a;
534 				break;
535 			}
536 			break;
537 
538 		default:
539 			*r++ = *a;
540 			break;
541 		}
542 	}
543 	*r = '\0';
544 }
545 
546 
547 /*
548  * cdinfo_url_len
549  *	Determine a string buffer size large enough to hold a
550  *	URL after it is expanded from its template.
551  *
552  * Args:
553  *	tmpl - Template string
554  *	up - Pointer to the associated URL attributes structure
555  *	trkpos - Pointer to the track position for %T or %t.
556  *
557  * Return:
558  *	The buffer length.
559  */
560 int
cdinfo_url_len(char * tmpl,url_attrib_t * up,int * trkpos)561 cdinfo_url_len(char *tmpl, url_attrib_t *up, int *trkpos)
562 {
563 	int	n,
564 		artist_len,
565 		title_len,
566 		dtitle_len;
567 
568 	artist_len = (cdinfo_dbp->disc.artist != NULL) ?
569 		strlen(cdinfo_dbp->disc.artist) : STR_BUF_SZ;
570 	title_len = (cdinfo_dbp->disc.title != NULL) ?
571 		strlen(cdinfo_dbp->disc.title) : STR_BUF_SZ;
572 	dtitle_len = artist_len + title_len;
573 
574 	n = 16 + strlen(tmpl) + ((up->acnt + up->dcnt) * dtitle_len);
575 
576 	if (*trkpos < 0 || cdinfo_dbp->track[*trkpos].title == NULL) {
577 	    *trkpos = -1;
578 	    n += ((up->rcnt * dtitle_len) + (up->tcnt * dtitle_len));
579 	}
580 	else {
581 	    n += (up->tcnt * strlen(cdinfo_dbp->track[*trkpos].title));
582 
583 	    if (cdinfo_dbp->track[*trkpos].artist != NULL)
584 		n += (up->rcnt * strlen(cdinfo_dbp->track[*trkpos].artist));
585 	    else
586 		n += (up->rcnt * artist_len);
587 	}
588 
589 	n += (up->xcnt * strlen(cdinfo_clinfo->prog));
590 	n += (up->vcnt * (strlen(VERSION_MAJ) + strlen(VERSION_MIN) + 1));
591 	n += (up->ncnt * STR_BUF_SZ);
592 	n += (up->hcnt * HOST_NAM_SZ);
593 	n += (up->lcnt * strlen(app_data.libdir));
594 	n += (up->pcnt * 2);
595 	if (cdinfo_dbp->disc.genre != NULL) {
596 		n += (up->ccnt *
597 		      strlen(cdinfo_genre_path(cdinfo_dbp->disc.genre)));
598 	}
599 	n += (up->icnt * 8);
600 	n++;
601 
602 	return (n);
603 }
604 
605 
606 /*
607  * cdinfo_init
608  *	Initialize CD information management services
609  *
610  * Args:
611  *	progname - The client program name string
612  *	username - The client user login name string
613  *
614  * Return:
615  *	Nothing.
616  */
617 void
cdinfo_init(cdinfo_client_t * clp)618 cdinfo_init(cdinfo_client_t *clp)
619 {
620 	char	*cp;
621 
622 	cdinfo_clinfo = (cdinfo_client_t *)(void *) MEM_ALLOC(
623 		"cdinfo_client_t", sizeof(cdinfo_client_t)
624 	);
625 	if (cdinfo_clinfo == NULL) {
626 		CDINFO_FATAL(app_data.str_nomemory);
627 		return;
628 	}
629 	(void) memcpy(cdinfo_clinfo, clp, sizeof(cdinfo_client_t));
630 
631 	/* Sanity check */
632 	if (clp->prog[0] == '\0')
633 		(void) strcpy(clp->prog, "unknown");
634 
635 	/* Hard wire file modes in case these are missing from the
636 	 * common.cfg file
637 	 */
638 	if (app_data.cdinfo_filemode == NULL)
639 		app_data.cdinfo_filemode = "0666";
640 	if (app_data.hist_filemode == NULL)
641 		app_data.hist_filemode = "0644";
642 
643 	/* Load XMCD_CDINFOPATH environment variable, if specified */
644 	if ((cp = (char *) getenv("XMCD_CDINFOPATH")) != NULL &&
645 	    !util_newstr(&app_data.cdinfo_path, cp)) {
646 		CDINFO_FATAL(app_data.str_nomemory);
647 		return;
648 	}
649 
650 	if (app_data.cdinfo_path == NULL &&
651 	    !util_newstr(&app_data.cdinfo_path, "CDDB;CDTEXT")) {
652 		CDINFO_FATAL(app_data.str_nomemory);
653 		return;
654 	}
655 
656 	if ((int) strlen(app_data.cdinfo_path) >= MAX_ENV_LEN) {
657 		CDINFO_FATAL(app_data.str_longpatherr);
658 		return;
659 	}
660 
661 	/* Allocate CD info incore structure */
662 	cdinfo_dbp = (cdinfo_incore_t *)(void *) MEM_ALLOC(
663 		"cdinfo_incore_t",
664 		sizeof(cdinfo_incore_t)
665 	);
666 	if (cdinfo_dbp == NULL) {
667 		CDINFO_FATAL(app_data.str_nomemory);
668 		return;
669 	}
670 	(void) memset(cdinfo_dbp, 0, sizeof(cdinfo_incore_t));
671 
672 	/* Set up CD info path list */
673 	cdinfo_reinit();
674 }
675 
676 
677 /*
678  * cdinfo_reinit
679  *	Re-initialize CD information management services.
680  *
681  * Args:
682  *	None.
683  *
684  * Return:
685  *	Nothing.
686  */
687 void
cdinfo_reinit(void)688 cdinfo_reinit(void)
689 {
690 	char		*cp,
691 			*path;
692 	cdinfo_path_t	*pp,
693 			*pp_next;
694 
695 	/* Deallocate CD info path list, if present */
696 	pp = cdinfo_dbp->pathlist;
697 	while (pp != NULL) {
698 		pp_next = pp->next;
699 
700 		if (pp->categ != NULL)
701 			MEM_FREE(pp->categ);
702 		if (pp->path != NULL)
703 			MEM_FREE(pp->path);
704 		MEM_FREE(pp);
705 
706 		pp = pp_next;
707 	}
708 	cdinfo_dbp->pathlist = NULL;
709 
710 	/* Create new CD info path list */
711 	path = app_data.cdinfo_path;
712 	while ((cp = strchr(path, CDINFOPATH_SEPCHAR)) != NULL) {
713 		*cp = '\0';
714 
715 		if (!cdinfo_add_pathent(path))
716 			return;
717 
718 		*cp = CDINFOPATH_SEPCHAR;
719 		path = cp + 1;
720 	}
721 	(void) cdinfo_add_pathent(path);
722 }
723 
724 
725 /*
726  * cdinfo_halt
727  *	Shut down cdinfo subsystem.
728  *
729  * Args:
730  *	s - Pointer to the curstat_t structure
731  *
732  * Return:
733  *	Nothing
734  */
735 void
cdinfo_halt(curstat_t * s)736 cdinfo_halt(curstat_t *s)
737 {
738 	if (cdinfo_cddbp != NULL)
739 		(void) cdinfo_closecddb(cdinfo_cddbp);
740 
741 	if (curfile[0] != '\0' && s->devlocked)
742 		(void) UNLINK(curfile);
743 }
744 
745 
746 /*
747  * cdinfo_submit
748  *	Submit current CD information to CDDB server
749  *
750  * Args:
751  *	s - Pointer to the curstat_t structure
752  *
753  * Return:
754  *	return code as defined by cdinfo_ret_t
755  */
756 /*ARGSUSED*/
757 cdinfo_ret_t
cdinfo_submit(curstat_t * s)758 cdinfo_submit(curstat_t *s)
759 {
760 	cdinfo_cddb_t	*cp;
761 	cdinfo_ret_t	retcode;
762 
763 	if (cdinfo_forkwait(&retcode))
764 		return (retcode);
765 
766 	/* Open CDDB connection */
767 	if ((cp = cdinfo_opencddb(s, FALSE, &retcode)) == NULL) {
768 		/* CDDB service failure */
769 		CH_RET(retcode);
770 	}
771 
772 	/* Submit disc info to CDDB */
773 	if (!cdinfo_submitcddb(cp, s, &retcode)) {
774 		/* Submit failed */
775 		(void) cdinfo_closecddb(cp);
776 		CH_RET(retcode);
777 	}
778 
779 	/* Close CDDB connection */
780 	(void) cdinfo_closecddb(cp);
781 
782 	/* Child exits here */
783 	CH_RET(0);
784 	/*NOTREACHED*/
785 }
786 
787 
788 /*
789  * cdinfo_submit_url
790  *	Submit to CDDB a URL pertaining to the current CD
791  *
792  * Args:
793  *	s - Pointer to the curstat_t structure
794  *	up - Pointer to the cdinfo_url_t structure
795  *
796  * Return:
797  *	return code as defined by cdinfo_ret_t
798  */
799 /*ARGSUSED*/
800 cdinfo_ret_t
cdinfo_submit_url(curstat_t * s,cdinfo_url_t * up)801 cdinfo_submit_url(curstat_t *s, cdinfo_url_t *up)
802 {
803 	cdinfo_cddb_t	*cp;
804 	cdinfo_ret_t	retcode;
805 
806 	if (up == NULL ||
807 	    up->categ == NULL || up->href == NULL || up->disptext == NULL)
808 		return CDINFO_SET_CODE(ARG_ERR, 0);
809 
810 	if (cdinfo_forkwait(&retcode))
811 		return (retcode);
812 
813 	/* Open CDDB connection */
814 	if ((cp = cdinfo_opencddb(s, FALSE, &retcode)) == NULL) {
815 		/* CDDB service failure */
816 		CH_RET(retcode);
817 	}
818 
819 	/* Submit disc info to CDDB */
820 	if (!cdinfo_submiturlcddb(cp, up, &retcode)) {
821 		/* Submit failed */
822 		(void) cdinfo_closecddb(cp);
823 		CH_RET(retcode);
824 	}
825 
826 	/* Close CDDB connection */
827 	(void) cdinfo_closecddb(cp);
828 
829 	/* Child exits here */
830 	CH_RET(0);
831 	/*NOTREACHED*/
832 }
833 
834 
835 /*
836  * cdinfo_flush
837  *	Flush CD information cache
838  *
839  * Args:
840  *	s - Pointer to the curstat_t structure
841  *
842  * Return:
843  *	return code as defined by cdinfo_ret_t
844  */
845 /*ARGSUSED*/
846 cdinfo_ret_t
cdinfo_flush(curstat_t * s)847 cdinfo_flush(curstat_t *s)
848 {
849 	cdinfo_cddb_t	*cp;
850 	cdinfo_ret_t	retcode;
851 
852 	if (cdinfo_forkwait(&retcode))
853 		return (retcode);
854 
855 	/* Open CDDB connection */
856 	if ((cp = cdinfo_opencddb(s, FALSE, &retcode)) == NULL) {
857 		/* CDDB service failure */
858 		CH_RET(retcode);
859 	}
860 
861 	/* Flush CDDB local cache */
862 	if (!cdinfo_flushcddb(cp)) {
863 		/* Cache flush failed */
864 		(void) cdinfo_closecddb(cp);
865 		CH_RET(FLUSH_ERR);
866 	}
867 
868 	/* Close CDDB connection */
869 	(void) cdinfo_closecddb(cp);
870 
871 	/* Child exits here */
872 	CH_RET(0);
873 	/*NOTREACHED*/
874 }
875 
876 
877 /*
878  * cdinfo_offline
879  *	Set the offline mode to what is specified by app_data.cdinfo_inetoffln
880  *
881  * Args:
882  *	s - Pointer to the curstat_t structure
883  *
884  * Return:
885  *	return code as defined by cdinfo_ret_t
886  */
887 /*ARGSUSED*/
888 cdinfo_ret_t
cdinfo_offline(curstat_t * s)889 cdinfo_offline(curstat_t *s)
890 {
891 	cdinfo_cddb_t	*cp;
892 	cdinfo_ret_t	retcode;
893 
894 	if (cdinfo_forkwait(&retcode))
895 		return (retcode);
896 
897 	/* Open CDDB connection */
898 	if ((cp = cdinfo_opencddb(s, FALSE, &retcode)) == NULL) {
899 		/* CDDB service failure */
900 		CH_RET(retcode);
901 	}
902 
903 	/* The cdinfo_opencddb call implicitly does the work, so
904 	 * we don't need to do anything else here.
905 	 */
906 
907 	/* Close CDDB connection */
908 	(void) cdinfo_closecddb(cp);
909 
910 	/* Child exits here */
911 	CH_RET(0);
912 	/*NOTREACHED*/
913 }
914 
915 
916 /*
917  * cdinfo_load
918  *	Load CD database entry for the currently inserted CD.
919  *
920  * Args:
921  *	s - Pointer to the curstat_t structure
922  *
923  * Return:
924  *	Return value will be 0 for a successful connection, and
925  *	a successful query will cause the CDINFO_MATCH bit to be
926  *	set in the flags field of the cdinfo_incore_t structure.
927  *	If an error occurred, then the return code is as defined
928  *	by cdinfo_ret_t.
929  */
930 cdinfo_ret_t
cdinfo_load(curstat_t * s)931 cdinfo_load(curstat_t *s)
932 {
933 #ifdef SYNCHRONOUS
934 	cdinfo_path_t		*pp;
935 	cdinfo_cddb_t		*cp;
936 	cdinfo_credit_t		*p;
937 	cdinfo_segment_t	*q;
938 	di_cdtext_t		*cdt;
939 	char			*path;
940 	int			i,
941 				retcode;
942 
943 	cdinfo_dbp->flags &= ~(CDINFO_MATCH | CDINFO_FROMLOC | CDINFO_FROMCDT);
944 
945 	if ((cdinfo_dbp->discid = cdinfo_discid(s)) == 0)
946 		return CDINFO_SET_CODE(ARG_ERR, 0);
947 
948 	if (cdinfo_cddb_iscfg() &&
949 	    (cdinfo_dbp->regionlist == NULL ||
950 	     cdinfo_dbp->langlist == NULL ||
951 	     cdinfo_dbp->rolelist == NULL ||
952 	     cdinfo_dbp->genrelist == NULL)) {
953 		DBGPRN(DBG_CDI)(errfp, "Initializing CDDB service...\n");
954 
955 		/* Open CDDB connection */
956 		if ((cp = cdinfo_opencddb(s, TRUE, &retcode)) != NULL) {
957 			/* Check user registration, set up region, lang, role
958 			 * and genre lists
959 			 */
960 			(void) cdinfo_initcddb(cp, &retcode);
961 			(void) cdinfo_closecddb(cp);
962 		}
963 		else if (retcode == AUTH_ERR) {
964 			/* Special case handling for proxy auth error */
965 			return CDINFO_SET_CODE(retcode, 0);
966 		}
967 	}
968 
969 	for (pp = cdinfo_dbp->pathlist; pp != NULL; pp = pp->next) {
970 		DBGPRN(DBG_CDI)(errfp, "*** CD info query: ");
971 		retcode = 0;
972 
973 		switch (pp->type) {
974 		case CDINFO_CDTEXT:
975 			/* Use CD-TEXT information from the CD */
976 			DBGPRN(DBG_CDI)(errfp, "Trying CD-TEXT...\n");
977 			cdt = &cdinfo_dbp->cdtext;
978 			di_load_cdtext(s, cdt);
979 
980 			if (cdt->cdtext_valid) {
981 				cdinfo_map_cdtext(s, cdt);
982 				cdinfo_dbp->flags |=
983 					(CDINFO_MATCH | CDINFO_FROMCDT);
984 			}
985 			break;
986 
987 		case CDINFO_RMT:
988 			/* Remote: Use CDDB service */
989 			DBGPRN(DBG_CDI)(errfp, "Trying CDDB service...\n");
990 
991 			/* Open CDDB connection */
992 			if ((cp = cdinfo_opencddb(s, TRUE, &retcode)) == NULL)
993 			{
994 				/* CDDB service failure */
995 				if (retcode == AUTH_ERR) {
996 					/* Special case handling for
997 					 * proxy auth error
998 					 */
999 					return CDINFO_SET_CODE(retcode, 0);
1000 				}
1001 				break;
1002 			}
1003 
1004 			/* Read CD information into incore structure */
1005 			if (!cdinfo_querycddb(cp, s, &retcode)) {
1006 				/* Query failed */
1007 				(void) cdinfo_closecddb(cp);
1008 				if (retcode == AUTH_ERR) {
1009 					/* Special case handling for
1010 					 * proxy auth error
1011 					 */
1012 					return CDINFO_SET_CODE(retcode, 0);
1013 				}
1014 				break;
1015 			}
1016 
1017 			if (cdinfo_dbp->matchlist != NULL) {
1018 				/* Inexact "fuzzy matches" found.
1019 				 * Query user to choose one
1020 				 */
1021 				cdinfo_dbp->match_tag = 0;
1022 
1023 				/* Don't close CDDB connection here,
1024 				 * but save pointer.
1025 				 */
1026 				cdinfo_dbp->sav_cddbp = cp;
1027 
1028 				/* We return here, and continue to
1029 				 * service this query via the callback
1030 				 * cdinfo_load_matchsel().
1031 				 */
1032 				return 0;
1033 			}
1034 
1035 			/* Close CDDB connection */
1036 			(void) cdinfo_closecddb(cp);
1037 
1038 			break;
1039 
1040 		case CDINFO_LOC:
1041 			/* Local: Open local CD info file */
1042 
1043 			path = (char *) MEM_ALLOC(
1044 				"path", strlen(pp->path) + 12
1045 			);
1046 			if (path == NULL)
1047 				return CDINFO_SET_CODE(MEM_ERR, errno);;
1048 
1049 			(void) sprintf(path, CDINFOFILE_PATH,
1050 				       pp->path, cdinfo_dbp->discid);
1051 
1052 			DBGPRN(DBG_CDI)(errfp, "Trying %s", path);
1053 			(void) cdinfo_load_locdb(path, pp->categ, s, &retcode);
1054 
1055 			MEM_FREE(path);
1056 			break;
1057 		}
1058 
1059 		if ((cdinfo_dbp->flags & CDINFO_MATCH) != 0 ||
1060 		    (cdinfo_dbp->flags & CDINFO_NEEDREG) != 0) {
1061 			/* Found a match or need user registration */
1062 			break;
1063 		}
1064 	}
1065 
1066 	/* If the display name field of the fullname structures are
1067 	 * NULL, * fill them with the appropriate text.  Conversely,
1068 	 * if the display name field is non NULL and the matching
1069 	 * name fields are NULL, back fill the data.
1070 	 */
1071 
1072 	if (cdinfo_dbp->disc.artistfname.dispname == NULL &&
1073 	    cdinfo_dbp->disc.artist != NULL &&
1074 	    !util_newstr(&cdinfo_dbp->disc.artistfname.dispname,
1075 			 cdinfo_dbp->disc.artist)) {
1076 		return CDINFO_SET_CODE(MEM_ERR, 0);
1077 	}
1078 	else if (cdinfo_dbp->disc.artist == NULL &&
1079 		 cdinfo_dbp->disc.artistfname.dispname != NULL &&
1080 		 !util_newstr(&cdinfo_dbp->disc.artist,
1081 			      cdinfo_dbp->disc.artistfname.dispname)) {
1082 		return CDINFO_SET_CODE(MEM_ERR, 0);
1083 	}
1084 
1085 	for (p = cdinfo_dbp->disc.credit_list; p != NULL; p = p->next) {
1086 		if (p->crinfo.fullname.dispname == NULL &&
1087 		    p->crinfo.name != NULL &&
1088 		    !util_newstr(&p->crinfo.fullname.dispname,
1089 				 p->crinfo.name)) {
1090 			return CDINFO_SET_CODE(MEM_ERR, 0);
1091 		}
1092 		else if (p->crinfo.name == NULL &&
1093 			 p->crinfo.fullname.dispname != NULL &&
1094 			 !util_newstr(&p->crinfo.name,
1095 				      p->crinfo.fullname.dispname)) {
1096 			return CDINFO_SET_CODE(MEM_ERR, 0);
1097 		}
1098 	}
1099 
1100 	for (q = cdinfo_dbp->disc.segment_list; q != NULL; q = q->next) {
1101 		for (p = q->credit_list; p != NULL; p = p->next) {
1102 			if (p->crinfo.fullname.dispname == NULL &&
1103 			    p->crinfo.name != NULL &&
1104 			    !util_newstr(&p->crinfo.fullname.dispname,
1105 					 p->crinfo.name)) {
1106 				return CDINFO_SET_CODE(MEM_ERR, 0);
1107 			}
1108 			else if (p->crinfo.name == NULL &&
1109 				 p->crinfo.fullname.dispname != NULL &&
1110 				 !util_newstr(&p->crinfo.name,
1111 					      p->crinfo.fullname.dispname)) {
1112 				return CDINFO_SET_CODE(MEM_ERR, 0);
1113 			}
1114 		}
1115 	}
1116 
1117 	for (i = 0; i < (int) s->tot_trks; i++) {
1118 		if (cdinfo_dbp->track[i].artistfname.dispname == NULL &&
1119 		    cdinfo_dbp->track[i].artist != NULL &&
1120 		    !util_newstr(&cdinfo_dbp->track[i].artistfname.dispname,
1121 				 cdinfo_dbp->track[i].artist)) {
1122 			return CDINFO_SET_CODE(MEM_ERR, 0);
1123 		}
1124 		else if (cdinfo_dbp->track[i].artist == NULL &&
1125 			 cdinfo_dbp->track[i].artistfname.dispname != NULL &&
1126 			 !util_newstr(&cdinfo_dbp->track[i].artist,
1127 				cdinfo_dbp->track[i].artistfname.dispname)) {
1128 			return CDINFO_SET_CODE(MEM_ERR, 0);
1129 		}
1130 
1131 		for (p = cdinfo_dbp->track[i].credit_list; p != NULL;
1132 		     p = p->next){
1133 			if (p->crinfo.fullname.dispname == NULL &&
1134 			    p->crinfo.name != NULL &&
1135 			    !util_newstr(&p->crinfo.fullname.dispname,
1136 					 p->crinfo.name)) {
1137 				return CDINFO_SET_CODE(MEM_ERR, 0);
1138 			}
1139 			else if (p->crinfo.name == NULL &&
1140 				 p->crinfo.fullname.dispname != NULL &&
1141 				 !util_newstr(&p->crinfo.name,
1142 					      p->crinfo.fullname.dispname)) {
1143 				return CDINFO_SET_CODE(MEM_ERR, 0);
1144 			}
1145 		}
1146 
1147 		/* If we have ISRC data that was read from the CD itself,
1148 		 * use these instead of the ones from CDDB.
1149 		 */
1150 		if (s->trkinfo[i].isrc[0] != 0 &&
1151 		    !util_newstr(&cdinfo_dbp->track[i].isrc,
1152 				 s->trkinfo[i].isrc)) {
1153 			return CDINFO_SET_CODE(MEM_ERR, 0);
1154 		}
1155 	}
1156 
1157 	return 0;
1158 
1159 #else	/* SYNCHRONOUS */
1160 	int			i,
1161 				ret,
1162 				retcode;
1163 	pid_t			cpid;
1164 	waitret_t		wstat;
1165 	cdinfo_path_t		*pp;
1166 	cdinfo_cddb_t		*cp;
1167 	cdinfo_credit_t		*p;
1168 	cdinfo_segment_t	*q;
1169 	cdinfo_pipe_t		*spp,
1170 				*rpp;
1171 	di_cdtext_t		*cdt;
1172 	char			*path;
1173 	bool_t			done;
1174 
1175 	cdinfo_dbp->flags &= ~(CDINFO_MATCH | CDINFO_FROMLOC | CDINFO_FROMCDT);
1176 
1177 	if ((cdinfo_dbp->discid = cdinfo_discid(s)) == 0)
1178 		return CDINFO_SET_CODE(ARG_ERR, 0);
1179 
1180 	/* Open pipes for IPC */
1181 	if ((rpp = cdinfo_openpipe(CDINFO_DATAIN)) == NULL)
1182 		return CDINFO_SET_CODE(OPEN_ERR, errno);
1183 	if ((spp = cdinfo_openpipe(CDINFO_DATAOUT)) == NULL) {
1184 		(void) cdinfo_closepipe(rpp);
1185 		return CDINFO_SET_CODE(OPEN_ERR, errno);
1186 	}
1187 
1188 	/* Fork child to performs actual I/O */
1189 	switch (cpid = FORK()) {
1190 	case 0:
1191 		/* Child process */
1192 		cdinfo_ischild = TRUE;
1193 
1194 		(void) util_signal(SIGTERM, cdinfo_onterm);
1195 		(void) util_signal(SIGPIPE, SIG_IGN);
1196 
1197 		/* Close unneeded descriptors */
1198 		(void) close(rpp->r.fd);
1199 		(void) close(spp->w.fd);
1200 		rpp->r.fd = spp->w.fd = -1;
1201 
1202 		/* Force uid and gid to original setting */
1203 		if (!util_set_ougid()) {
1204 			(void) cdinfo_closepipe(rpp);
1205 			(void) cdinfo_closepipe(spp);
1206 			CH_RET(SETUID_ERR);
1207 		}
1208 
1209 		break;
1210 
1211 	case -1:
1212 		(void) cdinfo_closepipe(rpp);
1213 		(void) cdinfo_closepipe(spp);
1214 		return CDINFO_SET_CODE(FORK_ERR, errno);
1215 
1216 	default:
1217 		/* Parent process */
1218 		child_pid = cpid;
1219 
1220 		/* Close unneeded descriptors */
1221 		(void) close(rpp->w.fd);
1222 		(void) close(spp->r.fd);
1223 		rpp->w.fd = spp->r.fd = -1;
1224 
1225 		/* Read CD info content from pipe into in-core structure */
1226 		(void) cdinfo_read_datapipe(rpp);
1227 
1228 		if (cdinfo_dbp->matchlist != NULL) {
1229 			/* Inexact "fuzzy matches" found.  Query user
1230 			 * to choose one.
1231 			 */
1232 			cdinfo_dbp->match_tag = 0;
1233 
1234 			/* Don't close pipes here, but save pointer */
1235 			cdinfo_dbp->sav_rpp = rpp;
1236 			cdinfo_dbp->sav_spp = spp;
1237 
1238 			/* We return with child process still running,
1239 			 * and continue to service this query via
1240 			 * the callback cdinfo_load_matchsel().
1241 			 */
1242 			return 0;
1243 		}
1244 
1245 		/* Close pipes */
1246 		(void) cdinfo_closepipe(rpp);
1247 		(void) cdinfo_closepipe(spp);
1248 
1249 		/* Parent process: wait for child to exit */
1250 		ret = util_waitchild(cpid, app_data.srv_timeout + 5,
1251 				     cdinfo_clinfo->workproc,
1252 				     cdinfo_clinfo->arg,
1253 				     TRUE, &wstat);
1254 		child_pid = 0;
1255 
1256 		if (ret < 0) {
1257 			return CDINFO_SET_CODE(WAIT_ERR, errno);
1258 		}
1259 		if (WIFEXITED(wstat)) {
1260 			if (WEXITSTATUS(wstat) == 0)
1261 				return 0;
1262 			else
1263 				return CDINFO_SET_CODE(WEXITSTATUS(wstat), 0);
1264 		}
1265 		else if (WIFSIGNALED(wstat)) {
1266 			return CDINFO_SET_CODE(KILLED_ERR, WTERMSIG(wstat));
1267 		}
1268 		else
1269 			return 0;
1270 
1271 		/*NOTREACHED*/
1272 	}
1273 
1274 	if (cdinfo_cddb_iscfg() &&
1275 	    (cdinfo_dbp->regionlist == NULL ||
1276 	     cdinfo_dbp->langlist == NULL ||
1277 	     cdinfo_dbp->rolelist == NULL ||
1278 	     cdinfo_dbp->genrelist == NULL)) {
1279 		DBGPRN(DBG_CDI)(errfp, "Initializing CDDB service...\n");
1280 
1281 		/* Open CDDB connection */
1282 		if ((cp = cdinfo_opencddb(s, TRUE, &retcode)) != NULL) {
1283 			/* Check user registration, set up region, lang, role
1284 			 * and genre lists
1285 			 */
1286 			(void) cdinfo_initcddb(cp, &retcode);
1287 			(void) cdinfo_closecddb(cp);
1288 		}
1289 		else if (retcode == AUTH_ERR) {
1290 			/* Special case handling for proxy auth error */
1291 			(void) cdinfo_closepipe(rpp);
1292 			(void) cdinfo_closepipe(spp);
1293 			CH_RET(retcode);
1294 		}
1295 	}
1296 
1297 	done = FALSE;
1298 	for (pp = cdinfo_dbp->pathlist; pp != NULL; pp = pp->next) {
1299 		DBGPRN(DBG_CDI)(errfp, "*** CD info query: ");
1300 		retcode = 0;
1301 
1302 		switch (pp->type) {
1303 		case CDINFO_CDTEXT:
1304 			/* Use CD-TEXT information from the CD */
1305 			DBGPRN(DBG_CDI)(errfp, "Trying CD-TEXT...\n");
1306 			cdt = &cdinfo_dbp->cdtext;
1307 			di_load_cdtext(s, cdt);
1308 
1309 			if (cdt->cdtext_valid) {
1310 				cdinfo_map_cdtext(s, cdt);
1311 				cdinfo_dbp->flags |=
1312 					(CDINFO_MATCH | CDINFO_FROMCDT);
1313 				done = TRUE;
1314 			}
1315 			break;
1316 
1317 		case CDINFO_RMT:
1318 			/* Remote: Use CDDB service */
1319 			DBGPRN(DBG_CDI)(errfp, "Trying CDDB service...\n");
1320 
1321 			/* Open CDDB connection */
1322 			if ((cp = cdinfo_opencddb(s, TRUE, &retcode)) == NULL)
1323 			{
1324 				/* CDDB service Failure */
1325 				if (retcode == AUTH_ERR) {
1326 					/* Special case handling for
1327 					 * proxy auth error
1328 					 */
1329 					(void) cdinfo_closepipe(rpp);
1330 					(void) cdinfo_closepipe(spp);
1331 					CH_RET(retcode);
1332 				}
1333 				break;
1334 			}
1335 
1336 			/* Read CD info content and update incore structures */
1337 			if (!cdinfo_querycddb(cp, s, &retcode)) {
1338 				/* Failure */
1339 				(void) cdinfo_closecddb(cp);
1340 				if (retcode == AUTH_ERR) {
1341 					/* Special case handling for
1342 					 * proxy auth error
1343 					 */
1344 					(void) cdinfo_closepipe(rpp);
1345 					(void) cdinfo_closepipe(spp);
1346 					CH_RET(retcode);
1347 				}
1348 				break;
1349 			}
1350 
1351 			if (cdinfo_dbp->matchlist != NULL) {
1352 				/* Inexact "fuzzy matches" found.
1353 				 * Transmit the list to parent process
1354 				 * for processing.
1355 				 */
1356 				cdinfo_dbp->match_tag = 0;
1357 
1358 				if (!cdinfo_write_datapipe(rpp, s)) {
1359 					/* Failure */
1360 					(void) cdinfo_closecddb(cp);
1361 					(void) cdinfo_closepipe(rpp);
1362 					(void) cdinfo_closepipe(spp);
1363 					cdinfo_free_matchlist();
1364 					CH_RET(WRITE_ERR);
1365 				}
1366 
1367 				util_delayms(2000);
1368 
1369 				/* Read user selection */
1370 				if (!cdinfo_read_selpipe(spp)) {
1371 					/* Failure */
1372 					(void) cdinfo_closecddb(cp);
1373 					(void) cdinfo_closepipe(rpp);
1374 					(void) cdinfo_closepipe(spp);
1375 					cdinfo_free_matchlist();
1376 					CH_RET(READ_ERR);
1377 				}
1378 
1379 				/* Read CD info content and update incore
1380 				 * structures
1381 				 */
1382 				if (!cdinfo_querycddb(cp, s, &retcode)) {
1383 					/* Failure */
1384 					(void) cdinfo_closecddb(cp);
1385 					if (retcode == AUTH_ERR) {
1386 						/* Special case handling for
1387 						 * proxy auth error
1388 						 */
1389 						(void) cdinfo_closepipe(rpp);
1390 						(void) cdinfo_closepipe(spp);
1391 						cdinfo_free_matchlist();
1392 						CH_RET(retcode);
1393 					}
1394 					break;
1395 				}
1396 
1397 				/* Deallocate the matchlist */
1398 				cdinfo_free_matchlist();
1399 			}
1400 
1401 			/* Close CDDB connection */
1402 			(void) cdinfo_closecddb(cp);
1403 
1404 			if ((cdinfo_dbp->flags & CDINFO_MATCH) != 0 ||
1405 			    (cdinfo_dbp->flags & CDINFO_NEEDREG) != 0) {
1406 				/* Found a match or needs user reg */
1407 				done = TRUE;
1408 			}
1409 			break;
1410 
1411 		case CDINFO_LOC:
1412 			/* Local: Open local CD info file */
1413 			path = (char *) MEM_ALLOC(
1414 				"path", strlen(pp->path) + 12
1415 			);
1416 			if (path == NULL) {
1417 				(void) cdinfo_closepipe(rpp);
1418 				(void) cdinfo_closepipe(spp);
1419 				CH_RET(MEM_ERR);
1420 			}
1421 
1422 			(void) sprintf(path, CDINFOFILE_PATH,
1423 				       pp->path, cdinfo_dbp->discid);
1424 
1425 			DBGPRN(DBG_CDI)(errfp, "Trying %s", path);
1426 			(void) cdinfo_load_locdb(path, pp->categ, s, &retcode);
1427 
1428 			MEM_FREE(path);
1429 
1430 			if ((cdinfo_dbp->flags & CDINFO_MATCH) != 0) {
1431 				/* Found a match */
1432 				done = TRUE;
1433 			}
1434 			break;
1435 		}
1436 
1437 		if (done)
1438 			break;
1439 	}
1440 
1441 	/* If the display name field of the fullname structures are
1442 	 * NULL, fill them with the appropriate text.  Conversely,
1443 	 * if the display name field is non NULL and the matching
1444 	 * name fields are NULL, back fill the data.
1445 	 */
1446 
1447 	if (cdinfo_dbp->disc.artistfname.dispname == NULL &&
1448 	    cdinfo_dbp->disc.artist != NULL &&
1449 	    !util_newstr(&cdinfo_dbp->disc.artistfname.dispname,
1450 			 cdinfo_dbp->disc.artist)) {
1451 		(void) cdinfo_closepipe(rpp);
1452 		(void) cdinfo_closepipe(spp);
1453 		CH_RET(MEM_ERR);
1454 	}
1455 	else if (cdinfo_dbp->disc.artist == NULL &&
1456 		 cdinfo_dbp->disc.artistfname.dispname != NULL &&
1457 		 !util_newstr(&cdinfo_dbp->disc.artist,
1458 			      cdinfo_dbp->disc.artistfname.dispname)) {
1459 		(void) cdinfo_closepipe(rpp);
1460 		(void) cdinfo_closepipe(spp);
1461 		CH_RET(MEM_ERR);
1462 	}
1463 
1464 	for (p = cdinfo_dbp->disc.credit_list; p != NULL; p = p->next) {
1465 		if (p->crinfo.fullname.dispname == NULL &&
1466 		    p->crinfo.name != NULL &&
1467 		    !util_newstr(&p->crinfo.fullname.dispname,
1468 				 p->crinfo.name)) {
1469 			(void) cdinfo_closepipe(rpp);
1470 			(void) cdinfo_closepipe(spp);
1471 			CH_RET(MEM_ERR);
1472 		}
1473 		else if (p->crinfo.name == NULL &&
1474 			 p->crinfo.fullname.dispname != NULL &&
1475 			 !util_newstr(&p->crinfo.name,
1476 				      p->crinfo.fullname.dispname)) {
1477 			(void) cdinfo_closepipe(rpp);
1478 			(void) cdinfo_closepipe(spp);
1479 			CH_RET(MEM_ERR);
1480 		}
1481 	}
1482 
1483 	for (q = cdinfo_dbp->disc.segment_list; q != NULL; q = q->next) {
1484 		for (p = q->credit_list; p != NULL; p = p->next) {
1485 			if (p->crinfo.fullname.dispname == NULL &&
1486 			    p->crinfo.name != NULL &&
1487 			    !util_newstr(&p->crinfo.fullname.dispname,
1488 					 p->crinfo.name)) {
1489 				(void) cdinfo_closepipe(rpp);
1490 				(void) cdinfo_closepipe(spp);
1491 				CH_RET(MEM_ERR);
1492 			}
1493 			else if (p->crinfo.name == NULL &&
1494 				 p->crinfo.fullname.dispname != NULL &&
1495 				 !util_newstr(&p->crinfo.name,
1496 					      p->crinfo.fullname.dispname)) {
1497 				(void) cdinfo_closepipe(rpp);
1498 				(void) cdinfo_closepipe(spp);
1499 				CH_RET(MEM_ERR);
1500 			}
1501 		}
1502 	}
1503 
1504 	for (i = 0; i < (int) s->tot_trks; i++) {
1505 		if (cdinfo_dbp->track[i].artistfname.dispname == NULL &&
1506 		    cdinfo_dbp->track[i].artist != NULL &&
1507 		    !util_newstr(&cdinfo_dbp->track[i].artistfname.dispname,
1508 				 cdinfo_dbp->track[i].artist)) {
1509 			CH_RET(MEM_ERR);
1510 		}
1511 		else if (cdinfo_dbp->track[i].artist == NULL &&
1512 			 cdinfo_dbp->track[i].artistfname.dispname != NULL &&
1513 			 !util_newstr(&cdinfo_dbp->track[i].artist,
1514 				cdinfo_dbp->track[i].artistfname.dispname)) {
1515 			(void) cdinfo_closepipe(rpp);
1516 			(void) cdinfo_closepipe(spp);
1517 			CH_RET(MEM_ERR);
1518 		}
1519 
1520 		for (p = cdinfo_dbp->track[i].credit_list; p != NULL;
1521 		     p = p->next) {
1522 			if (p->crinfo.fullname.dispname == NULL &&
1523 			    p->crinfo.name != NULL &&
1524 			    !util_newstr(&p->crinfo.fullname.dispname,
1525 					 p->crinfo.name)) {
1526 				(void) cdinfo_closepipe(rpp);
1527 				(void) cdinfo_closepipe(spp);
1528 				CH_RET(MEM_ERR);
1529 			}
1530 			else if (p->crinfo.name == NULL &&
1531 				 p->crinfo.fullname.dispname != NULL &&
1532 				 !util_newstr(&p->crinfo.name,
1533 					      p->crinfo.fullname.dispname)) {
1534 				(void) cdinfo_closepipe(rpp);
1535 				(void) cdinfo_closepipe(spp);
1536 				CH_RET(MEM_ERR);
1537 			}
1538 		}
1539 
1540 		/* If we have ISRC data that was read from the CD itself,
1541 		 * use these instead of the ones from CDDB.
1542 		 */
1543 		if (s->trkinfo[i].isrc[0] != 0 &&
1544 		    !util_newstr(&cdinfo_dbp->track[i].isrc,
1545 				 s->trkinfo[i].isrc)) {
1546 			(void) cdinfo_closepipe(rpp);
1547 			(void) cdinfo_closepipe(spp);
1548 			CH_RET(MEM_ERR);
1549 		}
1550 	}
1551 
1552 	/* Write incore CD info into pipe */
1553 	if (!cdinfo_write_datapipe(rpp, s))
1554 		retcode = WRITE_ERR;
1555 	else
1556 		retcode = 0;
1557 
1558 	(void) cdinfo_closepipe(rpp);
1559 	(void) cdinfo_closepipe(spp);
1560 	CH_RET(retcode);
1561 
1562 	/*NOTREACHED*/
1563 #endif	/* SYNCHRONOUS */
1564 }
1565 
1566 
1567 /*
1568  * cdinfo_load_matchsel
1569  *	Load CD database entry after user chooses a fuzzy match
1570  *
1571  * Args:
1572  *	s - Pointer to the curstat_t structure
1573  *
1574  * Return:
1575  *	Return value will be 0 for a successful connection, and
1576  *	a successful query will cause the CDINFO_MATCH bit to be
1577  *	set in the flags field of the cdinfo_incore_t structure.
1578  *	If an error occurred, then the return code is as defined
1579  *	by cdinfo_ret_t.
1580  */
1581 /*ARGSUSED*/
1582 cdinfo_ret_t
cdinfo_load_matchsel(curstat_t * s)1583 cdinfo_load_matchsel(curstat_t *s)
1584 {
1585 #ifdef SYNCHRONOUS
1586 	cdinfo_cddb_t	*cp;
1587 	cdinfo_ret_t	retcode;
1588 
1589 	cp = cdinfo_dbp->sav_cddbp;
1590 	if (cp == NULL)
1591 		return CDINFO_SET_CODE(READ_ERR, 0);
1592 
1593 	/* Read CD information into incore structure */
1594 	if (!cdinfo_querycddb(cp, s, &retcode)) {
1595 		/* Query failed */
1596 		(void) cdinfo_closecddb(cp);
1597 
1598 		/* Deallocate the matchlist */
1599 		cdinfo_free_matchlist();
1600 
1601 		return CDINFO_SET_CODE(retcode, 0);
1602 	}
1603 
1604 	/* Close CDDB connection */
1605 	(void) cdinfo_closecddb(cp);
1606 
1607 	/* Deallocate the matchlist */
1608 	cdinfo_free_matchlist();
1609 
1610 	return 0;
1611 
1612 #else	/* SYNCHRONOUS */
1613 	int		ret;
1614 	pid_t		cpid;
1615 	waitret_t	wstat;
1616 	cdinfo_pipe_t	*spp,
1617 			*rpp;
1618 	void		(*oh)(int);
1619 
1620 	/* Restore pointers */
1621 	rpp = cdinfo_dbp->sav_rpp;
1622 	spp = cdinfo_dbp->sav_spp;
1623 
1624 	/* Child pid */
1625 	cpid = child_pid;
1626 
1627 	if (rpp == NULL || spp == NULL || cpid == 0) {
1628 		/* Error */
1629 		return CDINFO_SET_CODE(READ_ERR, 0);
1630 	}
1631 
1632 	/* Child process should still be running.  Communicate user
1633 	 * selection to it.
1634 	 */
1635 	oh = util_signal(SIGPIPE, SIG_IGN);
1636 	if (!cdinfo_write_selpipe(spp)) {
1637 		/* Failure */
1638 		(void) cdinfo_closepipe(rpp);
1639 		(void) cdinfo_closepipe(spp);
1640 		cdinfo_load_cancel();
1641 		(void) util_signal(SIGPIPE, oh);
1642 
1643 		/* Deallocate the matchlist */
1644 		cdinfo_free_matchlist();
1645 
1646 		return CDINFO_SET_CODE(READ_ERR, 0);
1647 	}
1648 	(void) util_signal(SIGPIPE, oh);
1649 
1650 	/* Deallocate the matchlist */
1651 	cdinfo_free_matchlist();
1652 
1653 	/* Read CD info content from pipe into in-core structure */
1654 	(void) cdinfo_read_datapipe(rpp);
1655 
1656 	/* Close pipes */
1657 	(void) cdinfo_closepipe(rpp);
1658 	(void) cdinfo_closepipe(spp);
1659 
1660 	/* Parent process: wait for child to exit */
1661 	ret = util_waitchild(cpid, app_data.srv_timeout + 5,
1662 			     cdinfo_clinfo->workproc, cdinfo_clinfo->arg,
1663 			     TRUE, &wstat);
1664 	child_pid = 0;
1665 
1666 	if (ret < 0) {
1667 		return CDINFO_SET_CODE(WAIT_ERR, errno);
1668 	}
1669 	if (WIFEXITED(wstat)) {
1670 		if (WEXITSTATUS(wstat) == 0)
1671 			return 0;
1672 		else
1673 			return CDINFO_SET_CODE(WEXITSTATUS(wstat), 0);
1674 	}
1675 	else if (WIFSIGNALED(wstat)) {
1676 		return CDINFO_SET_CODE(KILLED_ERR, WTERMSIG(wstat));
1677 	}
1678 	else
1679 		return 0;
1680 #endif	/* SYNCHRONOUS */
1681 }
1682 
1683 
1684 /*
1685  * cdinfo_load_prog
1686  *	Load user-saved track program for the currently inserted CD.
1687  *
1688  * Args:
1689  *	s - Pointer to the curstat_t structure
1690  *
1691  * Return:
1692  *	Return value will be 0 for a successful load, or an error value
1693  *	on failure.
1694  */
1695 cdinfo_ret_t
cdinfo_load_prog(curstat_t * s)1696 cdinfo_load_prog(curstat_t *s)
1697 {
1698 	FILE		*fp;
1699 	char		*homepath,
1700 			trk[8],
1701 			buf[STR_BUF_SZ * 2],
1702 			junk[STR_BUF_SZ * 2],
1703 			path[FILE_PATH_SZ];
1704 #ifndef SYNCHRONOUS
1705 	pid_t		cpid;
1706 	waitret_t	wstat;
1707 	int		ret,
1708 			pfd[2];
1709 
1710 	if ((cdinfo_dbp->discid = cdinfo_discid(s)) == 0)
1711 		return CDINFO_SET_CODE(ARG_ERR, 0);
1712 
1713 	if (s->program || s->shuffle)
1714 		/* Program or shuffle already in progress */
1715 		return CDINFO_SET_CODE(BUSY_ERR, 0);
1716 
1717 	if (PIPE(pfd) < 0) {
1718 		DBGPRN(DBG_CDI)(errfp,
1719 			"cdinfo_load_prog: pipe failed (errno=%d)\n", errno);
1720 		return CDINFO_SET_CODE(OPEN_ERR, 0);
1721 	}
1722 
1723 	switch (cpid = FORK()) {
1724 	case 0:
1725 		/* Child */
1726 		cdinfo_ischild = TRUE;
1727 
1728 		(void) util_signal(SIGTERM, cdinfo_onterm);
1729 		(void) util_signal(SIGPIPE, SIG_IGN);
1730 
1731 		/* Close un-needed pipe descriptor */
1732 		(void) close(pfd[0]);
1733 
1734 		/* Force uid and gid to original setting */
1735 		if (!util_set_ougid()) {
1736 			(void) close(pfd[1]);
1737 			_exit(SETUID_ERR);
1738 		}
1739 
1740 		homepath = util_homedir(util_get_ouid());
1741 
1742 		(void) sprintf(path, USR_PROG_PATH, homepath);
1743 		(void) sprintf(path, "%s%c%08x%s",
1744 			path, DIR_END, cdinfo_dbp->discid,
1745 #ifdef __VMS
1746 			"."
1747 #else
1748 			""
1749 #endif
1750 		);
1751 
1752 		DBGPRN(DBG_CDI)(errfp, "Loading track program: %s\n", path);
1753 
1754 		if ((fp = fopen(path, "r")) == NULL) {
1755 			(void) close(pfd[1]);
1756 			_exit(OPEN_ERR);
1757 		}
1758 
1759 		while (fgets(buf, sizeof(buf), fp) != NULL) {
1760 			/* Skip comments */
1761 			if (buf[0] == '#' || buf[0] == '!' || buf[0] == '\n')
1762 				continue;
1763 			(void) write(pfd[1], buf, strlen(buf));
1764 		}
1765 		(void) write(pfd[1], ".\n", 2);
1766 
1767 		(void) close(pfd[1]);
1768 		(void) fclose(fp);
1769 
1770 		_exit(0);
1771 		/*NOTREACHED*/
1772 
1773 	case -1:
1774 		DBGPRN(DBG_CDI)(errfp,
1775 			"cdinfo_load_prog: fork failed (errno=%d)\n", errno);
1776 		(void) close(pfd[0]);
1777 		(void) close(pfd[1]);
1778 		return CDINFO_SET_CODE(FORK_ERR, 0);
1779 		/*NOTREACHED*/
1780 
1781 	default:
1782 		/* Parent */
1783 		child_pid = cpid;
1784 
1785 		/* Close un-needed pipe descriptor */
1786 		(void) close(pfd[1]);
1787 
1788 		if ((fp = fdopen(pfd[0], "r")) == NULL) {
1789 			DBGPRN(DBG_CDI)(errfp,
1790 				"cdinfo_load_prog: read pipe fdopen failed\n");
1791 			return CDINFO_SET_CODE(READ_ERR, 0);
1792 		}
1793 		break;
1794 	}
1795 #else
1796 	if ((cdinfo_dbp->discid = cdinfo_discid(s)) == 0)
1797 		return CDINFO_SET_CODE(ARG_ERR, 0);
1798 
1799 	if (s->program || s->shuffle)
1800 		/* Program or shuffle already in progress */
1801 		return CDINFO_SET_CODE(BUSY_ERR, 0);
1802 
1803 	homepath = util_homedir(util_get_ouid());
1804 
1805 	(void) sprintf(path, USR_PROG_PATH, homepath);
1806 	(void) sprintf(path, "%s%c%08x%s",
1807 		path, DIR_END, cdinfo_dbp->discid,
1808 #ifdef __VMS
1809 		"."
1810 #else
1811 		""
1812 #endif
1813 	);
1814 
1815 	DBGPRN(DBG_CDI)(errfp, "Loading track program: %s\n", path);
1816 
1817 	if ((fp = fopen(path, "r")) == NULL)
1818 		return CDINFO_SET_CODE(OPEN_ERR, 0);
1819 #endif	/* SYNCHRONOUS */
1820 
1821 	while (fgets(buf, sizeof(buf), fp) != NULL) {
1822 		int	n;
1823 
1824 		if (buf[0] == '.' && buf[1] == '\n')
1825 			/* Done */
1826 			break;
1827 
1828 		if (buf[0] == '#' || buf[0] == '!' || buf[0] == '\n')
1829 			/* Skip comments */
1830 			continue;
1831 
1832 		buf[strlen(buf)-1] = '\0';	/* Zap newline */
1833 
1834 		/* Parse the line and check for error */
1835 		if (sscanf(buf, "%2s %80s", trk, junk) < 1 ||
1836 			   (n = atoi(trk)) <= 0)
1837 			continue;
1838 
1839 		(void) sprintf(trk, "%d", n);
1840 		if (cdinfo_dbp->playorder == NULL) {
1841 			if (!util_newstr(&cdinfo_dbp->playorder, trk)) {
1842 				(void) fclose(fp);
1843 				return CDINFO_SET_CODE(MEM_ERR, errno);
1844 			}
1845 		}
1846 		else {
1847 			cdinfo_dbp->playorder = (char *) MEM_REALLOC(
1848 				"playorder",
1849 				cdinfo_dbp->playorder,
1850 				strlen(cdinfo_dbp->playorder) + strlen(trk) + 2
1851 			);
1852 			if (cdinfo_dbp->playorder == NULL) {
1853 				(void) fclose(fp);
1854 				return CDINFO_SET_CODE(MEM_ERR, errno);
1855 			}
1856 			(void) sprintf(cdinfo_dbp->playorder, "%s,%s",
1857 				       cdinfo_dbp->playorder, trk);
1858 		}
1859 	}
1860 
1861 	(void) fclose(fp);
1862 
1863 #ifndef SYNCHRONOUS
1864 	/* Parent process: wait for child to exit */
1865 	ret = util_waitchild(cpid, app_data.srv_timeout + 5,
1866 			     cdinfo_clinfo->workproc, cdinfo_clinfo->arg,
1867 			     TRUE, &wstat);
1868 	child_pid = 0;
1869 
1870 	if (ret < 0) {
1871 		return CDINFO_SET_CODE(WAIT_ERR, errno);
1872 	}
1873 	if (WIFEXITED(wstat)) {
1874 		if (WEXITSTATUS(wstat) == 0)
1875 			return 0;
1876 		else
1877 			return CDINFO_SET_CODE(WEXITSTATUS(wstat), 0);
1878 	}
1879 	else if (WIFSIGNALED(wstat)) {
1880 		return CDINFO_SET_CODE(KILLED_ERR, WTERMSIG(wstat));
1881 	}
1882 	else
1883 		return 0;
1884 #else
1885 	return 0;
1886 #endif
1887 }
1888 
1889 
1890 /*
1891  * cdinfo_save_prog
1892  *	Write user-defined track program into file for future retrieval
1893  *
1894  * Arg:
1895  *	s - Pointer to the curstat_t structure
1896  *
1897  * Return:
1898  *	Return value will be 0 for a successful write, or an error value
1899  *	on failure.
1900  */
1901 cdinfo_ret_t
cdinfo_save_prog(curstat_t * s)1902 cdinfo_save_prog(curstat_t *s)
1903 {
1904 	FILE		*fp;
1905 	char		*p,
1906 			*q,
1907 			*tartist,
1908 			*ttitle,
1909 			*homepath,
1910 			path[FILE_PATH_SZ];
1911 	cdinfo_ret_t	retcode;
1912 	int		i,
1913 			err;
1914 
1915 	if (cdinfo_dbp->discid == 0 ||
1916 	    cdinfo_dbp->playorder == NULL ||
1917 	    !isdigit((int) cdinfo_dbp->playorder[0]))
1918 		return (ARG_ERR);
1919 
1920 	if (cdinfo_forkwait(&retcode))
1921 		return (retcode);
1922 
1923 	homepath = util_homedir(util_get_ouid());
1924 
1925 	(void) sprintf(path, USR_PROG_PATH, homepath);
1926 	(void) sprintf(path, "%s%c%08x%s",
1927 		path, DIR_END, cdinfo_dbp->discid,
1928 #ifdef __VMS
1929 		"."
1930 #else
1931 		""
1932 #endif
1933 	);
1934 
1935 	/* Remove original file */
1936 	if (UNLINK(path) < 0 && errno != ENOENT) {
1937 		err = errno;
1938 		DBGPRN(DBG_CDI)(errfp, "Cannot unlink old %s\n", path);
1939 		CH_RET(err);
1940 	}
1941 
1942 	/* Write new file */
1943 	if ((fp = fopen(path, "w")) == NULL) {
1944 		err = errno;
1945 		DBGPRN(DBG_CDI)(errfp,
1946 			"Cannot open file for writing: %s\n", path);
1947 		CH_RET(err);
1948 	}
1949 
1950 	DBGPRN(DBG_CDI)(errfp, "Writing track program: %s\n", path);
1951 
1952 	(void) fprintf(fp, "# xmcd %s.%s track program file\n# %s\n#\n",
1953 		       VERSION_MAJ, VERSION_MIN, COPYRIGHT);
1954 	(void) fprintf(fp, "# ID:     %08x\n", cdinfo_dbp->discid);
1955 	(void) fprintf(fp, "# Artist: %.65s\n",
1956 			(cdinfo_dbp->disc.artist != NULL) ?
1957 				cdinfo_dbp->disc.artist : "<unknown>");
1958 	(void) fprintf(fp, "# Title:  %.65s\n",
1959 			(cdinfo_dbp->disc.title != NULL) ?
1960 				cdinfo_dbp->disc.title : "<unknown>");
1961 	(void) fprintf(fp, "# Genre:  %.65s\n#\n",
1962 			cdinfo_genre_name(cdinfo_dbp->disc.genre));
1963 
1964 	p = q = cdinfo_dbp->playorder;
1965 	do {
1966 		if ((q = strchr(p, ',')) != NULL)
1967 			*q = '\0';
1968 
1969 		for (i = 0; i < (int) s->tot_trks; i++) {
1970 			if (s->trkinfo[i].trkno == atoi(p))
1971 				break;
1972 		}
1973 
1974 		if (i < (int) s->tot_trks) {
1975 			tartist = cdinfo_dbp->track[i].artist;
1976 			ttitle = cdinfo_dbp->track[i].title;
1977 
1978 			(void) fprintf(fp, "%02d  %.25s%s%.45s\n",
1979 				s->trkinfo[i].trkno,
1980 				(tartist == NULL) ? "" : tartist,
1981 				(tartist != NULL && ttitle != NULL) ?
1982 					" / " : "",
1983 				(ttitle == NULL) ? "" : ttitle
1984 			);
1985 		}
1986 
1987 		if (q != NULL)
1988 			*q = ',';
1989 
1990 		p = q + 1;
1991 	} while (q != NULL);
1992 
1993 	(void) fclose(fp);
1994 
1995 	CH_RET(0);
1996 	/*NOTREACHED*/
1997 }
1998 
1999 
2000 /*
2001  * cdinfo_del_prog
2002  *	Delete the user-defined track program file for the loaded CD.
2003  *
2004  * Arg:
2005  *	None
2006  *
2007  * Return:
2008  *	Return value will be 0 for a successful delete, or an error value
2009  *	on failure.
2010  */
2011 cdinfo_ret_t
cdinfo_del_prog(void)2012 cdinfo_del_prog(void)
2013 {
2014 	char		*homepath,
2015 			path[FILE_PATH_SZ];
2016 	cdinfo_ret_t	retcode;
2017 
2018 	if (cdinfo_dbp->discid == 0)
2019 		return (ARG_ERR);
2020 
2021 	if (cdinfo_forkwait(&retcode))
2022 		return (retcode);
2023 
2024 	homepath = util_homedir(util_get_ouid());
2025 
2026 	(void) sprintf(path, USR_PROG_PATH, homepath);
2027 	(void) sprintf(path, "%s%c%08x%s",
2028 		path, DIR_END, cdinfo_dbp->discid,
2029 #ifdef __VMS
2030 		"."
2031 #else
2032 		""
2033 #endif
2034 	);
2035 
2036 	DBGPRN(DBG_CDI)(errfp, "Deleting track program: %s\n", path);
2037 
2038 	if (UNLINK(path) < 0)
2039 		CH_RET(errno);
2040 
2041 	CH_RET(0);
2042 	/*NOTREACHED*/
2043 }
2044 
2045 
2046 /*
2047  * cdinfo_reguser
2048  *	Register the user with CDDB
2049  *
2050  * Args:
2051  *	s - Pointer to the curstat_t structure.
2052  *
2053  * Return:
2054  *	Return value will be 0 for a successful connection, otherwise
2055  *	the return status will be as defined for cdinfo_ret_t.
2056  */
2057 cdinfo_ret_t
cdinfo_reguser(curstat_t * s)2058 cdinfo_reguser(curstat_t *s)
2059 {
2060 	cdinfo_cddb_t	*cp;
2061 	cdinfo_ret_t	retcode;
2062 
2063 	if (cdinfo_forkwait(&retcode)) {
2064 		if (retcode == 0) {
2065 			/* Clear flag */
2066 			cdinfo_dbp->flags &= ~CDINFO_NEEDREG;
2067 		}
2068 		return (retcode);
2069 	}
2070 
2071 	/* Open CDDB connection */
2072 	if ((cp = cdinfo_opencddb(s, FALSE, &retcode)) == NULL)
2073 		CH_RET(retcode);
2074 
2075 	/* Register user with CDDB */
2076 	if (!cdinfo_uregcddb(cp, &retcode)) {
2077 		(void) cdinfo_closecddb(cp);
2078 		CH_RET(retcode);
2079 	}
2080 
2081 	/* Close CDDB connection */
2082 	(void) cdinfo_closecddb(cp);
2083 
2084 	/* Clear flag */
2085 	cdinfo_dbp->flags &= ~CDINFO_NEEDREG;
2086 
2087 	/* Child exits here */
2088 	CH_RET(0);
2089 	/*NOTREACHED*/
2090 }
2091 
2092 
2093 /*
2094  * cdinfo_getpasshint
2095  *	Request CDDB to send password hint via e-mail
2096  *
2097  * Args:
2098  *	s - Pointer to the curstat_t structure.
2099  *
2100  * Return:
2101  *	Return value will be 0 for a successful connection, otherwise
2102  *	the return status will be as defined for cdinfo_ret_t.
2103  */
2104 cdinfo_ret_t
cdinfo_getpasshint(curstat_t * s)2105 cdinfo_getpasshint(curstat_t *s)
2106 {
2107 	cdinfo_cddb_t	*cp;
2108 	cdinfo_ret_t	retcode;
2109 
2110 	if (cdinfo_forkwait(&retcode))
2111 		return (retcode);
2112 
2113 	/* Open CDDB connection */
2114 	if ((cp = cdinfo_opencddb(s, FALSE, &retcode)) == NULL)
2115 		CH_RET(retcode);
2116 
2117 	/* Ask CDDB to send password hint */
2118 	if (!cdinfo_passhintcddb(cp, &retcode)) {
2119 		(void) cdinfo_closecddb(cp);
2120 		CH_RET(retcode);
2121 	}
2122 
2123 	/* Close CDDB connection */
2124 	(void) cdinfo_closecddb(cp);
2125 
2126 	/* Child exits here */
2127 	CH_RET(0);
2128 	/*NOTREACHED*/
2129 }
2130 
2131 
2132 /*
2133  * cdinfo_go_musicbrowser
2134  *	Invoke CDDB music browser.
2135  *
2136  * Args:
2137  *	s - Pointer to the curstat_t structure.
2138  *
2139  * Return:
2140  *	Return value will be 0 for a successful connection, otherwise
2141  *	the return status will be as defined for cdinfo_ret_t.
2142  */
2143 cdinfo_ret_t
cdinfo_go_musicbrowser(curstat_t * s)2144 cdinfo_go_musicbrowser(curstat_t *s)
2145 {
2146 	cdinfo_ret_t	retcode;
2147 	cdinfo_cddb_t	*cp;
2148 
2149 	if (cdinfo_forkwait(&retcode))
2150 		return (retcode);
2151 
2152 	/* Open CDDB connection */
2153 	if ((cp = cdinfo_opencddb(s, FALSE, &retcode)) == NULL) {
2154 		/* Failure */
2155 		CH_RET(retcode);
2156 	}
2157 
2158 	/* Invoke CDDB info browser */
2159 	if (!cdinfo_infobrowsercddb(cp)) {
2160 		/* Failure */
2161 		(void) cdinfo_closecddb(cp);
2162 		CH_RET(CMD_ERR);
2163 	}
2164 
2165 	/* Close CDDB connection */
2166 	(void) cdinfo_closecddb(cp);
2167 
2168 	/* Child exits here */
2169 	CH_RET(0);
2170 	/*NOTREACHED*/
2171 }
2172 
2173 
2174 /*
2175  * cdinfo_go_url
2176  *	Invoke web browser and go to the specified URL.
2177  *
2178  * Args:
2179  *	url - The URL string.
2180  *
2181  * Return:
2182  *	Return value will be 0 for a successful connection, otherwise
2183  *	the return status will be as defined for cdinfo_ret_t.
2184  */
2185 cdinfo_ret_t
cdinfo_go_url(char * url)2186 cdinfo_go_url(char *url)
2187 {
2188 	int	ret;
2189 	char	*cmd;
2190 
2191 	if (url == NULL || url[0] == '\0')
2192 		return CDINFO_SET_CODE(CMD_ERR, 0);
2193 
2194 	/* Invoke web browser to the specified URL */
2195 	cmd = (char *) MEM_ALLOC("urlstr", strlen(url) + FILE_PATH_SZ + 4);
2196 	if (cmd == NULL)
2197 		return CDINFO_SET_CODE(MEM_ERR, 0);
2198 
2199 	(void) sprintf(cmd, "%s '%s'", CDINFO_GOBROWSER, url);
2200 
2201 	ret = util_runcmd(cmd, cdinfo_clinfo->workproc, cdinfo_clinfo->arg);
2202 
2203 	MEM_FREE(cmd);
2204 
2205 #ifdef __VMS
2206 	return CDINFO_SET_CODE(
2207 		(ret == 0 || (ret & 0x1) == 1) ? 0 : CMD_ERR, 0
2208 	);
2209 #else
2210 	return CDINFO_SET_CODE((ret == 0) ? 0 : CMD_ERR, 0);
2211 #endif
2212 }
2213 
2214 
2215 /*
2216  * cdinfo_go_cddburl
2217  *	Invoke web browser to go to a URL supplied by CDDB
2218  *
2219  * Args:
2220  *	s - Pointer to the curstat_t structure.
2221  *	type - WTYPE_GEN or WTYPE_ALBUM
2222  *	idx - URL index as found in the cdinfo_dbp->gen_url_list
2223  *
2224  * Return:
2225  *	Return value will be 0 for a successful connection, otherwise
2226  *	the return status will be as defined for cdinfo_ret_t.
2227  */
2228 cdinfo_ret_t
cdinfo_go_cddburl(curstat_t * s,int type,int idx)2229 cdinfo_go_cddburl(curstat_t *s, int type, int idx)
2230 {
2231 	cdinfo_cddb_t	*cp;
2232 	cdinfo_ret_t	retcode;
2233 
2234 	if (type != WTYPE_GEN && type != WTYPE_ALBUM)
2235 		return CDINFO_SET_CODE(ARG_ERR, 0);
2236 
2237 	if (cdinfo_forkwait(&retcode))
2238 		return (retcode);
2239 
2240 	/* Open CDDB connection */
2241 	if ((cp = cdinfo_opencddb(s, FALSE, &retcode)) == NULL) {
2242 		/* Failure */
2243 		CH_RET(retcode);
2244 	}
2245 
2246 	/* Invoke web browser to go to the specified URL */
2247 	if (!cdinfo_urlcddb(cp, type, idx)) {
2248 		/* Failure */
2249 		(void) cdinfo_closecddb(cp);
2250 		CH_RET(CMD_ERR);
2251 	}
2252 
2253 	/* Close CDDB connection */
2254 	(void) cdinfo_closecddb(cp);
2255 
2256 	/* Child exits here */
2257 	CH_RET(0);
2258 	/*NOTREACHED*/
2259 }
2260 
2261 
2262 /*
2263  * cdinfo_gen_discog
2264  *	Write out local discography file containing the currently loaded
2265  *	CD information.
2266  *
2267  * Args:
2268  *	s - Pointer to the curstat_t structure
2269  *	baseurl - The URL to the output file
2270  *
2271  * Return:
2272  *	Return value will be 0 for a successful connection, otherwise
2273  *	the return status will be as defined for cdinfo_ret_t.
2274  */
2275 cdinfo_ret_t
cdinfo_gen_discog(curstat_t * s)2276 cdinfo_gen_discog(curstat_t *s)
2277 {
2278 	int		i,
2279 			n;
2280 	char		*path,
2281 			*url;
2282 	bool_t		err;
2283 	cdinfo_ret_t	retcode;
2284 
2285 	if (cdinfo_forkwait(&retcode))
2286 		return (retcode);
2287 
2288 	if (cdinfo_discog == NULL) {
2289 		/* Local discography not defined */
2290 		DBGPRN(DBG_CDI)(errfp,
2291 			"Local discography not defined in %s.\n", WWWWARP_CFG);
2292 		CH_RET(ENOENT);
2293 	}
2294 
2295 	i = -1;
2296 	n = cdinfo_url_len(cdinfo_discog->arg, &cdinfo_discog->attrib, &i);
2297 	if ((url = (char *) MEM_ALLOC("cdinfo_genurl", n)) == NULL) {
2298 		DBGPRN(DBG_CDI)(errfp, "Out of memory.\n");
2299 		CH_RET(ENOMEM);
2300 	}
2301 
2302 	/* Make the URL from template */
2303 	cdinfo_tmpl_to_url(s, cdinfo_discog->arg, url, i);
2304 
2305 	if (util_urlchk(url, &path, &n) & IS_REMOTE_URL) {
2306 		/* Error: local discography cannot be remote */
2307 		DBGPRN(DBG_CDI)(errfp,
2308 			"Local discography URL error: %s\n", url);
2309 		MEM_FREE(url);
2310 		CH_RET(EINVAL);
2311 	}
2312 
2313 	/* Generate local discography */
2314 	err = !cdinfo_out_discog(path, s, url);
2315 
2316 	MEM_FREE(url);
2317 	CH_RET(err ? EIO : 0);
2318 	/*NOTREACHED*/
2319 }
2320 
2321 
2322 /*
2323  * cdinfo_clear
2324  *	Clear the in-core cdinfo_incore_t structure
2325  *
2326  * Args:
2327  *	reload - Whether this operation is due to a reload of the CD info
2328  *		(We don't want to clear a play sequence in this case).
2329  *
2330  * Return:
2331  *	Nothing
2332  */
2333 void
cdinfo_clear(bool_t reload)2334 cdinfo_clear(bool_t reload)
2335 {
2336 	int			i;
2337 	cdinfo_url_t		*u,
2338 				*v;
2339 	cdinfo_credit_t		*p,
2340 				*q;
2341 	cdinfo_segment_t	*r,
2342 				*s;
2343 
2344 	if (cdinfo_dbp == NULL)
2345 		return;
2346 
2347 	/* Clear CD-TEXT information */
2348 	di_clear_cdtext(&cdinfo_dbp->cdtext);
2349 
2350 	/* Cancel pending queries */
2351 	cdinfo_load_cancel();
2352 
2353 	/* Disc */
2354 	cdinfo_dbp->disc.compilation = FALSE;
2355 
2356 	if (cdinfo_dbp->disc.artistfname.dispname != NULL) {
2357 		MEM_FREE(cdinfo_dbp->disc.artistfname.dispname);
2358 		cdinfo_dbp->disc.artistfname.dispname = NULL;
2359 	}
2360 	if (cdinfo_dbp->disc.artistfname.lastname != NULL) {
2361 		MEM_FREE(cdinfo_dbp->disc.artistfname.lastname);
2362 		cdinfo_dbp->disc.artistfname.lastname = NULL;
2363 	}
2364 	if (cdinfo_dbp->disc.artistfname.firstname != NULL) {
2365 		MEM_FREE(cdinfo_dbp->disc.artistfname.firstname);
2366 		cdinfo_dbp->disc.artistfname.firstname = NULL;
2367 	}
2368 	if (cdinfo_dbp->disc.artistfname.the != NULL) {
2369 		MEM_FREE(cdinfo_dbp->disc.artistfname.the);
2370 		cdinfo_dbp->disc.artistfname.the = NULL;
2371 	}
2372 	if (cdinfo_dbp->disc.artist != NULL) {
2373 		MEM_FREE(cdinfo_dbp->disc.artist);
2374 		cdinfo_dbp->disc.artist = NULL;
2375 	}
2376 	if (cdinfo_dbp->disc.title != NULL) {
2377 		MEM_FREE(cdinfo_dbp->disc.title);
2378 		cdinfo_dbp->disc.title = NULL;
2379 	}
2380 	if (cdinfo_dbp->disc.sorttitle != NULL) {
2381 		MEM_FREE(cdinfo_dbp->disc.sorttitle);
2382 		cdinfo_dbp->disc.sorttitle = NULL;
2383 	}
2384 	if (cdinfo_dbp->disc.title_the != NULL) {
2385 		MEM_FREE(cdinfo_dbp->disc.title_the);
2386 		cdinfo_dbp->disc.title_the = NULL;
2387 	}
2388 	if (cdinfo_dbp->disc.year != NULL) {
2389 		MEM_FREE(cdinfo_dbp->disc.year);
2390 		cdinfo_dbp->disc.year = NULL;
2391 	}
2392 	if (cdinfo_dbp->disc.label != NULL) {
2393 		MEM_FREE(cdinfo_dbp->disc.label);
2394 		cdinfo_dbp->disc.label = NULL;
2395 	}
2396 	if (cdinfo_dbp->disc.genre != NULL) {
2397 		MEM_FREE(cdinfo_dbp->disc.genre);
2398 		cdinfo_dbp->disc.genre = NULL;
2399 	}
2400 	if (cdinfo_dbp->disc.genre2 != NULL) {
2401 		MEM_FREE(cdinfo_dbp->disc.genre2);
2402 		cdinfo_dbp->disc.genre2 = NULL;
2403 	}
2404 	if (cdinfo_dbp->disc.dnum != NULL) {
2405 		MEM_FREE(cdinfo_dbp->disc.dnum);
2406 		cdinfo_dbp->disc.dnum = NULL;
2407 	}
2408 	if (cdinfo_dbp->disc.tnum != NULL) {
2409 		MEM_FREE(cdinfo_dbp->disc.tnum);
2410 		cdinfo_dbp->disc.tnum = NULL;
2411 	}
2412 	if (cdinfo_dbp->disc.region != NULL) {
2413 		MEM_FREE(cdinfo_dbp->disc.region);
2414 		cdinfo_dbp->disc.region = NULL;
2415 	}
2416 	if (cdinfo_dbp->disc.lang != NULL) {
2417 		MEM_FREE(cdinfo_dbp->disc.lang);
2418 		cdinfo_dbp->disc.lang = NULL;
2419 	}
2420 	if (cdinfo_dbp->disc.notes != NULL) {
2421 		MEM_FREE(cdinfo_dbp->disc.notes);
2422 		cdinfo_dbp->disc.notes = NULL;
2423 	}
2424 	if (cdinfo_dbp->disc.mediaid != NULL) {
2425 		MEM_FREE(cdinfo_dbp->disc.mediaid);
2426 		cdinfo_dbp->disc.mediaid = NULL;
2427 	}
2428 	if (cdinfo_dbp->disc.muiid != NULL) {
2429 		MEM_FREE(cdinfo_dbp->disc.muiid);
2430 		cdinfo_dbp->disc.muiid = NULL;
2431 	}
2432 	if (cdinfo_dbp->disc.titleuid != NULL) {
2433 		MEM_FREE(cdinfo_dbp->disc.titleuid);
2434 		cdinfo_dbp->disc.titleuid = NULL;
2435 	}
2436 	if (cdinfo_dbp->disc.revision != NULL) {
2437 		MEM_FREE(cdinfo_dbp->disc.revision);
2438 		cdinfo_dbp->disc.revision = NULL;
2439 	}
2440 	if (cdinfo_dbp->disc.revtag != NULL) {
2441 		MEM_FREE(cdinfo_dbp->disc.revtag);
2442 		cdinfo_dbp->disc.revtag = NULL;
2443 	}
2444 	if (cdinfo_dbp->disc.certifier != NULL) {
2445 		MEM_FREE(cdinfo_dbp->disc.certifier);
2446 		cdinfo_dbp->disc.certifier = NULL;
2447 	}
2448 	if (cdinfo_dbp->disc.lang != NULL) {
2449 		MEM_FREE(cdinfo_dbp->disc.lang);
2450 		cdinfo_dbp->disc.lang = NULL;
2451 	}
2452 	for (p = cdinfo_dbp->disc.credit_list; p != NULL; p = q) {
2453 		q = p->next;
2454 		if (p->crinfo.name != NULL)
2455 			MEM_FREE(p->crinfo.name);
2456 		if (p->crinfo.fullname.dispname != NULL)
2457 			MEM_FREE(p->crinfo.fullname.dispname);
2458 		if (p->crinfo.fullname.lastname != NULL)
2459 			MEM_FREE(p->crinfo.fullname.lastname);
2460 		if (p->crinfo.fullname.firstname != NULL)
2461 			MEM_FREE(p->crinfo.fullname.firstname);
2462 		if (p->crinfo.fullname.the != NULL)
2463 			MEM_FREE(p->crinfo.fullname.the);
2464 		if (p->notes != NULL)
2465 			MEM_FREE(p->notes);
2466 		MEM_FREE(p);
2467 	}
2468 	cdinfo_dbp->disc.credit_list = NULL;
2469 
2470 	for (r = cdinfo_dbp->disc.segment_list; r != NULL; r = s) {
2471 		s = r->next;
2472 		if (r->name != NULL)
2473 			MEM_FREE(r->name);
2474 		if (r->notes != NULL)
2475 			MEM_FREE(r->notes);
2476 		if (r->start_track != NULL)
2477 			MEM_FREE(r->start_track);
2478 		if (r->start_frame != NULL)
2479 			MEM_FREE(r->start_frame);
2480 		if (r->end_track != NULL)
2481 			MEM_FREE(r->end_track);
2482 		if (r->end_frame != NULL)
2483 			MEM_FREE(r->end_frame);
2484 		for (p = r->credit_list; p != NULL; p = q) {
2485 			q = p->next;
2486 			if (p->crinfo.name != NULL)
2487 				MEM_FREE(p->crinfo.name);
2488 			if (p->crinfo.fullname.dispname != NULL)
2489 				MEM_FREE(p->crinfo.fullname.dispname);
2490 			if (p->crinfo.fullname.lastname != NULL)
2491 				MEM_FREE(p->crinfo.fullname.lastname);
2492 			if (p->crinfo.fullname.firstname != NULL)
2493 				MEM_FREE(p->crinfo.fullname.firstname);
2494 			if (p->crinfo.fullname.the != NULL)
2495 				MEM_FREE(p->crinfo.fullname.the);
2496 			if (p->notes != NULL)
2497 				MEM_FREE(p->notes);
2498 			MEM_FREE(p);
2499 		}
2500 		MEM_FREE(r);
2501 	}
2502 	cdinfo_dbp->disc.segment_list = NULL;
2503 
2504 	/* Tracks */
2505 	for (i = MAXTRACK-1; i >= 0; i--) {
2506 		if (cdinfo_dbp->track[i].artistfname.dispname != NULL) {
2507 			MEM_FREE(cdinfo_dbp->track[i].artistfname.dispname);
2508 			cdinfo_dbp->track[i].artistfname.dispname = NULL;
2509 		}
2510 		if (cdinfo_dbp->track[i].artistfname.lastname != NULL) {
2511 			MEM_FREE(cdinfo_dbp->track[i].artistfname.lastname);
2512 			cdinfo_dbp->track[i].artistfname.lastname = NULL;
2513 		}
2514 		if (cdinfo_dbp->track[i].artistfname.firstname != NULL) {
2515 			MEM_FREE(cdinfo_dbp->track[i].artistfname.firstname);
2516 			cdinfo_dbp->track[i].artistfname.firstname = NULL;
2517 		}
2518 		if (cdinfo_dbp->track[i].artistfname.the != NULL) {
2519 			MEM_FREE(cdinfo_dbp->track[i].artistfname.the);
2520 			cdinfo_dbp->track[i].artistfname.the = NULL;
2521 		}
2522 		if (cdinfo_dbp->track[i].artist != NULL) {
2523 			MEM_FREE(cdinfo_dbp->track[i].artist);
2524 			cdinfo_dbp->track[i].artist = NULL;
2525 		}
2526 		if (cdinfo_dbp->track[i].title != NULL) {
2527 			MEM_FREE(cdinfo_dbp->track[i].title);
2528 			cdinfo_dbp->track[i].title = NULL;
2529 		}
2530 		if (cdinfo_dbp->track[i].sorttitle != NULL) {
2531 			MEM_FREE(cdinfo_dbp->track[i].sorttitle);
2532 			cdinfo_dbp->track[i].sorttitle = NULL;
2533 		}
2534 		if (cdinfo_dbp->track[i].title_the != NULL) {
2535 			MEM_FREE(cdinfo_dbp->track[i].title_the);
2536 			cdinfo_dbp->track[i].title_the = NULL;
2537 		}
2538 		if (cdinfo_dbp->track[i].year != NULL) {
2539 			MEM_FREE(cdinfo_dbp->track[i].year);
2540 			cdinfo_dbp->track[i].year = NULL;
2541 		}
2542 		if (cdinfo_dbp->track[i].label != NULL) {
2543 			MEM_FREE(cdinfo_dbp->track[i].label);
2544 			cdinfo_dbp->track[i].label = NULL;
2545 		}
2546 		if (cdinfo_dbp->track[i].genre != NULL) {
2547 			MEM_FREE(cdinfo_dbp->track[i].genre);
2548 			cdinfo_dbp->track[i].genre = NULL;
2549 		}
2550 		if (cdinfo_dbp->track[i].genre2 != NULL) {
2551 			MEM_FREE(cdinfo_dbp->track[i].genre2);
2552 			cdinfo_dbp->track[i].genre2 = NULL;
2553 		}
2554 		if (cdinfo_dbp->track[i].bpm != NULL) {
2555 			MEM_FREE(cdinfo_dbp->track[i].bpm);
2556 			cdinfo_dbp->track[i].bpm = NULL;
2557 		}
2558 		if (cdinfo_dbp->track[i].notes != NULL) {
2559 			MEM_FREE(cdinfo_dbp->track[i].notes);
2560 			cdinfo_dbp->track[i].notes = NULL;
2561 		}
2562 		if (cdinfo_dbp->track[i].isrc != NULL) {
2563 			MEM_FREE(cdinfo_dbp->track[i].isrc);
2564 			cdinfo_dbp->track[i].isrc = NULL;
2565 		}
2566 		for (p = cdinfo_dbp->track[i].credit_list; p != NULL; p = q) {
2567 			q = p->next;
2568 			if (p->crinfo.name != NULL)
2569 				MEM_FREE(p->crinfo.name);
2570 			if (p->crinfo.fullname.dispname != NULL)
2571 				MEM_FREE(p->crinfo.fullname.dispname);
2572 			if (p->crinfo.fullname.lastname != NULL)
2573 				MEM_FREE(p->crinfo.fullname.lastname);
2574 			if (p->crinfo.fullname.firstname != NULL)
2575 				MEM_FREE(p->crinfo.fullname.firstname);
2576 			if (p->crinfo.fullname.the != NULL)
2577 				MEM_FREE(p->crinfo.fullname.the);
2578 			if (p->notes != NULL)
2579 				MEM_FREE(p->notes);
2580 			MEM_FREE(p);
2581 		}
2582 		cdinfo_dbp->track[i].credit_list = NULL;
2583 	}
2584 
2585 	/* Album-related URLs */
2586 	for (u = v = cdinfo_dbp->disc_url_list; u != NULL; u = v) {
2587 		v = u->next;
2588 		if (u->disptext != NULL)
2589 			MEM_FREE(u->disptext);
2590 		if (u->modifier != NULL)
2591 			MEM_FREE(u->modifier);
2592 		if (u->keyname != NULL)
2593 			MEM_FREE(u->keyname);
2594 		MEM_FREE(u);
2595 	}
2596 	cdinfo_dbp->disc_url_list = NULL;
2597 
2598 	/* Misc */
2599 	if (!reload && cdinfo_dbp->playorder != NULL) {
2600 		MEM_FREE(cdinfo_dbp->playorder);
2601 		cdinfo_dbp->playorder = NULL;
2602 	}
2603 
2604 	cdinfo_dbp->discid = 0;
2605 	cdinfo_dbp->flags = 0;
2606 
2607 	/* Deallocate the matchlist */
2608 	cdinfo_free_matchlist();
2609 }
2610 
2611 
2612 /*
2613  * cdinfo_genre
2614  *	Given a genre ID, return a pointer to the associated genre structure.
2615  *
2616  * Args:
2617  *	id - The genre ID string.
2618  *
2619  * Return:
2620  *	Pointer to the genre structure.
2621  */
2622 cdinfo_genre_t *
cdinfo_genre(char * id)2623 cdinfo_genre(char *id)
2624 {
2625 	cdinfo_genre_t	*gp,
2626 			*sgp;
2627 
2628 	if (id == NULL)
2629 		return NULL;
2630 
2631 	for (gp = cdinfo_dbp->genrelist; gp != NULL; gp = gp->next) {
2632 		if (gp->id != NULL && strcmp(gp->id, id) == 0)
2633 			return (gp);
2634 
2635 		for (sgp = gp->child; sgp != NULL; sgp = sgp->next) {
2636 			if (sgp->id != NULL && strcmp(sgp->id, id) == 0)
2637 				return (sgp);
2638 		}
2639 	}
2640 
2641 	/* Can't find genre entry */
2642 	return NULL;
2643 }
2644 
2645 
2646 /*
2647  * cdinfo_genre_name
2648  *	Given a genre ID, return the genre name string.
2649  *
2650  * Args:
2651  *	id - The genre ID string.
2652  *
2653  * Return:
2654  *	Pointer to the genre name string.
2655  */
2656 char *
cdinfo_genre_name(char * id)2657 cdinfo_genre_name(char *id)
2658 {
2659 	cdinfo_genre_t	*gp;
2660 	static char	name[STR_BUF_SZ * 2 + 8];
2661 
2662 	if (id == NULL)
2663 		return ("");
2664 
2665 	if ((gp = cdinfo_genre(id)) == NULL) {
2666 		/* If genre entry is not found, just use the ID */
2667 		return (id);
2668 	}
2669 
2670 	if (gp->parent == NULL) {
2671 		/* Genre */
2672 		(void) sprintf(name, "%.63s",
2673 				gp->name == NULL ? id : gp->name);
2674 		return (name);
2675 	}
2676 	else {
2677 		/* Subgenre */
2678 		(void) sprintf(name, "%.63s -> %.63s",
2679 				gp->parent->name == NULL ?
2680 					gp->parent->id : gp->parent->name,
2681 				gp->name == NULL ? gp->id : gp->name);
2682 		return (name);
2683 	}
2684 
2685 	/*NOTREACHED*/
2686 }
2687 
2688 
2689 /*
2690  * cdinfo_genre_path
2691  *	Return a relative file path name for a given genre ID.
2692  *
2693  * Args:
2694  *	id - The genre ID string
2695  *
2696  * Return:
2697  *	Pointer to the path name string.
2698  */
2699 char *
cdinfo_genre_path(char * id)2700 cdinfo_genre_path(char *id)
2701 {
2702 	char		*cp,
2703 			*cp2;
2704 	cdinfo_genre_t	*gp;
2705 	static char	buf[STR_BUF_SZ * 2 + 2];
2706 
2707 	if (id == NULL)
2708 #ifdef __VMS
2709 		return ("Unknown.Unknown");
2710 #else
2711 		return ("Unknown/Unknown");
2712 #endif
2713 
2714 	if ((gp = cdinfo_genre(id)) == NULL) {
2715 		/* If genre entry is not found, just use the ID */
2716 		return (id);
2717 	}
2718 
2719 	if (gp->parent == NULL) {
2720 		/* Genre */
2721 		cp = cdinfo_mkpath(
2722 			gp->name == NULL ? gp->id : gp->name,
2723 			STR_BUF_SZ * 2
2724 		);
2725 		if (cp == NULL) {
2726 			CDINFO_FATAL(app_data.str_nomemory);
2727 			return ("");
2728 		}
2729 
2730 		(void) strcpy(buf, cp);
2731 		MEM_FREE(cp);
2732 
2733 		return (buf);
2734 	}
2735 	else {
2736 		/* Subgenre */
2737 		cp = cdinfo_mkpath(
2738 			gp->parent->name == NULL ?
2739 				gp->parent->id : gp->parent->name,
2740 			STR_BUF_SZ
2741 		);
2742 		cp2 = cdinfo_mkpath(
2743 			gp->name == NULL ? gp->id : gp->name,
2744 			STR_BUF_SZ
2745 		);
2746 		if (cp == NULL || cp2 == NULL) {
2747 			CDINFO_FATAL(app_data.str_nomemory);
2748 			return ("");
2749 		}
2750 
2751 		(void) sprintf(buf, "%s%c%s", cp, DIR_SEP, cp2);
2752 		MEM_FREE(cp);
2753 		MEM_FREE(cp2);
2754 
2755 		return (buf);
2756 	}
2757 
2758 	/*NOTREACHED*/
2759 }
2760 
2761 
2762 /*
2763  * cdinfo_region_name
2764  *	Given a region ID, return the region name.
2765  *
2766  * Args:
2767  *	id - The region ID string.
2768  *
2769  * Return:
2770  *	Pointer to the region name string.
2771  */
2772 char *
cdinfo_region_name(char * id)2773 cdinfo_region_name(char *id)
2774 {
2775 	cdinfo_region_t	*rp;
2776 
2777 	if (id == NULL)
2778 		return ("");
2779 
2780 	for (rp = cdinfo_dbp->regionlist; rp != NULL; rp = rp->next) {
2781 		if (rp->id != NULL && strcmp(rp->id, id) == 0)
2782 			return (rp->name);
2783 	}
2784 	return ("");
2785 }
2786 
2787 
2788 /*
2789  * cdinfo_lang_name
2790  *	Given a language ID, return the language name.
2791  *
2792  * Args:
2793  *	id - The language ID string.
2794  *
2795  * Return:
2796  *	Pointer to the language name string.
2797  */
2798 char *
cdinfo_lang_name(char * id)2799 cdinfo_lang_name(char *id)
2800 {
2801 	cdinfo_lang_t	*lp;
2802 
2803 	if (id == NULL)
2804 		return ("");
2805 
2806 	for (lp = cdinfo_dbp->langlist; lp != NULL; lp = lp->next) {
2807 		if (lp->id != NULL && strcmp(lp->id, id) == 0)
2808 			return (lp->name);
2809 	}
2810 	return ("");
2811 }
2812 
2813 
2814 /*
2815  * cdinfo_role
2816  *	Given a role ID, return a pointer to the role list element.
2817  *
2818  * Args:
2819  *	id - The role ID string.
2820  *
2821  * Return:
2822  *	Pointer to the role list element.
2823  */
2824 cdinfo_role_t *
cdinfo_role(char * id)2825 cdinfo_role(char *id)
2826 {
2827 	cdinfo_role_t	*rp,
2828 			*srp;
2829 
2830 	if (id == NULL)
2831 		return NULL;
2832 
2833 	for (rp = cdinfo_dbp->rolelist; rp != NULL; rp = rp->next) {
2834 		if (rp->id != NULL && strcmp(rp->id, id) == 0)
2835 			return (rp);
2836 
2837 		for (srp = rp->child; srp != NULL; srp = srp->next) {
2838 			if (srp->id != NULL && strcmp(srp->id, id) == 0)
2839 				return (srp);
2840 		}
2841 	}
2842 
2843 	return NULL;
2844 }
2845 
2846 
2847 /*
2848  * cdinfo_role_name
2849  *	Given a role ID, return the role name.
2850  *
2851  * Args:
2852  *	id - The role ID string.
2853  *
2854  * Return:
2855  *	Pointer to the role name string.
2856  */
2857 char *
cdinfo_role_name(char * id)2858 cdinfo_role_name(char *id)
2859 {
2860 	cdinfo_role_t	*rp;
2861 
2862 	if ((rp = cdinfo_role(id)) == NULL)
2863 		return ("unknown role");
2864 
2865 	return (rp->name);
2866 }
2867 
2868 
2869 /*
2870  * cdinfo_load_cancel
2871  *	Cancel asynchronous CD info load operation, if active.
2872  *
2873  * Args:
2874  *	None.
2875  *
2876  * Return:
2877  *	nothing.
2878  */
2879 void
cdinfo_load_cancel(void)2880 cdinfo_load_cancel(void)
2881 {
2882 	if (child_pid > 0) {
2883 		/* Kill child process */
2884 		(void) kill(child_pid, SIGTERM);
2885 		child_pid = 0;
2886 	}
2887 }
2888 
2889 
2890 /*
2891  * cdinfo_cddb_ver
2892  *	Returns the version string of the CDDB interface
2893  *
2894  * Args:
2895  *	None.
2896  *
2897  * Return:
2898  *	The CDDB interface version number
2899  */
2900 int
cdinfo_cddb_ver(void)2901 cdinfo_cddb_ver(void)
2902 {
2903 	return (cddb_ifver());
2904 }
2905 
2906 
2907 /*
2908  * cdinfo_cddbctrl_ver
2909  *	Returns the version string of the CDDB control
2910  *
2911  * Args:
2912  *	None.
2913  *
2914  * Return:
2915  *	The version string.
2916  */
2917 char *
cdinfo_cddbctrl_ver(void)2918 cdinfo_cddbctrl_ver(void)
2919 {
2920 	static char	buf[STR_BUF_SZ];
2921 
2922 	if (cdinfo_dbp->ctrl_ver == NULL)
2923 		return ("");
2924 
2925 	(void) sprintf(buf, "%.63s\n", cdinfo_dbp->ctrl_ver);
2926 	return (buf);
2927 }
2928 
2929 
2930 /*
2931  * cdinfo_cddb_iscfg
2932  *	Returns whether CDDB is configured in the cdinfoPath parameter.
2933  *
2934  * Args:
2935  *	None.
2936  *
2937  * Return:
2938  *	TRUE - CDDB is configured
2939  *	FALSE - CDDB not configured
2940  */
2941 bool_t
cdinfo_cddb_iscfg(void)2942 cdinfo_cddb_iscfg(void)
2943 {
2944 	cdinfo_path_t	*pp;
2945 
2946 	for (pp = cdinfo_dbp->pathlist; pp != NULL; pp = pp->next) {
2947 		if (pp->type == CDINFO_RMT)
2948 			return TRUE;
2949 	}
2950 	return FALSE;
2951 }
2952 
2953 
2954 /*
2955  * cdinfo_cdtext_iscfg
2956  *	Returns whether CD-TEXT is configured in the cdinfoPath parameter.
2957  *
2958  * Args:
2959  *	None.
2960  *
2961  * Return:
2962  *	TRUE - CDDB is configured
2963  *	FALSE - CDDB not configured
2964  */
2965 bool_t
cdinfo_cdtext_iscfg(void)2966 cdinfo_cdtext_iscfg(void)
2967 {
2968 	cdinfo_path_t	*pp;
2969 
2970 	for (pp = cdinfo_dbp->pathlist; pp != NULL; pp = pp->next) {
2971 		if (pp->type == CDINFO_CDTEXT)
2972 			return TRUE;
2973 	}
2974 	return FALSE;
2975 }
2976 
2977 
2978 /*
2979  * cdinfo_wwwwarp_parmload
2980  *	Load the wwwwarp configuration file and initialize structures.
2981  *
2982  * Args:
2983  *	None.
2984  *
2985  * Return:
2986  *	Nothing.
2987  */
2988 void
cdinfo_wwwwarp_parmload(void)2989 cdinfo_wwwwarp_parmload(void)
2990 {
2991 	FILE		*fp;
2992 	char		*buf,
2993 			*name,
2994 			*p,
2995 			*q,
2996 			trypath[FILE_PATH_SZ];
2997 	w_ent_t		*wp,
2998 			*wp1,
2999 			*wp2,
3000 			*wp3;
3001 	bool_t		newmenu;
3002 #ifndef SYNCHRONOUS
3003 	pid_t		cpid;
3004 	waitret_t	wstat;
3005 	int		ret,
3006 			pfd[2];
3007 
3008 	if (PIPE(pfd) < 0) {
3009 		DBGPRN(DBG_CDI)(errfp,
3010 			"cdinfo_wwwwarp_parmload: pipe failed (errno=%d)\n",
3011 			errno);
3012 		return;
3013 	}
3014 
3015 	switch (cpid = FORK()) {
3016 	case 0:
3017 		/* Child */
3018 		cdinfo_ischild = TRUE;
3019 
3020 		(void) util_signal(SIGTERM, cdinfo_onterm);
3021 		(void) util_signal(SIGPIPE, SIG_IGN);
3022 
3023 		/* Close un-needed pipe descriptor */
3024 		(void) close(pfd[0]);
3025 
3026 		/* Force uid and gid to original setting */
3027 		if (!util_set_ougid()) {
3028 			(void) close(pfd[1]);
3029 			_exit(1);
3030 		}
3031 
3032 		/* Try the user's own wwwwarp.cfg, if it exists */
3033 		(void) sprintf(trypath, USR_DSCFG_PATH,
3034 				util_homedir(util_get_ouid()), WWWWARP_CFG);
3035 		DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3036 			"Loading wwwwarp parameters: %s\n", trypath);
3037 		fp = fopen(trypath, "r");
3038 
3039 		if (fp == NULL) {
3040 			DBGPRN(DBG_GEN|DBG_CDI)(errfp, "    Cannot open %s\n",
3041 					trypath);
3042 
3043 			/* Try the system-wide wwwwarp.cfg, if it exists */
3044 			(void) sprintf(trypath, SYS_DSCFG_PATH,
3045 					app_data.libdir, WWWWARP_CFG);
3046 			DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3047 				"Loading wwwwarp parameters: %s\n", trypath);
3048 			fp = fopen(trypath, "r");
3049 		}
3050 
3051 		if (fp == NULL) {
3052 			DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3053 					"    Cannot open %s\n", trypath);
3054 
3055 			(void) close(pfd[1]);
3056 			_exit(0);
3057 		}
3058 
3059 		while ((buf = cdinfo_fgetline(fp)) != NULL) {
3060 			(void) write(pfd[1], buf, strlen(buf));
3061 			(void) write(pfd[1], "\n", 1);
3062 			MEM_FREE(buf);
3063 		}
3064 		(void) write(pfd[1], ".\n", 2);
3065 
3066 		(void) fclose(fp);
3067 		(void) close(pfd[1]);
3068 
3069 		_exit(0);
3070 		/*NOTREACHED*/
3071 
3072 	case -1:
3073 		DBGPRN(DBG_CDI)(errfp,
3074 			"cdinfo_wwwwarp_parmload: fork failed (errno=%d)\n",
3075 			errno);
3076 		(void) close(pfd[0]);
3077 		(void) close(pfd[1]);
3078 		return;
3079 
3080 	default:
3081 		/* Parent */
3082 		child_pid = cpid;
3083 
3084 		/* Close un-needed pipe descriptor */
3085 		(void) close(pfd[1]);
3086 
3087 		if ((fp = fdopen(pfd[0], "r")) == NULL) {
3088 			DBGPRN(DBG_CDI)(errfp,
3089 			"cdinfo_common_parmload: read pipe fdopen failed\n");
3090 			return;
3091 		}
3092 		break;
3093 	}
3094 #else
3095 	/* Try the user's own wwwwarp.cfg, if it exists */
3096 	(void) sprintf(trypath, USR_DSCFG_PATH,
3097 			util_homedir(util_get_ouid()), WWWWARP_CFG);
3098 	DBGPRN(DBG_GEN|DBG_CDI)(errfp, "Loading wwwwarp parameters: %s\n",
3099 				trypath);
3100 	fp = fopen(trypath, "r");
3101 
3102 	if (fp == NULL) {
3103 		DBGPRN(DBG_GEN|DBG_CDI)(errfp, "    Cannot open %s\n",
3104 					trypath);
3105 
3106 		/* Try the system-wide wwwwarp.cfg, if it exists */
3107 		(void) sprintf(trypath, SYS_DSCFG_PATH,
3108 				app_data.libdir, WWWWARP_CFG);
3109 		DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3110 					"Loading wwwwarp parameters: %s\n",
3111 					trypath);
3112 		fp = fopen(trypath, "r");
3113 	}
3114 
3115 	if (fp == NULL) {
3116 		DBGPRN(DBG_GEN|DBG_CDI)(errfp, "    Cannot open %s\n",
3117 					trypath);
3118 		return;
3119 	}
3120 
3121 #endif	/* SYNCHRONOUS */
3122 
3123 	newmenu = FALSE;
3124 	wp1 = wp2 = NULL;
3125 	name = NULL;
3126 
3127 	/* Read in common parameters */
3128 	while ((buf = cdinfo_fgetline(fp)) != NULL) {
3129 		if (strcmp(buf, ".") == 0) {
3130 			/* Done */
3131 			MEM_FREE(buf);
3132 			break;
3133 		}
3134 
3135 		p = q = cdinfo_skip_whitespace(buf);
3136 
3137 		if (*p == '#' || *p == '!' || *p == '\0') {
3138 			/* Skip comments and blank lines */
3139 			MEM_FREE(buf);
3140 			continue;
3141 		}
3142 
3143 		if (*p == '{' || *p == '}') {
3144 			/* Do nothing.  These are only visual aids for
3145 			 * the user.
3146 			 */
3147 			MEM_FREE(buf);
3148 			continue;
3149 		}
3150 
3151 		if (strncmp(p, "Menu", 4) == 0) {
3152 			/* Start of new menu */
3153 			p = cdinfo_skip_whitespace(p + 4);
3154 			if (*p == '\0') {
3155 				DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3156 					"    Error in %s file:\nline: %s\n",
3157 					WWWWARP_CFG, buf);
3158 				MEM_FREE(buf);
3159 				break;
3160 			}
3161 			p = cdinfo_skip_whitespace(p);
3162 			q = cdinfo_skip_nowhitespace(p);
3163 			*q = '\0';
3164 
3165 			if (!util_newstr(&name, p)) {
3166 				CDINFO_FATAL(app_data.str_nomemory);
3167 				return;
3168 			}
3169 
3170 			newmenu = TRUE;
3171 		}
3172 		else {
3173 			/* Menu entry */
3174 			wp = (w_ent_t *)(void *) MEM_ALLOC(
3175 				"w_ent_t", sizeof(w_ent_t)
3176 			);
3177 			if (wp == NULL) {
3178 				CDINFO_FATAL(app_data.str_nomemory);
3179 				return;
3180 			}
3181 			(void) memset(wp, 0, sizeof(w_ent_t));
3182 
3183 			if (newmenu) {
3184 				if (cdinfo_dbp->wwwwarp_list == NULL)
3185 					cdinfo_dbp->wwwwarp_list = wp1 = wp;
3186 				else {
3187 					wp1->nextmenu = wp;
3188 					wp1 = wp;
3189 				}
3190 			}
3191 			else if (wp2 == NULL) {
3192 				DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3193 					"    Error in %s file:\nline: %s\n",
3194 					WWWWARP_CFG, buf);
3195 				MEM_FREE(buf);
3196 				break;
3197 			}
3198 			else
3199 				wp2->nextent = wp;
3200 
3201 			wp2 = wp;
3202 
3203 			if (!util_newstr(&wp->name, name)) {
3204 				CDINFO_FATAL(app_data.str_nomemory);
3205 				return;
3206 			}
3207 
3208 			/* Description */
3209 			if (*p != '"') {
3210 				DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3211 					"    Error in %s file:\nline: %s\n",
3212 					WWWWARP_CFG, buf);
3213 				wp->type = WTYPE_NULL;
3214 				MEM_FREE(buf);
3215 				break;
3216 			}
3217 			p++;
3218 
3219 			if ((q = strchr(p, '"')) == NULL) {
3220 				DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3221 					"    Error in %s file:\nline: %s\n",
3222 					WWWWARP_CFG, buf);
3223 				wp->type = WTYPE_NULL;
3224 				MEM_FREE(buf);
3225 				break;
3226 			}
3227 			*q = '\0';
3228 
3229 			if (!util_newstr(&wp->desc, p)) {
3230 				CDINFO_FATAL(app_data.str_nomemory);
3231 				return;
3232 			}
3233 
3234 			p = cdinfo_skip_whitespace(q+1);
3235 
3236 			/* Shortcut key */
3237 			if (strncmp(p, "f.", 2) != 0) {
3238 				char	*cp;
3239 
3240 				q = cdinfo_skip_nowhitespace(p);
3241 				if (*q == '\0') {
3242 					DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3243 						"    Error in %s file:\n"
3244 						"line: %s\n",
3245 						WWWWARP_CFG, buf);
3246 					MEM_FREE(buf);
3247 					break;
3248 				}
3249 				*q = '\0';
3250 
3251 				if ((cp = strchr(p, '-')) == NULL) {
3252 					DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3253 						"    Error in %s file:\n"
3254 						"line: %s\n",
3255 						WWWWARP_CFG, buf);
3256 					MEM_FREE(buf);
3257 					break;
3258 				}
3259 				*cp = '\0';
3260 
3261 				if (!util_newstr(&wp->modifier, p)) {
3262 					CDINFO_FATAL(app_data.str_nomemory);
3263 					return;
3264 				}
3265 				if (!util_newstr(&wp->keyname, cp+1)) {
3266 					CDINFO_FATAL(app_data.str_nomemory);
3267 					return;
3268 				}
3269 
3270 				p = cdinfo_skip_whitespace(q+1);
3271 			}
3272 			else {
3273 				wp->modifier = NULL;
3274 				wp->keyname = NULL;
3275 			}
3276 
3277 			/* Type */
3278 			if (strncmp(p, "f.title", 7) == 0) {
3279 				q = p + 7;
3280 				wp->type = WTYPE_TITLE;
3281 			}
3282 			else if (strncmp(p, "f.separator2", 12) == 0) {
3283 				q = p + 12;
3284 				wp->type = WTYPE_SEP2;
3285 			}
3286 			else if (strncmp(p, "f.separator", 11) == 0) {
3287 				q = p + 11;
3288 				wp->type = WTYPE_SEP;
3289 			}
3290 			else if (strncmp(p, "f.about", 7) == 0) {
3291 				q = p + 7;
3292 				wp->type = WTYPE_ABOUT;
3293 			}
3294 			else if (strncmp(p, "f.help", 6) == 0) {
3295 				q = p + 6;
3296 				wp->type = WTYPE_HELP;
3297 			}
3298 			else if (strncmp(p, "f.motd", 6) == 0) {
3299 				q = p + 6;
3300 				wp->type = WTYPE_MOTD;
3301 			}
3302 			else if (strncmp(p, "f.goto", 6) == 0) {
3303 				q = p + 6;
3304 				wp->type = WTYPE_GOTO;
3305 			}
3306 			else if (strncmp(p, "f.menu", 6) == 0) {
3307 				q = p + 6;
3308 				wp->type = WTYPE_SUBMENU;
3309 			}
3310 			else if (strncmp(p, "f.infoBrowser", 13) == 0) {
3311 				q = p + 13;
3312 				wp->type = WTYPE_BROWSER;
3313 			}
3314 			else if (strncmp(p, "f.discog", 8) == 0) {
3315 				q = p + 8;
3316 				wp->type = WTYPE_DISCOG;
3317 			}
3318 			else if (strncmp(p, "f.generalSites", 14) == 0) {
3319 				q = p + 14;
3320 				wp->type = WTYPE_GEN;
3321 			}
3322 			else if (strncmp(p, "f.albumSites", 12) == 0) {
3323 				q = p + 12;
3324 				wp->type = WTYPE_ALBUM;
3325 			}
3326 			else if (strncmp(p, "f.submitURL", 11) == 0) {
3327 				q = p + 11;
3328 				wp->type = WTYPE_SUBMITURL;
3329 			}
3330 			else {
3331 				DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3332 					"    Error in %s file:\nline: %s\n",
3333 					WWWWARP_CFG, buf);
3334 				wp->type = WTYPE_NULL;
3335 				MEM_FREE(buf);
3336 				break;
3337 			}
3338 
3339 			p = cdinfo_skip_whitespace(q);
3340 
3341 			/* Args */
3342 			switch(wp->type) {
3343 			case WTYPE_SUBMENU:
3344 				q = cdinfo_skip_nowhitespace(p);
3345 				*q = '\0';
3346 
3347 				if (!util_newstr(&wp->arg, p)) {
3348 					CDINFO_FATAL(app_data.str_nomemory);
3349 					return;
3350 				}
3351 				break;
3352 
3353 			case WTYPE_GOTO:
3354 			case WTYPE_DISCOG:
3355 				if (*p == '"')
3356 					p++;
3357 
3358 				q = cdinfo_skip_nowhitespace(p);
3359 				if (*(q-1) == '"')
3360 					*(q-1) = '\0';
3361 				else
3362 					*q = '\0';
3363 
3364 				if (!util_newstr(&wp->arg, p)) {
3365 					CDINFO_FATAL(app_data.str_nomemory);
3366 					return;
3367 				}
3368 
3369 				/* Scan URL and record its attributes */
3370 				cdinfo_scan_url_attrib(wp->arg, &wp->attrib);
3371 
3372 				/* Save menu pointer for later use */
3373 				if (wp->type == WTYPE_DISCOG)
3374 					cdinfo_discog = wp;
3375 				else if (strcmp(wp->name,
3376 						"searchMenu") == 0 &&
3377 					 util_strncasecmp(wp->desc,
3378 							  "CDDB", 4) == 0) {
3379 					cdinfo_scddb = wp;
3380 				}
3381 
3382 				break;
3383 
3384 			case WTYPE_BROWSER:
3385 			case WTYPE_ALBUM:
3386 				wp->attrib.acnt++;
3387 				wp->attrib.dcnt++;
3388 				wp->attrib.tcnt++;
3389 				wp->attrib.icnt++;
3390 				wp->attrib.ccnt++;
3391 				break;
3392 
3393 			default:
3394 				break;
3395 			}
3396 
3397 			newmenu = FALSE;
3398 		}
3399 
3400 		MEM_FREE(buf);
3401 	}
3402 	if (name != NULL)
3403 		MEM_FREE(name);
3404 
3405 	(void) fclose(fp);
3406 
3407 	/* Bind submenus */
3408 	for (wp = cdinfo_dbp->wwwwarp_list; wp != NULL; wp = wp->nextmenu) {
3409 	    for (wp2 = wp; wp2 != NULL; wp2 = wp2->nextent) {
3410 		if (wp2->type != WTYPE_SUBMENU)
3411 			continue;
3412 
3413 		for (wp3 = cdinfo_dbp->wwwwarp_list; wp3 != NULL;
3414 		     wp3 = wp3->nextmenu) {
3415 			if (strcmp(wp2->arg, wp3->name) == 0) {
3416 				wp2->submenu = wp3;
3417 				break;
3418 			}
3419 		}
3420 		if (wp3 == NULL) {
3421 			DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3422 				"    Error in %s file: %s not found\n",
3423 				WWWWARP_CFG, wp2->arg);
3424 		}
3425 	    }
3426 	}
3427 
3428 	/* Check for circular menu links */
3429 	for (wp = cdinfo_dbp->wwwwarp_list; wp != NULL; wp = wp->nextmenu) {
3430 		if (!cdinfo_wwwmenu_chk(wp, TRUE)) {
3431 			DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3432 				"\n    Error in %s file: "
3433 				"circular menu link.\n",
3434 				WWWWARP_CFG);
3435 		}
3436 	}
3437 
3438 #ifndef SYNCHRONOUS
3439 	ret = util_waitchild(cpid, app_data.srv_timeout + 5,
3440 			     NULL, 0, FALSE, &wstat);
3441 	child_pid = 0;
3442 	if (ret < 0) {
3443 		DBGPRN(DBG_GEN|DBG_CDI)(errfp, "cdinfo_wwwwarp_parmload: "
3444 				"waitpid failed (errno=%d)\n",
3445 				errno);
3446 	}
3447 	else if (WIFEXITED(wstat)) {
3448 		if (WEXITSTATUS(wstat) != 0) {
3449 			DBGPRN(DBG_GEN|DBG_CDI)(errfp,
3450 					"cdinfo_wwwwarp_parmload: "
3451 					"child exited (status=%d)\n",
3452 					WEXITSTATUS(wstat));
3453 		}
3454 	}
3455 	else if (WIFSIGNALED(wstat)) {
3456 		DBGPRN(DBG_GEN|DBG_CDI)(errfp, "cdinfo_wwwwarp_parmload: "
3457 				"child killed (signal=%d)\n",
3458 				WTERMSIG(wstat));
3459 	}
3460 #endif
3461 }
3462 
3463 
3464 /*
3465  * cdinfo_wwwwarp_ckkey
3466  *	Given a shortcut key, check to see if it is unique amongst
3467  *	existing wwwwarp menu shortcuts.
3468  *
3469  * Args:
3470  *	keyname - The key name to check
3471  *
3472  * Return:
3473  *	TRUE  - is unique
3474  *	FALSE - not unique
3475  */
3476 bool_t
cdinfo_wwwwarp_ckkey(char * keyname)3477 cdinfo_wwwwarp_ckkey(char *keyname)
3478 {
3479 	w_ent_t	*p,
3480 		*q;
3481 
3482 	if (keyname == NULL)
3483 		return FALSE;
3484 
3485 	for (p = cdinfo_dbp->wwwwarp_list; p != NULL; p = p->nextmenu) {
3486 		for (q = p; q != NULL; q = q->nextent) {
3487 			if (q->keyname != NULL &&
3488 			    util_strcasecmp(q->keyname, keyname) == 0)
3489 				return FALSE;
3490 		}
3491 	}
3492 	return TRUE;
3493 }
3494 
3495 
3496 /*
3497  * cdinfo_curfileupd
3498  *	Update the current disc info file.
3499  *
3500  * Args:
3501  *	None.
3502  *
3503  * Return:
3504  *	Nothing
3505  */
3506 void
cdinfo_curfileupd(void)3507 cdinfo_curfileupd(void)
3508 {
3509 #ifndef __VMS
3510 	FILE		*fp;
3511 	struct stat	stbuf;
3512 	char		*artist,
3513 			*title,
3514 			*ttitle,
3515 			str[FILE_PATH_SZ * 3];
3516 	static char	prev[FILE_PATH_SZ * 3],
3517 			prev_artist[STR_BUF_SZ],
3518 			prev_title[STR_BUF_SZ],
3519 			prev_ttitle[STR_BUF_SZ];
3520 	curstat_t	*s;
3521 	bool_t		playing,
3522 			changed;
3523 
3524 	if (!app_data.write_curfile ||			/* disabled */
3525 	    strncmp(app_data.device, "(sim", 4) == 0)	/* demo */
3526 		return;
3527 
3528 	if (cdinfo_clinfo->curstat_addr == NULL)
3529 		return;
3530 
3531 	s = cdinfo_clinfo->curstat_addr();
3532 
3533 	if (!s->devlocked)
3534 		/* Don't write curfile if we don't own the device */
3535 		return;
3536 
3537 	playing = FALSE;
3538 	artist = title = ttitle = NULL;
3539 
3540 	if (s->mode != MOD_NODISC) {
3541 		char	modestr[24];
3542 
3543 		artist = cdinfo_dbp->disc.artist;
3544 		title = cdinfo_dbp->disc.title;
3545 
3546 		switch (s->mode) {
3547 		case MOD_PLAY:
3548 		case MOD_PAUSE:
3549 		case MOD_SAMPLE:
3550 			playing = TRUE;
3551 
3552 			(void) sprintf(modestr, "Playing track %d",
3553 				       s->cur_trk);
3554 
3555 			if (s->cur_trk > 0) {
3556 				int	n;
3557 
3558 				n = (int) s->cur_trk - 1;
3559 
3560 				if (s->trkinfo[n].trkno != s->cur_trk) {
3561 					for (n = 0; n < MAXTRACK; n++) {
3562 						if (s->trkinfo[n].trkno ==
3563 						    s->cur_trk)
3564 							break;
3565 					}
3566 				}
3567 
3568 				ttitle = cdinfo_dbp->track[n].title;
3569 			}
3570 			break;
3571 
3572 		case MOD_STOP:
3573 			(void) strcpy(modestr, "Stopped");
3574 			break;
3575 
3576 		default:
3577 			modestr[0] = '\0';
3578 			break;
3579 		}
3580 
3581 		(void) sprintf(str,
3582 			       "%s\t\t%s\n%s\t\t%s\n%s\t\t%s\n",
3583 			       "Device:", s->curdev == NULL ? "-" : s->curdev,
3584 			       "Genre:",
3585 			       cdinfo_genre_name(cdinfo_dbp->disc.genre),
3586 			       "Status:", modestr);
3587 	}
3588 	else {
3589 		(void) sprintf(str,
3590 			       "%s\t\t%s\n%s\t\t%s\n%s\t\t%s\n",
3591 			       "Device:", s->curdev == NULL ? "-" : s->curdev,
3592 			       "Genre:", "-",
3593 			       "Status:", "No_Disc");
3594 	}
3595 
3596 	changed = FALSE;
3597 
3598 	if (playing) {
3599 		if (ttitle == NULL && prev_ttitle[0] != '\0')
3600 			changed = TRUE;
3601 		else if (ttitle != NULL &&
3602 			 strncmp(ttitle, prev_ttitle, STR_BUF_SZ - 1) != 0)
3603 			changed = TRUE;
3604 	}
3605 
3606 	if (s->mode != MOD_NODISC) {
3607 		if (artist == NULL && prev_artist[0] != '\0')
3608 			changed = TRUE;
3609 		else if (title == NULL && prev_title[0] != '\0')
3610 			changed = TRUE;
3611 		else if (artist != NULL &&
3612 			 strncmp(artist, prev_artist, STR_BUF_SZ - 1) != 0)
3613 			changed = TRUE;
3614 		else if (title != NULL &&
3615 			 strncmp(title, prev_title, STR_BUF_SZ - 1) != 0)
3616 			changed = TRUE;
3617 	}
3618 
3619 	if (strcmp(str, prev) != 0)
3620 		changed = TRUE;
3621 
3622 	if (!changed)
3623 		return;
3624 
3625 	/* Write to file */
3626 	if (curfile[0] == '\0') {
3627 		if (stat(app_data.device, &stbuf) < 0) {
3628 			DBGPRN(DBG_CDI)(errfp,
3629 				"\nCannot stat %s\n", app_data.device);
3630 			return;
3631 		}
3632 
3633 		(void) sprintf(curfile, "%s/curr.%x",
3634 			       TEMP_DIR, (int) stbuf.st_rdev);
3635 	}
3636 
3637 	/* Remove original file */
3638 	if (UNLINK(curfile) < 0 && errno != ENOENT) {
3639 		DBGPRN(DBG_CDI)(errfp, "\nCannot unlink old %s\n", curfile);
3640 		return;
3641 	}
3642 
3643 	/* Write new file */
3644 	if ((fp = fopen(curfile, "w")) == NULL) {
3645 		DBGPRN(DBG_CDI)(errfp,
3646 			"\nCannot open %s for writing\n", curfile);
3647 		return;
3648 	}
3649 
3650 	DBGPRN(DBG_CDI)(errfp,
3651 		"\nWriting current disc info file: %s\n", curfile);
3652 
3653 	(void) fprintf(fp, "#\n# Xmcd current CD information\n#\n%s", str);
3654 
3655 	if (s->mode != MOD_NODISC) {
3656 		(void) fprintf(fp, "Disc:\t\t%s / %s\n",
3657 			       artist == NULL ?
3658 			       "<unknown artist>" : artist,
3659 			       title == NULL ?
3660 			       "<unknown disc title>" : title);
3661 	}
3662 	else {
3663 		(void) fprintf(fp, "Disc:\t\t-\n");
3664 	}
3665 
3666 	if (playing) {
3667 		(void) fprintf(fp, "Track:\t\t%s\n",
3668 			       ttitle == NULL ?
3669 			       "<unknown track title>" : ttitle);
3670 	}
3671 	else {
3672 		(void) fprintf(fp, "Track:\t\t-\n");
3673 	}
3674 
3675 	(void) fclose(fp);
3676 	(void) chmod(curfile, 0644);
3677 
3678 	if (ttitle == NULL)
3679 		prev_ttitle[0] = '\0';
3680 	else {
3681 		(void) strncpy(prev_ttitle, ttitle, STR_BUF_SZ - 1);
3682 		prev_ttitle[STR_BUF_SZ - 1] = '\0';
3683 	}
3684 
3685 	if (artist == NULL)
3686 		prev_artist[0] = '\0';
3687 	else {
3688 		(void) strncpy(prev_artist, artist, STR_BUF_SZ - 1);
3689 		prev_artist[STR_BUF_SZ - 1] = '\0';
3690 	}
3691 
3692 	if (title == NULL)
3693 		prev_title[0] = '\0';
3694 	else {
3695 		(void) strncpy(prev_title, title, STR_BUF_SZ - 1);
3696 		prev_title[STR_BUF_SZ - 1] = '\0';
3697 	}
3698 
3699 	(void) strcpy(prev, str);
3700 #endif	/* __VMS */
3701 }
3702 
3703 
3704 /*
3705  * cdinfo_issync
3706  *	Return a boolean value indicating whether we are running in
3707  *	synchronous mode.
3708  *
3709  * Args:
3710  *	None.
3711  *
3712  * Return:
3713  *	TRUE - synchronous mode
3714  *	FALSE - asynchronous mode
3715  */
3716 bool_t
cdinfo_issync(void)3717 cdinfo_issync(void)
3718 {
3719 #ifdef SYNCHRONOUS
3720 	return TRUE;
3721 #else
3722 	return FALSE;
3723 #endif
3724 }
3725 
3726 
3727 /*
3728  * cdinfo_dump_incore
3729  *	Displays the contents of the cdinfo_incore_t structure.
3730  *
3731  * Args:
3732  *	s - Pointer to the curstat_t structure.
3733  *
3734  * Returns:
3735  *	Nothing.
3736  */
3737 void
cdinfo_dump_incore(curstat_t * s)3738 cdinfo_dump_incore(curstat_t *s)
3739 {
3740 	int		i,
3741 			j;
3742 
3743 	if (cdinfo_dbp == NULL)
3744 		return;
3745 
3746 	(void) fprintf(errfp,
3747 		       "\nDumping the cdinfo_incore_t structure at 0x%lx:\n",
3748 		       (unsigned long) cdinfo_dbp);
3749 	(void) fprintf(errfp, "ctrl_ver=%s\n",
3750 			cdinfo_dbp->ctrl_ver == NULL ?
3751 				"NULL" : cdinfo_dbp->ctrl_ver);
3752 	(void) fprintf(errfp, "discid=%08x flags=0x%x\n",
3753 		       cdinfo_dbp->discid, cdinfo_dbp->flags);
3754 	(void) fprintf(errfp, "disc: ----------------------\n");
3755 	(void) fprintf(errfp, "  revision=%s revtag=%s region=%s lang=%s\n",
3756 		       cdinfo_dbp->disc.revision == NULL ?
3757 				"NULL" : cdinfo_dbp->disc.revision,
3758 		       cdinfo_dbp->disc.revtag == NULL ?
3759 				"NULL" : cdinfo_dbp->disc.revtag,
3760 		       cdinfo_dbp->disc.region == NULL ?
3761 				"NULL" : cdinfo_dbp->disc.region,
3762 		       cdinfo_dbp->disc.lang == NULL ?
3763 				"NULL" : cdinfo_dbp->disc.lang);
3764 	(void) fprintf(errfp, "  compilation=%d dnum=%s tnum=%s\n",
3765 		       (int) cdinfo_dbp->disc.compilation,
3766 		       cdinfo_dbp->disc.dnum == NULL ?
3767 				"NULL" : cdinfo_dbp->disc.dnum,
3768 		       cdinfo_dbp->disc.tnum == NULL ?
3769 				"NULL" : cdinfo_dbp->disc.tnum);
3770 	(void) fprintf(errfp,
3771 		       "  fullname: dispname=%s "
3772 		       "lastname=%s firstname=%s the=%s\n",
3773 		       cdinfo_dbp->disc.artistfname.dispname == NULL ?
3774 				"NULL" : cdinfo_dbp->disc.artistfname.dispname,
3775 		       cdinfo_dbp->disc.artistfname.lastname == NULL ?
3776 				"NULL" : cdinfo_dbp->disc.artistfname.lastname,
3777 		       cdinfo_dbp->disc.artistfname.firstname == NULL ?
3778 				"NULL" : cdinfo_dbp->disc.artistfname.firstname,
3779 		       cdinfo_dbp->disc.artistfname.the == NULL ?
3780 				"NULL" : cdinfo_dbp->disc.artistfname.the);
3781 	(void) fprintf(errfp,
3782 		       "  artist=%s title=%s\n  sorttitle=%s title_the=%s\n",
3783 		       cdinfo_dbp->disc.artist == NULL ?
3784 				"NULL" : cdinfo_dbp->disc.artist,
3785 		       cdinfo_dbp->disc.title == NULL ?
3786 				"NULL" : cdinfo_dbp->disc.title,
3787 		       cdinfo_dbp->disc.sorttitle == NULL ?
3788 				"NULL" : cdinfo_dbp->disc.sorttitle,
3789 		       cdinfo_dbp->disc.title_the == NULL ?
3790 				"NULL" : cdinfo_dbp->disc.title_the);
3791 	(void) fprintf(errfp, "  year=%s label=%s genre=%s genre2=%s\n",
3792 		       cdinfo_dbp->disc.year == NULL ?
3793 				"NULL" : cdinfo_dbp->disc.year,
3794 		       cdinfo_dbp->disc.label == NULL ?
3795 				"NULL" : cdinfo_dbp->disc.label,
3796 		       cdinfo_dbp->disc.genre == NULL ?
3797 				"NULL" : cdinfo_dbp->disc.genre,
3798 		       cdinfo_dbp->disc.genre2 == NULL ?
3799 				"NULL" : cdinfo_dbp->disc.genre2);
3800 	(void) fprintf(errfp, "  mediaid=%s\n  muiid=%s titleuid=%s\n",
3801 		       cdinfo_dbp->disc.mediaid == NULL ?
3802 				"NULL" : cdinfo_dbp->disc.mediaid,
3803 		       cdinfo_dbp->disc.muiid == NULL ?
3804 				"NULL" : cdinfo_dbp->disc.muiid,
3805 		       cdinfo_dbp->disc.titleuid == NULL ?
3806 				"NULL" : cdinfo_dbp->disc.titleuid);
3807 	(void) fprintf(errfp, "  certifier=%s\n",
3808 		       cdinfo_dbp->disc.certifier == NULL ?
3809 				"NULL" : cdinfo_dbp->disc.certifier);
3810 	if (cdinfo_dbp->disc.notes == NULL)
3811 		(void) fprintf(errfp, "  notes=NULL\n");
3812 	else
3813 		(void) fprintf(errfp, "  notes=\n%s\n", cdinfo_dbp->disc.notes);
3814 
3815 	if (cdinfo_dbp->disc.credit_list == NULL)
3816 		(void) fprintf(errfp, "  credit_list=NULL\n");
3817 	else {
3818 		cdinfo_credit_t	*p;
3819 
3820 		(void) fprintf(errfp, "credit_list=\n");
3821 		for (i = 0, p = cdinfo_dbp->disc.credit_list; p != NULL;
3822 		     p = p->next, i++) {
3823 			(void) fprintf(errfp, "  credit[%d]\n", i);
3824 			(void) fprintf(errfp, "    role=%s name=%s\n",
3825 				    p->crinfo.role == NULL ?
3826 					"NULL" : p->crinfo.role->name,
3827 				    p->crinfo.name == NULL ?
3828 					"NULL" : p->crinfo.name);
3829 			(void) fprintf(errfp,
3830 			       "    fullname: dispname=%s lastname=%s "
3831 			       "firstname=%s the=%s\n",
3832 			       p->crinfo.fullname.dispname == NULL ?
3833 				    "NULL" :
3834 				    p->crinfo.fullname.dispname,
3835 			       p->crinfo.fullname.lastname == NULL ?
3836 				    "NULL" :
3837 				    p->crinfo.fullname.lastname,
3838 			       p->crinfo.fullname.firstname == NULL ?
3839 				    "NULL" :
3840 				    p->crinfo.fullname.firstname,
3841 			       p->crinfo.fullname.the == NULL ?
3842 				    "NULL" :
3843 				    p->crinfo.fullname.the);
3844 			if (p->notes == NULL)
3845 				(void) fprintf(errfp,
3846 				    "    notes=NULL\n");
3847 			else
3848 				(void) fprintf(errfp,
3849 				    "    notes=\n%s\n", p->notes);
3850 		}
3851 	}
3852 
3853 	if (cdinfo_dbp->disc.segment_list == NULL)
3854 		(void) fprintf(errfp, "  segment_list=NULL\n");
3855 	else {
3856 		cdinfo_segment_t	*q;
3857 
3858 		(void) fprintf(errfp, "segment_list=\n");
3859 		for (i = 0, q = cdinfo_dbp->disc.segment_list; q != NULL;
3860 		     q = q->next, i++) {
3861 			(void) fprintf(errfp, "  segment[%d]\n", i);
3862 			(void) fprintf(errfp, "    name=%s start=%s/%s ",
3863 				    q->name == NULL ? "NULL" : q->name,
3864 				    q->start_track == NULL ?
3865 					    "?" : q->start_track,
3866 				    q->start_frame == NULL ?
3867 					    "?" : q->start_frame);
3868 			(void) fprintf(errfp, "end=%s/%s\n",
3869 				    q->end_track == NULL ?
3870 					    "?" : q->end_track,
3871 				    q->end_frame == NULL ?
3872 					    "?" : q->end_frame);
3873 			if (q->notes == NULL)
3874 				(void) fprintf(errfp, "    notes=NULL\n");
3875 			else
3876 				(void) fprintf(errfp,
3877 					       "    notes=\n%s\n", q->notes);
3878 
3879 			if (q->credit_list == NULL)
3880 			    (void) fprintf(errfp, "    credit_list=NULL\n");
3881 			else {
3882 			    cdinfo_credit_t	*p;
3883 
3884 			    for (j = 0, p = q->credit_list; p != NULL;
3885 				 p = p->next, j++) {
3886 				(void) fprintf(errfp, "    credit[%d]\n", j);
3887 				(void) fprintf(errfp,
3888 					    "      role=%s name=%s\n",
3889 					    p->crinfo.role == NULL ?
3890 						"NULL" : p->crinfo.role->name,
3891 					    p->crinfo.name == NULL ?
3892 						"NULL" : p->crinfo.name);
3893 				(void) fprintf(errfp,
3894 				       "      fullname: dispname=%s "
3895 				       "lastname=%s firstname=%s the=%s\n",
3896 				       p->crinfo.fullname.dispname == NULL ?
3897 					    "NULL" :
3898 					    p->crinfo.fullname.dispname,
3899 				       p->crinfo.fullname.lastname == NULL ?
3900 					    "NULL" :
3901 					    p->crinfo.fullname.lastname,
3902 				       p->crinfo.fullname.firstname == NULL ?
3903 					    "NULL" :
3904 					    p->crinfo.fullname.firstname,
3905 				       p->crinfo.fullname.the == NULL ?
3906 					    "NULL" :
3907 					    p->crinfo.fullname.the);
3908 					if (p->notes == NULL)
3909 					    (void) fprintf(errfp,
3910 						    "      notes=NULL\n");
3911 					else
3912 					    (void) fprintf(errfp,
3913 						    "      notes=\n%s\n",
3914 						    p->notes);
3915 			    }
3916 			}
3917 		}
3918 	}
3919 
3920 	for (i = 0; i < (int) s->tot_trks; i++) {
3921 		(void) fprintf(errfp, "track[%d] ----------------------\n", i);
3922 
3923 		(void) fprintf(errfp,
3924 			       "  fullname: dispname=%s lastname=%s "
3925 			       "firstname=%s the=%s\n",
3926 			   cdinfo_dbp->track[i].artistfname.dispname == NULL ?
3927 				    "NULL" :
3928 				    cdinfo_dbp->track[i].artistfname.dispname,
3929 			   cdinfo_dbp->track[i].artistfname.lastname == NULL ?
3930 				    "NULL" :
3931 				    cdinfo_dbp->track[i].artistfname.lastname,
3932 			   cdinfo_dbp->track[i].artistfname.firstname == NULL ?
3933 				    "NULL" :
3934 				    cdinfo_dbp->track[i].artistfname.firstname,
3935 			   cdinfo_dbp->track[i].artistfname.the == NULL ?
3936 				    "NULL" :
3937 				    cdinfo_dbp->track[i].artistfname.the);
3938 		(void) fprintf(errfp, "  artist=%s title=%s\n",
3939 			       cdinfo_dbp->track[i].artist == NULL ?
3940 					"NULL" : cdinfo_dbp->track[i].artist,
3941 			       cdinfo_dbp->track[i].title == NULL ?
3942 					"NULL" : cdinfo_dbp->track[i].title);
3943 		(void) fprintf(errfp, "  sorttitle=%s title_the=%s\n",
3944 			       cdinfo_dbp->track[i].sorttitle == NULL ?
3945 					"NULL" :
3946 					cdinfo_dbp->track[i].sorttitle,
3947 			       cdinfo_dbp->track[i].title_the == NULL ?
3948 					"NULL" :
3949 					cdinfo_dbp->track[i].title_the);
3950 		(void) fprintf(errfp,
3951 			       "  year=%s label=%s genre=%s genre2=%s\n",
3952 			       cdinfo_dbp->track[i].year == NULL ?
3953 					"NULL" : cdinfo_dbp->track[i].year,
3954 			       cdinfo_dbp->track[i].label == NULL ?
3955 					"NULL" : cdinfo_dbp->track[i].label,
3956 			       cdinfo_dbp->track[i].genre == NULL ?
3957 					"NULL" : cdinfo_dbp->track[i].genre,
3958 			       cdinfo_dbp->track[i].genre2 == NULL ?
3959 					"NULL" :
3960 					cdinfo_dbp->track[i].genre2);
3961 		(void) fprintf(errfp, "  isrc=%s\n",
3962 			       cdinfo_dbp->track[i].isrc == NULL ?
3963 					"NULL" : cdinfo_dbp->track[i].isrc);
3964 		if (cdinfo_dbp->track[i].notes == NULL)
3965 			(void) fprintf(errfp, "  notes=NULL\n");
3966 		else
3967 			(void) fprintf(errfp, "  notes=\n%s\n",
3968 				       cdinfo_dbp->track[i].notes);
3969 
3970 		if (cdinfo_dbp->track[i].credit_list == NULL)
3971 			(void) fprintf(errfp, "  credit_list=NULL\n");
3972 		else {
3973 			cdinfo_credit_t	*p;
3974 
3975 			(void) fprintf(errfp, "  credit_list=\n");
3976 			for (j = 0, p = cdinfo_dbp->track[j].credit_list;
3977 			     p != NULL; p = p->next, j++) {
3978 				(void) fprintf(errfp, "  credit[%d]\n", j);
3979 				(void) fprintf(errfp, "    role=%s name=%s\n",
3980 					    p->crinfo.role == NULL ?
3981 						"NULL" : p->crinfo.role->name,
3982 					    p->crinfo.name == NULL ?
3983 						"NULL" : p->crinfo.name);
3984 				(void) fprintf(errfp,
3985 				       "    fullname: dispname=%s lastname=%s "
3986 				       "firstname=%s the=%s\n",
3987 				       p->crinfo.fullname.dispname == NULL ?
3988 					    "NULL" :
3989 					    p->crinfo.fullname.dispname,
3990 				       p->crinfo.fullname.lastname == NULL ?
3991 					    "NULL" :
3992 					    p->crinfo.fullname.lastname,
3993 				       p->crinfo.fullname.firstname == NULL ?
3994 					    "NULL" :
3995 					    p->crinfo.fullname.firstname,
3996 				       p->crinfo.fullname.the == NULL ?
3997 					    "NULL" :
3998 					    p->crinfo.fullname.the);
3999 				if (p->notes == NULL)
4000 					(void) fprintf(errfp,
4001 					    "    notes=NULL\n");
4002 				else
4003 					(void) fprintf(errfp,
4004 					    "    notes=\n%s\n", p->notes);
4005 			}
4006 		}
4007 	}
4008 
4009 	(void) fprintf(errfp, "----------------------\n");
4010 
4011 	if (cdinfo_dbp->genrelist == NULL)
4012 		(void) fprintf(errfp, "genrelist=NULL\n");
4013 	else {
4014 		cdinfo_genre_t	*p,
4015 				*q;
4016 
4017 		(void) fprintf(errfp, "genrelist:\n");
4018 		for (p = cdinfo_dbp->genrelist, i = 0; p != NULL;
4019 		     p = p->next, i++) {
4020 		    (void) fprintf(errfp, "genre[%d]: %s (%s)\n",
4021 		    		   i, p->name, p->id);
4022 
4023 		    for (q = p->child, j = 0; q != NULL; q = q->next, j++) {
4024 			(void) fprintf(errfp, "  subgenre[%d]: %s (%s)\n",
4025 					j, q->name, q->id);
4026 		    }
4027 		}
4028 	}
4029 
4030 	if (cdinfo_dbp->regionlist == NULL)
4031 		(void) fprintf(errfp, "regionlist=NULL\n");
4032 	else {
4033 		cdinfo_region_t	*p;
4034 
4035 		(void) fprintf(errfp, "regionlist:\n");
4036 		for (p = cdinfo_dbp->regionlist, i = 0; p != NULL;
4037 		     p = p->next, i++) {
4038 			(void) fprintf(errfp, "region[%d]: %s (%s)\n",
4039 				       i, p->name, p->id);
4040 		}
4041 	}
4042 
4043 	if (cdinfo_dbp->langlist == NULL)
4044 		(void) fprintf(errfp, "langlist=NULL\n");
4045 	else {
4046 		cdinfo_lang_t	*p;
4047 
4048 		(void) fprintf(errfp, "langlist:\n");
4049 		for (p = cdinfo_dbp->langlist, i = 0; p != NULL;
4050 		     p = p->next, i++) {
4051 			(void) fprintf(errfp, "lang[%d]: %s (%s)\n",
4052 				       i, p->name, p->id);
4053 		}
4054 	}
4055 
4056 	if (cdinfo_dbp->rolelist == NULL)
4057 		(void) fprintf(errfp, "rolelist=NULL\n");
4058 	else {
4059 		cdinfo_role_t	*p,
4060 				*q;
4061 
4062 		(void) fprintf(errfp, "rolelist:\n");
4063 		for (p = cdinfo_dbp->rolelist, i = 0; p != NULL;
4064 		     p = p->next, i++) {
4065 			(void) fprintf(errfp, "role[%d]: %s (%s)\n",
4066 				       i, p->name, p->id);
4067 			for (q = p->child, j = 0; q != NULL; q = q->next, j++){
4068 				(void) fprintf(errfp,
4069 					       "  subrole[%d]: %s (%s)\n",
4070 					       j, q->name, q->id);
4071 			}
4072 		}
4073 	}
4074 
4075 	if (cdinfo_dbp->matchlist == NULL)
4076 		(void) fprintf(errfp, "matchlist=NULL\n");
4077 	else {
4078 		cdinfo_match_t	*p;
4079 
4080 		for (p = cdinfo_dbp->matchlist, i = 0; p != NULL;
4081 		     p = p->next, i++) {
4082 			(void) fprintf(errfp,
4083 				       "matchlist[%d] ptr=0x%lx genre=%s ",
4084 				       i, (unsigned long) p,
4085 				       p->genre == NULL ? "NULL" : p->genre);
4086 			(void) fprintf(errfp, "artist=%s title=%s\n",
4087 				       p->artist == NULL ? "NULL" : p->artist,
4088 				       p->title == NULL ? "NULL" : p->title);
4089 		}
4090 	}
4091 	if (cdinfo_dbp->match_aux == NULL)
4092 		(void) fprintf(errfp, "match_aux=NULL\n");
4093 	else
4094 		(void) fprintf(errfp, "match_aux=0x%lx\n",
4095 			       (unsigned long) cdinfo_dbp->match_aux);
4096 	(void) fprintf(errfp, "match_tag=%ld\n", cdinfo_dbp->match_tag);
4097 
4098 	if (cdinfo_dbp->gen_url_list == NULL)
4099 		(void) fprintf(errfp, "gen_url_list=NULL\n");
4100 	else {
4101 		cdinfo_url_t	*u;
4102 
4103 		for (u = cdinfo_dbp->gen_url_list, i = 0; u != NULL;
4104 		     u = u->next, i++) {
4105 			(void) fprintf(errfp,
4106 			       "gen_url_list[%d] ptr=0x%lx key=%s-%s "
4107 			       "type=%s size=%s weight=%s categ=%s\n",
4108 			       i, (unsigned long) u,
4109 			       u->modifier == NULL ? "NULL" : u->modifier,
4110 			       u->keyname == NULL ? "NULL" : u->keyname,
4111 			       u->type == NULL ? "NULL" : u->type,
4112 			       u->size == NULL ? "NULL" : u->size,
4113 			       u->weight == NULL ? "NULL" : u->weight,
4114 			       u->categ == NULL ? "NULL" : u->categ);
4115 
4116 			(void) fprintf(errfp,
4117 			       "                 href=%s\n",
4118 			       u->href == NULL ? "NULL" : u->href);
4119 			(void) fprintf(errfp,
4120 			       "                 displink=%s\n",
4121 			       u->displink == NULL ? "NULL" : u->displink);
4122 			(void) fprintf(errfp,
4123 			       "                 disptext=%s\n",
4124 			       u->disptext == NULL ? "NULL" : u->disptext);
4125 		}
4126 	}
4127 	if (cdinfo_dbp->disc_url_list == NULL)
4128 		(void) fprintf(errfp, "disc_url_list=NULL\n");
4129 	else {
4130 		cdinfo_url_t	*u;
4131 
4132 		for (u = cdinfo_dbp->disc_url_list, i = 0; u != NULL;
4133 		     u = u->next, i++) {
4134 			(void) fprintf(errfp,
4135 			       "gen_disc_list[%d] ptr=0x%lx key=%s-%s "
4136 			       "type=%s size=%s weight=%s categ=%s\n",
4137 			       i, (unsigned long) u,
4138 			       u->modifier == NULL ? "NULL" : u->modifier,
4139 			       u->keyname == NULL ? "NULL" : u->keyname,
4140 			       u->type == NULL ? "NULL" : u->type,
4141 			       u->size == NULL ? "NULL" : u->size,
4142 			       u->weight == NULL ? "NULL" : u->weight,
4143 			       u->categ == NULL ? "NULL" : u->categ);
4144 
4145 			(void) fprintf(errfp,
4146 			       "                  href=%s\n",
4147 			       u->href == NULL ? "NULL" : u->href);
4148 			(void) fprintf(errfp,
4149 			       "                  displink=%s\n",
4150 			       u->displink == NULL ? "NULL" : u->displink);
4151 			(void) fprintf(errfp,
4152 			       "                  disptext=%s\n",
4153 			       u->disptext == NULL ? "NULL" : u->disptext);
4154 		}
4155 	}
4156 
4157 	(void) fprintf(errfp, "playorder=%s\n",
4158 		       cdinfo_dbp->playorder == NULL ?
4159 				"NULL" : cdinfo_dbp->playorder);
4160 }
4161 
4162 
4163