Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* Audacious 00002 * Copyright (C) 2005-2009 Audacious development team. 00003 * 00004 * BMP - Cross-platform multimedia player 00005 * Copyright (C) 2003-2004 BMP development team. 00006 * 00007 * Based on XMMS: 00008 * Copyright (C) 1998-2003 XMMS development team. 00009 * 00010 * This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; under version 3 of the License. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU General Public License 00020 * along with this program. If not, see <http://www.gnu.org/licenses>. 00021 * 00022 * The Audacious team does not consider modular code linking to 00023 * Audacious or using our public API to be a derived work. 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 gchar * 00045 escape_shell_chars(const gchar * string) 00046 { 00047 const gchar *special = "$`\"\\"; /* Characters to escape */ 00048 const gchar *in = string; 00049 gchar *out, *escaped; 00050 gint num = 0; 00051 00052 while (*in != '\0') 00053 if (strchr(special, *in++)) 00054 num++; 00055 00056 escaped = g_malloc(strlen(string) + num + 1); 00057 00058 in = string; 00059 out = escaped; 00060 00061 while (*in != '\0') { 00062 if (strchr(special, *in)) 00063 *out++ = '\\'; 00064 *out++ = *in++; 00065 } 00066 *out = '\0'; 00067 00068 return escaped; 00069 } 00070 00077 static gchar * 00078 str_replace_drive_letter(gchar * str) 00079 { 00080 gchar *match, *match_end; 00081 00082 g_return_val_if_fail(str != NULL, NULL); 00083 00084 while ((match = strstr(str, ":\\")) != NULL) { 00085 match--; 00086 match_end = match + 3; 00087 *match++ = '/'; 00088 while (*match_end) 00089 *match++ = *match_end++; 00090 *match = 0; /* the end of line */ 00091 } 00092 00093 return str; 00094 } 00095 00096 gchar * 00097 str_append(gchar * str, const gchar * add_str) 00098 { 00099 return str_replace(str, g_strconcat(str, add_str, NULL)); 00100 } 00101 00102 gchar * 00103 str_replace(gchar * str, gchar * new_str) 00104 { 00105 g_free(str); 00106 return new_str; 00107 } 00108 00109 void 00110 str_replace_in(gchar ** str, gchar * new_str) 00111 { 00112 *str = str_replace(*str, new_str); 00113 } 00114 00115 gboolean 00116 str_has_prefix_nocase(const gchar * str, const gchar * prefix) 00117 { 00118 /* strncasecmp causes segfaults when str is NULL*/ 00119 return (str != NULL && (strncasecmp(str, prefix, strlen(prefix)) == 0)); 00120 } 00121 00122 gboolean str_has_suffix_nocase (const gchar * str, const gchar * suffix) 00123 { 00124 return (str && strlen (str) >= strlen (suffix) && ! strcasecmp (str + strlen 00125 (str) - strlen (suffix), suffix)); 00126 } 00127 00128 gboolean 00129 str_has_suffixes_nocase(const gchar * str, gchar * const *suffixes) 00130 { 00131 gchar *const *suffix; 00132 00133 g_return_val_if_fail(str != NULL, FALSE); 00134 g_return_val_if_fail(suffixes != NULL, FALSE); 00135 00136 for (suffix = suffixes; *suffix; suffix++) 00137 if (str_has_suffix_nocase(str, *suffix)) 00138 return TRUE; 00139 00140 return FALSE; 00141 } 00142 00143 gchar * 00144 str_to_utf8_fallback(const gchar * str) 00145 { 00146 gchar *out_str, *convert_str, *chr; 00147 00148 if (!str) 00149 return NULL; 00150 00151 convert_str = g_strdup(str); 00152 for (chr = convert_str; *chr; chr++) { 00153 if (*chr & 0x80) 00154 *chr = '?'; 00155 } 00156 00157 out_str = g_strconcat(convert_str, _(" (invalid UTF-8)"), NULL); 00158 g_free(convert_str); 00159 00160 return out_str; 00161 } 00162 00169 gchar *(*str_to_utf8)(const gchar * str) = str_to_utf8_fallback; 00170 00171 gchar *(*chardet_to_utf8)(const gchar *str, gssize len, 00172 gsize *arg_bytes_read, gsize *arg_bytes_write, 00173 GError **arg_error) = NULL; 00174 00175 #ifdef HAVE_EXECINFO_H 00176 # include <execinfo.h> 00177 #endif 00178 00193 gchar * 00194 str_assert_utf8(const gchar * str) 00195 { 00196 /* NULL in NULL out */ 00197 if (str == NULL) 00198 return NULL; 00199 00200 /* already UTF-8? */ 00201 if (!g_utf8_validate(str, -1, NULL)) { 00202 #ifdef HAVE_EXECINFO_H 00203 gint i, nsymbols; 00204 const gint nsymmax = 50; 00205 void *addrbuf[nsymmax]; 00206 gchar **symbols; 00207 nsymbols = backtrace(addrbuf, nsymmax); 00208 symbols = backtrace_symbols(addrbuf, nsymbols); 00209 00210 fprintf(stderr, "String '%s' was not UTF-8! Backtrace (%d):\n", str, nsymbols); 00211 00212 for (i = 0; i < nsymbols; i++) 00213 fprintf(stderr, " #%d: %s\n", i, symbols[i]); 00214 00215 free(symbols); 00216 #else 00217 g_warning("String '%s' was not UTF-8!", str); 00218 #endif 00219 return str_to_utf8(str); 00220 } else 00221 return g_strdup(str); 00222 } 00223 00224 00225 const gchar * 00226 str_skip_chars(const gchar * str, const gchar * chars) 00227 { 00228 while (strchr(chars, *str) != NULL) 00229 str++; 00230 return str; 00231 } 00232 00233 const void * memfind (const void * mem, gint size, const void * token, gint 00234 length) 00235 { 00236 if (! length) 00237 return mem; 00238 00239 size -= length - 1; 00240 00241 while (size > 0) 00242 { 00243 const void * maybe = memchr (mem, * (guchar *) token, size); 00244 00245 if (maybe == NULL) 00246 return NULL; 00247 00248 if (! memcmp (maybe, token, length)) 00249 return maybe; 00250 00251 size -= (guchar *) maybe + 1 - (guchar *) mem; 00252 mem = (guchar *) maybe + 1; 00253 } 00254 00255 return NULL; 00256 } 00257 00258 gchar * 00259 convert_dos_path(gchar * path) 00260 { 00261 g_return_val_if_fail(path != NULL, NULL); 00262 00263 /* replace drive letter with '/' */ 00264 str_replace_drive_letter(path); 00265 00266 /* replace '\' with '/' */ 00267 string_replace_char (path, '\\', '/'); 00268 00269 return path; 00270 } 00271 00284 gchar * 00285 filename_get_subtune(const gchar * filename, gint * track) 00286 { 00287 gchar *pos; 00288 00289 if ((pos = strrchr(filename, '?')) != NULL) 00290 { 00291 const gchar *s = pos + 1; 00292 while (*s != '\0' && g_ascii_isdigit(*s)) s++; 00293 if (*s == '\0') { 00294 if (track != NULL) 00295 *track = atoi(pos + 1); 00296 return pos; 00297 } 00298 } 00299 00300 return NULL; 00301 } 00302 00315 gchar * 00316 filename_split_subtune(const gchar * filename, gint * track) 00317 { 00318 gchar *result; 00319 gchar *pos; 00320 00321 g_return_val_if_fail(filename != NULL, NULL); 00322 00323 result = g_strdup(filename); 00324 g_return_val_if_fail(result != NULL, NULL); 00325 00326 if ((pos = filename_get_subtune(result, track)) != NULL) 00327 *pos = '\0'; 00328 00329 return result; 00330 } 00331 00332 void string_replace_char (gchar * string, gchar old_str, gchar new_str) 00333 { 00334 while ((string = strchr (string, old_str)) != NULL) 00335 * string = new_str; 00336 } 00337 00338 static inline gchar get_hex_digit (const gchar * * get) 00339 { 00340 gchar c = * * get; 00341 00342 if (! c) 00343 return 0; 00344 00345 (* get) ++; 00346 00347 if (c < 'A') 00348 return c - '0'; 00349 if (c < 'a') 00350 return c - 'A' + 10; 00351 00352 return c - 'a' + 10; 00353 } 00354 00355 /* Requires that the destination be large enough to hold the decoded string. 00356 * The source and destination may be the same string. USE EXTREME CAUTION. */ 00357 00358 static void string_decode_percent_2 (const gchar * from, gchar * to) 00359 { 00360 gchar c; 00361 while ((c = * from ++)) 00362 * to ++ = (c != '%') ? c : ((get_hex_digit (& from) << 4) | get_hex_digit 00363 (& from)); 00364 00365 * to = 0; 00366 } 00367 00368 /* Decodes a percent-encoded string in-place. */ 00369 00370 void string_decode_percent (gchar * s) 00371 { 00372 string_decode_percent_2 (s, s); 00373 } 00374 00375 /* we encode any character except the "unreserved" characters of RFC 3986 and 00376 * (optionally) the forward slash */ 00377 static gboolean is_legal_char (gchar c, gboolean is_filename) 00378 { 00379 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= 00380 '9') || (strchr ("-_.~", c) != NULL) || (is_filename && c == '/'); 00381 } 00382 00383 static gchar make_hex_digit (gint i) 00384 { 00385 if (i < 10) 00386 return '0' + i; 00387 else 00388 return ('A' - 10) + i; 00389 } 00390 00391 /* is_filename specifies whether the forward slash should be left intact */ 00392 /* returns string allocated with g_malloc */ 00393 gchar * string_encode_percent (const gchar * string, gboolean is_filename) 00394 { 00395 gint length = 0; 00396 const gchar * get; 00397 gchar c; 00398 gchar * new, * set; 00399 00400 for (get = string; (c = * get); get ++) 00401 { 00402 if (is_legal_char (c, is_filename)) 00403 length ++; 00404 else 00405 length += 3; 00406 } 00407 00408 new = g_malloc (length + 1); 00409 set = new; 00410 00411 for (get = string; (c = * get); get ++) 00412 { 00413 if (is_legal_char (c, is_filename)) 00414 * set ++ = c; 00415 else 00416 { 00417 * set ++ = '%'; 00418 * set ++ = make_hex_digit (((guchar) c) >> 4); 00419 * set ++ = make_hex_digit (c & 0xF); 00420 } 00421 } 00422 00423 * set = 0; 00424 return new; 00425 } 00426 00427 /* Determines whether a URI is valid UTF-8. If not and <warn> is nonzero, 00428 * prints a warning to stderr. */ 00429 00430 gboolean uri_is_utf8 (const gchar * uri, gboolean warn) 00431 { 00432 gchar buf[strlen (uri) + 1]; 00433 string_decode_percent_2 (uri, buf); 00434 00435 if (g_utf8_validate (buf, -1, NULL)) 00436 return TRUE; 00437 00438 if (warn) 00439 fprintf (stderr, "URI is not UTF-8: %s.\n", buf); 00440 00441 return FALSE; 00442 } 00443 00444 /* Converts a URI to UTF-8 encoding. The returned URI must be freed with g_free. 00445 * 00446 * Note: The function intentionally converts only URI's that are encoded in the 00447 * system locale and refer to local files. 00448 * 00449 * Rationale: 00450 * 00451 * 1. Local files. The URI was probably created by percent-encoding a raw 00452 * filename. 00453 * a. If that filename was in the system locale, then we can convert the URI 00454 * to a UTF-8 one, allowing us to display the name correctly and to access 00455 * the file by converting back to the system locale. 00456 * b. If that filename was in a different locale (perhaps copied from another 00457 * machine), then we do not want to convert it to UTF-8 (even assuming we 00458 * can do so correctly), because we will not know what encoding to convert 00459 * back to when we want to access the file. 00460 * 2. Remote files. The URI was probably created by percent-encoding a raw 00461 * filename in whatever locale the remote system is using. We do not want 00462 * to convert it to UTF-8 because we do not know whether the remote system 00463 * can handle UTF-8 requests. */ 00464 00465 gchar * uri_to_utf8 (const gchar * uri) 00466 { 00467 if (strncmp (uri, "file://", 7)) 00468 return g_strdup (uri); 00469 00470 /* recover the raw filename */ 00471 gchar buf[strlen (uri + 7) + 1]; 00472 string_decode_percent_2 (uri + 7, buf); 00473 00474 /* convert it to a URI again, in UTF-8 if possible */ 00475 return filename_to_uri (buf); 00476 } 00477 00478 /* Check that a URI is valid UTF-8. If not, prints a warning to stderr if 00479 * <warn> is nonzero, frees the old URI with g_free, and sets <uri> to the 00480 * converted URI, which must be freed with g_free when no longer needed. */ 00481 00482 void uri_check_utf8 (gchar * * uri, gboolean warn) 00483 { 00484 if (uri_is_utf8 (* uri, warn)) 00485 return; 00486 00487 gchar * copy = uri_to_utf8 (* uri); 00488 g_free (* uri); 00489 * uri = copy; 00490 } 00491 00492 /* Like g_filename_to_uri, but converts the filename from the system locale to 00493 * UTF-8 before percent-encoding. */ 00494 00495 gchar * filename_to_uri (const gchar * name) 00496 { 00497 gchar * utf8 = g_locale_to_utf8 (name, -1, NULL, NULL, NULL); 00498 gchar * enc = string_encode_percent (utf8 ? utf8 : name, TRUE); 00499 g_free (utf8); 00500 gchar * uri = g_strdup_printf ("file://%s", enc); 00501 g_free (enc); 00502 return uri; 00503 } 00504 00505 /* Like g_filename_from_uri, but converts the filename from UTF-8 to the system 00506 * locale after percent-decoding. */ 00507 00508 gchar * uri_to_filename (const gchar * uri) 00509 { 00510 g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL); 00511 gchar buf[strlen (uri + 7) + 1]; 00512 string_decode_percent_2 (uri + 7, buf); 00513 gchar * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL); 00514 return name ? name : g_strdup (buf); 00515 } 00516 00517 void string_cut_extension(gchar *string) 00518 { 00519 gchar *period = strrchr(string, '.'); 00520 00521 if (period != NULL) 00522 *period = 0; 00523 } 00524 00525 /* Like strcasecmp, but orders numbers correctly (2 before 10). */ 00526 /* Non-ASCII characters are treated exactly as is. */ 00527 /* Handles NULL gracefully. */ 00528 00529 gint string_compare (const gchar * ap, const gchar * bp) 00530 { 00531 if (ap == NULL) 00532 return (bp == NULL) ? 0 : -1; 00533 if (bp == NULL) 00534 return 1; 00535 00536 guchar a = * ap ++, b = * bp ++; 00537 for (; a || b; a = * ap ++, b = * bp ++) 00538 { 00539 if (a > '9' || b > '9' || a < '0' || b < '0') 00540 { 00541 if (a <= 'Z' && a >= 'A') 00542 a += 'a' - 'A'; 00543 if (b <= 'Z' && b >= 'A') 00544 b += 'a' - 'A'; 00545 00546 if (a > b) 00547 return 1; 00548 if (a < b) 00549 return -1; 00550 } 00551 else 00552 { 00553 gint x = a - '0'; 00554 for (; (a = * ap) <= '9' && a >= '0'; ap ++) 00555 x = 10 * x + (a - '0'); 00556 00557 gint y = b - '0'; 00558 for (; (b = * bp) >= '0' && b <= '9'; bp ++) 00559 y = 10 * y + (b - '0'); 00560 00561 if (x > y) 00562 return 1; 00563 if (x < y) 00564 return -1; 00565 } 00566 } 00567 00568 return 0; 00569 } 00570 00571 /* Decodes percent-encoded strings, then compares then with string_compare. */ 00572 00573 gint string_compare_encoded (const gchar * ap, const gchar * bp) 00574 { 00575 if (ap == NULL) 00576 return (bp == NULL) ? 0 : -1; 00577 if (bp == NULL) 00578 return 1; 00579 00580 guchar a = * ap ++, b = * bp ++; 00581 for (; a || b; a = * ap ++, b = * bp ++) 00582 { 00583 if (a == '%') 00584 a = (get_hex_digit (& ap) << 4) | get_hex_digit (& ap); 00585 if (b == '%') 00586 b = (get_hex_digit (& bp) << 4) | get_hex_digit (& bp); 00587 00588 if (a > '9' || b > '9' || a < '0' || b < '0') 00589 { 00590 if (a <= 'Z' && a >= 'A') 00591 a += 'a' - 'A'; 00592 if (b <= 'Z' && b >= 'A') 00593 b += 'a' - 'A'; 00594 00595 if (a > b) 00596 return 1; 00597 if (a < b) 00598 return -1; 00599 } 00600 else 00601 { 00602 gint x = a - '0'; 00603 for (; (a = * ap) <= '9' && a >= '0'; ap ++) 00604 x = 10 * x + (a - '0'); 00605 00606 gint y = b - '0'; 00607 for (; (b = * bp) >= '0' && b <= '9'; bp ++) 00608 y = 10 * y + (b - '0'); 00609 00610 if (x > y) 00611 return 1; 00612 if (x < y) 00613 return -1; 00614 } 00615 } 00616 00617 return 0; 00618 }