• Main Page
  • Related Pages
  • Classes
  • Files
  • File List
  • File Members

audstrings.c

Go to the documentation of this file.
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
00123 str_has_suffix_nocase(const gchar * str, const gchar * suffix)
00124 {
00125     return (str != NULL && strcasecmp(str + strlen(str) - strlen(suffix), suffix) == 0);
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 }

Generated on Wed Apr 6 2011 for Audacious by  doxygen 1.7.1