00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #ifdef HAVE_CONFIG_H
00027 # include "config.h"
00028 #endif
00029
00030 #include "audstrings.h"
00031
00032 #include <math.h>
00033 #include <stdio.h>
00034 #include <glib.h>
00035 #include <audacious/i18n.h>
00036 #include <string.h>
00037 #include <ctype.h>
00038
00045 static gchar *
00046 str_replace_drive_letter(gchar * str)
00047 {
00048 gchar *match, *match_end;
00049
00050 g_return_val_if_fail(str != NULL, NULL);
00051
00052 while ((match = strstr(str, ":\\")) != NULL) {
00053 match--;
00054 match_end = match + 3;
00055 *match++ = '/';
00056 while (*match_end)
00057 *match++ = *match_end++;
00058 *match = 0;
00059 }
00060
00061 return str;
00062 }
00063
00064 gchar *
00065 str_append(gchar * str, const gchar * add_str)
00066 {
00067 return str_replace(str, g_strconcat(str, add_str, NULL));
00068 }
00069
00070 gchar *
00071 str_replace(gchar * str, gchar * new_str)
00072 {
00073 g_free(str);
00074 return new_str;
00075 }
00076
00077 void
00078 str_replace_in(gchar ** str, gchar * new_str)
00079 {
00080 *str = str_replace(*str, new_str);
00081 }
00082
00083 gboolean
00084 str_has_prefix_nocase(const gchar * str, const gchar * prefix)
00085 {
00086
00087 return (str != NULL && (strncasecmp(str, prefix, strlen(prefix)) == 0));
00088 }
00089
00090 gboolean str_has_suffix_nocase (const gchar * str, const gchar * suffix)
00091 {
00092 return (str && strlen (str) >= strlen (suffix) && ! strcasecmp (str + strlen
00093 (str) - strlen (suffix), suffix));
00094 }
00095
00096 gboolean
00097 str_has_suffixes_nocase(const gchar * str, gchar * const *suffixes)
00098 {
00099 gchar *const *suffix;
00100
00101 g_return_val_if_fail(str != NULL, FALSE);
00102 g_return_val_if_fail(suffixes != NULL, FALSE);
00103
00104 for (suffix = suffixes; *suffix; suffix++)
00105 if (str_has_suffix_nocase(str, *suffix))
00106 return TRUE;
00107
00108 return FALSE;
00109 }
00110
00111 static gchar * (* str_to_utf8_impl) (const gchar *) = NULL;
00112 static gchar * (* str_to_utf8_full_impl) (const gchar *, gssize, gsize *,
00113 gsize *, GError * *) = NULL;
00114
00115 void str_set_utf8_impl (gchar * (* stu_impl) (const gchar *),
00116 gchar * (* stuf_impl) (const gchar *, gssize, gsize *, gsize *, GError * *))
00117 {
00118 str_to_utf8_impl = stu_impl;
00119 str_to_utf8_full_impl = stuf_impl;
00120 }
00121
00129 gchar * str_to_utf8 (const gchar * str)
00130 {
00131 g_return_val_if_fail (str_to_utf8_impl, NULL);
00132 return str_to_utf8_impl (str);
00133 }
00134
00135 gchar * str_to_utf8_full (const gchar * str, gssize len, gsize * bytes_read,
00136 gsize * bytes_written, GError * * err)
00137 {
00138 g_return_val_if_fail (str_to_utf8_full_impl, NULL);
00139 return str_to_utf8_full_impl (str, len, bytes_read, bytes_written, err);
00140 }
00141
00142 #ifdef HAVE_EXECINFO_H
00143 # include <execinfo.h>
00144 #endif
00145
00160 gchar *
00161 str_assert_utf8(const gchar * str)
00162 {
00163
00164 if (str == NULL)
00165 return NULL;
00166
00167
00168 if (!g_utf8_validate(str, -1, NULL)) {
00169 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
00170 gint i, nsymbols;
00171 const gint nsymmax = 50;
00172 void *addrbuf[nsymmax];
00173 gchar **symbols;
00174 nsymbols = backtrace(addrbuf, nsymmax);
00175 symbols = backtrace_symbols(addrbuf, nsymbols);
00176
00177 fprintf(stderr, "String '%s' was not UTF-8! Backtrace (%d):\n", str, nsymbols);
00178
00179 for (i = 0; i < nsymbols; i++)
00180 fprintf(stderr, " #%d: %s\n", i, symbols[i]);
00181
00182 free(symbols);
00183 #else
00184 g_warning("String '%s' was not UTF-8!", str);
00185 #endif
00186 return str_to_utf8(str);
00187 } else
00188 return g_strdup(str);
00189 }
00190
00191
00192 const gchar *
00193 str_skip_chars(const gchar * str, const gchar * chars)
00194 {
00195 while (strchr(chars, *str) != NULL)
00196 str++;
00197 return str;
00198 }
00199
00200 gchar *
00201 convert_dos_path(gchar * path)
00202 {
00203 g_return_val_if_fail(path != NULL, NULL);
00204
00205
00206 str_replace_drive_letter(path);
00207
00208
00209 string_replace_char (path, '\\', '/');
00210
00211 return path;
00212 }
00213
00226 gchar *
00227 filename_get_subtune(const gchar * filename, gint * track)
00228 {
00229 gchar *pos;
00230
00231 if ((pos = strrchr(filename, '?')) != NULL)
00232 {
00233 const gchar *s = pos + 1;
00234 while (*s != '\0' && g_ascii_isdigit(*s)) s++;
00235 if (*s == '\0') {
00236 if (track != NULL)
00237 *track = atoi(pos + 1);
00238 return pos;
00239 }
00240 }
00241
00242 return NULL;
00243 }
00244
00257 gchar *
00258 filename_split_subtune(const gchar * filename, gint * track)
00259 {
00260 gchar *result;
00261 gchar *pos;
00262
00263 g_return_val_if_fail(filename != NULL, NULL);
00264
00265 result = g_strdup(filename);
00266 g_return_val_if_fail(result != NULL, NULL);
00267
00268 if ((pos = filename_get_subtune(result, track)) != NULL)
00269 *pos = '\0';
00270
00271 return result;
00272 }
00273
00274 void string_replace_char (gchar * string, gchar old_str, gchar new_str)
00275 {
00276 while ((string = strchr (string, old_str)) != NULL)
00277 * string = new_str;
00278 }
00279
00280 static inline gchar get_hex_digit (const gchar * * get)
00281 {
00282 gchar c = * * get;
00283
00284 if (! c)
00285 return 0;
00286
00287 (* get) ++;
00288
00289 if (c < 'A')
00290 return c - '0';
00291 if (c < 'a')
00292 return c - 'A' + 10;
00293
00294 return c - 'a' + 10;
00295 }
00296
00297
00298
00299
00300 static void string_decode_percent_2 (const gchar * from, gchar * to)
00301 {
00302 gchar c;
00303 while ((c = * from ++))
00304 * to ++ = (c != '%') ? c : ((get_hex_digit (& from) << 4) | get_hex_digit
00305 (& from));
00306
00307 * to = 0;
00308 }
00309
00310
00311
00312 void string_decode_percent (gchar * s)
00313 {
00314 string_decode_percent_2 (s, s);
00315 }
00316
00317
00318
00319
00320 static gboolean is_legal_char (gchar c, gboolean is_filename)
00321 {
00322 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <=
00323 '9') || (strchr ("-_.~", c) != NULL) ||
00324 #ifdef _WIN32
00325 (is_filename && strchr ("/:", c) != NULL);
00326 #else
00327 (is_filename && c == '/');
00328 #endif
00329 }
00330
00331 static gchar make_hex_digit (gint i)
00332 {
00333 if (i < 10)
00334 return '0' + i;
00335 else
00336 return ('A' - 10) + i;
00337 }
00338
00339
00340
00341 gchar * string_encode_percent (const gchar * string, gboolean is_filename)
00342 {
00343 gint length = 0;
00344 const gchar * get;
00345 gchar c;
00346 gchar * new, * set;
00347
00348 for (get = string; (c = * get); get ++)
00349 {
00350 if (is_legal_char (c, is_filename))
00351 length ++;
00352 else
00353 length += 3;
00354 }
00355
00356 new = g_malloc (length + 1);
00357 set = new;
00358
00359 for (get = string; (c = * get); get ++)
00360 {
00361 if (is_legal_char (c, is_filename))
00362 * set ++ = c;
00363 else
00364 {
00365 * set ++ = '%';
00366 * set ++ = make_hex_digit (((guchar) c) >> 4);
00367 * set ++ = make_hex_digit (c & 0xF);
00368 }
00369 }
00370
00371 * set = 0;
00372 return new;
00373 }
00374
00375
00376
00377
00378 gboolean uri_is_utf8 (const gchar * uri, gboolean warn)
00379 {
00380 gchar buf[strlen (uri) + 1];
00381 string_decode_percent_2 (uri, buf);
00382
00383 if (g_utf8_validate (buf, -1, NULL))
00384 return TRUE;
00385
00386 if (warn)
00387 fprintf (stderr, "URI is not UTF-8: %s.\n", buf);
00388
00389 return FALSE;
00390 }
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413 gchar * uri_to_utf8 (const gchar * uri)
00414 {
00415 if (strncmp (uri, "file://", 7))
00416 return g_strdup (uri);
00417
00418
00419 gchar buf[strlen (uri + 7) + 1];
00420 string_decode_percent_2 (uri + 7, buf);
00421
00422
00423 return filename_to_uri (buf);
00424 }
00425
00426
00427
00428
00429
00430 void uri_check_utf8 (gchar * * uri, gboolean warn)
00431 {
00432 if (uri_is_utf8 (* uri, warn))
00433 return;
00434
00435 gchar * copy = uri_to_utf8 (* uri);
00436 g_free (* uri);
00437 * uri = copy;
00438 }
00439
00440
00441
00442
00443
00444 gchar * filename_to_uri (const gchar * name)
00445 {
00446 gchar * utf8 = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
00447 #ifdef _WIN32
00448 string_replace_char (utf8, '\\', '/');
00449 #endif
00450 gchar * enc = string_encode_percent (utf8 ? utf8 : name, TRUE);
00451 g_free (utf8);
00452 #ifdef _WIN32
00453 gchar * uri = g_strdup_printf ("file:///%s", enc);
00454 #else
00455 gchar * uri = g_strdup_printf ("file://%s", enc);
00456 #endif
00457 g_free (enc);
00458 return uri;
00459 }
00460
00461
00462
00463
00464
00465 gchar * uri_to_filename (const gchar * uri)
00466 {
00467 #ifdef _WIN32
00468 g_return_val_if_fail (! strncmp (uri, "file:///", 8), NULL);
00469 gchar buf[strlen (uri + 8) + 1];
00470 string_decode_percent_2 (uri + 8, buf);
00471 #else
00472 g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL);
00473 gchar buf[strlen (uri + 7) + 1];
00474 string_decode_percent_2 (uri + 7, buf);
00475 #endif
00476 #ifdef _WIN32
00477 string_replace_char (buf, '/', '\\');
00478 #endif
00479 gchar * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL);
00480 return name ? name : g_strdup (buf);
00481 }
00482
00483
00484
00485
00486
00487 gchar * uri_to_display (const gchar * uri)
00488 {
00489 gchar buf[strlen (uri) + 1];
00490
00491 #ifdef _WIN32
00492 if (! strncmp (uri, "file:///", 8))
00493 {
00494 string_decode_percent_2 (uri + 8, buf);
00495 string_replace_char (buf, '/', '\\');
00496 }
00497 #else
00498 if (! strncmp (uri, "file://", 7))
00499 string_decode_percent_2 (uri + 7, buf);
00500 #endif
00501 else
00502 string_decode_percent_2 (uri, buf);
00503
00504 return str_to_utf8 (buf);
00505 }
00506
00507 gchar * uri_get_extension (const gchar * uri)
00508 {
00509 const gchar * slash = strrchr (uri, '/');
00510 if (! slash)
00511 return NULL;
00512
00513 gchar * lower = g_ascii_strdown (slash + 1, -1);
00514
00515 gchar * qmark = strchr (lower, '?');
00516 if (qmark)
00517 * qmark = 0;
00518
00519 gchar * dot = strrchr (lower, '.');
00520 gchar * ext = dot ? g_strdup (dot + 1) : NULL;
00521
00522 g_free (lower);
00523 return ext;
00524 }
00525
00526 void string_cut_extension(gchar *string)
00527 {
00528 gchar *period = strrchr(string, '.');
00529
00530 if (period != NULL)
00531 *period = 0;
00532 }
00533
00534
00535
00536
00537
00538 gint string_compare (const gchar * ap, const gchar * bp)
00539 {
00540 if (ap == NULL)
00541 return (bp == NULL) ? 0 : -1;
00542 if (bp == NULL)
00543 return 1;
00544
00545 guchar a = * ap ++, b = * bp ++;
00546 for (; a || b; a = * ap ++, b = * bp ++)
00547 {
00548 if (a > '9' || b > '9' || a < '0' || b < '0')
00549 {
00550 if (a <= 'Z' && a >= 'A')
00551 a += 'a' - 'A';
00552 if (b <= 'Z' && b >= 'A')
00553 b += 'a' - 'A';
00554
00555 if (a > b)
00556 return 1;
00557 if (a < b)
00558 return -1;
00559 }
00560 else
00561 {
00562 gint x = a - '0';
00563 for (; (a = * ap) <= '9' && a >= '0'; ap ++)
00564 x = 10 * x + (a - '0');
00565
00566 gint y = b - '0';
00567 for (; (b = * bp) >= '0' && b <= '9'; bp ++)
00568 y = 10 * y + (b - '0');
00569
00570 if (x > y)
00571 return 1;
00572 if (x < y)
00573 return -1;
00574 }
00575 }
00576
00577 return 0;
00578 }
00579
00580
00581
00582 gint string_compare_encoded (const gchar * ap, const gchar * bp)
00583 {
00584 if (ap == NULL)
00585 return (bp == NULL) ? 0 : -1;
00586 if (bp == NULL)
00587 return 1;
00588
00589 guchar a = * ap ++, b = * bp ++;
00590 for (; a || b; a = * ap ++, b = * bp ++)
00591 {
00592 if (a == '%')
00593 a = (get_hex_digit (& ap) << 4) | get_hex_digit (& ap);
00594 if (b == '%')
00595 b = (get_hex_digit (& bp) << 4) | get_hex_digit (& bp);
00596
00597 if (a > '9' || b > '9' || a < '0' || b < '0')
00598 {
00599 if (a <= 'Z' && a >= 'A')
00600 a += 'a' - 'A';
00601 if (b <= 'Z' && b >= 'A')
00602 b += 'a' - 'A';
00603
00604 if (a > b)
00605 return 1;
00606 if (a < b)
00607 return -1;
00608 }
00609 else
00610 {
00611 gint x = a - '0';
00612 for (; (a = * ap) <= '9' && a >= '0'; ap ++)
00613 x = 10 * x + (a - '0');
00614
00615 gint y = b - '0';
00616 for (; (b = * bp) >= '0' && b <= '9'; bp ++)
00617 y = 10 * y + (b - '0');
00618
00619 if (x > y)
00620 return 1;
00621 if (x < y)
00622 return -1;
00623 }
00624 }
00625
00626 return 0;
00627 }
00628
00629 const void * memfind (const void * mem, gint size, const void * token, gint
00630 length)
00631 {
00632 if (! length)
00633 return mem;
00634
00635 size -= length - 1;
00636
00637 while (size > 0)
00638 {
00639 const void * maybe = memchr (mem, * (guchar *) token, size);
00640
00641 if (maybe == NULL)
00642 return NULL;
00643
00644 if (! memcmp (maybe, token, length))
00645 return maybe;
00646
00647 size -= (guchar *) maybe + 1 - (guchar *) mem;
00648 mem = (guchar *) maybe + 1;
00649 }
00650
00651 return NULL;
00652 }
00653
00654 gchar *
00655 str_replace_fragment(gchar *s, gint size, const gchar *old, const gchar *new)
00656 {
00657 gchar *ptr = s;
00658 gint left = strlen(s);
00659 gint avail = size - (left + 1);
00660 gint oldlen = strlen(old);
00661 gint newlen = strlen(new);
00662 gint diff = newlen - oldlen;
00663
00664 while (left >= oldlen)
00665 {
00666 if (strncmp(ptr, old, oldlen))
00667 {
00668 left--;
00669 ptr++;
00670 continue;
00671 }
00672
00673 if (diff > avail)
00674 break;
00675
00676 if (diff != 0)
00677 memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen);
00678
00679 memcpy(ptr, new, newlen);
00680 ptr += newlen;
00681 left -= oldlen;
00682 }
00683
00684 return s;
00685 }
00686
00687 void
00688 string_canonize_case(gchar *str)
00689 {
00690 while (*str)
00691 {
00692 *str = g_ascii_toupper(*str);
00693 str++;
00694 }
00695 }
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712 gboolean string_to_int (const gchar * string, gint * addr)
00713 {
00714 gboolean neg = (string[0] == '-');
00715 if (neg)
00716 string ++;
00717
00718 gint val = 0;
00719 gchar c;
00720
00721 while ((c = * string ++))
00722 {
00723 if (c < '0' || c > '9' || val > 100000000)
00724 goto ERR;
00725
00726 val = val * 10 + (c - '0');
00727 }
00728
00729 if (val > 1000000000)
00730 goto ERR;
00731
00732 * addr = neg ? -val : val;
00733 return TRUE;
00734
00735 ERR:
00736 return FALSE;
00737 }
00738
00739 gboolean string_to_double (const gchar * string, gdouble * addr)
00740 {
00741 gboolean neg = (string[0] == '-');
00742 if (neg)
00743 string ++;
00744
00745 const gchar * p = strchr (string, '.');
00746 gint i, f;
00747
00748 if (p)
00749 {
00750 gchar buf[11];
00751 gint len;
00752
00753 len = p - string;
00754 if (len > 10)
00755 goto ERR;
00756
00757 memcpy (buf, string, len);
00758 buf[len] = 0;
00759
00760 if (! string_to_int (buf, & i))
00761 goto ERR;
00762
00763 len = strlen (p + 1);
00764 if (len > 6)
00765 goto ERR;
00766
00767 memcpy (buf, p + 1, len);
00768 memset (buf + len, '0', 6 - len);
00769 buf[6] = 0;
00770
00771 if (! string_to_int (buf, & f))
00772 goto ERR;
00773 }
00774 else
00775 {
00776 if (! string_to_int (string, & i))
00777 goto ERR;
00778
00779 f = 0;
00780 }
00781
00782 gdouble val = i + (gdouble) f / 1000000;
00783 if (val > 1000000000)
00784 goto ERR;
00785
00786 * addr = neg ? -val : val;
00787 return TRUE;
00788
00789 ERR:
00790 return FALSE;
00791 }
00792
00793 gchar * int_to_string (gint val)
00794 {
00795 g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
00796 return g_strdup_printf ("%d", val);
00797 }
00798
00799 gchar * double_to_string (gdouble val)
00800 {
00801 g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
00802
00803 gboolean neg = (val < 0);
00804 if (neg)
00805 val = -val;
00806
00807 gint i = floor (val);
00808 gint f = round ((val - i) * 1000000);
00809
00810 if (f == 1000000)
00811 {
00812 i ++;
00813 f = 0;
00814 }
00815
00816 gchar * s = neg ? g_strdup_printf ("-%d.%06d", i, f) : g_strdup_printf ("%d.%06d", i, f);
00817
00818 gchar * c = s + strlen (s);
00819 while (* (c - 1) == '0')
00820 c --;
00821 if (* (c - 1) == '.')
00822 c --;
00823 * c = 0;
00824
00825 return s;
00826 }
00827
00828 gboolean string_to_double_array (const gchar * string, gdouble * array, gint count)
00829 {
00830 gchar * * split = g_strsplit (string, ",", -1);
00831 if (g_strv_length (split) != count)
00832 goto ERR;
00833
00834 for (gint i = 0; i < count; i ++)
00835 {
00836 if (! string_to_double (split[i], & array[i]))
00837 goto ERR;
00838 }
00839
00840 g_strfreev (split);
00841 return TRUE;
00842
00843 ERR:
00844 g_strfreev (split);
00845 return FALSE;
00846 }
00847
00848 gchar * double_array_to_string (const gdouble * array, gint count)
00849 {
00850 gchar * * split = g_malloc0 (sizeof (gchar *) * (count + 1));
00851
00852 for (gint i = 0; i < count; i ++)
00853 {
00854 split[i] = double_to_string (array[i]);
00855 if (! split[i])
00856 goto ERR;
00857 }
00858
00859 gchar * string = g_strjoinv (",", split);
00860 g_strfreev (split);
00861 return string;
00862
00863 ERR:
00864 g_strfreev (split);
00865 return NULL;
00866 }