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_str, char new_str)
78 {
79  while ((string = strchr (string, old_str)) != NULL)
80  * string = new_str;
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 /* Like strcasecmp, but orders numbers correctly (2 before 10). */
257 /* Non-ASCII characters are treated exactly as is. */
258 /* Handles NULL gracefully. */
259 
260 EXPORT int string_compare (const char * ap, const char * bp)
261 {
262  if (ap == NULL)
263  return (bp == NULL) ? 0 : -1;
264  if (bp == NULL)
265  return 1;
266 
267  unsigned char a = * ap ++, b = * bp ++;
268  for (; a || b; a = * ap ++, b = * bp ++)
269  {
270  if (a > '9' || b > '9' || a < '0' || b < '0')
271  {
272  if (a <= 'Z' && a >= 'A')
273  a += 'a' - 'A';
274  if (b <= 'Z' && b >= 'A')
275  b += 'a' - 'A';
276 
277  if (a > b)
278  return 1;
279  if (a < b)
280  return -1;
281  }
282  else
283  {
284  int x = a - '0';
285  for (; (a = * ap) <= '9' && a >= '0'; ap ++)
286  x = 10 * x + (a - '0');
287 
288  int y = b - '0';
289  for (; (b = * bp) >= '0' && b <= '9'; bp ++)
290  y = 10 * y + (b - '0');
291 
292  if (x > y)
293  return 1;
294  if (x < y)
295  return -1;
296  }
297  }
298 
299  return 0;
300 }
301 
302 /* Decodes percent-encoded strings, then compares then with string_compare. */
303 
304 EXPORT int string_compare_encoded (const char * ap, const char * bp)
305 {
306  if (ap == NULL)
307  return (bp == NULL) ? 0 : -1;
308  if (bp == NULL)
309  return 1;
310 
311  unsigned char a = * ap ++, b = * bp ++;
312  for (; a || b; a = * ap ++, b = * bp ++)
313  {
314  if (a == '%' && ap[0] && ap[1])
315  {
316  a = (FROM_HEX (ap[0]) << 4) | FROM_HEX (ap[1]);
317  ap += 2;
318  }
319  if (b == '%' && bp[0] && bp[1])
320  {
321  b = (FROM_HEX (bp[0]) << 4) | FROM_HEX (bp[1]);
322  bp += 2;
323  }
324 
325  if (a > '9' || b > '9' || a < '0' || b < '0')
326  {
327  if (a <= 'Z' && a >= 'A')
328  a += 'a' - 'A';
329  if (b <= 'Z' && b >= 'A')
330  b += 'a' - 'A';
331 
332  if (a > b)
333  return 1;
334  if (a < b)
335  return -1;
336  }
337  else
338  {
339  int x = a - '0';
340  for (; (a = * ap) <= '9' && a >= '0'; ap ++)
341  x = 10 * x + (a - '0');
342 
343  int y = b - '0';
344  for (; (b = * bp) >= '0' && b <= '9'; bp ++)
345  y = 10 * y + (b - '0');
346 
347  if (x > y)
348  return 1;
349  if (x < y)
350  return -1;
351  }
352  }
353 
354  return 0;
355 }
356 
357 EXPORT char *
358 str_replace_fragment(char *s, int size, const char *old, const char *new)
359 {
360  char *ptr = s;
361  int left = strlen(s);
362  int avail = size - (left + 1);
363  int oldlen = strlen(old);
364  int newlen = strlen(new);
365  int diff = newlen - oldlen;
366 
367  while (left >= oldlen)
368  {
369  if (strncmp(ptr, old, oldlen))
370  {
371  left--;
372  ptr++;
373  continue;
374  }
375 
376  if (diff > avail)
377  break;
378 
379  if (diff != 0)
380  memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen);
381 
382  memcpy(ptr, new, newlen);
383  ptr += newlen;
384  left -= oldlen;
385  }
386 
387  return s;
388 }
389 
390 /*
391  * Routines to convert numbers between string and binary representations.
392  *
393  * Goals:
394  *
395  * - Accuracy, meaning that we can convert back and forth between string and
396  * binary without the number changing slightly each time.
397  * - Consistency, meaning that we get the same results no matter what
398  * architecture or locale we have to deal with.
399  * - Readability, meaning that the number one is rendered "1", not "1.000".
400  *
401  * Values are limited between -1,000,000,000 and 1,000,000,000 (inclusive) and
402  * have an accuracy of 6 decimal places.
403  */
404 
405 EXPORT bool_t string_to_int (const char * string, int * addr)
406 {
407  bool_t neg = (string[0] == '-');
408  if (neg)
409  string ++;
410 
411  int val = 0;
412  char c;
413 
414  while ((c = * string ++))
415  {
416  if (c < '0' || c > '9' || val > 100000000)
417  goto ERR;
418 
419  val = val * 10 + (c - '0');
420  }
421 
422  if (val > 1000000000)
423  goto ERR;
424 
425  * addr = neg ? -val : val;
426  return TRUE;
427 
428 ERR:
429  return FALSE;
430 }
431 
432 EXPORT bool_t string_to_double (const char * string, double * addr)
433 {
434  bool_t neg = (string[0] == '-');
435  if (neg)
436  string ++;
437 
438  const char * p = strchr (string, '.');
439  int i, f;
440 
441  if (p)
442  {
443  char buf[11];
444  int len;
445 
446  len = p - string;
447  if (len > 10)
448  goto ERR;
449 
450  memcpy (buf, string, len);
451  buf[len] = 0;
452 
453  if (! string_to_int (buf, & i))
454  goto ERR;
455 
456  len = strlen (p + 1);
457  if (len > 6)
458  goto ERR;
459 
460  memcpy (buf, p + 1, len);
461  memset (buf + len, '0', 6 - len);
462  buf[6] = 0;
463 
464  if (! string_to_int (buf, & f))
465  goto ERR;
466  }
467  else
468  {
469  if (! string_to_int (string, & i))
470  goto ERR;
471 
472  f = 0;
473  }
474 
475  double val = i + (double) f / 1000000;
476  if (val > 1000000000)
477  goto ERR;
478 
479  * addr = neg ? -val : val;
480  return TRUE;
481 
482 ERR:
483  return FALSE;
484 }
485 
486 EXPORT char * int_to_string (int val)
487 {
488  g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
489  return g_strdup_printf ("%d", val);
490 }
491 
492 EXPORT char * double_to_string (double val)
493 {
494  g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
495 
496  bool_t neg = (val < 0);
497  if (neg)
498  val = -val;
499 
500  int i = floor (val);
501  int f = round ((val - i) * 1000000);
502 
503  if (f == 1000000)
504  {
505  i ++;
506  f = 0;
507  }
508 
509  char * s = neg ? g_strdup_printf ("-%d.%06d", i, f) : g_strdup_printf ("%d.%06d", i, f);
510 
511  char * c = s + strlen (s);
512  while (* (c - 1) == '0')
513  c --;
514  if (* (c - 1) == '.')
515  c --;
516  * c = 0;
517 
518  return s;
519 }
520 
521 EXPORT bool_t string_to_double_array (const char * string, double * array, int count)
522 {
523  char * * split = g_strsplit (string, ",", -1);
524  if (g_strv_length (split) != count)
525  goto ERR;
526 
527  for (int i = 0; i < count; i ++)
528  {
529  if (! string_to_double (split[i], & array[i]))
530  goto ERR;
531  }
532 
533  g_strfreev (split);
534  return TRUE;
535 
536 ERR:
537  g_strfreev (split);
538  return FALSE;
539 }
540 
541 EXPORT char * double_array_to_string (const double * array, int count)
542 {
543  char * * split = g_malloc0 (sizeof (char *) * (count + 1));
544 
545  for (int i = 0; i < count; i ++)
546  {
547  split[i] = double_to_string (array[i]);
548  if (! split[i])
549  goto ERR;
550  }
551 
552  char * string = g_strjoinv (",", split);
553  g_strfreev (split);
554  return string;
555 
556 ERR:
557  g_strfreev (split);
558  return NULL;
559 }