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