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 *_wr_gen_c_ident_ = "@(#)wr_gen.c 7.164 04/03/17";
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 #include "cdda_d/common.h"
31
32 #ifdef CDDA_SUPPORTED
33
34 #include "cdinfo_d/cdinfo.h"
35 #include "cdda_d/wr_gen.h"
36 #include "cdda_d/au.h"
37 #include "cdda_d/wav.h"
38 #include "cdda_d/aiff.h"
39 #include "cdda_d/if_lame.h"
40 #include "cdda_d/if_vorb.h"
41 #include "cdda_d/if_flac.h"
42 #include "cdda_d/if_faac.h"
43 #include "cdda_d/id3map.h"
44
45
46 #ifdef __VMS
47 #include <unixlib.h>
48 /* The C headers exclude fsync for _POSIX_C_SOURCE and _ANSI_C_SOURCE */
49 extern int fsync(int);
50 /* The unixlib.h header file has these only on the later releases
51 * of Compaq C, but the functions were available earlier.
52 */
53 extern int decc$set_child_standard_streams(int, int, int);
54 extern int decc$write_eof_to_mbx(int);
55 #endif
56
57
58 /* Defines used by gen_open_pipe() */
59 #define STR_SHPATH "/bin/sh" /* Path to shell */
60 #define STR_SHNAME "sh" /* Name of shell */
61 #define STR_SHARG "-c" /* Shell arg */
62
63
64 extern appdata_t app_data;
65 extern FILE *errfp;
66 extern cdda_client_t *cdda_clinfo;
67
68 #ifdef HAS_LAME
69 extern char *lamepath;
70 #endif
71
72 #ifdef HAS_FAAC
73 extern char *faacpath;
74 #endif
75
76
77 STATIC uid_t ruid, /* Real user id */
78 euid; /* Effective user id */
79 STATIC gid_t rgid, /* Real group id */
80 egid; /* Effective group id */
81
82 char *tagcomment =
83 "xmcd-" VERSION_MAJ "." VERSION_MIN "." VERSION_TEENY;
84 /* Tag comment string */
85
86 /*********************
87 * Private functions *
88 *********************/
89
90
91 #ifdef __VMS
92 /*
93 * gen_exec_vms
94 * Parse a command string, create an argv array from it, and pass it
95 * to execvp() for execution.
96 *
97 * Args:
98 * cmd - The command string.
99 *
100 * Return:
101 * On success, this function does not return.
102 */
103 STATIC void
gen_exec_vms(char * cmd)104 gen_exec_vms(char *cmd)
105 {
106 char **largv = NULL,
107 *cmd_copy;
108 int largc = 0,
109 i,
110 j,
111 k,
112 l,
113 c,
114 saverr = 0;
115 bool_t white = FALSE,
116 string = FALSE,
117 quote = FALSE;
118
119 l = strlen(cmd);
120 if ((cmd_copy = (char *) MEM_ALLOC("cmd_copy", l + 1)) == NULL) {
121 (void) fprintf(errfp, "gen_exec_vms: Out of memory.\n");
122 errno = ENOMEM;
123 return;
124 }
125
126 for (i = j = k = 0; (c = cmd[i]) != '\0'; i++) {
127 switch (c) {
128 case '"':
129 if (quote) {
130 cmd_copy[k++] = c;
131 quote = FALSE;
132 }
133 else
134 string = !string;
135 break;
136 case '\\':
137 if (quote) {
138 cmd_copy[k++] = c;
139 quote = FALSE;
140 }
141 else
142 quote = TRUE;
143 break;
144 case ' ':
145 case '\t':
146 if (string || quote) {
147 cmd_copy[k++] = c;
148
149 if (quote)
150 quote = FALSE;
151 }
152 else {
153 if (white)
154 continue;
155
156 cmd_copy[k++] = 0;
157
158 largv = (char **) MEM_REALLOC("largv",
159 largv, (largc+2) * sizeof(*largv)
160 );
161
162 largv[largc] = &cmd_copy[j];
163 largc++;
164 white = TRUE;
165 }
166 break;
167 default:
168 if (white) {
169 j = k;
170 white = FALSE;
171 }
172
173 quote = FALSE;
174 cmd_copy[k++] = c;
175 break;
176 }
177 }
178 cmd_copy[k] = '\0';
179
180 if (cmd_copy[0] != '\0') {
181 if (!white) {
182 largv = (char **) MEM_REALLOC("largv",
183 largv, (largc + 2) * sizeof(*largv)
184 );
185 largv[largc] = &cmd_copy[j];
186 largc++;
187 }
188 largv[largc] = NULL;
189
190 if (app_data.debug & DBG_GEN) {
191 (void) fprintf(errfp, "gen_vms_exec: cmd='%s'\n", cmd);
192 for (i = 0; largv[i]; i++) {
193 (void) fprintf(errfp, " largv[%d]='%s'\n",
194 i, largv[i]);
195 }
196 }
197
198 /* Exec the program */
199 (void) execvp(largv[0], largv);
200 saverr = errno;
201 }
202
203 MEM_FREE(largv);
204 MEM_FREE(cmd_copy);
205 errno = saverr;
206 }
207 #endif /* __VMS */
208
209
210 /*
211 * gen_write_au_hdr
212 * Writes an au file header to the output file.
213 *
214 * Args:
215 * gdp - Pointer to the gen_desc_t structure
216 *
217 * Return:
218 * FALSE - failure
219 * TRUE - success
220 */
221 STATIC bool_t
gen_write_au_hdr(gen_desc_t * gdp)222 gen_write_au_hdr(gen_desc_t *gdp)
223 {
224 au_filehdr_t hdr;
225
226 if (gdp->datalen == 0) {
227 struct stat stbuf;
228
229 /* Get file information */
230 #ifndef NO_FSYNC
231 (void) fsync(gdp->fd);
232 #endif
233 if (fstat(gdp->fd, &stbuf) < 0) {
234 (void) sprintf(gdp->cdp->i->msgbuf,
235 "gen_write_au_hdr: fstat failed (errno=%d)",
236 errno);
237 DBGPRN(DBG_SND)(errfp, "%s\n", gdp->cdp->i->msgbuf);
238 return FALSE;
239 }
240
241 /* Set actual data length */
242 gdp->datalen = stbuf.st_size - sizeof(hdr) -
243 strlen(tagcomment);
244
245 /* Rewind to beginning of file */
246 if (!gen_seek(gdp, (off_t) 0, SEEK_SET))
247 return FALSE;
248 }
249
250 (void) memset(&hdr, 0, sizeof(hdr));
251
252 /* Initialize an au file header. */
253 hdr.au_magic = util_bswap32(AUDIO_AU_FILE_MAGIC);
254 hdr.au_offset = util_bswap32(sizeof(hdr) + strlen(tagcomment));
255 hdr.au_data_size = util_bswap32((word32_t) gdp->datalen);
256 hdr.au_encoding = util_bswap32(AUDIO_AU_ENCODING_LINEAR_16);
257 hdr.au_sample_rate = util_bswap32(44100);
258 hdr.au_channels = util_bswap32(2);
259
260 /* Write header */
261 if (!gen_write_chunk(gdp, (byte_t *) &hdr, sizeof(hdr)))
262 return FALSE;
263
264 /* Write comment */
265 if (!gen_write_chunk(gdp, (byte_t *) tagcomment, strlen(tagcomment)))
266 return FALSE;
267
268 return TRUE;
269 }
270
271
272 /*
273 * gen_write_wav_hdr
274 * Writes an wav file header to the output file.
275 *
276 * Args:
277 * gdp - Pointer to the gen_desc_t structure
278 *
279 * Return:
280 * FALSE - failure
281 * TRUE - success
282 */
283 STATIC bool_t
gen_write_wav_hdr(gen_desc_t * gdp)284 gen_write_wav_hdr(gen_desc_t *gdp)
285 {
286 wav_filehdr_t hdr;
287
288 if (gdp->datalen == 0) {
289 struct stat stbuf;
290
291 /* Get file information */
292 #ifndef NO_FSYNC
293 (void) fsync(gdp->fd);
294 #endif
295 if (fstat(gdp->fd, &stbuf) < 0) {
296 (void) sprintf(gdp->cdp->i->msgbuf,
297 "gen_write_wav_hdr: fstat failed (errno=%d)",
298 errno);
299 DBGPRN(DBG_SND)(errfp, "%s\n", gdp->cdp->i->msgbuf);
300 return FALSE;
301 }
302
303 /* Set actual data length */
304 gdp->datalen = stbuf.st_size - sizeof(hdr);
305
306 /* Rewind to beginning of file */
307 if (!gen_seek(gdp, (off_t) 0, SEEK_SET))
308 return FALSE;
309 }
310
311 (void) memset(&hdr, 0, sizeof(hdr));
312
313 /* RIFF chunk */
314 (void) strncpy(hdr.r_riff, "RIFF", 4);
315 hdr.r_length = util_lswap32((word32_t) gdp->datalen + 36);
316 (void) strncpy(hdr.r_wave, "WAVE", 4);
317
318 /* FORMAT chunk */
319 (void) strncpy(hdr.f_format, "fmt ", 4);
320 hdr.f_length = util_lswap32(16);
321 hdr.f_const = util_lswap16(1);
322 hdr.f_channels = util_lswap16(2);
323 hdr.f_sample_rate = util_lswap32(44100);
324 hdr.f_bytes_per_s = util_lswap32(44100 * 2 * 2);
325 hdr.f_block_align = util_lswap16(4);
326 hdr.f_bits_per_sample = util_lswap16(16);
327
328 /* DATA chunk */
329 (void) strncpy(hdr.d_data, "data", 4);
330 hdr.d_length = util_lswap32((word32_t) gdp->datalen);
331
332 /* Write header */
333 if (!gen_write_chunk(gdp, (byte_t *) &hdr, sizeof(hdr)))
334 return FALSE;
335
336 return TRUE;
337 }
338
339
340 /*
341 * gen_write_aiff_hdr
342 * Writes an aiff file header to the output file.
343 *
344 * Args:
345 * gdp - Pointer to the gen_desc_t structure
346 *
347 * Return:
348 * FALSE - failure
349 * TRUE - success
350 */
351 STATIC bool_t
gen_write_aiff_hdr(gen_desc_t * gdp)352 gen_write_aiff_hdr(gen_desc_t *gdp)
353 {
354 word32_t tmp;
355 aiff_filehdr_t hdr;
356
357 if (gdp->datalen == 0) {
358 struct stat stbuf;
359
360 /* Get file information */
361 #ifndef NO_FSYNC
362 (void) fsync(gdp->fd);
363 #endif
364 if (fstat(gdp->fd, &stbuf) < 0) {
365 (void) sprintf(gdp->cdp->i->msgbuf,
366 "gen_write_aiff_hdr: fstat failed (errno=%d)",
367 errno);
368 DBGPRN(DBG_SND)(errfp, "%s\n", gdp->cdp->i->msgbuf);
369 return FALSE;
370 }
371
372 /* Set actual data length */
373 gdp->datalen = stbuf.st_size - AIFF_HDRSZ;
374
375 /* Rewind to beginning of file */
376 if (!gen_seek(gdp, (off_t) 0, SEEK_SET))
377 return FALSE;
378 }
379
380 (void) memset(&hdr, 0, sizeof(hdr));
381
382 /* FORM chunk */
383 (void) strncpy(hdr.a_form, "FORM", 4);
384 tmp = (word32_t) gdp->datalen + AIFF_HDRSZ - 8;
385 hdr.a_length[0] = (tmp & 0xff000000) >> 24;
386 hdr.a_length[1] = (tmp & 0x00ff0000) >> 16;
387 hdr.a_length[2] = (tmp & 0x0000ff00) >> 8;
388 hdr.a_length[3] = (tmp & 0x000000ff);
389 (void) strncpy(hdr.a_aiff, "AIFF", 4);
390
391 /* COMM chunk */
392 (void) strncpy(hdr.c_comm, "COMM", 4);
393 tmp = 18;
394 hdr.c_length[0] = (tmp & 0xff000000) >> 24;
395 hdr.c_length[1] = (tmp & 0x00ff0000) >> 16;
396 hdr.c_length[2] = (tmp & 0x0000ff00) >> 8;
397 hdr.c_length[3] = (tmp & 0x000000ff);
398 tmp = 2;
399 hdr.c_channels[0] = (tmp & 0xff00) >> 8;
400 hdr.c_channels[1] = (tmp & 0x00ff);
401 tmp = (word32_t) gdp->datalen >> 2;
402 hdr.c_frames[0] = (tmp & 0xff000000) >> 24;
403 hdr.c_frames[1] = (tmp & 0x00ff0000) >> 16;
404 hdr.c_frames[2] = (tmp & 0x0000ff00) >> 8;
405 hdr.c_frames[3] = (tmp & 0x000000ff);
406 tmp = 16;
407 hdr.c_sample_size[0] = (tmp & 0xff00) >> 8;
408 hdr.c_sample_size[1] = (tmp & 0x00ff);
409 (void) strncpy(hdr.c_sample_rate, "@\016\254D\0\0\0\0\0\0", 10);
410 /* 44100 in 80-bit IEEE floating point */
411
412 /* SSND chunk */
413 (void) strncpy(hdr.s_ssnd, "SSND", 4);
414 tmp = (word32_t) gdp->datalen + 8;
415 hdr.s_length[0] = (tmp & 0xff000000) >> 24;
416 hdr.s_length[1] = (tmp & 0x00ff0000) >> 16;
417 hdr.s_length[2] = (tmp & 0x0000ff00) >> 8;
418 hdr.s_length[3] = (tmp & 0x000000ff);
419
420 /* Write header */
421 if (!gen_write_chunk(gdp, (byte_t *) &hdr, AIFF_HDRSZ))
422 return FALSE;
423
424 return TRUE;
425 }
426
427
428 /*
429 * gen_write_aifc_hdr
430 * Writes an aifc file header to the output file.
431 *
432 * Args:
433 * gdp - Pointer to the gen_desc_t structure
434 *
435 * Return:
436 * FALSE - failure
437 * TRUE - success
438 */
439 STATIC bool_t
gen_write_aifc_hdr(gen_desc_t * gdp)440 gen_write_aifc_hdr(gen_desc_t *gdp)
441 {
442 word32_t tmp;
443 aifc_filehdr_t hdr;
444
445 if (gdp->datalen == 0) {
446 struct stat stbuf;
447
448 /* Get file information */
449 #ifndef NO_FSYNC
450 (void) fsync(gdp->fd);
451 #endif
452 if (fstat(gdp->fd, &stbuf) < 0) {
453 (void) sprintf(gdp->cdp->i->msgbuf,
454 "gen_write_aifc_hdr: fstat failed (errno=%d)",
455 errno);
456 DBGPRN(DBG_SND)(errfp, "%s\n", gdp->cdp->i->msgbuf);
457 return FALSE;
458 }
459
460 /* Set actual data length */
461 gdp->datalen = stbuf.st_size - AIFC_HDRSZ;
462
463 /* Rewind to beginning of file */
464 if (!gen_seek(gdp, (off_t) 0, SEEK_SET))
465 return FALSE;
466 }
467
468 (void) memset(&hdr, 0, sizeof(hdr));
469
470 /* FORM chunk */
471 (void) strncpy(hdr.a_form, "FORM", 4);
472 tmp = (word32_t) gdp->datalen + AIFC_HDRSZ - 8;
473 hdr.a_length[0] = (tmp & 0xff000000) >> 24;
474 hdr.a_length[1] = (tmp & 0x00ff0000) >> 16;
475 hdr.a_length[2] = (tmp & 0x0000ff00) >> 8;
476 hdr.a_length[3] = (tmp & 0x000000ff);
477 (void) strncpy(hdr.a_aifc, "AIFC", 4);
478
479 /* FVER chunk */
480 (void) strncpy(hdr.f_fver, "FVER", 4);
481 tmp = 4;
482 hdr.f_length[0] = (tmp & 0xff000000) >> 24;
483 hdr.f_length[1] = (tmp & 0x00ff0000) >> 16;
484 hdr.f_length[2] = (tmp & 0x0000ff00) >> 8;
485 hdr.f_length[3] = (tmp & 0x000000ff);
486 tmp = 2726318400UL;
487 hdr.f_version[0] = (tmp & 0xff000000) >> 24;
488 hdr.f_version[1] = (tmp & 0x00ff0000) >> 16;
489 hdr.f_version[2] = (tmp & 0x0000ff00) >> 8;
490 hdr.f_version[3] = (tmp & 0x000000ff);
491
492 /* COMM chunk */
493 (void) strncpy(hdr.c_comm, "COMM", 4);
494 tmp = 18;
495 hdr.c_length[0] = (tmp & 0xff000000) >> 24;
496 hdr.c_length[1] = (tmp & 0x00ff0000) >> 16;
497 hdr.c_length[2] = (tmp & 0x0000ff00) >> 8;
498 hdr.c_length[3] = (tmp & 0x000000ff);
499 tmp = 2;
500 hdr.c_channels[0] = (tmp & 0xff00) >> 8;
501 hdr.c_channels[1] = (tmp & 0x00ff);
502 tmp = (word32_t) gdp->datalen >> 2;
503 hdr.c_frames[0] = (tmp & 0xff000000) >> 24;
504 hdr.c_frames[1] = (tmp & 0x00ff0000) >> 16;
505 hdr.c_frames[2] = (tmp & 0x0000ff00) >> 8;
506 hdr.c_frames[3] = (tmp & 0x000000ff);
507 tmp = 16;
508 hdr.c_sample_size[0] = (tmp & 0xff00) >> 8;
509 hdr.c_sample_size[1] = (tmp & 0x00ff);
510 (void) strncpy(hdr.c_sample_rate, "@\016\254D\0\0\0\0\0\0", 10);
511 /* 44100 in 80-bit IEEE floating point */
512 (void) strncpy(hdr.c_comptype, "NONE", 4);
513 hdr.c_complength = 14;
514 (void) strncpy(hdr.c_compstr, "not compressed", 14);
515
516 /* SSND chunk */
517 (void) strncpy(hdr.s_ssnd, "SSND", 4);
518 tmp = (word32_t) gdp->datalen + 8;
519 hdr.s_length[0] = (tmp & 0xff000000) >> 24;
520 hdr.s_length[1] = (tmp & 0x00ff0000) >> 16;
521 hdr.s_length[2] = (tmp & 0x0000ff00) >> 8;
522 hdr.s_length[3] = (tmp & 0x000000ff);
523
524 /* Write header */
525 if (!gen_write_chunk(gdp, (byte_t *) &hdr, AIFC_HDRSZ))
526 return FALSE;
527
528 return TRUE;
529 }
530
531
532 /********************
533 * Public functions *
534 ********************/
535
536
537 /*
538 * gen_esc_shellquote
539 * Handle quote characters in a quoted shell command line string.
540 *
541 * Args:
542 * gdp - Pointer to the gen_desc_t structure
543 * str - Input string
544 *
545 * Return:
546 * Return string. This is a dynamically allocated string. The caller
547 * should deallocate it via MEM_FREE when done. A NULL is returned
548 * if the input string is NULL, or if the output string buffer cannot
549 * be allocated.
550 */
551 char *
gen_esc_shellquote(gen_desc_t * gdp,char * str)552 gen_esc_shellquote(gen_desc_t *gdp, char *str)
553 {
554 char *ret,
555 *cp;
556
557 if (str == NULL)
558 return NULL;
559
560 #ifdef __VMS
561 /* For VMS, just dup the string. All character validity
562 * checks has been done prior to getting here.
563 */
564 ret = NULL;
565 if (!util_newstr(&ret, str)) {
566 (void) strcpy(gdp->cdp->i->msgbuf,
567 "gen_esc_shellquote: Out of memory.");
568 DBGPRN(DBG_GEN)(errfp, "%s\n", gdp->cdp->i->msgbuf);
569 return NULL;
570 }
571 #else
572 if ((ret = (char *) MEM_ALLOC("ret", strlen(str) + 1)) == NULL) {
573 (void) strcpy(gdp->cdp->i->msgbuf,
574 "gen_esc_shellquote: Out of memory.");
575 DBGPRN(DBG_GEN)(errfp, "%s\n", gdp->cdp->i->msgbuf);
576 return NULL;
577 }
578
579 for (cp = ret; *str != '\0'; cp++, str++) {
580 if (*str == '\047')
581 *cp = '`'; /* Change single quote to backquote */
582 else
583 *cp = *str;
584 }
585 *cp = '\0';
586 #endif
587
588 return (ret);
589 }
590
591
592 /*
593 * gen_genre_cddb2id3
594 * Map CDDB2 genre to ID3tag genre. This function now does a sparse
595 * mapping because the two genre systems are not one-to-one mappable.
596 * Moreover this has to work for both classic CDDB and CDDB2.
597 *
598 * Args:
599 * genreid - CDDB genre ID string
600 *
601 * Return:
602 * ID3tag genre ID string.
603 */
604 char *
gen_genre_cddb2id3(char * genreid)605 gen_genre_cddb2id3(char *genreid)
606 {
607 cdinfo_genre_t *gp;
608 char *id;
609 int i;
610
611 if ((gp = cdinfo_genre(genreid)) == NULL)
612 return ("12"); /* "Other" */
613
614 if (gp->parent == NULL)
615 id = gp->id; /* CDDB Genre */
616 else
617 id = gp->parent->id; /* CDDB Subgenre */
618
619 /* Look for mapping in the genre mapping table */
620 for (i = 0; id3_gmap[i].cddbgenre != NULL; i++) {
621 if (strcmp(id, id3_gmap[i].cddbgenre) == 0)
622 return (id3_gmap[i].id3genre);
623 }
624
625 return ("12"); /* Other */
626 }
627
628
629 /*
630 * gen_set_eid
631 * Set effective uid/gid to that of the invoking user. This is
632 * called before accessing any device or files for security reasons.
633 *
634 * Args:
635 * cdp - Pointer to the cd_state_t structure
636 *
637 * Return:
638 * FALSE - failure
639 * TRUE - success
640 */
641 bool_t
gen_set_eid(cd_state_t * cdp)642 gen_set_eid(cd_state_t *cdp)
643 {
644 #ifdef HAS_EUID
645 if (util_seteuid(ruid) < 0 || geteuid() != ruid) {
646 (void) strcpy(cdp->i->msgbuf, "gen_set_eid: Cannot set uid");
647 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
648 return FALSE;
649 }
650 if (util_setegid(rgid) < 0 || getegid() != rgid) {
651 (void) strcpy(cdp->i->msgbuf, "gen_set_eid: Cannot set gid");
652 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
653 return FALSE;
654 }
655 #endif
656 return TRUE;
657 }
658
659
660 /*
661 * gen_reset_eid
662 * Restore saved uid/gid after the use of gen_set_eid()..
663 *
664 * Args:
665 * cdp - Pointer to the cd_state_t structure
666 *
667 * Return:
668 * FALSE - failure
669 * TRUE - success
670 */
671 bool_t
gen_reset_eid(cd_state_t * cdp)672 gen_reset_eid(cd_state_t *cdp)
673 {
674 #ifdef HAS_EUID
675 if (util_seteuid(euid) < 0 || geteuid() != euid) {
676 (void) strcpy(cdp->i->msgbuf, "gen_reset_eid: Cannot set uid");
677 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
678 return FALSE;
679 }
680 if (util_setegid(egid) < 0 || getegid() != egid) {
681 (void) strcpy(cdp->i->msgbuf, "gen_reset_eid: Cannot set gid");
682 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
683 return FALSE;
684 }
685 #endif
686
687 return TRUE;
688 }
689
690
691 /*
692 * gen_open_file
693 * Open an output file.
694 *
695 * Args:
696 * cdp - Pointer to the cd_state_t structure
697 * path - File path name
698 * oflag - Open flags
699 * mode - Open mode, or 0 if the mode is not to be set
700 * fmt - File header format to be written
701 * datalen - Audio data length in bytes
702 *
703 * Return:
704 * Pointer to a gen_desc_t structure associated with this open,
705 * or NULL on failure.
706 */
707 gen_desc_t *
gen_open_file(cd_state_t * cdp,char * path,int oflag,mode_t mode,int fmt,size_t datalen)708 gen_open_file(
709 cd_state_t *cdp,
710 char *path,
711 int oflag,
712 mode_t mode,
713 int fmt,
714 size_t datalen
715 )
716 {
717 gen_desc_t *gdp;
718 int fd;
719 char *oflag_str;
720 struct stat stbuf;
721
722 if (!cdda_filefmt_supp(fmt)) {
723 (void) sprintf(cdp->i->msgbuf,
724 "gen_open_file: Support for the selected file "
725 "format (%d) is not compiled-in.",
726 fmt);
727 DBGPRN(DBG_SND)(errfp, "%s\n", cdp->i->msgbuf);
728 return NULL;
729 }
730
731 if (!gen_set_eid(cdp))
732 return NULL;
733
734 if ((oflag & 0x03) == O_WRONLY && (oflag & O_CREAT) != 0) {
735 char *dpath;
736 mode_t dmode;
737
738 if ((dpath = util_dirname(path)) == NULL) {
739 (void) sprintf(cdp->i->msgbuf,
740 "gen_open_file: Invalid directory path:\n%s",
741 dpath);
742 DBGPRN(DBG_SND)(errfp, "%s\n", cdp->i->msgbuf);
743 (void) gen_reset_eid(cdp);
744 return NULL;
745 }
746
747 if (util_dirstat(dpath, &stbuf, FALSE) < 0 ) {
748 if (errno == ENOENT) {
749 /* Set directory perm based on file perm */
750 dmode = (mode | S_IXUSR);
751 if (mode & S_IRGRP)
752 dmode |= (S_IRGRP | S_IXGRP);
753 if (mode & S_IWGRP)
754 dmode |= (S_IWGRP | S_IXGRP);
755 if (mode & S_IROTH)
756 dmode |= (S_IROTH | S_IXOTH);
757 if (mode & S_IWOTH)
758 dmode |= (S_IWOTH | S_IXOTH);
759
760 /* Make directory if necessary */
761 if (!util_mkdir(dpath, dmode)) {
762 (void) sprintf(cdp->i->msgbuf,
763 "gen_open_file: "
764 "Cannot create directory:\n%s",
765 dpath
766 );
767 DBGPRN(DBG_SND)(errfp, "%s\n",
768 cdp->i->msgbuf);
769 MEM_FREE(dpath);
770 (void) gen_reset_eid(cdp);
771 return NULL;
772 }
773 }
774 else {
775 (void) sprintf(cdp->i->msgbuf,
776 "gen_open_file: "
777 "Cannot stat %s (errno=%d)",
778 dpath,
779 errno
780 );
781 DBGPRN(DBG_SND)(errfp, "%s\n", cdp->i->msgbuf);
782 MEM_FREE(dpath);
783 (void) gen_reset_eid(cdp);
784 return NULL;
785 }
786 }
787 else if (!S_ISDIR(stbuf.st_mode)) {
788 (void) sprintf(cdp->i->msgbuf,
789 "gen_open_file: %s:\nNot a directory.",
790 dpath);
791 DBGPRN(DBG_SND)(errfp, "%s\n", cdp->i->msgbuf);
792 MEM_FREE(dpath);
793 (void) gen_reset_eid(cdp);
794 return NULL;
795 }
796
797 MEM_FREE(dpath);
798 }
799
800 #if defined(HAS_LAME) || defined(HAS_FAAC)
801 switch (fmt) {
802 case FILEFMT_MP3:
803 case FILEFMT_AAC:
804 case FILEFMT_MP4:
805 {
806 char *cmd;
807
808 /* Use the external encoder program, pipe audio
809 * data to it.
810 */
811 #ifdef HAS_LAME
812 if (fmt == FILEFMT_MP3 && lamepath == NULL) {
813 (void) strcpy(cdp->i->msgbuf,
814 "LAME encoder program not found.");
815 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
816 (void) gen_reset_eid(cdp);
817 return NULL;
818 }
819 #endif
820
821 #ifdef HAS_FAAC
822 if ((fmt == FILEFMT_AAC || fmt == FILEFMT_MP4) &&
823 faacpath == NULL) {
824 (void) strcpy(cdp->i->msgbuf,
825 "FAAC encoder program not found.");
826 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
827 (void) gen_reset_eid(cdp);
828 return NULL;
829 }
830 #endif
831
832 /* Create temporary gen_desc_t structure */
833 gdp = (gen_desc_t *) MEM_ALLOC(
834 "gen_desc_t", sizeof(gen_desc_t)
835 );
836 if (gdp == NULL) {
837 (void) strcpy(cdp->i->msgbuf,
838 "gen_open_file: Out of memory.");
839 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
840 (void) gen_reset_eid(cdp);
841 return NULL;
842 }
843 (void) memset(gdp, 0, sizeof(gen_desc_t));
844
845 gdp->fd = -1;
846 gdp->mode = mode;
847 gdp->fmt = fmt;
848 gdp->cdp = cdp;
849 gdp->datalen = datalen;
850 gdp->fp = NULL;
851
852 if (!util_newstr(&gdp->path, path)) {
853 MEM_FREE(gdp);
854 (void) strcpy(cdp->i->msgbuf,
855 "gen_open_file: Out of memory.");
856 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
857 (void) gen_reset_eid(cdp);
858 return NULL;
859 }
860
861 switch (fmt) {
862 #ifdef HAS_LAME
863 case FILEFMT_MP3:
864 cmd = if_lame_gencmd(gdp, lamepath);
865 break;
866 #endif
867
868 #ifdef HAS_FAAC
869 case FILEFMT_AAC:
870 case FILEFMT_MP4:
871 cmd = if_faac_gencmd(gdp, faacpath);
872 break;
873 #endif
874
875 default:
876 cmd = NULL;
877 break;
878 }
879
880 if (cmd == NULL) {
881 MEM_FREE(gdp->path);
882 MEM_FREE(gdp);
883 (void) gen_reset_eid(cdp);
884 return NULL;
885 }
886
887 MEM_FREE(gdp->path);
888 MEM_FREE(gdp);
889
890 /* Spawn external encoder and open pipe to it */
891 gdp = gen_open_pipe(cdp, cmd, NULL, fmt, datalen);
892
893 MEM_FREE(cmd);
894
895 (void) gen_reset_eid(cdp);
896 return (gdp);
897 }
898 /*NOTREACHED*/
899
900 default:
901 break;
902 }
903 #endif /* HAS_LAME HAS_FAAC */
904
905 /* Open file */
906 if ((fd = open(path, oflag, mode)) < 0) {
907 (void) sprintf(cdp->i->msgbuf,
908 "gen_open_file: open of %s failed (errno=%d)",
909 path, errno);
910 DBGPRN(DBG_SND)(errfp, "%s\n", cdp->i->msgbuf);
911 (void) gen_reset_eid(cdp);
912 return NULL;
913 }
914
915 DBGPRN(DBG_GEN|DBG_SND)(errfp, "\nOpened [%s]\n", path);
916
917 gdp = (gen_desc_t *) MEM_ALLOC("gen_desc_t", sizeof(gen_desc_t));
918 if (gdp == NULL) {
919 (void) close(fd);
920 (void) strcpy(cdp->i->msgbuf, "gen_open_file: Out of memory.");
921 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
922 (void) gen_reset_eid(cdp);
923 return NULL;
924 }
925 (void) memset(gdp, 0, sizeof(gen_desc_t));
926
927 switch ((int) (oflag & 0x03)) {
928 case O_WRONLY:
929 oflag_str = "w";
930 if (mode != (mode_t) 0)
931 (void) chmod(path, mode);
932 break;
933 case O_RDWR:
934 oflag_str = "r+";
935 if (mode != (mode_t) 0)
936 (void) chmod(path, mode);
937 break;
938 case O_RDONLY:
939 default:
940 oflag_str = "r";
941 break;
942 }
943
944 gdp->fd = fd;
945 gdp->mode = mode;
946 gdp->fmt = fmt;
947 gdp->cdp = cdp;
948 gdp->datalen = datalen;
949
950 #ifdef __VMS
951 gdp->fp = NULL;
952 #else
953 if (fstat(gdp->fd, &stbuf) == 0 &&
954 (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))) {
955 gdp->fp = NULL; /* Use non-stdio on devices special files */
956 }
957 else {
958 if ((gdp->fp = fdopen(gdp->fd, oflag_str)) == NULL) {
959 DBGPRN(DBG_SND)(errfp,
960 "%s: fdopen failed.\n"
961 "Using non-buffered file I/O.\n",
962 path);
963 }
964 else {
965 gdp->buf = (char *) MEM_ALLOC("gdp_buf", GDESC_BUFSZ);
966 if (gdp->buf != NULL)
967 setbuf(gdp->fp, gdp->buf);
968 }
969 }
970 #endif
971
972 if (!util_newstr(&gdp->path, path)) {
973 if (gdp->fp != NULL)
974 (void) fclose(gdp->fp);
975 else
976 (void) close(gdp->fd);
977 MEM_FREE(gdp);
978 (void) strcpy(cdp->i->msgbuf, "gen_open_file: Out of memory.");
979 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
980 (void) gen_reset_eid(cdp);
981 return NULL;
982 }
983
984 /* Update file header */
985 switch (fmt) {
986 case FILEFMT_AU:
987 if (!gen_write_au_hdr(gdp)) {
988 (void) close(fd);
989 MEM_FREE(gdp->path);
990 MEM_FREE(gdp);
991 (void) gen_reset_eid(cdp);
992 return NULL;
993 }
994 break;
995
996 case FILEFMT_WAV:
997 if (!gen_write_wav_hdr(gdp)) {
998 (void) close(fd);
999 MEM_FREE(gdp->path);
1000 MEM_FREE(gdp);
1001 (void) gen_reset_eid(cdp);
1002 return NULL;
1003 }
1004 break;
1005
1006 case FILEFMT_AIFF:
1007 if (!gen_write_aiff_hdr(gdp)) {
1008 (void) close(fd);
1009 MEM_FREE(gdp->path);
1010 MEM_FREE(gdp);
1011 (void) gen_reset_eid(cdp);
1012 return NULL;
1013 }
1014 break;
1015
1016 case FILEFMT_AIFC:
1017 if (!gen_write_aifc_hdr(gdp)) {
1018 (void) close(fd);
1019 MEM_FREE(gdp->path);
1020 MEM_FREE(gdp);
1021 (void) gen_reset_eid(cdp);
1022 return NULL;
1023 }
1024 break;
1025
1026 #ifdef HAS_VORBIS
1027 case FILEFMT_OGG:
1028 if (!if_vorbis_init(gdp)) {
1029 (void) close(fd);
1030 MEM_FREE(gdp->path);
1031 MEM_FREE(gdp);
1032 (void) gen_reset_eid(cdp);
1033 return NULL;
1034 }
1035 break;
1036 #endif /* HAS_VORBIS */
1037
1038 #ifdef HAS_FLAC
1039 case FILEFMT_FLAC:
1040 if (!if_flac_init(gdp)) {
1041 (void) close(fd);
1042 MEM_FREE(gdp->path);
1043 MEM_FREE(gdp);
1044 (void) gen_reset_eid(cdp);
1045 return NULL;
1046 }
1047 break;
1048 #endif /* HAS_FLAC */
1049
1050 default:
1051 break;
1052 }
1053
1054 (void) gen_reset_eid(cdp);
1055 return (gdp);
1056 }
1057
1058
1059 /*
1060 * gen_close_file
1061 * Close a file opened with gen_open_file().
1062 *
1063 * Args:
1064 * gdp - Pointer to the gen_desc_t structure
1065 *
1066 * Return:
1067 * FALSE - failure
1068 * TRUE - success
1069 */
1070 bool_t
gen_close_file(gen_desc_t * gdp)1071 gen_close_file(gen_desc_t *gdp)
1072 {
1073 cd_state_t *cdp = gdp->cdp;
1074 bool_t ret;
1075
1076 if (!gen_set_eid(cdp))
1077 return FALSE;
1078
1079 gdp->datalen = 0;
1080 if (gdp->fp != NULL)
1081 (void) fflush(gdp->fp);
1082
1083 switch (gdp->fmt) {
1084 case FILEFMT_AU:
1085 (void) gen_write_au_hdr(gdp);
1086 break;
1087
1088 case FILEFMT_WAV:
1089 (void) gen_write_wav_hdr(gdp);
1090 break;
1091
1092 case FILEFMT_AIFF:
1093 (void) gen_write_aiff_hdr(gdp);
1094 break;
1095
1096 case FILEFMT_AIFC:
1097 (void) gen_write_aifc_hdr(gdp);
1098 break;
1099
1100 #ifdef HAS_LAME
1101 case FILEFMT_MP3:
1102 ret = gen_close_pipe(gdp);
1103 (void) gen_reset_eid(cdp);
1104 return (ret);
1105 #endif /* HAS_LAME */
1106
1107 #ifdef HAS_VORBIS
1108 case FILEFMT_OGG:
1109 if_vorbis_halt(gdp);
1110 break;
1111 #endif /* HAS_VORBIS */
1112
1113 #ifdef HAS_FLAC
1114 case FILEFMT_FLAC:
1115 if_flac_halt(gdp);
1116 break;
1117 #endif /* HAS_FLAC */
1118
1119 #ifdef HAS_FAAC
1120 case FILEFMT_AAC:
1121 case FILEFMT_MP4:
1122 ret = gen_close_pipe(gdp);
1123 (void) gen_reset_eid(cdp);
1124 return (ret);
1125 #endif /* HAS_FAAC */
1126
1127 default:
1128 break;
1129 }
1130
1131 /* Close file */
1132 if (gdp->fp != NULL)
1133 ret = (fclose(gdp->fp) == 0);
1134 else
1135 ret = (close(gdp->fd) == 0);
1136
1137 if (ret) {
1138 DBGPRN(DBG_GEN|DBG_SND)(errfp, "\nClosed [%s]\n", gdp->path);
1139 }
1140 else {
1141 DBGPRN(DBG_GEN|DBG_SND)(errfp,
1142 "\nClose [%s] failed: errno=%d\n",
1143 gdp->path, errno);
1144 }
1145
1146 MEM_FREE(gdp->path);
1147 MEM_FREE(gdp);
1148
1149 (void) gen_reset_eid(cdp);
1150 return (ret);
1151 }
1152
1153
1154 /*
1155 * gen_open_pipe
1156 * Spawn an external program and connect a pipe to its stdin
1157 *
1158 * Args:
1159 * cdp - Pointer to the cd_state_t structure
1160 * progpath - Command line of program to spawn
1161 * ret_pid - Child process ID return
1162 * fmt - File header format to be written
1163 * datalen - Audio data length in bytes
1164 *
1165 * Return:
1166 * Pointer to a gen_desc_t structure associated with this open,
1167 * or NULL on failure.
1168 */
1169 gen_desc_t *
gen_open_pipe(cd_state_t * cdp,char * progpath,pid_t * ret_pid,int fmt,size_t datalen)1170 gen_open_pipe(
1171 cd_state_t *cdp,
1172 char *progpath,
1173 pid_t *ret_pid,
1174 int fmt,
1175 size_t datalen
1176 )
1177 {
1178 gen_desc_t *gdp;
1179 char *cmd;
1180 pid_t cpid;
1181 int i,
1182 pfd[2];
1183
1184 if (!cdda_filefmt_supp(fmt)) {
1185 (void) sprintf(cdp->i->msgbuf,
1186 "gen_open_pipe: Support for the selected file "
1187 "format (%d) is not compiled-in.",
1188 fmt);
1189 DBGPRN(DBG_SND)(errfp, "%s\n", cdp->i->msgbuf);
1190 return NULL;
1191 }
1192
1193 if (!gen_set_eid(cdp))
1194 return NULL;
1195
1196 if (PIPE(pfd) < 0) {
1197 (void) sprintf(cdp->i->msgbuf,
1198 "gen_open_pipe: output pipe failed (errno=%d)",
1199 errno);
1200 DBGPRN(DBG_SND)(errfp, "%s\n", cdp->i->msgbuf);
1201 (void) gen_reset_eid(cdp);
1202 return NULL;
1203 }
1204
1205 cmd = NULL;
1206 if (!util_newstr(&cmd, progpath)) {
1207 (void) strcpy(cdp->i->msgbuf, "gen_open_pipe: Out of memory.");
1208 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
1209 (void) gen_reset_eid(cdp);
1210 return NULL;
1211 }
1212
1213 #if defined(HAS_LAME) || defined(HAS_FAAC)
1214 if (ret_pid != NULL) {
1215 switch (fmt) {
1216 case FILEFMT_MP3:
1217 case FILEFMT_AAC:
1218 case FILEFMT_MP4:
1219 /* Use the external encoder program, pipe audio
1220 * data to it.
1221 */
1222 #ifdef HAS_LAME
1223 if (fmt == FILEFMT_MP3 && lamepath == NULL) {
1224 (void) strcpy(cdp->i->msgbuf,
1225 "LAME encoder program not found.");
1226 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
1227 (void) gen_reset_eid(cdp);
1228 return NULL;
1229 }
1230 #endif
1231 #ifdef HAS_FAAC
1232 if ((fmt == FILEFMT_AAC || fmt == FILEFMT_MP4) &&
1233 faacpath == NULL) {
1234 (void) strcpy(cdp->i->msgbuf,
1235 "FAAC encoder program not found.");
1236 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
1237 (void) gen_reset_eid(cdp);
1238 return NULL;
1239 }
1240 #endif
1241
1242 /* Create temporary gen_desc_t structure */
1243 gdp = (gen_desc_t *) MEM_ALLOC(
1244 "gen_desc_t", sizeof(gen_desc_t)
1245 );
1246 if (gdp == NULL) {
1247 (void) strcpy(cdp->i->msgbuf,
1248 "gen_open_file: Out of memory.");
1249 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
1250 (void) gen_reset_eid(cdp);
1251 return NULL;
1252 }
1253 (void) memset(gdp, 0, sizeof(gen_desc_t));
1254
1255 gdp->fd = -1;
1256 gdp->fmt = fmt;
1257 gdp->cdp = cdp;
1258 gdp->datalen = datalen;
1259 gdp->fp = NULL;
1260 gdp->flags |= GDESC_ISPIPE;
1261
1262 if (!util_newstr(&gdp->path, progpath)) {
1263 MEM_FREE(gdp);
1264 (void) strcpy(cdp->i->msgbuf,
1265 "gen_open_pipe: Out of memory.");
1266 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
1267 (void) gen_reset_eid(cdp);
1268 return NULL;
1269 }
1270
1271 if (cmd != NULL)
1272 MEM_FREE(cmd);
1273
1274 switch (fmt) {
1275 #ifdef HAS_LAME
1276 case FILEFMT_MP3:
1277 cmd = if_lame_gencmd(gdp, lamepath);
1278 break;
1279 #endif
1280
1281 #ifdef HAS_FAAC
1282 case FILEFMT_AAC:
1283 case FILEFMT_MP4:
1284 cmd = if_faac_gencmd(gdp, faacpath);
1285 break;
1286 #endif
1287
1288 default:
1289 cmd = NULL;
1290 }
1291
1292 if (cmd == NULL) {
1293 MEM_FREE(gdp->path);
1294 MEM_FREE(gdp);
1295 (void) gen_reset_eid(cdp);
1296 return NULL;
1297 }
1298
1299 MEM_FREE(gdp->path);
1300 MEM_FREE(gdp);
1301 break;
1302
1303 default:
1304 break;
1305 }
1306 }
1307 #endif /* HAS_LAME HAS_FAAC */
1308
1309 DBGPRN(DBG_GEN|DBG_SND)(errfp, "\nInvoking [%s]\n", cmd);
1310
1311 switch (cpid = FORK()) {
1312 case -1:
1313 (void) sprintf(cdp->i->msgbuf,
1314 "gen_open_pipe: fork failed (errno=%d)",
1315 errno);
1316 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
1317 (void) gen_reset_eid(cdp);
1318 return NULL;
1319
1320 case 0:
1321 /* Child */
1322
1323 (void) util_signal(SIGTERM, SIG_DFL);
1324 (void) util_signal(SIGALRM, SIG_DFL);
1325 (void) util_signal(SIGPIPE, SIG_DFL);
1326
1327 #ifdef __VMS
1328 /* Special handling to work with VMS vfork */
1329
1330 /* Close on exec for unneeded pipe/file descriptors */
1331 (void) fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
1332
1333 for (i = 3; i < 255; i++) {
1334 if (i != pfd[0])
1335 (void) fcntl(i, F_SETFD, FD_CLOEXEC);
1336 }
1337
1338 /* Connect stdin to pipe, leave stdout and stderr as is */
1339 if (decc$set_child_standard_streams(pfd[0], -1, -1) < 0) {
1340 (void) fprintf(errfp,
1341 "gen_open_pipe: "
1342 "decc$set_child_standard_streams failed "
1343 "(errno=%d)\n",
1344 errno
1345 );
1346 _exit(1);
1347 }
1348
1349 gen_exec_vms(cmd);
1350 #else
1351 /* Close unneeded pipe descriptors */
1352 (void) close(pfd[1]);
1353
1354 for (i = 3; i < 255; i++) {
1355 /* Close unneeded fds */
1356 if (i != pfd[0])
1357 (void) close(i);
1358 }
1359
1360 (void) close(0); /* Close stdin */
1361
1362 if (dup(pfd[0]) < 0) { /* Connect stdin to pipe */
1363 (void) fprintf(errfp,
1364 "gen_open_pipe: dup failed (errno=%d)\n",
1365 errno);
1366 _exit(1);
1367 }
1368
1369 /* Exec the pipe program */
1370 (void) execl(STR_SHPATH, STR_SHNAME, STR_SHARG, cmd, NULL);
1371 #endif
1372
1373 /* An error has occurred */
1374 _exit(errno);
1375
1376 /*NOTREACHED*/
1377
1378 default:
1379 /* Parent */
1380 if (ret_pid != NULL)
1381 *ret_pid = cpid;
1382
1383 /* Close unneeded pipe descriptor */
1384 (void) close(pfd[0]);
1385 break;
1386 }
1387
1388 gdp = (gen_desc_t *) MEM_ALLOC("gen_desc_t", sizeof(gen_desc_t));
1389 if (gdp == NULL) {
1390 (void) close(pfd[1]);
1391 (void) strcpy(cdp->i->msgbuf, "gen_open_pipe: Out of memory.");
1392 DBGPRN(DBG_GEN)(errfp, "%s\n", cdp->i->msgbuf);
1393 (void) gen_reset_eid(cdp);
1394 return NULL;
1395 }
1396 (void) memset(gdp, 0, sizeof(gen_desc_t));
1397
1398 gdp->fd = pfd[1];
1399 gdp->fmt = fmt;
1400 gdp->cdp = cdp;
1401 gdp->datalen = datalen;
1402 gdp->flags |= GDESC_ISPIPE;
1403 gdp->path = cmd;
1404 gdp->cpid = cpid;
1405
1406 #ifdef __VMS
1407 gdp->fp = NULL;
1408 #else
1409 if ((gdp->fp = fdopen(gdp->fd, "w")) == NULL) {
1410 DBGPRN(DBG_SND)(errfp,
1411 "%s: fdopen failed.\n"
1412 "Using non-buffered file I/O.\n",
1413 gdp->path);
1414 }
1415 else {
1416 gdp->buf = (char *) MEM_ALLOC("gdp_buf", GDESC_BUFSZ);
1417 if (gdp->buf != NULL)
1418 setbuf(gdp->fp, gdp->buf);
1419 }
1420 #endif
1421
1422 switch (fmt) {
1423 case FILEFMT_AU:
1424 if (!gen_write_au_hdr(gdp)) {
1425 (void) close(pfd[1]);
1426 MEM_FREE(gdp->path);
1427 MEM_FREE(gdp);
1428 (void) gen_reset_eid(cdp);
1429 return NULL;
1430 }
1431 break;
1432
1433 case FILEFMT_WAV:
1434 if (!gen_write_wav_hdr(gdp)) {
1435 (void) close(pfd[1]);
1436 MEM_FREE(gdp->path);
1437 MEM_FREE(gdp);
1438 (void) gen_reset_eid(cdp);
1439 return NULL;
1440 }
1441 break;
1442
1443 case FILEFMT_AIFF:
1444 if (!gen_write_aiff_hdr(gdp)) {
1445 (void) close(pfd[1]);
1446 MEM_FREE(gdp->path);
1447 MEM_FREE(gdp);
1448 (void) gen_reset_eid(cdp);
1449 return NULL;
1450 }
1451 break;
1452
1453 case FILEFMT_AIFC:
1454 if (!gen_write_aifc_hdr(gdp)) {
1455 (void) close(pfd[1]);
1456 MEM_FREE(gdp->path);
1457 MEM_FREE(gdp);
1458 (void) gen_reset_eid(cdp);
1459 return NULL;
1460 }
1461 break;
1462
1463 #ifdef HAS_VORBIS
1464 case FILEFMT_OGG:
1465 if (!if_vorbis_init(gdp)) {
1466 (void) close(pfd[1]);
1467 MEM_FREE(gdp->path);
1468 MEM_FREE(gdp);
1469 (void) gen_reset_eid(cdp);
1470 return NULL;
1471 }
1472 break;
1473 #endif /* HAS_VORBIS */
1474
1475 #ifdef HAS_FLAC
1476 case FILEFMT_FLAC:
1477 if (!if_flac_init(gdp)) {
1478 (void) close(pfd[1]);
1479 MEM_FREE(gdp->path);
1480 MEM_FREE(gdp);
1481 (void) gen_reset_eid(cdp);
1482 return NULL;
1483 }
1484 break;
1485 #endif /* HAS_FLAC */
1486
1487 default:
1488 break;
1489 }
1490
1491 (void) gen_reset_eid(cdp);
1492 return (gdp);
1493 }
1494
1495
1496 /*
1497 * gen_close_pipe
1498 * Close the program pipe that was opened via gen_open_pipe()
1499 *
1500 * Args:
1501 * gdp - Pointer to the gen_desc_t structure
1502 *
1503 * Return:
1504 * FALSE - failure
1505 * TRUE - success
1506 */
1507 bool_t
gen_close_pipe(gen_desc_t * gdp)1508 gen_close_pipe(gen_desc_t *gdp)
1509 {
1510 cd_state_t *cdp = gdp->cdp;
1511 char *cp;
1512 int ret,
1513 wstat;
1514
1515 if (!gen_set_eid(cdp))
1516 return FALSE;
1517
1518 switch (gdp->fmt) {
1519 #ifdef HAS_VORBIS
1520 case FILEFMT_OGG:
1521 if_vorbis_halt(gdp);
1522 break;
1523 #endif /* HAS_VORBIS */
1524
1525 #ifdef HAS_FLAC
1526 case FILEFMT_FLAC:
1527 if_flac_halt(gdp);
1528 break;
1529 #endif /* HAS_FLAC */
1530
1531 default:
1532 break;
1533 }
1534
1535 /* Close pipe */
1536 if (gdp->fp != NULL)
1537 (void) fclose(gdp->fp);
1538 else {
1539 #ifdef __VMS
1540 decc$write_eof_to_mbx(gdp->fd);
1541 #endif
1542 (void) close(gdp->fd);
1543 }
1544
1545 /* Cut off anything beyond argv[0] */
1546 if ((cp = strchr(gdp->path, ' ')) != NULL)
1547 *cp = '\0';
1548
1549 /* Wait for pipe program and get exit status */
1550 ret = util_waitchild(gdp->cpid, app_data.hb_timeout,
1551 NULL, 0, TRUE, &wstat);
1552 if (ret < 0) {
1553 DBGPRN(DBG_GEN|DBG_SND)(errfp,
1554 "%s: waitpid failed (pid=%d errno=%d)\n",
1555 gdp->path, (int) gdp->cpid, errno);
1556 ret = -1;
1557 }
1558 else if (WIFEXITED(wstat)) {
1559 ret = WEXITSTATUS(wstat);
1560 if (ret == 0) {
1561 DBGPRN(DBG_GEN|DBG_SND)(errfp,
1562 "%s: Program exited (pid=%d status=%d)\n",
1563 gdp->path, (int) gdp->cpid, ret);
1564 }
1565 else {
1566 (void) sprintf(cdp->i->msgbuf,
1567 "NOTICE: %s\nProgram exited abnormally "
1568 "(pid=%d status=%d)",
1569 gdp->path, (int) gdp->cpid, ret
1570 );
1571 DBGPRN(DBG_GEN|DBG_SND)(errfp, "%s\n", cdp->i->msgbuf);
1572 }
1573 }
1574 else if (WIFSIGNALED(wstat)) {
1575 ret = WTERMSIG(wstat);
1576 (void) sprintf(cdp->i->msgbuf,
1577 "NOTICE: %s\nProgram killed (pid=%d status=%d)",
1578 gdp->path, (int) gdp->cpid, ret
1579 );
1580 DBGPRN(DBG_GEN|DBG_SND)(errfp, "%s\n", cdp->i->msgbuf);
1581 }
1582
1583 MEM_FREE(gdp->path);
1584 MEM_FREE(gdp);
1585
1586 (void) gen_reset_eid(cdp);
1587 return (ret == 0);
1588 }
1589
1590
1591 /*
1592 * gen_read_chunk
1593 * Reads data from the opened file descriptor, catering for possible
1594 * interrupts.
1595 *
1596 * Args:
1597 * gdp - Pointer to the gen_desc_t structure
1598 * data - Data buffer
1599 * len - Data length
1600 *
1601 * Return:
1602 * FALSE - failure
1603 * TRUE - success
1604 */
1605 bool_t
gen_read_chunk(gen_desc_t * gdp,byte_t * data,size_t len)1606 gen_read_chunk(gen_desc_t *gdp, byte_t *data, size_t len)
1607 {
1608 int offset = 0,
1609 req,
1610 n;
1611
1612 if (data == NULL || len == 0)
1613 /* Nothing to do */
1614 return TRUE;
1615
1616 if (!gen_set_eid(gdp->cdp))
1617 return FALSE;
1618
1619 if ((app_data.debug & DBG_SND) != 0) {
1620 if ((gdp->flags & GDESC_ISPIPE) != 0) {
1621 char *cp,
1622 sav = '\0';
1623
1624 if ((cp = strchr(gdp->path, ' ')) != NULL) {
1625 sav = *cp;
1626 *cp = '\0';
1627 }
1628
1629 (void) fprintf(errfp,
1630 "\nReading pipe [%s]: %d bytes\n",
1631 gdp->path, (int) len);
1632
1633 if (cp != NULL)
1634 *cp = sav;
1635 }
1636 else
1637 (void) fprintf(errfp, "\nReading [%s]: %d bytes\n",
1638 gdp->path, (int) len);
1639 }
1640
1641 /* Read in */
1642 do {
1643 errno = 0;
1644 req = len - offset;
1645
1646 if (gdp->fp != NULL) {
1647 n = fread(&data[offset], 1, req, gdp->fp);
1648 if (n < req) {
1649 if (feof(gdp->fp)) {
1650 (void) gen_reset_eid(gdp->cdp);
1651 return TRUE;
1652 }
1653 else if (ferror(gdp->fp))
1654 break;
1655 }
1656 }
1657 else {
1658 if ((n = read(gdp->fd, &data[offset], req)) < 0)
1659 break;
1660 }
1661
1662 /* Have we finished? */
1663 if (n == req || n == 0) {
1664 (void) gen_reset_eid(gdp->cdp);
1665 return TRUE;
1666 }
1667
1668 offset += n;
1669
1670 } while (offset < len);
1671
1672 if (n >= 0) {
1673 (void) sprintf(gdp->cdp->i->msgbuf,
1674 "gen_read_chunk: read failed (ret=%d)", n);
1675 }
1676 else {
1677 (void) sprintf(gdp->cdp->i->msgbuf,
1678 "gen_read_chunk: read failed (errno=%d)",
1679 errno);
1680 }
1681 DBGPRN(DBG_SND)(errfp, "%s\n", gdp->cdp->i->msgbuf);
1682
1683 (void) gen_reset_eid(gdp->cdp);
1684 return FALSE;
1685 }
1686
1687
1688 /*
1689 * gen_write_chunk
1690 * Writes data to the opened file descriptor, catering for possible
1691 * interrupts.
1692 *
1693 * Args:
1694 * gdp - Pointer to the gen_desc_t structure
1695 * data - Data buffer
1696 * len - Data length
1697 *
1698 * Return:
1699 * FALSE - failure
1700 * TRUE - success
1701 */
1702 bool_t
gen_write_chunk(gen_desc_t * gdp,byte_t * data,size_t len)1703 gen_write_chunk(gen_desc_t *gdp, byte_t *data, size_t len)
1704 {
1705 int offset = 0,
1706 req,
1707 n;
1708 bool_t ret;
1709
1710 if (!gen_set_eid(gdp->cdp))
1711 return FALSE;
1712
1713 if (data == NULL && len == 0) {
1714 /* Flush buffered I/O */
1715 if (gdp->fp != NULL)
1716 (void) fflush(gdp->fp);
1717
1718 /* Write EOF */
1719 ret = !(write(gdp->fd, NULL, 0) < 0);
1720
1721 (void) gen_reset_eid(gdp->cdp);
1722 return (ret);
1723 }
1724
1725 if (len == 0) {
1726 /* Nothing to do */
1727 (void) gen_reset_eid(gdp->cdp);
1728 return TRUE;
1729 }
1730
1731 if ((gdp->flags & GDESC_WRITEOUT) == 0) {
1732 switch (gdp->fmt) {
1733 #ifdef HAS_VORBIS
1734 case FILEFMT_OGG:
1735 ret = if_vorbis_encode_chunk(gdp, data, len);
1736 (void) gen_reset_eid(gdp->cdp);
1737 return (ret);
1738 #endif /* HAS_VORBIS */
1739
1740 #ifdef HAS_FLAC
1741 case FILEFMT_FLAC:
1742 ret = if_flac_encode_chunk(gdp, data, len);
1743 (void) gen_reset_eid(gdp->cdp);
1744 return (ret);
1745 #endif /* HAS_FLAC */
1746
1747 default:
1748 break;
1749 }
1750 }
1751
1752 gdp->flags &= ~GDESC_WRITEOUT;
1753
1754 if ((app_data.debug & DBG_SND) != 0) {
1755 if ((gdp->flags & GDESC_ISPIPE) != 0) {
1756 char *cp,
1757 sav = '\0';
1758
1759 if ((cp = strchr(gdp->path, ' ')) != NULL) {
1760 sav = *cp;
1761 *cp = '\0';
1762 }
1763
1764 (void) fprintf(errfp,
1765 "\nWriting pipe [%s]: %d bytes\n",
1766 gdp->path, (int) len);
1767
1768 if (cp != NULL)
1769 *cp = sav;
1770 }
1771 else
1772 (void) fprintf(errfp, "\nWriting [%s]: %d bytes\n",
1773 gdp->path, (int) len);
1774 }
1775
1776 /* Write out */
1777 do {
1778 req = len - offset;
1779 errno = 0;
1780
1781 if (gdp->fp != NULL) {
1782 n = fwrite(&data[offset], 1, req, gdp->fp);
1783 if (n < req && ferror(gdp->fp))
1784 break;
1785 }
1786 else {
1787 if ((n = write(gdp->fd, &data[offset], req)) <= 0)
1788 break;
1789 }
1790
1791 /* Have we finished? */
1792 if (n == req) {
1793 (void) gen_reset_eid(gdp->cdp);
1794 return TRUE;
1795 }
1796
1797 offset += n;
1798
1799 } while (offset < len);
1800
1801 if (n >= 0) {
1802 (void) sprintf(gdp->cdp->i->msgbuf,
1803 "gen_write_chunk: write failed (ret=%d)", n);
1804 }
1805 else {
1806 (void) sprintf(gdp->cdp->i->msgbuf,
1807 "gen_write_chunk: write failed (errno=%d)",
1808 errno);
1809 }
1810 DBGPRN(DBG_SND)(errfp, "%s\n", gdp->cdp->i->msgbuf);
1811
1812 (void) gen_reset_eid(gdp->cdp);
1813 return FALSE;
1814 }
1815
1816
1817 /*
1818 * gen_seek
1819 * Wrapper function for fseek(3) and lseek(2). It uses the appropriate
1820 * function based on whether a buffered stdio stream is opened.
1821 *
1822 * Args:
1823 * gdp - Pointer to the gen_desc_t structure
1824 * offset - Seek offset
1825 * whence - Seek type (SEEK_SET, SEEK_CUR or SEEK_END)
1826 *
1827 * Return:
1828 * FALSE - failure
1829 * TRUE - success
1830 */
1831 bool_t
gen_seek(gen_desc_t * gdp,off_t offset,int whence)1832 gen_seek(gen_desc_t *gdp, off_t offset, int whence)
1833 {
1834 off_t ret;
1835
1836 if (gdp->fp != NULL) {
1837 if ((ret = fseek(gdp->fp, (long) offset, whence)) < 0) {
1838 (void) sprintf(gdp->cdp->i->msgbuf,
1839 "gen_seek: %s: fseek failed (errno=%d)",
1840 gdp->path, errno);
1841 DBGPRN(DBG_SND)(errfp, "%s\n", gdp->cdp->i->msgbuf);
1842 }
1843 }
1844 else {
1845 if ((ret = lseek(gdp->fd, offset, whence)) < 0) {
1846 (void) sprintf(gdp->cdp->i->msgbuf,
1847 "gen_seek: %s: lseek failed (errno=%d)",
1848 gdp->path, errno);
1849 DBGPRN(DBG_SND)(errfp, "%s\n", gdp->cdp->i->msgbuf);
1850 }
1851 }
1852
1853 return ((bool_t) (ret >= 0));
1854 }
1855
1856
1857 #ifndef __VMS
1858 /*
1859 * gen_ioctl
1860 * Wrapper for ioctl(2) function.
1861 *
1862 * Args:
1863 * gdp - Pointer to the gen_desc_t structure
1864 * req - ioctl request
1865 * arg - ioctl argument
1866 *
1867 * Return:
1868 * FALSE - failure
1869 * TRUE - success
1870 */
1871 bool_t
gen_ioctl(gen_desc_t * gdp,int req,void * arg)1872 gen_ioctl(gen_desc_t *gdp, int req, void *arg)
1873 {
1874 /* Flush buffered I/O first */
1875 if (gdp->fp != NULL)
1876 (void) fflush(gdp->fp);
1877
1878 /* Do the ioctl */
1879 return (!(ioctl(gdp->fd, req, arg) < 0));
1880 }
1881 #endif
1882
1883
1884 /*
1885 * gen_byteswap
1886 * Carry out byte swapping.
1887 *
1888 * Args:
1889 * srcbuf - audio data source buffer
1890 * tgtbuf - audio data target buffer
1891 * len - data length in bytes
1892 *
1893 * Return:
1894 * Nothing.
1895 */
1896 void
gen_byteswap(byte_t * srcbuf,byte_t * tgtbuf,size_t len)1897 gen_byteswap(byte_t *srcbuf, byte_t *tgtbuf, size_t len)
1898 {
1899 int i;
1900
1901 /* Run through samples */
1902 for (i = 0; i < (int) len; i += 2) {
1903 /* Byte swapping */
1904 tgtbuf[i] = srcbuf[i+1];
1905 tgtbuf[i+1] = srcbuf[i];;
1906 }
1907 }
1908
1909
1910 /*
1911 * gen_filefmt_be
1912 * Determine whether the audio data should be in big endian for the
1913 * format of file and platform we're running on.
1914 *
1915 * Args:
1916 * filefmt - File format code
1917 *
1918 * Return:
1919 * FALSE - should be little endian
1920 * TRUE - should be big endian
1921 */
1922 bool_t
gen_filefmt_be(int filefmt)1923 gen_filefmt_be(int filefmt)
1924 {
1925 switch (filefmt) {
1926 case FILEFMT_FLAC:
1927 /* Always native endian */
1928 #if _BYTE_ORDER_ == _B_ENDIAN_
1929 return TRUE;
1930 #else
1931 return FALSE;
1932 #endif
1933
1934 case FILEFMT_AU:
1935 case FILEFMT_AIFF:
1936 case FILEFMT_AIFC:
1937 case FILEFMT_MP3:
1938 case FILEFMT_AAC:
1939 case FILEFMT_MP4:
1940 /* Always big endian */
1941 return TRUE;
1942
1943 case FILEFMT_WAV:
1944 case FILEFMT_OGG:
1945 default:
1946 /* Always little endian */
1947 return FALSE;
1948 }
1949 }
1950
1951
1952 /*
1953 * gen_chroute_att
1954 * Other optional processing may need be carried out on the audio
1955 * prior to sending it to the audio device, file and/or pipe stream.
1956 *
1957 * Args:
1958 * chroute - Channel routing value:
1959 * CHROUTE_NORMAL Leave as is
1960 * CHROUTE_REVERSE Swaps left and right
1961 * CHROUTE_L_MONO Feed left to both channels
1962 * CHROUTE_R_MONO Feed right to both channels
1963 * CHROUTE_MONO Feed left and right avrg to both channels
1964 * att - Attenuation level (0-100)
1965 * data - Data to transform
1966 * len - Data length in bytes
1967 *
1968 * Return:
1969 * Nothing.
1970 */
1971 void
gen_chroute_att(int chroute,int att,sword16_t * data,size_t len)1972 gen_chroute_att(int chroute, int att, sword16_t *data, size_t len)
1973 {
1974 int nsamples,
1975 i,
1976 l,
1977 r;
1978 sword16_t temp1,
1979 temp2,
1980 temp3;
1981
1982 nsamples = (int) (len / (sizeof(sword16_t) << 1));
1983
1984 switch (chroute) {
1985 case CHROUTE_NORMAL:
1986 if (att >= 100)
1987 break;
1988
1989 for (i = 0; i < nsamples; i++) {
1990 l = i << 1;
1991 r = l + 1;
1992
1993 /* Set attenuation */
1994 temp1 = util_lswap16(data[l]);
1995 temp2 = util_lswap16(data[r]);
1996 if (att == 0) {
1997 temp1 = temp2 = 0;
1998 }
1999 else {
2000 temp1 = (sword16_t) ((temp1 * att) / 100);
2001 temp2 = (sword16_t) ((temp2 * att) / 100);
2002 }
2003
2004 data[l] = util_lswap16(temp1);
2005 data[r] = util_lswap16(temp2);
2006 }
2007 break;
2008
2009 case CHROUTE_REVERSE:
2010 for (i = 0; i < nsamples; i++) {
2011 l = i << 1;
2012 r = l + 1;
2013
2014 /* Set attenuation */
2015 temp1 = util_lswap16(data[l]);
2016 temp2 = util_lswap16(data[r]);
2017 if (att == 0) {
2018 temp1 = temp2 = 0;
2019 }
2020 else if (att < 100) {
2021 temp1 = (sword16_t) ((temp1 * att) / 100);
2022 temp2 = (sword16_t) ((temp2 * att) / 100);
2023 }
2024
2025 /* Swap left and right samples */
2026 data[l] = util_lswap16(temp2);
2027 data[r] = util_lswap16(temp1);
2028 }
2029 break;
2030
2031 case CHROUTE_L_MONO:
2032 for (i = 0; i < nsamples; i++) {
2033 l = i << 1;
2034 r = l + 1;
2035
2036 /* Set attenuation */
2037 temp1 = util_lswap16(data[l]);
2038 if (att == 0)
2039 temp1 = 0;
2040 else if (att < 100)
2041 temp1 = (sword16_t) ((temp1 * att) / 100);
2042
2043 /* Make right same as left */
2044 data[l] = data[r] = util_lswap16(temp1);
2045 }
2046 break;
2047
2048 case CHROUTE_R_MONO:
2049 for (i = 0; i < nsamples; i++) {
2050 l = i << 1;
2051 r = l + 1;
2052
2053 /* Set attenuation */
2054 temp2 = util_lswap16(data[r]);
2055 if (att == 0)
2056 temp2 = 0;
2057 else if (att < 100)
2058 temp2 = (sword16_t) ((temp2 * att) / 100);
2059
2060 /* Make left same as right */
2061 data[l] = data[r] = util_lswap16(temp2);
2062 }
2063 break;
2064
2065 case CHROUTE_MONO:
2066 for (i = 0; i < nsamples; i++) {
2067 l = i << 1;
2068 r = l + 1;
2069
2070 /* Set attenuation */
2071 temp1 = util_lswap16(data[l]);
2072 temp2 = util_lswap16(data[r]);
2073 if (att == 0) {
2074 temp1 = temp2 = 0;
2075 }
2076 else if (att < 100) {
2077 temp1 = (sword16_t) ((temp1 * att) / 100);
2078 temp2 = (sword16_t) ((temp2 * att) / 100);
2079 }
2080
2081 /* Average left and right */
2082 temp3 = util_lswap16((temp1 + temp2) >> 1);
2083 data[l] = data[r] = temp3;
2084 }
2085 break;
2086
2087 default:
2088 break;
2089 }
2090 }
2091
2092
2093 /*
2094 * gen_write_init
2095 * Generic write services initialization
2096 *
2097 * Args:
2098 * None.
2099 *
2100 * Return:
2101 * Nothing.
2102 */
2103 void
gen_write_init(void)2104 gen_write_init(void)
2105 {
2106 static bool_t gen_write_initted = FALSE;
2107
2108 if (!gen_write_initted) {
2109 gen_write_initted = TRUE;
2110
2111 ruid = util_get_ouid();
2112 rgid = util_get_ogid();
2113 euid = geteuid();
2114 egid = getegid();
2115 }
2116 }
2117
2118 #endif /* CDDA_SUPPORTED */
2119
2120
2121