1 /* Implements the public API for using libSoX file formats.
2 * All public functions & data are prefixed with sox_ .
3 *
4 * (c) 2005-8 Chris Bagwell and SoX contributors
5 *
6 * This library is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or (at
9 * your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this library; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "sox_i.h"
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #ifdef HAVE_IO_H
33 #include <io.h>
34 #endif
35
36 #if HAVE_MAGIC
37 #include <magic.h>
38 #endif
39
40 #define PIPE_AUTO_DETECT_SIZE 256 /* Only as much as we can rewind a pipe */
41 #define AUTO_DETECT_SIZE 4096 /* For seekable file, so no restriction */
42
auto_detect_format(sox_format_t * ft,char const * ext)43 static char const * auto_detect_format(sox_format_t * ft, char const * ext)
44 {
45 char data[AUTO_DETECT_SIZE];
46 size_t len = lsx_readbuf(ft, data, ft->seekable? sizeof(data) : PIPE_AUTO_DETECT_SIZE);
47 #define CHECK(type, p2, l2, d2, p1, l1, d1) if (len >= p1 + l1 && \
48 !memcmp(data + p1, d1, (size_t)l1) && !memcmp(data + p2, d2, (size_t)l2)) return #type;
49 CHECK(voc , 0, 0, "" , 0, 20, "Creative Voice File\x1a")
50 CHECK(smp , 0, 0, "" , 0, 17, "SOUND SAMPLE DATA")
51 CHECK(wve , 0, 0, "" , 0, 15, "ALawSoundFile**")
52 CHECK(gsrt , 0, 0, "" , 16, 9, "ring.bin")
53 CHECK(amr-wb, 0, 0, "" , 0, 9, "#!AMR-WB\n")
54 CHECK(prc , 0, 0, "" , 0, 8, "\x37\x00\x00\x10\x6d\x00\x00\x10")
55 CHECK(sph , 0, 0, "" , 0, 7, "NIST_1A")
56 CHECK(amr-nb, 0, 0, "" , 0, 6, "#!AMR\n")
57 CHECK(txw , 0, 0, "" , 0, 6, "LM8953")
58 CHECK(sndt , 0, 0, "" , 0, 6, "SOUND\x1a")
59 CHECK(vorbis, 0, 4, "OggS" , 29, 6, "vorbis")
60 CHECK(opus , 0, 4, "OggS" , 28, 8, "OpusHead")
61 CHECK(speex , 0, 4, "OggS" , 28, 6, "Speex")
62 CHECK(hcom ,65, 4, "FSSD" , 128,4, "HCOM")
63 CHECK(wav , 0, 4, "RIFF" , 8, 4, "WAVE")
64 CHECK(wav , 0, 4, "RIFX" , 8, 4, "WAVE")
65 CHECK(wav , 0, 4, "RF64" , 8, 4, "WAVE")
66 CHECK(aiff , 0, 4, "FORM" , 8, 4, "AIFF")
67 CHECK(aifc , 0, 4, "FORM" , 8, 4, "AIFC")
68 CHECK(8svx , 0, 4, "FORM" , 8, 4, "8SVX")
69 CHECK(maud , 0, 4, "FORM" , 8, 4, "MAUD")
70 CHECK(xa , 0, 0, "" , 0, 4, "XA\0\0")
71 CHECK(xa , 0, 0, "" , 0, 4, "XAI\0")
72 CHECK(xa , 0, 0, "" , 0, 4, "XAJ\0")
73 CHECK(au , 0, 0, "" , 0, 4, ".snd")
74 CHECK(au , 0, 0, "" , 0, 4, "dns.")
75 CHECK(au , 0, 0, "" , 0, 4, "\0ds.")
76 CHECK(au , 0, 0, "" , 0, 4, ".sd\0")
77 CHECK(flac , 0, 0, "" , 0, 4, "fLaC")
78 CHECK(avr , 0, 0, "" , 0, 4, "2BIT")
79 CHECK(caf , 0, 0, "" , 0, 4, "caff")
80 CHECK(wv , 0, 0, "" , 0, 4, "wvpk")
81 CHECK(paf , 0, 0, "" , 0, 4, " paf")
82 CHECK(sf , 0, 0, "" , 0, 4, "\144\243\001\0")
83 CHECK(sf , 0, 0, "" , 0, 4, "\0\001\243\144")
84 CHECK(sf , 0, 0, "" , 0, 4, "\144\243\002\0")
85 CHECK(sf , 0, 0, "" , 0, 4, "\0\002\243\144")
86 CHECK(sf , 0, 0, "" , 0, 4, "\144\243\003\0")
87 CHECK(sf , 0, 0, "" , 0, 4, "\0\003\243\144")
88 CHECK(sf , 0, 0, "" , 0, 4, "\144\243\004\0")
89 CHECK(sox , 0, 0, "" , 0, 4, ".SoX")
90 CHECK(sox , 0, 0, "" , 0, 4, "XoS.")
91
92 if (ext && !strcasecmp(ext, "snd"))
93 CHECK(sndr , 7, 1, "" , 0, 2, "\0")
94 #undef CHECK
95
96 #if HAVE_MAGIC
97 if (sox_globals.use_magic) {
98 static magic_t magic;
99 char const * filetype = NULL;
100 if (!magic) {
101 magic = magic_open(MAGIC_MIME | MAGIC_SYMLINK);
102 if (magic)
103 magic_load(magic, NULL);
104 }
105 if (magic)
106 filetype = magic_buffer(magic, data, len);
107 if (filetype && strncmp(filetype, "application/octet-stream", (size_t)24) &&
108 !lsx_strends(filetype, "/unknown") &&
109 strncmp(filetype, "text/plain", (size_t)10) )
110 return filetype;
111 else if (filetype)
112 lsx_debug("libmagic detected %s", filetype);
113 }
114 #endif
115 return NULL;
116 }
117
118 static sox_encodings_info_t const s_sox_encodings_info[] = {
119 {sox_encodings_none , "n/a" , "Unknown or not applicable"},
120 {sox_encodings_none , "Signed PCM" , "Signed Integer PCM"},
121 {sox_encodings_none , "Unsigned PCM" , "Unsigned Integer PCM"},
122 {sox_encodings_none , "F.P. PCM" , "Floating Point PCM"},
123 {sox_encodings_none , "F.P. PCM" , "Floating Point (text) PCM"},
124 {sox_encodings_none , "FLAC" , "FLAC"},
125 {sox_encodings_none , "HCOM" , "HCOM"},
126 {sox_encodings_none , "WavPack" , "WavPack"},
127 {sox_encodings_none , "F.P. WavPack" , "Floating Point WavPack"},
128 {sox_encodings_lossy1, "u-law" , "u-law"},
129 {sox_encodings_lossy1, "A-law" , "A-law"},
130 {sox_encodings_lossy1, "G.721 ADPCM" , "G.721 ADPCM"},
131 {sox_encodings_lossy1, "G.723 ADPCM" , "G.723 ADPCM"},
132 {sox_encodings_lossy1, "CL ADPCM (8)" , "CL ADPCM (from 8-bit)"},
133 {sox_encodings_lossy1, "CL ADPCM (16)", "CL ADPCM (from 16-bit)"},
134 {sox_encodings_lossy1, "MS ADPCM" , "MS ADPCM"},
135 {sox_encodings_lossy1, "IMA ADPCM" , "IMA ADPCM"},
136 {sox_encodings_lossy1, "OKI ADPCM" , "OKI ADPCM"},
137 {sox_encodings_lossy1, "DPCM" , "DPCM"},
138 {sox_encodings_none , "DWVW" , "DWVW"},
139 {sox_encodings_none , "DWVWN" , "DWVWN"},
140 {sox_encodings_lossy2, "GSM" , "GSM"},
141 {sox_encodings_lossy2, "MPEG audio" , "MPEG audio (layer I, II or III)"},
142 {sox_encodings_lossy2, "Vorbis" , "Vorbis"},
143 {sox_encodings_lossy2, "AMR-WB" , "AMR-WB"},
144 {sox_encodings_lossy2, "AMR-NB" , "AMR-NB"},
145 {sox_encodings_lossy2, "CVSD" , "CVSD"},
146 {sox_encodings_lossy2, "LPC10" , "LPC10"},
147 {sox_encodings_lossy2, "Opus" , "Opus"},
148 };
149
150 assert_static(array_length(s_sox_encodings_info) == SOX_ENCODINGS,
151 SIZE_MISMATCH_BETWEEN_sox_encoding_t_AND_sox_encodings_info);
152
153 sox_encodings_info_t const *
sox_get_encodings_info(void)154 sox_get_encodings_info(void)
155 {
156 return s_sox_encodings_info;
157 }
158
sox_precision(sox_encoding_t encoding,unsigned bits_per_sample)159 unsigned sox_precision(sox_encoding_t encoding, unsigned bits_per_sample)
160 {
161 switch (encoding) {
162 case SOX_ENCODING_DWVW: return bits_per_sample;
163 case SOX_ENCODING_DWVWN: return !bits_per_sample? 16: 0; /* ? */
164 case SOX_ENCODING_HCOM: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 1? bits_per_sample: 0;
165 case SOX_ENCODING_WAVPACK:
166 case SOX_ENCODING_FLAC: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0;
167 case SOX_ENCODING_SIGN2: return bits_per_sample <= 32? bits_per_sample : 0;
168 case SOX_ENCODING_UNSIGNED: return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0;
169
170 case SOX_ENCODING_ALAW: return bits_per_sample == 8? 13: 0;
171 case SOX_ENCODING_ULAW: return bits_per_sample == 8? 14: 0;
172
173 case SOX_ENCODING_CL_ADPCM: return bits_per_sample? 8: 0;
174 case SOX_ENCODING_CL_ADPCM16: return bits_per_sample == 4? 13: 0;
175 case SOX_ENCODING_MS_ADPCM: return bits_per_sample == 4? 14: 0;
176 case SOX_ENCODING_IMA_ADPCM: return bits_per_sample == 4? 13: 0;
177 case SOX_ENCODING_OKI_ADPCM: return bits_per_sample == 4? 12: 0;
178 case SOX_ENCODING_G721: return bits_per_sample == 4? 12: 0;
179 case SOX_ENCODING_G723: return bits_per_sample == 3? 8:
180 bits_per_sample == 5? 14: 0;
181 case SOX_ENCODING_CVSD: return bits_per_sample == 1? 16: 0;
182 case SOX_ENCODING_DPCM: return bits_per_sample; /* ? */
183
184 case SOX_ENCODING_MP3: return 0; /* Accept the precision returned by the format. */
185
186 case SOX_ENCODING_GSM:
187 case SOX_ENCODING_VORBIS:
188 case SOX_ENCODING_OPUS:
189 case SOX_ENCODING_AMR_WB:
190 case SOX_ENCODING_AMR_NB:
191 case SOX_ENCODING_LPC10: return !bits_per_sample? 16: 0;
192
193 case SOX_ENCODING_WAVPACKF:
194 case SOX_ENCODING_FLOAT: return bits_per_sample == 32 ? 25: bits_per_sample == 64 ? 54: 0;
195 case SOX_ENCODING_FLOAT_TEXT: return !bits_per_sample? 54: 0;
196
197 case SOX_ENCODINGS:
198 case SOX_ENCODING_UNKNOWN: break;
199 }
200 return 0;
201 }
202
sox_init_encodinginfo(sox_encodinginfo_t * e)203 void sox_init_encodinginfo(sox_encodinginfo_t * e)
204 {
205 e->reverse_bytes = sox_option_default;
206 e->reverse_nibbles = sox_option_default;
207 e->reverse_bits = sox_option_default;
208 e->compression = HUGE_VAL;
209 }
210
211 /*--------------------------------- Comments ---------------------------------*/
212
sox_num_comments(sox_comments_t comments)213 size_t sox_num_comments(sox_comments_t comments)
214 {
215 size_t result = 0;
216 if (!comments)
217 return 0;
218 while (*comments++)
219 ++result;
220 return result;
221 }
222
sox_append_comment(sox_comments_t * comments,char const * comment)223 void sox_append_comment(sox_comments_t * comments, char const * comment)
224 {
225 size_t n = sox_num_comments(*comments);
226 *comments = lsx_realloc(*comments, (n + 2) * sizeof(**comments));
227 assert(comment);
228 (*comments)[n++] = lsx_strdup(comment);
229 (*comments)[n] = 0;
230 }
231
sox_append_comments(sox_comments_t * comments,char const * comment)232 void sox_append_comments(sox_comments_t * comments, char const * comment)
233 {
234 char * end;
235 if (comment) {
236 while ((end = strchr(comment, '\n'))) {
237 size_t len = end - comment;
238 char * c = lsx_malloc((len + 1) * sizeof(*c));
239 strncpy(c, comment, len);
240 c[len] = '\0';
241 sox_append_comment(comments, c);
242 comment += len + 1;
243 free(c);
244 }
245 if (*comment)
246 sox_append_comment(comments, comment);
247 }
248 }
249
sox_copy_comments(sox_comments_t comments)250 sox_comments_t sox_copy_comments(sox_comments_t comments)
251 {
252 sox_comments_t result = 0;
253
254 if (comments) while (*comments)
255 sox_append_comment(&result, *comments++);
256 return result;
257 }
258
sox_delete_comments(sox_comments_t * comments)259 void sox_delete_comments(sox_comments_t * comments)
260 {
261 sox_comments_t p = *comments;
262
263 if (p) while (*p)
264 free(*p++);
265 free(*comments);
266 *comments = 0;
267 }
268
lsx_cat_comments(sox_comments_t comments)269 char * lsx_cat_comments(sox_comments_t comments)
270 {
271 sox_comments_t p = comments;
272 size_t len = 0;
273 char * result;
274
275 if (p) while (*p)
276 len += strlen(*p++) + 1;
277
278 result = lsx_calloc(len? len : 1, sizeof(*result));
279
280 if ((p = comments) && *p) {
281 strcpy(result, *p);
282 while (*++p)
283 strcat(strcat(result, "\n"), *p);
284 }
285 return result;
286 }
287
sox_find_comment(sox_comments_t comments,char const * id)288 char const * sox_find_comment(sox_comments_t comments, char const * id)
289 {
290 size_t len = strlen(id);
291
292 if (comments) for (;*comments; ++comments)
293 if (!strncasecmp(*comments, id, len) && (*comments)[len] == '=')
294 return *comments + len + 1;
295 return NULL;
296 }
297
set_endiannesses(sox_format_t * ft)298 static void set_endiannesses(sox_format_t * ft)
299 {
300 if (ft->encoding.opposite_endian)
301 ft->encoding.reverse_bytes = (ft->handler.flags & SOX_FILE_ENDIAN)?
302 !(ft->handler.flags & SOX_FILE_ENDBIG) != MACHINE_IS_BIGENDIAN : sox_true;
303 else if (ft->encoding.reverse_bytes == sox_option_default)
304 ft->encoding.reverse_bytes = (ft->handler.flags & SOX_FILE_ENDIAN)?
305 !(ft->handler.flags & SOX_FILE_ENDBIG) == MACHINE_IS_BIGENDIAN : sox_false;
306
307 /* FIXME: Change reports to suitable warnings if trying
308 * to override something that can't be overridden. */
309
310 if (ft->handler.flags & SOX_FILE_ENDIAN) {
311 if (ft->encoding.reverse_bytes == (sox_option_t)
312 (!(ft->handler.flags & SOX_FILE_ENDBIG) != MACHINE_IS_BIGENDIAN))
313 lsx_report("`%s': overriding file-type byte-order", ft->filename);
314 } else if (ft->encoding.reverse_bytes == sox_option_yes)
315 lsx_report("`%s': overriding machine byte-order", ft->filename);
316
317 if (ft->encoding.reverse_bits == sox_option_default)
318 ft->encoding.reverse_bits = !!(ft->handler.flags & SOX_FILE_BIT_REV);
319 else if (ft->encoding.reverse_bits == !(ft->handler.flags & SOX_FILE_BIT_REV))
320 lsx_report("`%s': overriding file-type bit-order", ft->filename);
321
322 if (ft->encoding.reverse_nibbles == sox_option_default)
323 ft->encoding.reverse_nibbles = !!(ft->handler.flags & SOX_FILE_NIB_REV);
324 else
325 if (ft->encoding.reverse_nibbles == !(ft->handler.flags & SOX_FILE_NIB_REV))
326 lsx_report("`%s': overriding file-type nibble-order", ft->filename);
327 }
328
is_seekable(sox_format_t const * ft)329 static sox_bool is_seekable(sox_format_t const * ft)
330 {
331 struct stat st;
332
333 assert(ft);
334 if (!ft->fp)
335 return sox_false;
336 fstat(fileno((FILE*)ft->fp), &st);
337 return ((st.st_mode & S_IFMT) == S_IFREG);
338 }
339
340 /* check that all settings have been given */
sox_checkformat(sox_format_t * ft)341 static int sox_checkformat(sox_format_t * ft)
342 {
343 ft->sox_errno = SOX_SUCCESS;
344
345 if (!ft->signal.rate) {
346 lsx_fail_errno(ft,SOX_EFMT,"sampling rate was not specified");
347 return SOX_EOF;
348 }
349 if (!ft->signal.precision) {
350 lsx_fail_errno(ft,SOX_EFMT,"data encoding or sample size was not specified");
351 return SOX_EOF;
352 }
353 return SOX_SUCCESS;
354 }
355
is_url(char const * text)356 static sox_bool is_url(char const * text) /* detects only wget-supported URLs */
357 {
358 return !(
359 strncasecmp(text, "http:" , (size_t)5) &&
360 strncasecmp(text, "https:", (size_t)6) &&
361 strncasecmp(text, "ftp:" , (size_t)4));
362 }
363
xfclose(FILE * file,lsx_io_type io_type)364 static int xfclose(FILE * file, lsx_io_type io_type)
365 {
366 return
367 #ifdef HAVE_POPEN
368 io_type != lsx_io_file? pclose(file) :
369 #endif
370 fclose(file);
371 }
372
xfopen(char const * identifier,char const * mode,lsx_io_type * io_type)373 static FILE * xfopen(char const * identifier, char const * mode, lsx_io_type * io_type)
374 {
375 *io_type = lsx_io_file;
376
377 if (*identifier == '|') {
378 FILE * f = NULL;
379 #ifdef HAVE_POPEN
380 #ifndef POPEN_MODE
381 #define POPEN_MODE "r"
382 #endif
383 f = popen(identifier + 1, POPEN_MODE);
384 *io_type = lsx_io_pipe;
385 #else
386 lsx_fail("this build of SoX cannot open pipes");
387 #endif
388 return f;
389 }
390 else if (is_url(identifier)) {
391 FILE * f = NULL;
392 #ifdef HAVE_POPEN
393 char const * const command_format = "wget --no-check-certificate -q -O- \"%s\"";
394 char * command = lsx_malloc(strlen(command_format) + strlen(identifier));
395 sprintf(command, command_format, identifier);
396 f = popen(command, POPEN_MODE);
397 free(command);
398 *io_type = lsx_io_url;
399 #else
400 lsx_fail("this build of SoX cannot open URLs");
401 #endif
402 return f;
403 }
404 return fopen(identifier, mode);
405 }
406
407 /* Hack to rewind pipes (a small amount).
408 * Works by resetting the FILE buffer pointer */
rewind_pipe(FILE * fp)409 static void UNUSED rewind_pipe(FILE * fp)
410 {
411 /* _FSTDIO is for Torek stdio (i.e. most BSD-derived libc's)
412 * In theory, we no longer need to check _NEWLIB_VERSION or __APPLE__ */
413 #if defined _FSTDIO || defined _NEWLIB_VERSION || defined __APPLE__
414 # ifdef __DragonFly__
415 struct __FILE_public *fpp = (struct __FILE_public *)fp;
416 fpp->_p -= PIPE_AUTO_DETECT_SIZE;
417 fpp->_r += PIPE_AUTO_DETECT_SIZE;
418 # else
419 fp->_p -= PIPE_AUTO_DETECT_SIZE;
420 fp->_r += PIPE_AUTO_DETECT_SIZE;
421 # endif
422 #elif defined __GLIBC__
423 fp->_IO_read_ptr = fp->_IO_read_base;
424 #elif defined _MSC_VER || defined _WIN32 || defined _WIN64 || \
425 defined _ISO_STDIO_ISO_H || defined __sgi
426 fp->_ptr = fp->_base;
427 #else
428 /* To fix this #error, either simply remove the #error line and live without
429 * file-type detection with pipes, or add support for your compiler in the
430 * lines above. Test with cat monkey.wav | ./sox --info - */
431 #error FIX NEEDED HERE
432 #define NO_REWIND_PIPE
433 (void)fp;
434 #endif
435 }
436
open_read(char const * path,void * buffer UNUSED,size_t buffer_size UNUSED,sox_signalinfo_t const * signal,sox_encodinginfo_t const * encoding,char const * filetype)437 static sox_format_t * open_read(
438 char const * path,
439 void * buffer UNUSED,
440 size_t buffer_size UNUSED,
441 sox_signalinfo_t const * signal,
442 sox_encodinginfo_t const * encoding,
443 char const * filetype)
444 {
445 sox_format_t * ft = lsx_calloc(1, sizeof(*ft));
446 sox_format_handler_t const * handler;
447 char const * const io_types[] = {"file", "pipe", "file URL"};
448 char const * type = "";
449 size_t input_bufsiz = sox_globals.input_bufsiz?
450 sox_globals.input_bufsiz : sox_globals.bufsiz;
451
452 if (filetype) {
453 if (!(handler = sox_find_format(filetype, sox_false))) {
454 lsx_fail("no handler for given file type `%s'", filetype);
455 goto error;
456 }
457 ft->handler = *handler;
458 }
459
460 if (!(ft->handler.flags & SOX_FILE_NOSTDIO)) {
461 if (!strcmp(path, "-")) { /* Use stdin if the filename is "-" */
462 if (sox_globals.stdin_in_use_by) {
463 lsx_fail("`-' (stdin) already in use by `%s'", sox_globals.stdin_in_use_by);
464 goto error;
465 }
466 sox_globals.stdin_in_use_by = "audio input";
467 SET_BINARY_MODE(stdin);
468 ft->fp = stdin;
469 }
470 else {
471 ft->fp =
472 #ifdef HAVE_FMEMOPEN
473 buffer? fmemopen(buffer, buffer_size, "rb") :
474 #endif
475 xfopen(path, "rb", &ft->io_type);
476 type = io_types[ft->io_type];
477 if (ft->fp == NULL) {
478 lsx_fail("can't open input %s `%s': %s", type, path, strerror(errno));
479 goto error;
480 }
481 }
482 if (setvbuf (ft->fp, NULL, _IOFBF, sizeof(char) * input_bufsiz)) {
483 lsx_fail("Can't set read buffer");
484 goto error;
485 }
486 ft->seekable = is_seekable(ft);
487 }
488
489 if (!filetype) {
490 if (ft->seekable) {
491 filetype = auto_detect_format(ft, lsx_find_file_extension(path));
492 lsx_rewind(ft);
493 }
494 #ifndef NO_REWIND_PIPE
495 else if (!(ft->handler.flags & SOX_FILE_NOSTDIO) &&
496 input_bufsiz >= PIPE_AUTO_DETECT_SIZE) {
497 filetype = auto_detect_format(ft, lsx_find_file_extension(path));
498 rewind_pipe(ft->fp);
499 ft->tell_off = 0;
500 }
501 #endif
502
503 if (filetype) {
504 lsx_report("detected file format type `%s'", filetype);
505 if (!(handler = sox_find_format(filetype, sox_false))) {
506 lsx_fail("no handler for detected file type `%s'", filetype);
507 goto error;
508 }
509 }
510 else {
511 if (ft->io_type == lsx_io_pipe) {
512 filetype = "sox"; /* With successful pipe rewind, this isn't useful */
513 lsx_report("assuming input pipe `%s' has file-type `sox'", path);
514 }
515 else if (!(filetype = lsx_find_file_extension(path))) {
516 lsx_fail("can't determine type of %s `%s'", type, path);
517 goto error;
518 }
519 if (!(handler = sox_find_format(filetype, sox_true))) {
520 lsx_fail("no handler for file extension `%s'", filetype);
521 goto error;
522 }
523 }
524 ft->handler = *handler;
525 if (ft->handler.flags & SOX_FILE_NOSTDIO) {
526 xfclose(ft->fp, ft->io_type);
527 ft->fp = NULL;
528 }
529 }
530 if (!ft->handler.startread && !ft->handler.read) {
531 lsx_fail("file type `%s' isn't readable", filetype);
532 goto error;
533 }
534
535 ft->mode = 'r';
536 ft->filetype = lsx_strdup(filetype);
537 ft->filename = lsx_strdup(path);
538 if (signal)
539 ft->signal = *signal;
540
541 if (encoding)
542 ft->encoding = *encoding;
543 else sox_init_encodinginfo(&ft->encoding);
544 set_endiannesses(ft);
545
546 if ((ft->handler.flags & SOX_FILE_DEVICE) && !(ft->handler.flags & SOX_FILE_PHONY))
547 lsx_set_signal_defaults(ft);
548
549 ft->priv = lsx_calloc(1, ft->handler.priv_size);
550 /* Read and write starters can change their formats. */
551 if (ft->handler.startread && (*ft->handler.startread)(ft) != SOX_SUCCESS) {
552 lsx_fail("can't open input %s `%s': %s", type, ft->filename, ft->sox_errstr);
553 goto error;
554 }
555
556 /* Fill in some defaults: */
557 if (sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample))
558 ft->signal.precision = sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample);
559 if (!(ft->handler.flags & SOX_FILE_PHONY) && !ft->signal.channels)
560 ft->signal.channels = 1;
561
562 if (sox_checkformat(ft) != SOX_SUCCESS) {
563 lsx_fail("bad input format for %s `%s': %s", type, ft->filename, ft->sox_errstr);
564 goto error;
565 }
566
567 if (signal) {
568 if (signal->rate && signal->rate != ft->signal.rate)
569 lsx_warn("can't set sample rate %g; using %g", signal->rate, ft->signal.rate);
570 if (signal->channels && signal->channels != ft->signal.channels)
571 lsx_warn("can't set %u channels; using %u", signal->channels, ft->signal.channels);
572 }
573 return ft;
574
575 error:
576 if (ft->fp && ft->fp != stdin)
577 xfclose(ft->fp, ft->io_type);
578 free(ft->priv);
579 free(ft->filename);
580 free(ft->filetype);
581 free(ft);
582 return NULL;
583 }
584
sox_open_read(char const * path,sox_signalinfo_t const * signal,sox_encodinginfo_t const * encoding,char const * filetype)585 sox_format_t * sox_open_read(
586 char const * path,
587 sox_signalinfo_t const * signal,
588 sox_encodinginfo_t const * encoding,
589 char const * filetype)
590 {
591 return open_read(path, NULL, (size_t)0, signal, encoding, filetype);
592 }
593
sox_open_mem_read(void * buffer,size_t buffer_size,sox_signalinfo_t const * signal,sox_encodinginfo_t const * encoding,char const * filetype)594 sox_format_t * sox_open_mem_read(
595 void * buffer,
596 size_t buffer_size,
597 sox_signalinfo_t const * signal,
598 sox_encodinginfo_t const * encoding,
599 char const * filetype)
600 {
601 return open_read("", buffer, buffer_size, signal,encoding,filetype);
602 }
603
sox_format_supports_encoding(char const * path,char const * filetype,sox_encodinginfo_t const * encoding)604 sox_bool sox_format_supports_encoding(
605 char const * path,
606 char const * filetype,
607 sox_encodinginfo_t const * encoding)
608 {
609 #define enc_arg(T) (T)handler->write_formats[i++]
610 sox_bool is_file_extension = filetype == NULL;
611 sox_format_handler_t const * handler;
612 unsigned i = 0, s;
613 sox_encoding_t e;
614
615 assert(path || filetype);
616 assert(encoding);
617 if (!filetype)
618 filetype = lsx_find_file_extension(path);
619
620 if (!filetype || !(handler = sox_find_format(filetype, is_file_extension)) ||
621 !handler->write_formats)
622 return sox_false;
623 while ((e = enc_arg(sox_encoding_t))) {
624 if (e == encoding->encoding) {
625 sox_bool has_bits;
626 for (has_bits = sox_false; (s = enc_arg(unsigned)); has_bits = sox_true)
627 if (s == encoding->bits_per_sample)
628 return sox_true;
629 if (!has_bits && !encoding->bits_per_sample)
630 return sox_true;
631 break;
632 }
633 while (enc_arg(unsigned));
634 }
635 return sox_false;
636 #undef enc_arg
637 }
638
set_output_format(sox_format_t * ft)639 static void set_output_format(sox_format_t * ft)
640 {
641 sox_encoding_t e = SOX_ENCODING_UNKNOWN;
642 unsigned i, s;
643 unsigned const * encodings = ft->handler.write_formats;
644 #define enc_arg(T) (T)encodings[i++]
645
646 if (ft->handler.write_rates){
647 if (!ft->signal.rate)
648 ft->signal.rate = ft->handler.write_rates[0];
649 else {
650 sox_rate_t r;
651 i = 0;
652 while ((r = ft->handler.write_rates[i++])) {
653 if (r == ft->signal.rate)
654 break;
655 }
656 if (r != ft->signal.rate) {
657 sox_rate_t given = ft->signal.rate, max = 0;
658 ft->signal.rate = HUGE_VAL;
659 i = 0;
660 while ((r = ft->handler.write_rates[i++])) {
661 if (r > given && r < ft->signal.rate)
662 ft->signal.rate = r;
663 else max = max(r, max);
664 }
665 if (ft->signal.rate == HUGE_VAL)
666 ft->signal.rate = max;
667 lsx_warn("%s can't encode at %gHz; using %gHz", ft->handler.names[0], given, ft->signal.rate);
668 }
669 }
670 }
671 else if (!ft->signal.rate)
672 ft->signal.rate = SOX_DEFAULT_RATE;
673
674 if (ft->handler.flags & SOX_FILE_CHANS) {
675 if (ft->signal.channels == 1 && !(ft->handler.flags & SOX_FILE_MONO)) {
676 ft->signal.channels = (ft->handler.flags & SOX_FILE_STEREO)? 2 : 4;
677 lsx_warn("%s can't encode mono; setting channels to %u", ft->handler.names[0], ft->signal.channels);
678 } else
679 if (ft->signal.channels == 2 && !(ft->handler.flags & SOX_FILE_STEREO)) {
680 ft->signal.channels = (ft->handler.flags & SOX_FILE_QUAD)? 4 : 1;
681 lsx_warn("%s can't encode stereo; setting channels to %u", ft->handler.names[0], ft->signal.channels);
682 } else
683 if (ft->signal.channels == 4 && !(ft->handler.flags & SOX_FILE_QUAD)) {
684 ft->signal.channels = (ft->handler.flags & SOX_FILE_STEREO)? 2 : 1;
685 lsx_warn("%s can't encode quad; setting channels to %u", ft->handler.names[0], ft->signal.channels);
686 }
687 } else ft->signal.channels = max(ft->signal.channels, 1);
688
689 if (!encodings)
690 return;
691 /* If an encoding has been given, check if it supported by this handler */
692 if (ft->encoding.encoding) {
693 i = 0;
694 while ((e = enc_arg(sox_encoding_t))) {
695 if (e == ft->encoding.encoding)
696 break;
697 while (enc_arg(unsigned));
698 }
699 if (e != ft->encoding.encoding) {
700 lsx_warn("%s can't encode %s", ft->handler.names[0], sox_encodings_info[ft->encoding.encoding].desc);
701 ft->encoding.encoding = 0;
702 }
703 else {
704 unsigned max_p = 0;
705 unsigned max_p_s = 0;
706 unsigned given_size = 0;
707 sox_bool found = sox_false;
708 if (ft->encoding.bits_per_sample)
709 given_size = ft->encoding.bits_per_sample;
710 ft->encoding.bits_per_sample = 65;
711 while ((s = enc_arg(unsigned))) {
712 if (s == given_size)
713 found = sox_true;
714 if (sox_precision(e, s) >= ft->signal.precision) {
715 if (s < ft->encoding.bits_per_sample)
716 ft->encoding.bits_per_sample = s;
717 }
718 else if (sox_precision(e, s) > max_p) {
719 max_p = sox_precision(e, s);
720 max_p_s = s;
721 }
722 }
723 if (ft->encoding.bits_per_sample == 65)
724 ft->encoding.bits_per_sample = max_p_s;
725 if (given_size) {
726 if (found)
727 ft->encoding.bits_per_sample = given_size;
728 else lsx_warn("%s can't encode %s to %u-bit", ft->handler.names[0], sox_encodings_info[ft->encoding.encoding].desc, given_size);
729 }
730 }
731 }
732
733 /* If a size has been given, check if it supported by this handler */
734 if (!ft->encoding.encoding && ft->encoding.bits_per_sample) {
735 i = 0;
736 s= 0;
737 while (s != ft->encoding.bits_per_sample && (e = enc_arg(sox_encoding_t)))
738 while ((s = enc_arg(unsigned)) && s != ft->encoding.bits_per_sample);
739 if (s != ft->encoding.bits_per_sample) {
740 lsx_warn("%s can't encode to %u-bit", ft->handler.names[0], ft->encoding.bits_per_sample);
741 ft->encoding.bits_per_sample = 0;
742 }
743 else ft->encoding.encoding = e;
744 }
745
746 /* Find the smallest lossless encoding with precision >= signal.precision */
747 if (!ft->encoding.encoding) {
748 ft->encoding.bits_per_sample = 65;
749 i = 0;
750 while ((e = enc_arg(sox_encoding_t)))
751 while ((s = enc_arg(unsigned)))
752 if (!(sox_encodings_info[e].flags & (sox_encodings_lossy1 | sox_encodings_lossy2)) &&
753 sox_precision(e, s) >= ft->signal.precision && s < ft->encoding.bits_per_sample) {
754 ft->encoding.encoding = e;
755 ft->encoding.bits_per_sample = s;
756 }
757 }
758
759 /* Find the smallest lossy encoding with precision >= signal precision,
760 * or, if none such, the highest precision encoding */
761 if (!ft->encoding.encoding) {
762 unsigned max_p = 0;
763 sox_encoding_t max_p_e = 0;
764 unsigned max_p_s = 0;
765 i = 0;
766 while ((e = enc_arg(sox_encoding_t)))
767 do {
768 s = enc_arg(unsigned);
769 if (sox_precision(e, s) >= ft->signal.precision) {
770 if (s < ft->encoding.bits_per_sample) {
771 ft->encoding.encoding = e;
772 ft->encoding.bits_per_sample = s;
773 }
774 }
775 else if (sox_precision(e, s) > max_p) {
776 max_p = sox_precision(e, s);
777 max_p_e = e;
778 max_p_s = s;
779 }
780 } while (s);
781 if (!ft->encoding.encoding) {
782 ft->encoding.encoding = max_p_e;
783 ft->encoding.bits_per_sample = max_p_s;
784 }
785 }
786 ft->signal.precision = sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample);
787 #undef enc_arg
788 }
789
sox_write_handler(char const * path,char const * filetype,char const ** filetype1)790 sox_format_handler_t const * sox_write_handler(
791 char const * path,
792 char const * filetype,
793 char const * * filetype1)
794 {
795 sox_format_handler_t const * handler;
796 if (filetype) {
797 if (!(handler = sox_find_format(filetype, sox_false))) {
798 if (filetype1)
799 lsx_fail("no handler for given file type `%s'", filetype);
800 return NULL;
801 }
802 }
803 else if (path) {
804 if (!(filetype = lsx_find_file_extension(path))) {
805 if (filetype1)
806 lsx_fail("can't determine type of `%s'", path);
807 return NULL;
808 }
809 if (!(handler = sox_find_format(filetype, sox_true))) {
810 if (filetype1)
811 lsx_fail("no handler for file extension `%s'", filetype);
812 return NULL;
813 }
814 }
815 else return NULL;
816 if (!handler->startwrite && !handler->write) {
817 if (filetype1)
818 lsx_fail("file type `%s' isn't writable", filetype);
819 return NULL;
820 }
821 if (filetype1)
822 *filetype1 = filetype;
823 return handler;
824 }
825
open_write(char const * path,void * buffer UNUSED,size_t buffer_size UNUSED,char ** buffer_ptr UNUSED,size_t * buffer_size_ptr UNUSED,sox_signalinfo_t const * signal,sox_encodinginfo_t const * encoding,char const * filetype,sox_oob_t const * oob,sox_bool (* overwrite_permitted)(const char * filename))826 static sox_format_t * open_write(
827 char const * path,
828 void * buffer UNUSED,
829 size_t buffer_size UNUSED,
830 char * * buffer_ptr UNUSED,
831 size_t * buffer_size_ptr UNUSED,
832 sox_signalinfo_t const * signal,
833 sox_encodinginfo_t const * encoding,
834 char const * filetype,
835 sox_oob_t const * oob,
836 sox_bool (*overwrite_permitted)(const char *filename))
837 {
838 sox_format_t * ft = lsx_calloc(sizeof(*ft), 1);
839 sox_format_handler_t const * handler;
840
841 if (!path || !signal) {
842 lsx_fail("must specify file name and signal parameters to write file");
843 goto error;
844 }
845
846 if (!(handler = sox_write_handler(path, filetype, &filetype)))
847 goto error;
848
849 ft->handler = *handler;
850
851 if (!(ft->handler.flags & SOX_FILE_NOSTDIO)) {
852 if (!strcmp(path, "-")) { /* Use stdout if the filename is "-" */
853 if (sox_globals.stdout_in_use_by) {
854 lsx_fail("`-' (stdout) already in use by `%s'", sox_globals.stdout_in_use_by);
855 goto error;
856 }
857 sox_globals.stdout_in_use_by = "audio output";
858 SET_BINARY_MODE(stdout);
859 ft->fp = stdout;
860 }
861 else {
862 struct stat st;
863 if (!stat(path, &st) && (st.st_mode & S_IFMT) == S_IFREG &&
864 (overwrite_permitted && !overwrite_permitted(path))) {
865 lsx_fail("permission to overwrite `%s' denied", path);
866 goto error;
867 }
868 ft->fp =
869 #ifdef HAVE_FMEMOPEN
870 buffer? fmemopen(buffer, buffer_size, "w+b") :
871 buffer_ptr? open_memstream(buffer_ptr, buffer_size_ptr) :
872 #endif
873 fopen(path, "w+b");
874 if (ft->fp == NULL) {
875 lsx_fail("can't open output file `%s': %s", path, strerror(errno));
876 goto error;
877 }
878 }
879
880 /* stdout tends to be line-buffered. Override this */
881 /* to be Full Buffering. */
882 if (setvbuf (ft->fp, NULL, _IOFBF, sizeof(char) * sox_globals.bufsiz)) {
883 lsx_fail("Can't set write buffer");
884 goto error;
885 }
886 ft->seekable = is_seekable(ft);
887 }
888
889 ft->filetype = lsx_strdup(filetype);
890 ft->filename = lsx_strdup(path);
891 ft->mode = 'w';
892 ft->signal = *signal;
893
894 if (encoding)
895 ft->encoding = *encoding;
896 else sox_init_encodinginfo(&ft->encoding);
897 set_endiannesses(ft);
898
899 if (oob) {
900 ft->oob = *oob;
901 /* deep copy: */
902 ft->oob.comments = sox_copy_comments(oob->comments);
903 }
904
905 set_output_format(ft);
906
907 /* FIXME: doesn't cover the situation where
908 * codec changes audio length due to block alignment (e.g. 8svx, gsm): */
909 if (signal->rate && signal->channels)
910 ft->signal.length = ft->signal.length * ft->signal.rate / signal->rate *
911 ft->signal.channels / signal->channels + .5;
912
913 if ((ft->handler.flags & SOX_FILE_REWIND) && strcmp(ft->filetype, "sox") && !ft->signal.length && !ft->seekable)
914 lsx_warn("can't seek in output file `%s'; length in file header will be unspecified", ft->filename);
915
916 ft->priv = lsx_calloc(1, ft->handler.priv_size);
917 /* Read and write starters can change their formats. */
918 if (ft->handler.startwrite && (ft->handler.startwrite)(ft) != SOX_SUCCESS){
919 lsx_fail("can't open output file `%s': %s", ft->filename, ft->sox_errstr);
920 goto error;
921 }
922
923 if (sox_checkformat(ft) != SOX_SUCCESS) {
924 lsx_fail("bad format for output file `%s': %s", ft->filename, ft->sox_errstr);
925 goto error;
926 }
927
928 if ((ft->handler.flags & SOX_FILE_DEVICE) && signal) {
929 if (signal->rate && signal->rate != ft->signal.rate)
930 lsx_report("can't set sample rate %g; using %g", signal->rate, ft->signal.rate);
931 if (signal->channels && signal->channels != ft->signal.channels)
932 lsx_report("can't set %u channels; using %u", signal->channels, ft->signal.channels);
933 }
934 return ft;
935
936 error:
937 if (ft->fp && ft->fp != stdout)
938 xfclose(ft->fp, ft->io_type);
939 free(ft->priv);
940 free(ft->filename);
941 free(ft->filetype);
942 free(ft);
943 return NULL;
944 }
945
sox_open_write(char const * path,sox_signalinfo_t const * signal,sox_encodinginfo_t const * encoding,char const * filetype,sox_oob_t const * oob,sox_bool (* overwrite_permitted)(const char * filename))946 sox_format_t * sox_open_write(
947 char const * path,
948 sox_signalinfo_t const * signal,
949 sox_encodinginfo_t const * encoding,
950 char const * filetype,
951 sox_oob_t const * oob,
952 sox_bool (*overwrite_permitted)(const char *filename))
953 {
954 return open_write(path, NULL, (size_t)0, NULL, NULL, signal, encoding, filetype, oob, overwrite_permitted);
955 }
956
sox_open_mem_write(void * buffer,size_t buffer_size,sox_signalinfo_t const * signal,sox_encodinginfo_t const * encoding,char const * filetype,sox_oob_t const * oob)957 sox_format_t * sox_open_mem_write(
958 void * buffer,
959 size_t buffer_size,
960 sox_signalinfo_t const * signal,
961 sox_encodinginfo_t const * encoding,
962 char const * filetype,
963 sox_oob_t const * oob)
964 {
965 return open_write("", buffer, buffer_size, NULL, NULL, signal, encoding, filetype, oob, NULL);
966 }
967
sox_open_memstream_write(char ** buffer_ptr,size_t * buffer_size_ptr,sox_signalinfo_t const * signal,sox_encodinginfo_t const * encoding,char const * filetype,sox_oob_t const * oob)968 sox_format_t * sox_open_memstream_write(
969 char * * buffer_ptr,
970 size_t * buffer_size_ptr,
971 sox_signalinfo_t const * signal,
972 sox_encodinginfo_t const * encoding,
973 char const * filetype,
974 sox_oob_t const * oob)
975 {
976 return open_write("", NULL, (size_t)0, buffer_ptr, buffer_size_ptr, signal, encoding, filetype, oob, NULL);
977 }
978
sox_read(sox_format_t * ft,sox_sample_t * buf,size_t len)979 size_t sox_read(sox_format_t * ft, sox_sample_t * buf, size_t len)
980 {
981 size_t actual;
982 if (ft->signal.length != SOX_UNSPEC)
983 len = min(len, ft->signal.length - ft->olength);
984 actual = ft->handler.read? (*ft->handler.read)(ft, buf, len) : 0;
985 actual = actual > len? 0 : actual;
986 ft->olength += actual;
987 return actual;
988 }
989
sox_write(sox_format_t * ft,const sox_sample_t * buf,size_t len)990 size_t sox_write(sox_format_t * ft, const sox_sample_t *buf, size_t len)
991 {
992 size_t actual = ft->handler.write? (*ft->handler.write)(ft, buf, len) : 0;
993 ft->olength += actual;
994 return actual;
995 }
996
sox_close(sox_format_t * ft)997 int sox_close(sox_format_t * ft)
998 {
999 int result = SOX_SUCCESS;
1000
1001 if (ft->mode == 'r')
1002 result = ft->handler.stopread? (*ft->handler.stopread)(ft) : SOX_SUCCESS;
1003 else {
1004 if (ft->handler.flags & SOX_FILE_REWIND) {
1005 if (ft->olength != ft->signal.length && ft->seekable) {
1006 result = lsx_seeki(ft, (off_t)0, 0);
1007 if (result == SOX_SUCCESS)
1008 result = ft->handler.stopwrite? (*ft->handler.stopwrite)(ft)
1009 : ft->handler.startwrite?(*ft->handler.startwrite)(ft) : SOX_SUCCESS;
1010 }
1011 }
1012 else result = ft->handler.stopwrite? (*ft->handler.stopwrite)(ft) : SOX_SUCCESS;
1013 }
1014
1015 if (ft->fp && ft->fp != stdin && ft->fp != stdout)
1016 xfclose(ft->fp, ft->io_type);
1017 free(ft->priv);
1018 free(ft->filename);
1019 free(ft->filetype);
1020 sox_delete_comments(&ft->oob.comments);
1021
1022 free(ft);
1023 return result;
1024 }
1025
sox_seek(sox_format_t * ft,sox_uint64_t offset,int whence)1026 int sox_seek(sox_format_t * ft, sox_uint64_t offset, int whence)
1027 {
1028 /* FIXME: Implement SOX_SEEK_CUR and SOX_SEEK_END. */
1029 if (whence != SOX_SEEK_SET)
1030 return SOX_EOF; /* FIXME: return SOX_EINVAL */
1031
1032 /* If file is a seekable file and this handler supports seeking,
1033 * then invoke handler's function.
1034 */
1035 if (ft->seekable && ft->handler.seek)
1036 return (*ft->handler.seek)(ft, offset);
1037 return SOX_EOF; /* FIXME: return SOX_EBADF */
1038 }
1039
strcaseends(char const * str,char const * end)1040 static int strcaseends(char const * str, char const * end)
1041 {
1042 size_t str_len = strlen(str), end_len = strlen(end);
1043 return str_len >= end_len && !strcasecmp(str + str_len - end_len, end);
1044 }
1045
1046 typedef enum {None, M3u, Pls} playlist_t;
1047
playlist_type(char const * filename)1048 static playlist_t playlist_type(char const * filename)
1049 {
1050 char * x, * p;
1051 playlist_t result = None;
1052
1053 if (*filename == '|')
1054 return result;
1055 if (strcaseends(filename, ".m3u"))
1056 return M3u;
1057 if (strcaseends(filename, ".pls"))
1058 return Pls;
1059 x = lsx_strdup(filename);
1060 p = strrchr(x, '?');
1061 if (p) {
1062 *p = '\0';
1063 result = playlist_type(x);
1064 }
1065 free(x);
1066 return result;
1067 }
1068
sox_is_playlist(char const * filename)1069 sox_bool sox_is_playlist(char const * filename)
1070 {
1071 return playlist_type(filename) != None;
1072 }
1073
sox_parse_playlist(sox_playlist_callback_t callback,void * p,char const * const listname)1074 int sox_parse_playlist(sox_playlist_callback_t callback, void * p, char const * const listname)
1075 {
1076 sox_bool const is_pls = playlist_type(listname) == Pls;
1077 int const comment_char = "#;"[is_pls];
1078 size_t text_length = 100;
1079 char * text = lsx_malloc(text_length + 1);
1080 char * dirname = lsx_strdup(listname);
1081 char * slash_pos = LAST_SLASH(dirname);
1082 lsx_io_type io_type;
1083 FILE * file = xfopen(listname, "r", &io_type);
1084 char * filename;
1085 int c, result = SOX_SUCCESS;
1086
1087 if (!slash_pos)
1088 *dirname = '\0';
1089 else
1090 *slash_pos = '\0';
1091
1092 if (file == NULL) {
1093 lsx_fail("Can't open playlist file `%s': %s", listname, strerror(errno));
1094 result = SOX_EOF;
1095 }
1096 else {
1097 do {
1098 size_t i = 0;
1099 size_t begin = 0, end = 0;
1100
1101 while (isspace(c = getc(file)));
1102 if (c == EOF)
1103 break;
1104 while (c != EOF && !strchr("\r\n", c) && c != comment_char) {
1105 if (i == text_length)
1106 text = lsx_realloc(text, (text_length <<= 1) + 1);
1107 text[i++] = c;
1108 if (!strchr(" \t\f", c))
1109 end = i;
1110 c = getc(file);
1111 }
1112 if (ferror(file))
1113 break;
1114 if (c == comment_char) {
1115 do c = getc(file);
1116 while (c != EOF && !strchr("\r\n", c));
1117 if (ferror(file))
1118 break;
1119 }
1120 text[end] = '\0';
1121 if (is_pls) {
1122 char dummy;
1123 if (!strncasecmp(text, "file", (size_t) 4) && sscanf(text + 4, "%*u=%c", &dummy) == 1)
1124 begin = strchr(text + 5, '=') - text + 1;
1125 else end = 0;
1126 }
1127 if (begin != end) {
1128 char const * id = text + begin;
1129
1130 if (!dirname[0] || is_url(id) || IS_ABSOLUTE(id))
1131 filename = lsx_strdup(id);
1132 else {
1133 filename = lsx_malloc(strlen(dirname) + strlen(id) + 2);
1134 sprintf(filename, "%s/%s", dirname, id);
1135 }
1136 if (sox_is_playlist(filename))
1137 sox_parse_playlist(callback, p, filename);
1138 else if (callback(p, filename))
1139 c = EOF;
1140 free(filename);
1141 }
1142 } while (c != EOF);
1143
1144 if (ferror(file)) {
1145 lsx_fail("error reading playlist file `%s': %s", listname, strerror(errno));
1146 result = SOX_EOF;
1147 }
1148 if (xfclose(file, io_type) && io_type == lsx_io_url) {
1149 lsx_fail("error reading playlist file URL `%s'", listname);
1150 result = SOX_EOF;
1151 }
1152 }
1153 free(text);
1154 free(dirname);
1155 return result;
1156 }
1157
1158 /*----------------------------- Formats library ------------------------------*/
1159
1160 enum {
1161 #define FORMAT(f) f,
1162 #include "formats.h"
1163 #undef FORMAT
1164 NSTATIC_FORMATS
1165 };
1166
1167 static sox_bool plugins_initted = sox_false;
1168
1169 #ifdef HAVE_LIBLTDL /* Plugin format handlers */
1170 #define MAX_DYNAMIC_FORMATS 42
1171 #define MAX_FORMATS (NSTATIC_FORMATS + MAX_DYNAMIC_FORMATS)
1172 #define MAX_FORMATS_1 (MAX_FORMATS + 1)
1173 #define MAX_NAME_LEN (size_t)1024 /* FIXME: Use vasprintf */
1174 #else
1175 #define MAX_FORMATS_1
1176 #endif
1177
1178 #define FORMAT(f) extern sox_format_handler_t const * lsx_##f##_format_fn(void);
1179 #include "formats.h"
1180 #undef FORMAT
1181
1182 static sox_format_tab_t s_sox_format_fns[MAX_FORMATS_1] = {
1183 #define FORMAT(f) {NULL, lsx_##f##_format_fn},
1184 #include "formats.h"
1185 #undef FORMAT
1186 {NULL, NULL}
1187 };
1188
1189 const sox_format_tab_t *
sox_get_format_fns(void)1190 sox_get_format_fns(void)
1191 {
1192 return s_sox_format_fns;
1193 }
1194
1195 #ifdef HAVE_LIBLTDL /* Plugin format handlers */
1196 static unsigned nformats = NSTATIC_FORMATS;
1197
init_format(const char * file,lt_ptr data)1198 static int init_format(const char *file, lt_ptr data)
1199 {
1200 lt_dlhandle lth = lt_dlopenext(file);
1201 const char *end = file + strlen(file);
1202 const char prefix[] = "sox_fmt_";
1203 char fnname[MAX_NAME_LEN];
1204 char *start = strstr(file, prefix);
1205
1206 (void)data;
1207 if (start && (start += sizeof(prefix) - 1) < end) {
1208 int ret = snprintf(fnname, MAX_NAME_LEN,
1209 "lsx_%.*s_format_fn", (int)(end - start), start);
1210 if (ret > 0 && ret < (int)MAX_NAME_LEN) {
1211 union {sox_format_fn_t fn; lt_ptr ptr;} ltptr;
1212 ltptr.ptr = lt_dlsym(lth, fnname);
1213 lsx_debug("opening format plugin `%s': library %p, entry point %p\n",
1214 fnname, (void *)lth, ltptr.ptr);
1215 if (ltptr.fn && (ltptr.fn()->sox_lib_version_code & ~255) ==
1216 (SOX_LIB_VERSION_CODE & ~255)) { /* compatible version check */
1217 if (nformats == MAX_FORMATS) {
1218 lsx_warn("too many plugin formats");
1219 return -1;
1220 }
1221 s_sox_format_fns[nformats++].fn = ltptr.fn;
1222 }
1223 }
1224 }
1225 return 0;
1226 }
1227 #endif
1228
sox_format_init(void)1229 int sox_format_init(void) /* Find & load format handlers. */
1230 {
1231 if (plugins_initted)
1232 return SOX_EOF;
1233
1234 plugins_initted = sox_true;
1235 #ifdef HAVE_LIBLTDL
1236 {
1237 int error = lt_dlinit();
1238 if (error) {
1239 lsx_fail("lt_dlinit failed with %d error(s): %s", error, lt_dlerror());
1240 return SOX_EOF;
1241 }
1242 lt_dlforeachfile(PKGLIBDIR, init_format, NULL);
1243 }
1244 #endif
1245 return SOX_SUCCESS;
1246 }
1247
sox_format_quit(void)1248 void sox_format_quit(void) /* Cleanup things. */
1249 {
1250 #ifdef HAVE_LIBLTDL
1251 int ret;
1252 if (plugins_initted && (ret = lt_dlexit()) != 0)
1253 lsx_fail("lt_dlexit failed with %d error(s): %s", ret, lt_dlerror());
1254 plugins_initted = sox_false;
1255 nformats = NSTATIC_FORMATS;
1256 #endif
1257 }
1258
1259 /* Find a named format in the formats library.
1260 *
1261 * (c) 2005-9 Chris Bagwell and SoX contributors.
1262 * Copyright 1991 Lance Norskog And Sundry Contributors.
1263 *
1264 * This source code is freely redistributable and may be used for any
1265 * purpose. This copyright notice must be maintained.
1266 *
1267 * Lance Norskog, Sundry Contributors, Chris Bagwell and SoX contributors
1268 * are not responsible for the consequences of using this software.
1269 */
sox_find_format(char const * name0,sox_bool no_dev)1270 sox_format_handler_t const * sox_find_format(char const * name0, sox_bool no_dev)
1271 {
1272 size_t f, n;
1273
1274 if (name0) {
1275 char * name = lsx_strdup(name0);
1276 char * pos = strchr(name, ';');
1277 if (pos) /* Use only the 1st clause of a mime string */
1278 *pos = '\0';
1279 for (f = 0; s_sox_format_fns[f].fn; ++f) {
1280 sox_format_handler_t const * handler = s_sox_format_fns[f].fn();
1281
1282 if (!(no_dev && (handler->flags & SOX_FILE_DEVICE)))
1283 for (n = 0; handler->names[n]; ++n)
1284 if (!strcasecmp(handler->names[n], name)) {
1285 free(name);
1286 return handler; /* Found it. */
1287 }
1288 }
1289 free(name);
1290 }
1291 if (sox_format_init() == SOX_SUCCESS) /* Try again with plugins */
1292 return sox_find_format(name0, no_dev);
1293 return NULL;
1294 }
1295