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 
29 #include <audacious/i18n.h>
30 
31 #include "audstrings.h"
32 #include "config.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  fprintf (stderr, "Cannot convert filename from system locale: %s\n", name);
149  return NULL;
150  }
151 
152 #ifdef _WIN32
153  string_replace_char (utf8, '\\', '/');
154 #endif
155  char enc[3 * strlen (utf8) + 1];
156  str_encode_percent (utf8, -1, enc);
157 
158  g_free (utf8);
159 
160 #ifdef _WIN32
161  return g_strdup_printf ("file:///%s", enc);
162 #else
163  return g_strdup_printf ("file://%s", enc);
164 #endif
165 }
166 
167 /* Like g_filename_from_uri, but converts the filename from UTF-8 to the system
168  * locale after percent-decoding. On Windows, strips the leading '/' and
169  * replaces '/' with '\'. */
170 
171 EXPORT char * uri_to_filename (const char * uri)
172 {
173 #ifdef _WIN32
174  g_return_val_if_fail (! strncmp (uri, "file:///", 8), NULL);
175  char buf[strlen (uri + 8) + 1];
176  str_decode_percent (uri + 8, -1, buf);
177 #else
178  g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL);
179  char buf[strlen (uri + 7) + 1];
180  str_decode_percent (uri + 7, -1, buf);
181 #endif
182 #ifdef _WIN32
183  string_replace_char (buf, '/', '\\');
184 #endif
185 
186  char * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL);
187  if (! name)
188  fprintf (stderr, "Cannot convert filename to system locale: %s\n", buf);
189 
190  return name;
191 }
192 
193 /* Formats a URI for human-readable display. Percent-decodes and, for file://
194  * URI's, converts to filename format, but in UTF-8. */
195 
196 EXPORT char * uri_to_display (const char * uri)
197 {
198  if (! strncmp (uri, "cdda://?", 8))
199  return g_strdup_printf (_("Audio CD, track %s"), uri + 8);
200 
201  char buf[strlen (uri) + 1];
202 
203 #ifdef _WIN32
204  if (! strncmp (uri, "file:///", 8))
205  {
206  str_decode_percent (uri + 8, -1, buf);
207  string_replace_char (buf, '/', '\\');
208  }
209 #else
210  if (! strncmp (uri, "file://", 7))
211  str_decode_percent (uri + 7, -1, buf);
212 #endif
213  else
214  str_decode_percent (uri, -1, buf);
215 
216  return g_strdup (buf);
217 }
218 
219 EXPORT void uri_parse (const char * uri, const char * * base_p, const char * * ext_p,
220  const char * * sub_p, int * isub_p)
221 {
222  const char * end = uri + strlen (uri);
223  const char * base, * ext, * sub, * c;
224  int isub = 0;
225  char junk;
226 
227  if ((c = strrchr (uri, '/')))
228  base = c + 1;
229  else
230  base = end;
231 
232  if ((c = strrchr (base, '?')) && sscanf (c + 1, "%d%c", & isub, & junk) == 1)
233  sub = c;
234  else
235  sub = end;
236 
237  char buf[sub - base + 1];
238  memcpy (buf, base, sub - base);
239  buf[sub - base] = 0;
240 
241  if ((c = strrchr (buf, '.')))
242  ext = base + (c - buf);
243  else
244  ext = sub;
245 
246  if (base_p)
247  * base_p = base;
248  if (ext_p)
249  * ext_p = ext;
250  if (sub_p)
251  * sub_p = sub;
252  if (isub_p)
253  * isub_p = isub;
254 }
255 
256 EXPORT bool_t uri_get_extension (const char * uri, char * buf, int buflen)
257 {
258  const char * ext;
259  uri_parse (uri, NULL, & ext, NULL, NULL);
260 
261  if (ext[0] != '.')
262  return FALSE;
263 
264  g_strlcpy (buf, ext + 1, buflen);
265 
266  /* remove subtunes and HTTP query strings */
267  char * qmark;
268  if ((qmark = strchr (buf, '?')))
269  * qmark = 0;
270 
271  return (buf[0] != 0);
272 }
273 
274 /* Like strcasecmp, but orders numbers correctly (2 before 10). */
275 /* Non-ASCII characters are treated exactly as is. */
276 /* Handles NULL gracefully. */
277 
278 EXPORT int string_compare (const char * ap, const char * bp)
279 {
280  if (ap == NULL)
281  return (bp == NULL) ? 0 : -1;
282  if (bp == NULL)
283  return 1;
284 
285  unsigned char a = * ap ++, b = * bp ++;
286  for (; a || b; a = * ap ++, b = * bp ++)
287  {
288  if (a > '9' || b > '9' || a < '0' || b < '0')
289  {
290  if (a <= 'Z' && a >= 'A')
291  a += 'a' - 'A';
292  if (b <= 'Z' && b >= 'A')
293  b += 'a' - 'A';
294 
295  if (a > b)
296  return 1;
297  if (a < b)
298  return -1;
299  }
300  else
301  {
302  int x = a - '0';
303  for (; (a = * ap) <= '9' && a >= '0'; ap ++)
304  x = 10 * x + (a - '0');
305 
306  int y = b - '0';
307  for (; (b = * bp) >= '0' && b <= '9'; bp ++)
308  y = 10 * y + (b - '0');
309 
310  if (x > y)
311  return 1;
312  if (x < y)
313  return -1;
314  }
315  }
316 
317  return 0;
318 }
319 
320 /* Decodes percent-encoded strings, then compares then with string_compare. */
321 
322 EXPORT int string_compare_encoded (const char * ap, const char * bp)
323 {
324  if (ap == NULL)
325  return (bp == NULL) ? 0 : -1;
326  if (bp == NULL)
327  return 1;
328 
329  unsigned char a = * ap ++, b = * bp ++;
330  for (; a || b; a = * ap ++, b = * bp ++)
331  {
332  if (a == '%' && ap[0] && ap[1])
333  {
334  a = (FROM_HEX (ap[0]) << 4) | FROM_HEX (ap[1]);
335  ap += 2;
336  }
337  if (b == '%' && bp[0] && bp[1])
338  {
339  b = (FROM_HEX (bp[0]) << 4) | FROM_HEX (bp[1]);
340  bp += 2;
341  }
342 
343  if (a > '9' || b > '9' || a < '0' || b < '0')
344  {
345  if (a <= 'Z' && a >= 'A')
346  a += 'a' - 'A';
347  if (b <= 'Z' && b >= 'A')
348  b += 'a' - 'A';
349 
350  if (a > b)
351  return 1;
352  if (a < b)
353  return -1;
354  }
355  else
356  {
357  int x = a - '0';
358  for (; (a = * ap) <= '9' && a >= '0'; ap ++)
359  x = 10 * x + (a - '0');
360 
361  int y = b - '0';
362  for (; (b = * bp) >= '0' && b <= '9'; bp ++)
363  y = 10 * y + (b - '0');
364 
365  if (x > y)
366  return 1;
367  if (x < y)
368  return -1;
369  }
370  }
371 
372  return 0;
373 }
374 
375 EXPORT char *
376 str_replace_fragment(char *s, int size, const char *old, const char *new)
377 {
378  char *ptr = s;
379  int left = strlen(s);
380  int avail = size - (left + 1);
381  int oldlen = strlen(old);
382  int newlen = strlen(new);
383  int diff = newlen - oldlen;
384 
385  while (left >= oldlen)
386  {
387  if (strncmp(ptr, old, oldlen))
388  {
389  left--;
390  ptr++;
391  continue;
392  }
393 
394  if (diff > avail)
395  break;
396 
397  if (diff != 0)
398  memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen);
399 
400  memcpy(ptr, new, newlen);
401  ptr += newlen;
402  left -= oldlen;
403  }
404 
405  return s;
406 }
407 
408 /*
409  * Routines to convert numbers between string and binary representations.
410  *
411  * Goals:
412  *
413  * - Accuracy, meaning that we can convert back and forth between string and
414  * binary without the number changing slightly each time.
415  * - Consistency, meaning that we get the same results no matter what
416  * architecture or locale we have to deal with.
417  * - Readability, meaning that the number one is rendered "1", not "1.000".
418  *
419  * Values are limited between -1,000,000,000 and 1,000,000,000 (inclusive) and
420  * have an accuracy of 6 decimal places.
421  */
422 
423 EXPORT bool_t string_to_int (const char * string, int * addr)
424 {
425  bool_t neg = (string[0] == '-');
426  if (neg)
427  string ++;
428 
429  int val = 0;
430  char c;
431 
432  while ((c = * string ++))
433  {
434  if (c < '0' || c > '9' || val > 100000000)
435  goto ERR;
436 
437  val = val * 10 + (c - '0');
438  }
439 
440  if (val > 1000000000)
441  goto ERR;
442 
443  * addr = neg ? -val : val;
444  return TRUE;
445 
446 ERR:
447  return FALSE;
448 }
449 
450 EXPORT bool_t string_to_double (const char * string, double * addr)
451 {
452  bool_t neg = (string[0] == '-');
453  if (neg)
454  string ++;
455 
456  const char * p = strchr (string, '.');
457  int i, f;
458 
459  if (p)
460  {
461  char buf[11];
462  int len;
463 
464  len = p - string;
465  if (len > 10)
466  goto ERR;
467 
468  memcpy (buf, string, len);
469  buf[len] = 0;
470 
471  if (! string_to_int (buf, & i))
472  goto ERR;
473 
474  len = strlen (p + 1);
475  if (len > 6)
476  goto ERR;
477 
478  memcpy (buf, p + 1, len);
479  memset (buf + len, '0', 6 - len);
480  buf[6] = 0;
481 
482  if (! string_to_int (buf, & f))
483  goto ERR;
484  }
485  else
486  {
487  if (! string_to_int (string, & i))
488  goto ERR;
489 
490  f = 0;
491  }
492 
493  double val = i + (double) f / 1000000;
494  if (val > 1000000000)
495  goto ERR;
496 
497  * addr = neg ? -val : val;
498  return TRUE;
499 
500 ERR:
501  return FALSE;
502 }
503 
504 EXPORT char * int_to_string (int val)
505 {
506  g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
507  return g_strdup_printf ("%d", val);
508 }
509 
510 EXPORT char * double_to_string (double val)
511 {
512  g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
513 
514  bool_t neg = (val < 0);
515  if (neg)
516  val = -val;
517 
518  int i = floor (val);
519  int f = round ((val - i) * 1000000);
520 
521  if (f == 1000000)
522  {
523  i ++;
524  f = 0;
525  }
526 
527  char * s = neg ? g_strdup_printf ("-%d.%06d", i, f) : g_strdup_printf ("%d.%06d", i, f);
528 
529  char * c = s + strlen (s);
530  while (* (c - 1) == '0')
531  c --;
532  if (* (c - 1) == '.')
533  c --;
534  * c = 0;
535 
536  return s;
537 }
538 
539 EXPORT bool_t string_to_double_array (const char * string, double * array, int count)
540 {
541  char * * split = g_strsplit (string, ",", -1);
542  if (g_strv_length (split) != count)
543  goto ERR;
544 
545  for (int i = 0; i < count; i ++)
546  {
547  if (! string_to_double (split[i], & array[i]))
548  goto ERR;
549  }
550 
551  g_strfreev (split);
552  return TRUE;
553 
554 ERR:
555  g_strfreev (split);
556  return FALSE;
557 }
558 
559 EXPORT char * double_array_to_string (const double * array, int count)
560 {
561  char * * split = g_malloc0 (sizeof (char *) * (count + 1));
562 
563  for (int i = 0; i < count; i ++)
564  {
565  split[i] = double_to_string (array[i]);
566  if (! split[i])
567  goto ERR;
568  }
569 
570  char * string = g_strjoinv (",", split);
571  g_strfreev (split);
572  return string;
573 
574 ERR:
575  g_strfreev (split);
576  return NULL;
577 }