1 /* General purpose, i.e. non SoX specific, utility functions.
2  * Copyright (c) 2007-8 robs@users.sourceforge.net
3  *
4  * This library is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 2.1 of the License, or (at
7  * your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 #include "sox_i.h"
20 #include <ctype.h>
21 #include <stdio.h>
22 
lsx_strcasecmp(const char * s1,const char * s2)23 int lsx_strcasecmp(const char * s1, const char * s2)
24 {
25 #if defined(HAVE_STRCASECMP)
26   return strcasecmp(s1, s2);
27 #elif defined(_MSC_VER)
28   return _stricmp(s1, s2);
29 #else
30   while (*s1 && (toupper(*s1) == toupper(*s2)))
31     s1++, s2++;
32   return toupper(*s1) - toupper(*s2);
33 #endif
34 }
35 
lsx_strncasecmp(char const * s1,char const * s2,size_t n)36 int lsx_strncasecmp(char const * s1, char const * s2, size_t n)
37 {
38 #if defined(HAVE_STRCASECMP)
39   return strncasecmp(s1, s2, n);
40 #elif defined(_MSC_VER)
41   return _strnicmp(s1, s2, n);
42 #else
43   while (--n && *s1 && (toupper(*s1) == toupper(*s2)))
44     s1++, s2++;
45   return toupper(*s1) - toupper(*s2);
46 #endif
47 }
48 
lsx_strends(char const * str,char const * end)49 sox_bool lsx_strends(char const * str, char const * end)
50 {
51   size_t str_len = strlen(str), end_len = strlen(end);
52   return str_len >= end_len && !strcmp(str + str_len - end_len, end);
53 }
54 
lsx_find_file_extension(char const * pathname)55 char const * lsx_find_file_extension(char const * pathname)
56 {
57   /* First, chop off any path portions of filename.  This
58    * prevents the next search from considering that part. */
59   char const * result = LAST_SLASH(pathname);
60   if (!result)
61     result = pathname;
62 
63   /* Now look for an filename extension */
64   result = strrchr(result, '.');
65   if (result)
66     ++result;
67   return result;
68 }
69 
lsx_find_enum_text(char const * text,lsx_enum_item const * enum_items,int flags)70 lsx_enum_item const * lsx_find_enum_text(char const * text, lsx_enum_item const * enum_items, int flags)
71 {
72   lsx_enum_item const * result = NULL; /* Assume not found */
73   sox_bool sensitive = !!(flags & lsx_find_enum_item_case_sensitive);
74 
75   while (enum_items->text) {
76     if ((!sensitive && !strcasecmp(text, enum_items->text)) ||
77         ( sensitive && !    strcmp(text, enum_items->text)))
78       return enum_items;    /* Found exact match */
79     if ((!sensitive && !strncasecmp(text, enum_items->text, strlen(text))) ||
80         ( sensitive && !    strncmp(text, enum_items->text, strlen(text)))) {
81       if (result != NULL && result->value != enum_items->value)
82         return NULL;        /* Found ambiguity */
83       result = enum_items;  /* Found sub-string match */
84     }
85     ++enum_items;
86   }
87   return result;
88 }
89 
lsx_find_enum_value(unsigned value,lsx_enum_item const * enum_items)90 lsx_enum_item const * lsx_find_enum_value(unsigned value, lsx_enum_item const * enum_items)
91 {
92   for (;enum_items->text; ++enum_items)
93     if (value == enum_items->value)
94       return enum_items;
95   return NULL;
96 }
97 
lsx_enum_option(int c,char const * arg,lsx_enum_item const * items)98 int lsx_enum_option(int c, char const * arg, lsx_enum_item const * items)
99 {
100   lsx_enum_item const * p = lsx_find_enum_text(arg, items, sox_false);
101   if (p == NULL) {
102     size_t len = 1;
103     char * set = lsx_malloc(len);
104     *set = 0;
105     for (p = items; p->text; ++p) {
106       set = lsx_realloc(set, len += 2 + strlen(p->text));
107       strcat(set, ", "); strcat(set, p->text);
108     }
109     lsx_fail("-%c: `%s' is not one of: %s.", c, arg, set + 2);
110     free(set);
111     return INT_MAX;
112   }
113   return p->value;
114 }
115 
lsx_sigfigs3(double number)116 char const * lsx_sigfigs3(double number)
117 {
118   static char const symbols[] = "\0kMGTPEZY";
119   static char string[16][10];   /* FIXME: not thread-safe */
120   static unsigned n;            /* ditto */
121   unsigned a, b, c;
122   sprintf(string[n = (n+1) & 15], "%#.3g", number);
123   switch (sscanf(string[n], "%u.%ue%u", &a, &b, &c)) {
124     case 2: if (b) return string[n]; /* Can fall through */
125     case 1: c = 2; break;
126     case 3: a = 100*a + b; break;
127   }
128   if (c < array_length(symbols) * 3 - 3) switch (c%3) {
129     case 0: sprintf(string[n], "%u.%02u%c", a/100,a%100, symbols[c/3]); break;
130     case 1: sprintf(string[n], "%u.%u%c"  , a/10 ,a%10 , symbols[c/3]); break;
131     case 2: sprintf(string[n], "%u%c"     , a          , symbols[c/3]); break;
132   }
133   return string[n];
134 }
135 
lsx_sigfigs3p(double percentage)136 char const * lsx_sigfigs3p(double percentage)
137 {
138   static char string[16][10];
139   static unsigned n;
140   sprintf(string[n = (n+1) & 15], "%.1f%%", percentage);
141   if (strlen(string[n]) < 5)
142     sprintf(string[n], "%.2f%%", percentage);
143   else if (strlen(string[n]) > 5)
144     sprintf(string[n], "%.0f%%", percentage);
145   return string[n];
146 }
147 
lsx_open_dllibrary(int show_error_on_failure,const char * library_description,const char * const library_names[]UNUSED,const lsx_dlfunction_info func_infos[],lsx_dlptr selected_funcs[],lsx_dlhandle * pdl)148 int lsx_open_dllibrary(
149   int show_error_on_failure,
150   const char* library_description,
151   const char* const library_names[] UNUSED,
152   const lsx_dlfunction_info func_infos[],
153   lsx_dlptr selected_funcs[],
154   lsx_dlhandle* pdl)
155 {
156   int failed = 0;
157   lsx_dlhandle dl = NULL;
158 
159   /* Track enough information to give a good error message about one failure.
160    * Let failed symbol load override failed library open, and let failed
161    * library open override missing static symbols.
162    */
163   const char* failed_libname = NULL;
164   const char* failed_funcname = NULL;
165 
166 #ifdef HAVE_LIBLTDL
167   if (library_names && library_names[0])
168   {
169     const char* const* libname;
170     if (lt_dlinit())
171     {
172       lsx_fail(
173         "Unable to load %s - failed to initialize ltdl.",
174         library_description);
175       return 1;
176     }
177 
178     for (libname = library_names; *libname; libname++)
179     {
180       lsx_debug("Attempting to open %s (%s).", library_description, *libname);
181       dl = lt_dlopenext(*libname);
182       if (dl)
183       {
184         size_t i;
185         lsx_debug("Opened %s (%s).", library_description, *libname);
186         for (i = 0; func_infos[i].name; i++)
187         {
188           union {lsx_dlptr fn; lt_ptr ptr;} func;
189           func.ptr = lt_dlsym(dl, func_infos[i].name);
190           selected_funcs[i] = func.fn ? func.fn : func_infos[i].stub_func;
191           if (!selected_funcs[i])
192           {
193             lt_dlclose(dl);
194             dl = NULL;
195             failed_libname = *libname;
196             failed_funcname = func_infos[i].name;
197             lsx_debug("Cannot use %s (%s) - missing function \"%s\".", library_description, failed_libname, failed_funcname);
198             break;
199           }
200         }
201 
202         if (dl)
203           break;
204       }
205       else if (!failed_libname)
206       {
207         failed_libname = *libname;
208       }
209     }
210 
211     if (!dl)
212       lt_dlexit();
213   }
214 #endif /* HAVE_LIBLTDL */
215 
216   if (!dl)
217   {
218     size_t i;
219     for (i = 0; func_infos[i].name; i++)
220     {
221       selected_funcs[i] =
222           func_infos[i].static_func
223           ? func_infos[i].static_func
224           : func_infos[i].stub_func;
225       if (!selected_funcs[i])
226       {
227         if (!failed_libname)
228         {
229           failed_libname = "static";
230           failed_funcname = func_infos[i].name;
231         }
232 
233         failed = 1;
234         break;
235       }
236     }
237   }
238 
239   if (failed)
240   {
241     size_t i;
242     for (i = 0; func_infos[i].name; i++)
243       selected_funcs[i] = NULL;
244 #ifdef HAVE_LIBLTDL
245 #define LTDL_MISSING ""
246 #else
247 #define LTDL_MISSING " (Dynamic library support not configured.)"
248 #endif /* HAVE_LIBLTDL */
249     if (failed_funcname)
250     {
251       if (show_error_on_failure)
252         lsx_fail(
253           "Unable to load %s (%s) function \"%s\"." LTDL_MISSING,
254           library_description,
255           failed_libname,
256           failed_funcname);
257       else
258         lsx_report(
259           "Unable to load %s (%s) function \"%s\"." LTDL_MISSING,
260           library_description,
261           failed_libname,
262           failed_funcname);
263     }
264     else if (failed_libname)
265     {
266       if (show_error_on_failure)
267         lsx_fail(
268           "Unable to load %s (%s)." LTDL_MISSING,
269           library_description,
270           failed_libname);
271       else
272         lsx_report(
273           "Unable to load %s (%s)." LTDL_MISSING,
274           library_description,
275           failed_libname);
276     }
277     else
278     {
279       if (show_error_on_failure)
280         lsx_fail(
281           "Unable to load %s - no dynamic library names selected." LTDL_MISSING,
282           library_description);
283       else
284         lsx_report(
285           "Unable to load %s - no dynamic library names selected." LTDL_MISSING,
286           library_description);
287     }
288   }
289 
290   *pdl = dl;
291   return failed;
292 }
293 
lsx_close_dllibrary(lsx_dlhandle dl UNUSED)294 void lsx_close_dllibrary(
295   lsx_dlhandle dl UNUSED)
296 {
297 #ifdef HAVE_LIBLTDL
298   if (dl)
299   {
300     lt_dlclose(dl);
301     lt_dlexit();
302   }
303 #endif /* HAVE_LIBLTDL */
304 }
305