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