1 /*
2  *   cdda - CD Digital Audio support
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 #ifndef lint
22 static char *_if_lame_c_ident_ = "@(#)if_lame.c	7.12 04/03/11";
23 #endif
24 
25 #include "common_d/appenv.h"
26 #include "common_d/version.h"
27 #include "common_d/util.h"
28 #include "libdi_d/libdi.h"
29 #include "cdda_d/cdda.h"
30 
31 #if defined(CDDA_SUPPORTED) && defined(HAS_LAME)
32 
33 #include "cdinfo_d/cdinfo.h"
34 #include "cdda_d/wr_gen.h"
35 #include "cdda_d/if_lame.h"
36 
37 
38 extern appdata_t	app_data;
39 extern FILE		*errfp;
40 extern cdda_client_t	*cdda_clinfo;
41 
42 extern char		*tagcomment;		/* Tag comment */
43 
44 STATIC unsigned int	lame_ver = 0;		/* LAME version */
45 
46 
47 #ifdef __VMS
48 #define MP3_RETURN(x)	return ((bool_t) ((x) == 0))
49 #else
50 #define MP3_RETURN(x)	_exit((x))
51 #endif
52 
53 
54 /*
55  * if_lame_verchk
56  *	Check and store LAME encoder program version.  This is used
57  *	to deteremine command line options support in the if_lame_gencmd()
58  *	function.
59  *
60  * Args:
61  *	path - File path string to the lame executable.
62  *	ver_ret - Pointer to location for the lame version.
63  *
64  * Return:
65  *	TRUE  - success
66  *	FALSE - failure
67  */
68 bool_t
if_lame_verchk(char * path,unsigned int * ver_ret)69 if_lame_verchk(char *path, unsigned int *ver_ret)
70 {
71 	int		ret;
72 	size_t		bufsz = (STR_BUF_SZ << 1);
73 	unsigned int	vmaj,
74 			vmin,
75 			vtiny;
76 	FILE		*fp;
77 	char		*cmd,
78 			*tmpbuf;
79 #ifndef __VMS
80 	int		pfd[2];
81 	pid_t		cpid;
82 	waitret_t	wstat;
83 
84 	if (PIPE(pfd) < 0) {
85 		*ver_ret = lame_ver;
86 		return FALSE;
87 	}
88 
89 	switch (cpid = FORK()) {
90 	case 0:
91 		/* Child process */
92 
93 		(void) util_signal(SIGPIPE, SIG_IGN);
94 		(void) close(pfd[0]);
95 
96 		/* Force uid and gid to original setting */
97 		if (!util_set_ougid())
98 			_exit(1);
99 
100 		break;
101 
102 	case -1:
103 		DBGPRN(DBG_GEN)(errfp,
104 			"\nif_lame_verchk fork failed (errno=%d).\n",
105 			errno);
106 		*ver_ret = lame_ver;
107 		return FALSE;
108 
109 	default:
110 		(void) close(pfd[1]);
111 
112 		(void) read(pfd[0], &lame_ver, sizeof(lame_ver));
113 		*ver_ret = lame_ver;
114 
115 		(void) close(pfd[0]);
116 
117 		/* Parent: wait for child to finish */
118 		ret = util_waitchild(cpid, app_data.srv_timeout + 5,
119 				     NULL, 0, FALSE, &wstat);
120 
121 		if (ret < 0)
122 			return FALSE;
123 		else if (WIFEXITED(wstat))
124 			return ((bool_t) (WEXITSTATUS(wstat) == 0));
125 		else if (WIFSIGNALED(wstat))
126 			return FALSE;
127 	}
128 #endif
129 
130 	/* Run the lame program with the --help command line arg,
131 	 * which will cause it to output a usage help.  Pipe that
132 	 * into our stdin.  Note that if the lame usage help output
133 	 * format changes drastically this code may break.
134 	 */
135 
136 	/* Allocate temporary buffer */
137 	cmd = (char *) MEM_ALLOC("cmd", strlen(path) + (STR_BUF_SZ << 1));
138 	if (cmd == NULL) {
139 		*ver_ret = lame_ver;
140 		DBGPRN(DBG_GEN)(errfp, "\nOut of memory.\n");
141 		MP3_RETURN(2);
142 	}
143 
144 #ifndef __VMS
145 	(void) sprintf(cmd, "%s --help 2>&1", path);
146 #else
147 	(void) sprintf(cmd,
148 		"pipe ( define sys$error sys$output && mcr %s --help )",
149 		path
150 	);
151 #endif
152 
153 	if ((fp = popen(cmd, "r")) == NULL) {
154 		*ver_ret = lame_ver;
155 		MEM_FREE(cmd);
156 		DBGPRN(DBG_GEN)(errfp, "\npopen [%s] failed.\n", cmd);
157 		MP3_RETURN(3);
158 	}
159 
160 	/* Allocate temporary buffer */
161 	if ((tmpbuf = (char *) MEM_ALLOC("tmpbuf", bufsz)) == NULL) {
162 		*ver_ret = lame_ver;
163 		MEM_FREE(cmd);
164 		DBGPRN(DBG_GEN)(errfp, "\nOut of memory.\n");
165 		MP3_RETURN(2);
166 	}
167 
168 	/* Check the lame usage output */
169 	vmaj = vmin = vtiny = 0;
170 	while (fgets(tmpbuf, bufsz, fp) != NULL) {
171 		/* Skip blank lines */
172 		if (tmpbuf[0] == '\n' || tmpbuf[0] == '\0')
173 			continue;
174 
175 		if (sscanf(tmpbuf, "LAME version %u.%u.%u ",
176 			   &vmaj, &vmin, &vtiny) > 1) {
177 			lame_ver = ENCVER_MK(vmaj, vmin, vtiny);
178 			break;
179 		}
180 	}
181 
182 	(void) pclose(fp);
183 
184 	MEM_FREE(tmpbuf);
185 	MEM_FREE(cmd);
186 
187 	*ver_ret = lame_ver;
188 #ifndef __VMS
189 	(void) write(pfd[1], &lame_ver, sizeof(lame_ver));
190 	(void) close(pfd[1]);
191 #endif
192 
193 	MP3_RETURN(0);
194 	/*NOTREACHED*/
195 }
196 
197 
198 /*
199  * if_lame_gencmd
200  *	Create a command string to invoke the LAME encoder program.
201  *
202  * Args:
203  *	gdp - Pointer to the gen_desc_t structure.
204  *	path - File path string to the lame executable.
205  *
206  * Return:
207  *	The command string.  This is a dynamically allocated buffer.  The
208  *	caller should use MEM_FREE to deallocate it when done.  If there
209  *	is insufficient memory, NULL is returned.
210  */
211 char *
if_lame_gencmd(gen_desc_t * gdp,char * path)212 if_lame_gencmd(gen_desc_t *gdp, char *path)
213 {
214 	char		*sq,
215 			*cmd,
216 			*cp;
217 	curstat_t	*s = cdda_clinfo->curstat_addr();
218 	size_t		extra_len;
219 
220 #ifdef __VMS
221 	sq = "\"";
222 #else
223 	sq = "'";
224 #endif
225 
226 	if (app_data.lame_opts == NULL ||
227 	    app_data.lameopts_mode == LAMEOPTS_DISABLE)
228 		extra_len = 0;
229 	else
230 		extra_len = strlen(app_data.lame_opts);
231 
232 	cmd = (char *) MEM_ALLOC("cmd",
233 		strlen(path) + (strlen(gdp->path) << 1) +
234 		strlen(tagcomment) + extra_len + 400
235 	);
236 	if (cmd == NULL) {
237 		(void) strcpy(gdp->cdp->i->msgbuf,
238 				"if_lame_gencmd: Out of memory.");
239 		DBGPRN(DBG_GEN)(errfp, "%s\n", gdp->cdp->i->msgbuf);
240 		return NULL;
241 	}
242 
243 	(void) sprintf(cmd,
244 		(lame_ver < ENCVER_MK(3,90,0)) ?
245 		    "%s%s -r -s 44.1" : "%s%s -r -s 44.1 --bitwidth 16",
246 		path,
247 		((app_data.debug & (DBG_GEN|DBG_SND)) == 0) ? " -S" : ""
248 	);
249 
250 	if (app_data.lameopts_mode == LAMEOPTS_REPLACE) {
251 		(void) sprintf(cmd, "%s %s",
252 			cmd,
253 			app_data.lame_opts == NULL ? "" : app_data.lame_opts
254 		);
255 	}
256 	else {
257 		if (app_data.lameopts_mode == LAMEOPTS_INSERT &&
258 		    app_data.lame_opts != NULL)
259 			(void) sprintf(cmd, "%s %s", cmd, app_data.lame_opts);
260 
261 		switch (app_data.comp_mode) {
262 		case COMPMODE_0:
263 			(void) sprintf(cmd,
264 				(lame_ver < ENCVER_MK(3,91,0)) ?
265 				    "%s -b %d" : "%s --cbr -b %d",
266 				cmd,
267 				(app_data.bitrate > 0) ? app_data.bitrate : 128
268 			);
269 			break;
270 		case COMPMODE_1:
271 			(void) sprintf(cmd,
272 				(lame_ver < ENCVER_MK(3,84,0)) ?
273 				    "%s -v -V %d" : "%s --vbr-old -V %d",
274 				cmd, -(app_data.qual_factor - 10)
275 			);
276 			if (app_data.bitrate_min > 0)
277 				(void) sprintf(cmd, "%s -b %d", cmd,
278 						app_data.bitrate_min);
279 			if (app_data.bitrate_max > 0)
280 				(void) sprintf(cmd, "%s -B %d", cmd,
281 						app_data.bitrate_max);
282 			break;
283 		case COMPMODE_2:
284 			(void) sprintf(cmd,
285 				(lame_ver < ENCVER_MK(3,84,0)) ?
286 				    "%s -v -V %d" : "%s --vbr-new -V %d",
287 				cmd, -(app_data.qual_factor - 10)
288 			);
289 			if (app_data.bitrate_min > 0)
290 				(void) sprintf(cmd, "%s -b %d", cmd,
291 						app_data.bitrate_min);
292 			if (app_data.bitrate_max > 0)
293 				(void) sprintf(cmd, "%s -B %d", cmd,
294 						app_data.bitrate_max);
295 			break;
296 		case COMPMODE_3:
297 		default:
298 			(void) sprintf(cmd,
299 				(lame_ver < ENCVER_MK(3,84,0)) ?
300 				    "%s -b %d" : "%s --abr %d",
301 				cmd,
302 				(app_data.bitrate > 0) ? app_data.bitrate : 128
303 			);
304 			if (app_data.bitrate_min > 0)
305 				(void) sprintf(cmd, "%s -b %d", cmd,
306 						app_data.bitrate_min);
307 			if (app_data.bitrate_max > 0)
308 				(void) sprintf(cmd, "%s -B %d", cmd,
309 						app_data.bitrate_max);
310 			break;
311 		}
312 
313 		if (lame_ver > ENCVER_MK(3,88,0)) {
314 			(void) sprintf(cmd, "%s -q %d",
315 				cmd,
316 				-(app_data.comp_algo - 10)
317 			);
318 		}
319 
320 		switch (app_data.chan_mode) {
321 		case CH_STEREO:
322 			(void) strcat(cmd, " -m s");
323 			break;
324 		case CH_FORCEMS:
325 			(void) strcat(cmd, " -m f");
326 			break;
327 		case CH_MONO:
328 			(void) strcat(cmd, " -m s -a");
329 			break;
330 		case CH_JSTEREO:
331 		default:
332 			(void) strcat(cmd, " -m j");
333 			break;
334 		}
335 
336 		if (app_data.lowpass_mode == FILTER_OFF &&
337 		    app_data.highpass_mode == FILTER_OFF)
338 			(void) strcat(cmd, " -k");
339 		else {
340 			if (app_data.lowpass_mode == FILTER_MANUAL) {
341 				(void) sprintf(cmd,
342 				    "%s --lowpass %.1f --lowpass-width %.1f",
343 				    cmd,
344 				    (float) app_data.lowpass_freq / 1024.0,
345 				    (float) app_data.lowpass_width / 1024.0
346 				);
347 			}
348 			if (app_data.highpass_mode == FILTER_MANUAL) {
349 				(void) sprintf(cmd,
350 				    "%s --highpass %.1f --highpass-width %.1f",
351 				    cmd,
352 				    (float) app_data.highpass_freq / 1024.0,
353 				    (float) app_data.highpass_width / 1024.0
354 				);
355 			}
356 		}
357 
358 		if (app_data.copyright)
359 			(void) strcat(cmd, " -c");
360 		if (!app_data.original)
361 			(void) strcat(cmd, " -o");
362 		if (app_data.checksum)
363 			(void) strcat(cmd, " -p");
364 		if (app_data.nores)
365 			(void) strcat(cmd, " --nores");
366 		if (app_data.strict_iso)
367 			(void) strcat(cmd, " --strictly-enforce-ISO");
368 
369 		if (app_data.lameopts_mode == LAMEOPTS_APPEND &&
370 		    app_data.lame_opts != NULL)
371 			(void) sprintf(cmd, "%s %s", cmd, app_data.lame_opts);
372 	}
373 
374 	if (app_data.add_tag) {
375 		cdinfo_incore_t	*cdp = cdinfo_addr();
376 		chset_conv_t	*up;
377 		char		*p;
378 		int		idx = (int) gdp->cdp->i->trk_idx,
379 				sav_chset_xlat;
380 
381 		/* Force conversion to ISO-8859 regardless of locale */
382 		sav_chset_xlat = app_data.chset_xlat;
383 		app_data.chset_xlat = CHSET_XLAT_ISO8859;
384 
385 		if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL) {
386 			app_data.chset_xlat = sav_chset_xlat;
387 			(void) strcpy(gdp->cdp->i->msgbuf,
388 					"if_lame_gencmd: Out of memory.");
389 			DBGPRN(DBG_GEN)(errfp, "%s\n", gdp->cdp->i->msgbuf);
390 			return NULL;
391 		}
392 
393 		if (lame_ver > ENCVER_MK(3,70,0)) {
394 			switch (app_data.id3tag_mode) {
395 			case ID3TAG_V1:
396 				(void) strcat(cmd, " --id3v1-only");
397 				break;
398 			case ID3TAG_V2:
399 				(void) strcat(cmd, " --id3v2-only");
400 				break;
401 			case ID3TAG_BOTH:
402 			default:
403 				(void) strcat(cmd, " --add-id3v2");
404 				break;
405 			}
406 		}
407 
408 		if (cdp->disc.title != NULL) {
409 			p = NULL;
410 			if (!util_chset_conv(up, cdp->disc.title, &p, FALSE) &&
411 			    !util_newstr(&p, cdp->disc.title)) {
412 				util_chset_close(up);
413 				app_data.chset_xlat = sav_chset_xlat;
414 				(void) strcpy(gdp->cdp->i->msgbuf,
415 					    "if_lame_gencmd: Out of memory.");
416 				DBGPRN(DBG_GEN)(errfp, "%s\n",
417 						gdp->cdp->i->msgbuf);
418 				return NULL;
419 			}
420 			if (p != NULL) {
421 				cp = gen_esc_shellquote(gdp, p);
422 				if (cp == NULL) {
423 					util_chset_close(up);
424 					app_data.chset_xlat = sav_chset_xlat;
425 					return NULL;
426 				}
427 				(void) sprintf(cmd, "%s --tl %s%.80s%s",
428 						cmd, sq, cp, sq);
429 				MEM_FREE(p);
430 				MEM_FREE(cp);
431 			}
432 		}
433 
434 		if (app_data.cdda_trkfile && cdp->track[idx].artist != NULL) {
435 			p = NULL;
436 			if (!util_chset_conv(up, cdp->track[idx].artist,
437 					     &p, FALSE) &&
438 			    !util_newstr(&p, cdp->track[idx].artist)) {
439 				util_chset_close(up);
440 				app_data.chset_xlat = sav_chset_xlat;
441 				(void) strcpy(gdp->cdp->i->msgbuf,
442 					    "if_lame_gencmd: Out of memory.");
443 				DBGPRN(DBG_GEN)(errfp, "%s\n",
444 						gdp->cdp->i->msgbuf);
445 				return NULL;
446 			}
447 			if (p != NULL) {
448 				cp = gen_esc_shellquote(gdp, p);
449 				if (cp == NULL) {
450 					util_chset_close(up);
451 					app_data.chset_xlat = sav_chset_xlat;
452 					return NULL;
453 				}
454 				(void) sprintf(cmd, "%s --ta %s%.80s%s",
455 						cmd, sq, cp, sq);
456 				MEM_FREE(p);
457 				MEM_FREE(cp);
458 			}
459 		}
460 		else if (cdp->disc.artist != NULL) {
461 			p = NULL;
462 			if (!util_chset_conv(up, cdp->disc.artist,
463 					     &p, FALSE) &&
464 			    !util_newstr(&p, cdp->disc.artist)) {
465 				util_chset_close(up);
466 				app_data.chset_xlat = sav_chset_xlat;
467 				(void) strcpy(gdp->cdp->i->msgbuf,
468 					    "if_lame_gencmd: Out of memory.");
469 				DBGPRN(DBG_GEN)(errfp, "%s\n",
470 						gdp->cdp->i->msgbuf);
471 				return NULL;
472 			}
473 			if (p != NULL) {
474 				cp = gen_esc_shellquote(gdp, p);
475 				if (cp == NULL) {
476 					util_chset_close(up);
477 					app_data.chset_xlat = sav_chset_xlat;
478 					return NULL;
479 				}
480 				(void) sprintf(cmd, "%s --ta %s%.80s%s",
481 						cmd, sq, cp, sq);
482 				MEM_FREE(p);
483 				MEM_FREE(cp);
484 			}
485 		}
486 
487 		if (app_data.cdda_trkfile && cdp->track[idx].title != NULL) {
488 			p = NULL;
489 			if (!util_chset_conv(up, cdp->track[idx].title,
490 					     &p, FALSE) &&
491 			    !util_newstr(&p, cdp->track[idx].title)) {
492 				util_chset_close(up);
493 				app_data.chset_xlat = sav_chset_xlat;
494 				(void) strcpy(gdp->cdp->i->msgbuf,
495 					    "if_lame_gencmd: Out of memory.");
496 				DBGPRN(DBG_GEN)(errfp, "%s\n",
497 						gdp->cdp->i->msgbuf);
498 				return NULL;
499 			}
500 			if (p != NULL) {
501 				cp = gen_esc_shellquote(gdp, p);
502 				if (cp == NULL) {
503 					util_chset_close(up);
504 					app_data.chset_xlat = sav_chset_xlat;
505 					return NULL;
506 				}
507 				(void) sprintf(cmd, "%s --tt %s%.80s%s",
508 						cmd, sq, cp, sq);
509 				MEM_FREE(p);
510 				MEM_FREE(cp);
511 			}
512 		}
513 		else if (cdp->disc.title != NULL) {
514 			p = NULL;
515 			if (!util_chset_conv(up, cdp->disc.title, &p, FALSE) &&
516 			    !util_newstr(&p, cdp->disc.title)) {
517 				util_chset_close(up);
518 				app_data.chset_xlat = sav_chset_xlat;
519 				(void) strcpy(gdp->cdp->i->msgbuf,
520 					    "if_lame_gencmd: Out of memory.");
521 				DBGPRN(DBG_GEN)(errfp, "%s\n",
522 						gdp->cdp->i->msgbuf);
523 				return NULL;
524 			}
525 			if (p != NULL) {
526 				cp = gen_esc_shellquote(gdp, p);
527 				if (cp == NULL) {
528 					util_chset_close(up);
529 					app_data.chset_xlat = sav_chset_xlat;
530 					return NULL;
531 				}
532 				(void) sprintf(cmd, "%s --tt %s%.80s%s",
533 						cmd, sq, cp, sq);
534 				MEM_FREE(p);
535 				MEM_FREE(cp);
536 			}
537 		}
538 
539 		if (app_data.cdda_trkfile && cdp->track[idx].year != NULL) {
540 			p = NULL;
541 			if (!util_chset_conv(up, cdp->track[idx].year,
542 					     &p, FALSE) &&
543 			    !util_newstr(&p, cdp->track[idx].year)) {
544 				util_chset_close(up);
545 				app_data.chset_xlat = sav_chset_xlat;
546 				(void) strcpy(gdp->cdp->i->msgbuf,
547 					    "if_lame_gencmd: Out of memory.");
548 				DBGPRN(DBG_GEN)(errfp, "%s\n",
549 						gdp->cdp->i->msgbuf);
550 				return NULL;
551 			}
552 			if (p != NULL) {
553 				(void) sprintf(cmd, "%s --ty %s%.4s%s",
554 						cmd, sq, p, sq);
555 				MEM_FREE(p);
556 			}
557 		}
558 		else if (cdp->disc.year != NULL) {
559 			p = NULL;
560 			if (!util_chset_conv(up, cdp->disc.year, &p, FALSE) &&
561 			    !util_newstr(&p, cdp->disc.year)) {
562 				util_chset_close(up);
563 				app_data.chset_xlat = sav_chset_xlat;
564 				(void) strcpy(gdp->cdp->i->msgbuf,
565 					    "if_lame_gencmd: Out of memory.");
566 				DBGPRN(DBG_GEN)(errfp, "%s\n",
567 						gdp->cdp->i->msgbuf);
568 				return NULL;
569 			}
570 			if (p != NULL) {
571 				(void) sprintf(cmd, "%s --ty %s%.4s%s",
572 						cmd, sq, p, sq);
573 				MEM_FREE(p);
574 			}
575 		}
576 
577 		if (app_data.cdda_trkfile && cdp->track[idx].genre != NULL) {
578 			p = NULL;
579 			if (!util_chset_conv(up,
580 				    gen_genre_cddb2id3(cdp->track[idx].genre),
581 				    &p, FALSE) &&
582 			    !util_newstr(&p,
583 				    gen_genre_cddb2id3(
584 					cdp->track[idx].genre))) {
585 				util_chset_close(up);
586 				app_data.chset_xlat = sav_chset_xlat;
587 				(void) strcpy(gdp->cdp->i->msgbuf,
588 					    "if_lame_gencmd: Out of memory.");
589 				DBGPRN(DBG_GEN)(errfp, "%s\n",
590 						gdp->cdp->i->msgbuf);
591 				return NULL;
592 			}
593 			if (p != NULL) {
594 				(void) sprintf(cmd, "%s --tg %s%.4s%s",
595 						cmd, sq, p, sq);
596 				MEM_FREE(p);
597 			}
598 		}
599 		else if (cdp->disc.genre != NULL) {
600 			p = NULL;
601 			if (!util_chset_conv(up,
602 				    gen_genre_cddb2id3(cdp->disc.genre),
603 				    &p, FALSE) &&
604 			    !util_newstr(&p,
605 				    gen_genre_cddb2id3(cdp->disc.genre))) {
606 				util_chset_close(up);
607 				app_data.chset_xlat = sav_chset_xlat;
608 				(void) strcpy(gdp->cdp->i->msgbuf,
609 					    "if_lame_gencmd: Out of memory.");
610 				DBGPRN(DBG_GEN)(errfp, "%s\n",
611 						gdp->cdp->i->msgbuf);
612 				return NULL;
613 			}
614 			if (p != NULL) {
615 				(void) sprintf(cmd, "%s --tg %s%.4s%s",
616 						cmd, sq, p, sq);
617 				MEM_FREE(p);
618 			}
619 		}
620 
621 		if (app_data.cdda_trkfile) {
622 			(void) sprintf(cmd, "%s --tn %s%d%s", cmd,
623 					sq, (int) s->trkinfo[idx].trkno, sq);
624 		}
625 
626 		p = NULL;
627 		if (!util_chset_conv(up, tagcomment, &p, FALSE) &&
628 		    !util_newstr(&p, tagcomment)) {
629 			util_chset_close(up);
630 			app_data.chset_xlat = sav_chset_xlat;
631 			(void) strcpy(gdp->cdp->i->msgbuf,
632 					"if_lame_gencmd: Out of memory.");
633 			DBGPRN(DBG_GEN)(errfp, "%s\n", gdp->cdp->i->msgbuf);
634 			return NULL;
635 		}
636 		if (p != NULL) {
637 			(void) sprintf(cmd, "%s --tc %s%.40s%s",
638 					cmd, sq, p, sq);
639 			MEM_FREE(p);
640 		}
641 
642 		util_chset_close(up);
643 
644 		/* Restore charset conversion mode */
645 		app_data.chset_xlat = sav_chset_xlat;
646 	}
647 
648 	if ((gdp->flags & GDESC_ISPIPE) != 0) {
649 #ifdef __VMS
650 		/* Not possible in this VMS implementation */
651 		MEM_FREE(cmd);
652 		cmd = NULL;
653 		(void) sprintf(gdp->cdp->i->msgbuf, "ERROR: "
654 			"MP3 format not supported for pipe-to-program.");
655 		DBGPRN(DBG_GEN)(errfp, "%s\n", gdp->cdp->i->msgbuf);
656 #else
657 		/* gdp->path is the program to pipe to */
658 		(void) sprintf(cmd,
659 			"%s - - 2>%s | %s",
660 			cmd,
661 			((app_data.debug & (DBG_SND|DBG_GEN)) != 0) ?
662 				"/tmp/xmcd-lame.$$" : "/dev/null",
663 			gdp->path
664 		);
665 #endif
666 	}
667 	else {
668 		/* gdp->path is the file to write to */
669 		if ((cp = gen_esc_shellquote(gdp, gdp->path)) == NULL)
670 			return NULL;
671 
672 #ifdef __VMS
673 		(void) sprintf(cmd, "%s - %s", cmd, cp);
674 #else
675 		(void) sprintf(cmd,
676 			"%s - '%s' >%s 2>&1 && chmod %o '%s'",
677 			cmd,
678 			cp,
679 			((app_data.debug & (DBG_SND|DBG_GEN)) != 0) ?
680 				"/tmp/xmcd-lame.$$" : "/dev/null",
681 			(unsigned int) gdp->mode,
682 			cp
683 		);
684 #endif
685 
686 		MEM_FREE(cp);
687 	}
688 
689 	return (cmd);
690 }
691 
692 #endif	/* CDDA_SUPPORTED HAS_LAME */
693 
694