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