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 <stdio.h>
00033 #include <glib.h>
00034 #include <audacious/i18n.h>
00035 #include <string.h>
00036 #include <ctype.h>
00037
00044 static gchar *
00045 str_replace_drive_letter(gchar * str)
00046 {
00047 gchar *match, *match_end;
00048
00049 g_return_val_if_fail(str != NULL, NULL);
00050
00051 while ((match = strstr(str, ":\\")) != NULL) {
00052 match--;
00053 match_end = match + 3;
00054 *match++ = '/';
00055 while (*match_end)
00056 *match++ = *match_end++;
00057 *match = 0;
00058 }
00059
00060 return str;
00061 }
00062
00063 gchar *
00064 str_append(gchar * str, const gchar * add_str)
00065 {
00066 return str_replace(str, g_strconcat(str, add_str, NULL));
00067 }
00068
00069 gchar *
00070 str_replace(gchar * str, gchar * new_str)
00071 {
00072 g_free(str);
00073 return new_str;
00074 }
00075
00076 void
00077 str_replace_in(gchar ** str, gchar * new_str)
00078 {
00079 *str = str_replace(*str, new_str);
00080 }
00081
00082 gboolean
00083 str_has_prefix_nocase(const gchar * str, const gchar * prefix)
00084 {
00085
00086 return (str != NULL && (strncasecmp(str, prefix, strlen(prefix)) == 0));
00087 }
00088
00089 gboolean str_has_suffix_nocase (const gchar * str, const gchar * suffix)
00090 {
00091 return (str && strlen (str) >= strlen (suffix) && ! strcasecmp (str + strlen
00092 (str) - strlen (suffix), suffix));
00093 }
00094
00095 gboolean
00096 str_has_suffixes_nocase(const gchar * str, gchar * const *suffixes)
00097 {
00098 gchar *const *suffix;
00099
00100 g_return_val_if_fail(str != NULL, FALSE);
00101 g_return_val_if_fail(suffixes != NULL, FALSE);
00102
00103 for (suffix = suffixes; *suffix; suffix++)
00104 if (str_has_suffix_nocase(str, *suffix))
00105 return TRUE;
00106
00107 return FALSE;
00108 }
00109
00110 static gchar * (* str_to_utf8_impl) (const gchar *) = NULL;
00111 static gchar * (* str_to_utf8_full_impl) (const gchar *, gssize, gsize *,
00112 gsize *, GError * *) = NULL;
00113
00114 void str_set_utf8_impl (gchar * (* stu_impl) (const gchar *),
00115 gchar * (* stuf_impl) (const gchar *, gssize, gsize *, gsize *, GError * *))
00116 {
00117 str_to_utf8_impl = stu_impl;
00118 str_to_utf8_full_impl = stuf_impl;
00119 }
00120
00128 gchar * str_to_utf8 (const gchar * str)
00129 {
00130 g_return_val_if_fail (str_to_utf8_impl, NULL);
00131 return str_to_utf8_impl (str);
00132 }
00133
00134 gchar * str_to_utf8_full (const gchar * str, gssize len, gsize * bytes_read,
00135 gsize * bytes_written, GError * * err)
00136 {
00137 g_return_val_if_fail (str_to_utf8_full_impl, NULL);
00138 return str_to_utf8_full_impl (str, len, bytes_read, bytes_written, err);
00139 }
00140
00141 #ifdef HAVE_EXECINFO_H
00142 # include <execinfo.h>
00143 #endif
00144
00159 gchar *
00160 str_assert_utf8(const gchar * str)
00161 {
00162
00163 if (str == NULL)
00164 return NULL;
00165
00166
00167 if (!g_utf8_validate(str, -1, NULL)) {
00168 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
00169 gint i, nsymbols;
00170 const gint nsymmax = 50;
00171 void *addrbuf[nsymmax];
00172 gchar **symbols;
00173 nsymbols = backtrace(addrbuf, nsymmax);
00174 symbols = backtrace_symbols(addrbuf, nsymbols);
00175
00176 fprintf(stderr, "String '%s' was not UTF-8! Backtrace (%d):\n", str, nsymbols);
00177
00178 for (i = 0; i < nsymbols; i++)
00179 fprintf(stderr, " #%d: %s\n", i, symbols[i]);
00180
00181 free(symbols);
00182 #else
00183 g_warning("String '%s' was not UTF-8!", str);
00184 #endif
00185 return str_to_utf8(str);
00186 } else
00187 return g_strdup(str);
00188 }
00189
00190
00191 const gchar *
00192 str_skip_chars(const gchar * str, const gchar * chars)
00193 {
00194 while (strchr(chars, *str) != NULL)
00195 str++;
00196 return str;
00197 }
00198
00199 gchar *
00200 convert_dos_path(gchar * path)
00201 {
00202 g_return_val_if_fail(path != NULL, NULL);
00203
00204
00205 str_replace_drive_letter(path);
00206
00207
00208 string_replace_char (path, '\\', '/');
00209
00210 return path;
00211 }
00212
00225 gchar *
00226 filename_get_subtune(const gchar * filename, gint * track)
00227 {
00228 gchar *pos;
00229
00230 if ((pos = strrchr(filename, '?')) != NULL)
00231 {
00232 const gchar *s = pos + 1;
00233 while (*s != '\0' && g_ascii_isdigit(*s)) s++;
00234 if (*s == '\0') {
00235 if (track != NULL)
00236 *track = atoi(pos + 1);
00237 return pos;
00238 }
00239 }
00240
00241 return NULL;
00242 }
00243
00256 gchar *
00257 filename_split_subtune(const gchar * filename, gint * track)
00258 {
00259 gchar *result;
00260 gchar *pos;
00261
00262 g_return_val_if_fail(filename != NULL, NULL);
00263
00264 result = g_strdup(filename);
00265 g_return_val_if_fail(result != NULL, NULL);
00266
00267 if ((pos = filename_get_subtune(result, track)) != NULL)
00268 *pos = '\0';
00269
00270 return result;
00271 }
00272
00273 void string_replace_char (gchar * string, gchar old_str, gchar new_str)
00274 {
00275 while ((string = strchr (string, old_str)) != NULL)
00276 * string = new_str;
00277 }
00278
00279 static inline gchar get_hex_digit (const gchar * * get)
00280 {
00281 gchar c = * * get;
00282
00283 if (! c)
00284 return 0;
00285
00286 (* get) ++;
00287
00288 if (c < 'A')
00289 return c - '0';
00290 if (c < 'a')
00291 return c - 'A' + 10;
00292
00293 return c - 'a' + 10;
00294 }
00295
00296
00297
00298
00299 static void string_decode_percent_2 (const gchar * from, gchar * to)
00300 {
00301 gchar c;
00302 while ((c = * from ++))
00303 * to ++ = (c != '%') ? c : ((get_hex_digit (& from) << 4) | get_hex_digit
00304 (& from));
00305
00306 * to = 0;
00307 }
00308
00309
00310
00311 void string_decode_percent (gchar * s)
00312 {
00313 string_decode_percent_2 (s, s);
00314 }
00315
00316
00317
00318
00319 static gboolean is_legal_char (gchar c, gboolean is_filename)
00320 {
00321 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <=
00322 '9') || (strchr ("-_.~", c) != NULL) ||
00323 #ifdef _WIN32
00324 (is_filename && strchr ("/:", c) != NULL);
00325 #else
00326 (is_filename && c == '/');
00327 #endif
00328 }
00329
00330 static gchar make_hex_digit (gint i)
00331 {
00332 if (i < 10)
00333 return '0' + i;
00334 else
00335 return ('A' - 10) + i;
00336 }
00337
00338
00339
00340 gchar * string_encode_percent (const gchar * string, gboolean is_filename)
00341 {
00342 gint length = 0;
00343 const gchar * get;
00344 gchar c;
00345 gchar * new, * set;
00346
00347 for (get = string; (c = * get); get ++)
00348 {
00349 if (is_legal_char (c, is_filename))
00350 length ++;
00351 else
00352 length += 3;
00353 }
00354
00355 new = g_malloc (length + 1);
00356 set = new;
00357
00358 for (get = string; (c = * get); get ++)
00359 {
00360 if (is_legal_char (c, is_filename))
00361 * set ++ = c;
00362 else
00363 {
00364 * set ++ = '%';
00365 * set ++ = make_hex_digit (((guchar) c) >> 4);
00366 * set ++ = make_hex_digit (c & 0xF);
00367 }
00368 }
00369
00370 * set = 0;
00371 return new;
00372 }
00373
00374
00375
00376
00377 gboolean uri_is_utf8 (const gchar * uri, gboolean warn)
00378 {
00379 gchar buf[strlen (uri) + 1];
00380 string_decode_percent_2 (uri, buf);
00381
00382 if (g_utf8_validate (buf, -1, NULL))
00383 return TRUE;
00384
00385 if (warn)
00386 fprintf (stderr, "URI is not UTF-8: %s.\n", buf);
00387
00388 return FALSE;
00389 }
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412 gchar * uri_to_utf8 (const gchar * uri)
00413 {
00414 if (strncmp (uri, "file://", 7))
00415 return g_strdup (uri);
00416
00417
00418 gchar buf[strlen (uri + 7) + 1];
00419 string_decode_percent_2 (uri + 7, buf);
00420
00421
00422 return filename_to_uri (buf);
00423 }
00424
00425
00426
00427
00428
00429 void uri_check_utf8 (gchar * * uri, gboolean warn)
00430 {
00431 if (uri_is_utf8 (* uri, warn))
00432 return;
00433
00434 gchar * copy = uri_to_utf8 (* uri);
00435 g_free (* uri);
00436 * uri = copy;
00437 }
00438
00439
00440
00441
00442
00443 gchar * filename_to_uri (const gchar * name)
00444 {
00445 gchar * utf8 = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
00446 #ifdef _WIN32
00447 string_replace_char (utf8, '\\', '/');
00448 #endif
00449 gchar * enc = string_encode_percent (utf8 ? utf8 : name, TRUE);
00450 g_free (utf8);
00451 #ifdef _WIN32
00452 gchar * uri = g_strdup_printf ("file:///%s", enc);
00453 #else
00454 gchar * uri = g_strdup_printf ("file://%s", enc);
00455 #endif
00456 g_free (enc);
00457 return uri;
00458 }
00459
00460
00461
00462
00463
00464 gchar * uri_to_filename (const gchar * uri)
00465 {
00466 #ifdef _WIN32
00467 g_return_val_if_fail (! strncmp (uri, "file:///", 8), NULL);
00468 gchar buf[strlen (uri + 8) + 1];
00469 string_decode_percent_2 (uri + 8, buf);
00470 #else
00471 g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL);
00472 gchar buf[strlen (uri + 7) + 1];
00473 string_decode_percent_2 (uri + 7, buf);
00474 #endif
00475 #ifdef _WIN32
00476 string_replace_char (buf, '/', '\\');
00477 #endif
00478 gchar * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL);
00479 return name ? name : g_strdup (buf);
00480 }
00481
00482
00483
00484
00485
00486 gchar * uri_to_display (const gchar * uri)
00487 {
00488 gchar buf[strlen (uri) + 1];
00489
00490 #ifdef _WIN32
00491 if (! strncmp (uri, "file:///", 8))
00492 {
00493 string_decode_percent_2 (uri + 8, buf);
00494 string_replace_char (buf, '/', '\\');
00495 }
00496 #else
00497 if (! strncmp (uri, "file://", 7))
00498 string_decode_percent_2 (uri + 7, buf);
00499 #endif
00500 else
00501 string_decode_percent_2 (uri, buf);
00502
00503 return str_to_utf8 (buf);
00504 }
00505
00506 gchar * uri_get_extension (const gchar * uri)
00507 {
00508 const gchar * slash = strrchr (uri, '/');
00509 if (! slash)
00510 return NULL;
00511
00512 gchar * lower = g_ascii_strdown (slash + 1, -1);
00513
00514 gchar * qmark = strchr (lower, '?');
00515 if (qmark)
00516 * qmark = 0;
00517
00518 gchar * dot = strrchr (lower, '.');
00519 gchar * ext = dot ? g_strdup (dot + 1) : NULL;
00520
00521 g_free (lower);
00522 return ext;
00523 }
00524
00525 void string_cut_extension(gchar *string)
00526 {
00527 gchar *period = strrchr(string, '.');
00528
00529 if (period != NULL)
00530 *period = 0;
00531 }
00532
00533
00534
00535
00536
00537 gint string_compare (const gchar * ap, const gchar * bp)
00538 {
00539 if (ap == NULL)
00540 return (bp == NULL) ? 0 : -1;
00541 if (bp == NULL)
00542 return 1;
00543
00544 guchar a = * ap ++, b = * bp ++;
00545 for (; a || b; a = * ap ++, b = * bp ++)
00546 {
00547 if (a > '9' || b > '9' || a < '0' || b < '0')
00548 {
00549 if (a <= 'Z' && a >= 'A')
00550 a += 'a' - 'A';
00551 if (b <= 'Z' && b >= 'A')
00552 b += 'a' - 'A';
00553
00554 if (a > b)
00555 return 1;
00556 if (a < b)
00557 return -1;
00558 }
00559 else
00560 {
00561 gint x = a - '0';
00562 for (; (a = * ap) <= '9' && a >= '0'; ap ++)
00563 x = 10 * x + (a - '0');
00564
00565 gint y = b - '0';
00566 for (; (b = * bp) >= '0' && b <= '9'; bp ++)
00567 y = 10 * y + (b - '0');
00568
00569 if (x > y)
00570 return 1;
00571 if (x < y)
00572 return -1;
00573 }
00574 }
00575
00576 return 0;
00577 }
00578
00579
00580
00581 gint string_compare_encoded (const gchar * ap, const gchar * bp)
00582 {
00583 if (ap == NULL)
00584 return (bp == NULL) ? 0 : -1;
00585 if (bp == NULL)
00586 return 1;
00587
00588 guchar a = * ap ++, b = * bp ++;
00589 for (; a || b; a = * ap ++, b = * bp ++)
00590 {
00591 if (a == '%')
00592 a = (get_hex_digit (& ap) << 4) | get_hex_digit (& ap);
00593 if (b == '%')
00594 b = (get_hex_digit (& bp) << 4) | get_hex_digit (& bp);
00595
00596 if (a > '9' || b > '9' || a < '0' || b < '0')
00597 {
00598 if (a <= 'Z' && a >= 'A')
00599 a += 'a' - 'A';
00600 if (b <= 'Z' && b >= 'A')
00601 b += 'a' - 'A';
00602
00603 if (a > b)
00604 return 1;
00605 if (a < b)
00606 return -1;
00607 }
00608 else
00609 {
00610 gint x = a - '0';
00611 for (; (a = * ap) <= '9' && a >= '0'; ap ++)
00612 x = 10 * x + (a - '0');
00613
00614 gint y = b - '0';
00615 for (; (b = * bp) >= '0' && b <= '9'; bp ++)
00616 y = 10 * y + (b - '0');
00617
00618 if (x > y)
00619 return 1;
00620 if (x < y)
00621 return -1;
00622 }
00623 }
00624
00625 return 0;
00626 }
00627
00628 const void * memfind (const void * mem, gint size, const void * token, gint
00629 length)
00630 {
00631 if (! length)
00632 return mem;
00633
00634 size -= length - 1;
00635
00636 while (size > 0)
00637 {
00638 const void * maybe = memchr (mem, * (guchar *) token, size);
00639
00640 if (maybe == NULL)
00641 return NULL;
00642
00643 if (! memcmp (maybe, token, length))
00644 return maybe;
00645
00646 size -= (guchar *) maybe + 1 - (guchar *) mem;
00647 mem = (guchar *) maybe + 1;
00648 }
00649
00650 return NULL;
00651 }
00652
00653 gchar *
00654 str_replace_fragment(gchar *s, gint size, const gchar *old, const gchar *new)
00655 {
00656 gchar *ptr = s;
00657 gint left = strlen(s);
00658 gint avail = size - (left + 1);
00659 gint oldlen = strlen(old);
00660 gint newlen = strlen(new);
00661 gint diff = newlen - oldlen;
00662
00663 while (left >= oldlen)
00664 {
00665 if (strncmp(ptr, old, oldlen))
00666 {
00667 left--;
00668 ptr++;
00669 continue;
00670 }
00671
00672 if (diff > avail)
00673 break;
00674
00675 if (diff != 0)
00676 memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen);
00677
00678 memcpy(ptr, new, newlen);
00679 ptr += newlen;
00680 left -= oldlen;
00681 }
00682
00683 return s;
00684 }
00685
00686 void
00687 string_canonize_case(gchar *str)
00688 {
00689 while (*str)
00690 {
00691 *str = g_ascii_toupper(*str);
00692 str++;
00693 }
00694 }