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