Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
audstrings.c
Go to the documentation of this file.
1 /*
2  * audstrings.c
3  * Copyright 2009-2011 John Lindgren
4  * Copyright 2010 William Pitcock
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions, and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions, and the following disclaimer in the documentation
14  * provided with the distribution.
15  *
16  * This software is provided "as is" and without any warranty, express or
17  * implied. In no event shall the authors be liable for any damages arising from
18  * the use of this software.
19  */
20 
21 #include <limits.h>
22 #include <math.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <glib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <locale.h>
29 
30 #include <audacious/i18n.h>
31 
32 #include "audstrings.h"
33 
34 #define FROM_HEX(c) ((c) < 'A' ? (c) - '0' : (c) < 'a' ? 10 + (c) - 'A' : 10 + (c) - 'a')
35 #define TO_HEX(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
36 #define IS_LEGAL(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') \
37  || ((c) >= '0' && (c) <= '9') || (strchr ("-_.~/", (c))))
38 
39 EXPORT bool_t str_has_prefix_nocase (const char * str, const char * prefix)
40 {
41  return ! g_ascii_strncasecmp (str, prefix, strlen (prefix));
42 }
43 
44 EXPORT bool_t str_has_suffix_nocase (const char * str, const char * suffix)
45 {
46  int len1 = strlen (str);
47  int len2 = strlen (suffix);
48 
49  if (len2 > len1)
50  return FALSE;
51 
52  return ! g_ascii_strcasecmp (str + len1 - len2, suffix);
53 }
54 
55 static char * (* str_to_utf8_impl) (const char *) = NULL;
56 static char * (* str_to_utf8_full_impl) (const char *, int, int *, int *) = NULL;
57 
58 EXPORT void str_set_utf8_impl (char * (* stu_impl) (const char *),
59  char * (* stuf_impl) (const char *, int, int *, int *))
60 {
61  str_to_utf8_impl = stu_impl;
62  str_to_utf8_full_impl = stuf_impl;
63 }
64 
65 EXPORT char * str_to_utf8 (const char * str)
66 {
67  g_return_val_if_fail (str_to_utf8_impl, NULL);
68  return str_to_utf8_impl (str);
69 }
70 
71 EXPORT char * str_to_utf8_full (const char * str, int len, int * bytes_read, int * bytes_written)
72 {
73  g_return_val_if_fail (str_to_utf8_full_impl, NULL);
74  return str_to_utf8_full_impl (str, len, bytes_read, bytes_written);
75 }
76 
77 EXPORT void string_replace_char (char * string, char old_c, char new_c)
78 {
79  while ((string = strchr (string, old_c)))
80  * string ++ = new_c;
81 }
82 
83 /* Percent-decodes up to <len> bytes of <str> to <out>, which must be large
84  * enough to hold the decoded string (i.e., (len + 1) bytes). If <len> is
85  * negative, decodes all of <str>. */
86 
87 EXPORT void str_decode_percent (const char * str, int len, char * out)
88 {
89  if (len < 0)
90  len = INT_MAX;
91 
92  while (len --)
93  {
94  char c = * str ++;
95  if (! c)
96  break;
97 
98  if (c == '%' && len >= 2 && str[0] && str[1])
99  {
100  c = (FROM_HEX (str[0]) << 4) | FROM_HEX (str[1]);
101  str += 2;
102  len -= 2;
103  }
104 
105  * out ++ = c;
106  }
107 
108  * out = 0;
109 }
110 
111 /* Percent-encodes up to <len> bytes of <str> to <out>, which must be large
112  * enough to hold the encoded string (i.e., (3 * len + 1) bytes). If <len> is
113  * negative, decodes all of <str>. */
114 
115 EXPORT void str_encode_percent (const char * str, int len, char * out)
116 {
117  if (len < 0)
118  len = INT_MAX;
119 
120  while (len --)
121  {
122  char c = * str ++;
123  if (! c)
124  break;
125 
126  if (IS_LEGAL (c))
127  * out ++ = c;
128  else
129  {
130  * out ++ = '%';
131  * out ++ = TO_HEX ((unsigned char) c >> 4);
132  * out ++ = TO_HEX (c & 0xF);
133  }
134  }
135 
136  * out = 0;
137 }
138 
139 /* Like g_filename_to_uri, but converts the filename from the system locale to
140  * UTF-8 before percent-encoding. On Windows, replaces '\' with '/' and adds a
141  * leading '/'. */
142 
143 EXPORT char * filename_to_uri (const char * name)
144 {
145  char * utf8 = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
146  if (! utf8)
147  {
148  const char * locale = setlocale (LC_ALL, NULL);
149  fprintf (stderr, "Cannot convert filename from system locale (%s): %s\n", locale, name);
150  return NULL;
151  }
152 
153 #ifdef _WIN32
154  string_replace_char (utf8, '\\', '/');
155 #endif
156  char enc[3 * strlen (utf8) + 1];
157  str_encode_percent (utf8, -1, enc);
158 
159  g_free (utf8);
160 
161 #ifdef _WIN32
162  return g_strdup_printf ("file:///%s", enc);
163 #else
164  return g_strdup_printf ("file://%s", enc);
165 #endif
166 }
167 
168 /* Like g_filename_from_uri, but converts the filename from UTF-8 to the system
169  * locale after percent-decoding. On Windows, strips the leading '/' and
170  * replaces '/' with '\'. */
171 
172 EXPORT char * uri_to_filename (const char * uri)
173 {
174 #ifdef _WIN32
175  g_return_val_if_fail (! strncmp (uri, "file:///", 8), NULL);
176  char buf[strlen (uri + 8) + 1];
177  str_decode_percent (uri + 8, -1, buf);
178 #else
179  g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL);
180  char buf[strlen (uri + 7) + 1];
181  str_decode_percent (uri + 7, -1, buf);
182 #endif
183 #ifdef _WIN32
184  string_replace_char (buf, '/', '\\');
185 #endif
186 
187  char * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL);
188  if (! name)
189  {
190  const char * locale = setlocale (LC_ALL, NULL);
191  fprintf (stderr, "Cannot convert filename to system locale (%s): %s\n", locale, buf);
192  }
193 
194  return name;
195 }
196 
197 /* Formats a URI for human-readable display. Percent-decodes and, for file://
198  * URI's, converts to filename format, but in UTF-8. */
199 
200 EXPORT char * uri_to_display (const char * uri)
201 {
202  if (! strncmp (uri, "cdda://?", 8))
203  return g_strdup_printf (_("Audio CD, track %s"), uri + 8);
204 
205  char buf[strlen (uri) + 1];
206 
207 #ifdef _WIN32
208  if (! strncmp (uri, "file:///", 8))
209  {
210  str_decode_percent (uri + 8, -1, buf);
211  string_replace_char (buf, '/', '\\');
212  }
213 #else
214  if (! strncmp (uri, "file://", 7))
215  str_decode_percent (uri + 7, -1, buf);
216 #endif
217  else
218  str_decode_percent (uri, -1, buf);
219 
220  return g_strdup (buf);
221 }
222 
223 EXPORT void uri_parse (const char * uri, const char * * base_p, const char * * ext_p,
224  const char * * sub_p, int * isub_p)
225 {
226  const char * end = uri + strlen (uri);
227  const char * base, * ext, * sub, * c;
228  int isub = 0;
229  char junk;
230 
231  if ((c = strrchr (uri, '/')))
232  base = c + 1;
233  else
234  base = end;
235 
236  if ((c = strrchr (base, '?')) && sscanf (c + 1, "%d%c", & isub, & junk) == 1)
237  sub = c;
238  else
239  sub = end;
240 
241  char buf[sub - base + 1];
242  memcpy (buf, base, sub - base);
243  buf[sub - base] = 0;
244 
245  if ((c = strrchr (buf, '.')))
246  ext = base + (c - buf);
247  else
248  ext = sub;
249 
250  if (base_p)
251  * base_p = base;
252  if (ext_p)
253  * ext_p = ext;
254  if (sub_p)
255  * sub_p = sub;
256  if (isub_p)
257  * isub_p = isub;
258 }
259 
260 EXPORT bool_t uri_get_extension (const char * uri, char * buf, int buflen)
261 {
262  const char * ext;
263  uri_parse (uri, NULL, & ext, NULL, NULL);
264 
265  if (ext[0] != '.')
266  return FALSE;
267 
268  g_strlcpy (buf, ext + 1, buflen);
269 
270  /* remove subtunes and HTTP query strings */
271  char * qmark;
272  if ((qmark = strchr (buf, '?')))
273  * qmark = 0;
274 
275  return (buf[0] != 0);
276 }
277 
278 /* Like strcasecmp, but orders numbers correctly (2 before 10). */
279 /* Non-ASCII characters are treated exactly as is. */
280 /* Handles NULL gracefully. */
281 
282 EXPORT int string_compare (const char * ap, const char * bp)
283 {
284  if (ap == NULL)
285  return (bp == NULL) ? 0 : -1;
286  if (bp == NULL)
287  return 1;
288 
289  unsigned char a = * ap ++, b = * bp ++;
290  for (; a || b; a = * ap ++, b = * bp ++)
291  {
292  if (a > '9' || b > '9' || a < '0' || b < '0')
293  {
294  if (a <= 'Z' && a >= 'A')
295  a += 'a' - 'A';
296  if (b <= 'Z' && b >= 'A')
297  b += 'a' - 'A';
298 
299  if (a > b)
300  return 1;
301  if (a < b)
302  return -1;
303  }
304  else
305  {
306  int x = a - '0';
307  for (; (a = * ap) <= '9' && a >= '0'; ap ++)
308  x = 10 * x + (a - '0');
309 
310  int y = b - '0';
311  for (; (b = * bp) >= '0' && b <= '9'; bp ++)
312  y = 10 * y + (b - '0');
313 
314  if (x > y)
315  return 1;
316  if (x < y)
317  return -1;
318  }
319  }
320 
321  return 0;
322 }
323 
324 /* Decodes percent-encoded strings, then compares then with string_compare. */
325 
326 EXPORT int string_compare_encoded (const char * ap, const char * bp)
327 {
328  if (ap == NULL)
329  return (bp == NULL) ? 0 : -1;
330  if (bp == NULL)
331  return 1;
332 
333  unsigned char a = * ap ++, b = * bp ++;
334  for (; a || b; a = * ap ++, b = * bp ++)
335  {
336  if (a == '%' && ap[0] && ap[1])
337  {
338  a = (FROM_HEX (ap[0]) << 4) | FROM_HEX (ap[1]);
339  ap += 2;
340  }
341  if (b == '%' && bp[0] && bp[1])
342  {
343  b = (FROM_HEX (bp[0]) << 4) | FROM_HEX (bp[1]);
344  bp += 2;
345  }
346 
347  if (a > '9' || b > '9' || a < '0' || b < '0')
348  {
349  if (a <= 'Z' && a >= 'A')
350  a += 'a' - 'A';
351  if (b <= 'Z' && b >= 'A')
352  b += 'a' - 'A';
353 
354  if (a > b)
355  return 1;
356  if (a < b)
357  return -1;
358  }
359  else
360  {
361  int x = a - '0';
362  for (; (a = * ap) <= '9' && a >= '0'; ap ++)
363  x = 10 * x + (a - '0');
364 
365  int y = b - '0';
366  for (; (b = * bp) >= '0' && b <= '9'; bp ++)
367  y = 10 * y + (b - '0');
368 
369  if (x > y)
370  return 1;
371  if (x < y)
372  return -1;
373  }
374  }
375 
376  return 0;
377 }
378 
379 EXPORT char *
380 str_replace_fragment(char *s, int size, const char *old, const char *new)
381 {
382  char *ptr = s;
383  int left = strlen(s);
384  int avail = size - (left + 1);
385  int oldlen = strlen(old);
386  int newlen = strlen(new);
387  int diff = newlen - oldlen;
388 
389  while (left >= oldlen)
390  {
391  if (strncmp(ptr, old, oldlen))
392  {
393  left--;
394  ptr++;
395  continue;
396  }
397 
398  if (diff > avail)
399  break;
400 
401  if (diff != 0)
402  memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen);
403 
404  memcpy(ptr, new, newlen);
405  ptr += newlen;
406  left -= oldlen;
407  }
408 
409  return s;
410 }
411 
412 /*
413  * Routines to convert numbers between string and binary representations.
414  *
415  * Goals:
416  *
417  * - Accuracy, meaning that we can convert back and forth between string and
418  * binary without the number changing slightly each time.
419  * - Consistency, meaning that we get the same results no matter what
420  * architecture or locale we have to deal with.
421  * - Readability, meaning that the number one is rendered "1", not "1.000".
422  *
423  * Values are limited between -1,000,000,000 and 1,000,000,000 (inclusive) and
424  * have an accuracy of 6 decimal places.
425  */
426 
427 EXPORT bool_t string_to_int (const char * string, int * addr)
428 {
429  bool_t neg = (string[0] == '-');
430  if (neg)
431  string ++;
432 
433  int val = 0;
434  char c;
435 
436  while ((c = * string ++))
437  {
438  if (c < '0' || c > '9' || val > 100000000)
439  goto ERR;
440 
441  val = val * 10 + (c - '0');
442  }
443 
444  if (val > 1000000000)
445  goto ERR;
446 
447  * addr = neg ? -val : val;
448  return TRUE;
449 
450 ERR:
451  return FALSE;
452 }
453 
454 EXPORT bool_t string_to_double (const char * string, double * addr)
455 {
456  bool_t neg = (string[0] == '-');
457  if (neg)
458  string ++;
459 
460  const char * p = strchr (string, '.');
461  int i, f;
462 
463  if (p)
464  {
465  char buf[11];
466  int len;
467 
468  len = p - string;
469  if (len > 10)
470  goto ERR;
471 
472  memcpy (buf, string, len);
473  buf[len] = 0;
474 
475  if (! string_to_int (buf, & i))
476  goto ERR;
477 
478  len = strlen (p + 1);
479  if (len > 6)
480  goto ERR;
481 
482  memcpy (buf, p + 1, len);
483  memset (buf + len, '0', 6 - len);
484  buf[6] = 0;
485 
486  if (! string_to_int (buf, & f))
487  goto ERR;
488  }
489  else
490  {
491  if (! string_to_int (string, & i))
492  goto ERR;
493 
494  f = 0;
495  }
496 
497  double val = i + (double) f / 1000000;
498  if (val > 1000000000)
499  goto ERR;
500 
501  * addr = neg ? -val : val;
502  return TRUE;
503 
504 ERR:
505  return FALSE;
506 }
507 
508 EXPORT char * int_to_string (int val)
509 {
510  g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
511  return g_strdup_printf ("%d", val);
512 }
513 
514 EXPORT char * double_to_string (double val)
515 {
516  g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
517 
518  bool_t neg = (val < 0);
519  if (neg)
520  val = -val;
521 
522  int i = floor (val);
523  int f = round ((val - i) * 1000000);
524 
525  if (f == 1000000)
526  {
527  i ++;
528  f = 0;
529  }
530 
531  char * s = neg ? g_strdup_printf ("-%d.%06d", i, f) : g_strdup_printf ("%d.%06d", i, f);
532 
533  char * c = s + strlen (s);
534  while (* (c - 1) == '0')
535  c --;
536  if (* (c - 1) == '.')
537  c --;
538  * c = 0;
539 
540  return s;
541 }
542 
543 EXPORT bool_t string_to_int_array (const char * string, int * array, int count)
544 {
545  char * * split = g_strsplit (string, ",", -1);
546  if (g_strv_length (split) != count)
547  goto ERR;
548 
549  for (int i = 0; i < count; i ++)
550  {
551  if (! string_to_int (split[i], & array[i]))
552  goto ERR;
553  }
554 
555  g_strfreev (split);
556  return TRUE;
557 
558 ERR:
559  g_strfreev (split);
560  return FALSE;
561 }
562 
563 EXPORT char * int_array_to_string (const int * array, int count)
564 {
565  char * * split = g_malloc0 (sizeof (char *) * (count + 1));
566 
567  for (int i = 0; i < count; i ++)
568  {
569  split[i] = int_to_string (array[i]);
570  if (! split[i])
571  goto ERR;
572  }
573 
574  char * string = g_strjoinv (",", split);
575  g_strfreev (split);
576  return string;
577 
578 ERR:
579  g_strfreev (split);
580  return NULL;
581 }
582 
583 EXPORT bool_t string_to_double_array (const char * string, double * array, int count)
584 {
585  char * * split = g_strsplit (string, ",", -1);
586  if (g_strv_length (split) != count)
587  goto ERR;
588 
589  for (int i = 0; i < count; i ++)
590  {
591  if (! string_to_double (split[i], & array[i]))
592  goto ERR;
593  }
594 
595  g_strfreev (split);
596  return TRUE;
597 
598 ERR:
599  g_strfreev (split);
600  return FALSE;
601 }
602 
603 EXPORT char * double_array_to_string (const double * array, int count)
604 {
605  char * * split = g_malloc0 (sizeof (char *) * (count + 1));
606 
607  for (int i = 0; i < count; i ++)
608  {
609  split[i] = double_to_string (array[i]);
610  if (! split[i])
611  goto ERR;
612  }
613 
614  char * string = g_strjoinv (",", split);
615  g_strfreev (split);
616  return string;
617 
618 ERR:
619  g_strfreev (split);
620  return NULL;
621 }
EXPORT bool_t string_to_int(const char *string, int *addr)
Definition: audstrings.c:427
EXPORT char * uri_to_display(const char *uri)
Definition: audstrings.c:200
static char *(* str_to_utf8_impl)(const char *)
Definition: audstrings.c:55
static float a[EQ_BANDS][2]
Definition: equalizer.c:55
EXPORT bool_t string_to_int_array(const char *string, int *array, int count)
Definition: audstrings.c:543
EXPORT char * uri_to_filename(const char *uri)
Definition: audstrings.c:172
#define _(String)
Definition: i18n.h:25
#define TO_HEX(i)
Definition: audstrings.c:35
EXPORT bool_t string_to_double(const char *string, double *addr)
Definition: audstrings.c:454
const char PluginHandle decoder const char PluginHandle decoder const char PluginHandle decoder void const PreferencesWidget int
Definition: misc-api.h:103
EXPORT char * str_to_utf8_full(const char *str, int len, int *bytes_read, int *bytes_written)
Definition: audstrings.c:71
static char *(* str_to_utf8_full_impl)(const char *, int, int *, int *)
Definition: audstrings.c:56
static float b[EQ_BANDS][2]
Definition: equalizer.c:56
EXPORT bool_t str_has_suffix_nocase(const char *str, const char *suffix)
Definition: audstrings.c:44
EXPORT void string_replace_char(char *string, char old_c, char new_c)
Definition: audstrings.c:77
#define FALSE
Definition: core.h:37
EXPORT int string_compare(const char *ap, const char *bp)
Definition: audstrings.c:282
EXPORT char * int_array_to_string(const int *array, int count)
Definition: audstrings.c:563
Index Index bool_t
Definition: playlist-api.h:122
EXPORT char * str_replace_fragment(char *s, int size, const char *old, const char *new)
Definition: audstrings.c:380
EXPORT void str_decode_percent(const char *str, int len, char *out)
Definition: audstrings.c:87
#define IS_LEGAL(c)
Definition: audstrings.c:36
const char * name
Definition: plugin-init.c:38
EXPORT void str_set_utf8_impl(char *(*stu_impl)(const char *), char *(*stuf_impl)(const char *, int, int *, int *))
Definition: audstrings.c:58
#define NULL
Definition: core.h:29
EXPORT char * int_to_string(int val)
Definition: audstrings.c:508
EXPORT int string_compare_encoded(const char *ap, const char *bp)
Definition: audstrings.c:326
EXPORT char * double_array_to_string(const double *array, int count)
Definition: audstrings.c:603
#define TRUE
Definition: core.h:39
EXPORT bool_t string_to_double_array(const char *string, double *array, int count)
Definition: audstrings.c:583
EXPORT void str_encode_percent(const char *str, int len, char *out)
Definition: audstrings.c:115
EXPORT void uri_parse(const char *uri, const char **base_p, const char **ext_p, const char **sub_p, int *isub_p)
Definition: audstrings.c:223
EXPORT bool_t uri_get_extension(const char *uri, char *buf, int buflen)
Definition: audstrings.c:260
EXPORT char * double_to_string(double val)
Definition: audstrings.c:514
EXPORT bool_t str_has_prefix_nocase(const char *str, const char *prefix)
Definition: audstrings.c:39
#define FROM_HEX(c)
Definition: audstrings.c:34
EXPORT char * filename_to_uri(const char *name)
Definition: audstrings.c:143
struct @17::@18::@20 s
EXPORT char * str_to_utf8(const char *str)
Definition: audstrings.c:65