Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
audstrings.c
Go to the documentation of this file.
00001 /*
00002  * audstrings.c
00003  * Copyright 2009-2011 John Lindgren
00004  * Copyright 2010 William Pitcock
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright notice,
00010  *    this list of conditions, and the following disclaimer.
00011  *
00012  * 2. Redistributions in binary form must reproduce the above copyright notice,
00013  *    this list of conditions, and the following disclaimer in the documentation
00014  *    provided with the distribution.
00015  *
00016  * This software is provided "as is" and without any warranty, express or
00017  * implied. In no event shall the authors be liable for any damages arising from
00018  * the use of this software.
00019  */
00020 
00021 #include <limits.h>
00022 #include <math.h>
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <glib.h>
00026 #include <string.h>
00027 #include <ctype.h>
00028 
00029 #include <audacious/i18n.h>
00030 
00031 #include "audstrings.h"
00032 #include "config.h"
00033 
00034 #define FROM_HEX(c) ((c) < 'A' ? (c) - '0' : (c) < 'a' ? 10 + (c) - 'A' : 10 + (c) - 'a')
00035 #define TO_HEX(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
00036 #define IS_LEGAL(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') \
00037                   || ((c) >= '0' && (c) <= '9') || (strchr ("-_.~/", (c))))
00038 
00039 EXPORT bool_t str_has_prefix_nocase (const char * str, const char * prefix)
00040 {
00041     return ! g_ascii_strncasecmp (str, prefix, strlen (prefix));
00042 }
00043 
00044 EXPORT bool_t str_has_suffix_nocase (const char * str, const char * suffix)
00045 {
00046     int len1 = strlen (str);
00047     int len2 = strlen (suffix);
00048 
00049     if (len2 > len1)
00050         return FALSE;
00051 
00052     return ! g_ascii_strcasecmp (str + len1 - len2, suffix);
00053 }
00054 
00055 static char * (* str_to_utf8_impl) (const char *) = NULL;
00056 static char * (* str_to_utf8_full_impl) (const char *, int, int *, int *) = NULL;
00057 
00058 EXPORT void str_set_utf8_impl (char * (* stu_impl) (const char *),
00059  char * (* stuf_impl) (const char *, int, int *, int *))
00060 {
00061     str_to_utf8_impl = stu_impl;
00062     str_to_utf8_full_impl = stuf_impl;
00063 }
00064 
00065 EXPORT char * str_to_utf8 (const char * str)
00066 {
00067     g_return_val_if_fail (str_to_utf8_impl, NULL);
00068     return str_to_utf8_impl (str);
00069 }
00070 
00071 EXPORT char * str_to_utf8_full (const char * str, int len, int * bytes_read, int * bytes_written)
00072 {
00073     g_return_val_if_fail (str_to_utf8_full_impl, NULL);
00074     return str_to_utf8_full_impl (str, len, bytes_read, bytes_written);
00075 }
00076 
00077 EXPORT void string_replace_char (char * string, char old_str, char new_str)
00078 {
00079     while ((string = strchr (string, old_str)) != NULL)
00080         * string = new_str;
00081 }
00082 
00083 /* Percent-decodes up to <len> bytes of <str> to <out>, which must be large
00084  * enough to hold the decoded string (i.e., (len + 1) bytes).  If <len> is
00085  * negative, decodes all of <str>. */
00086 
00087 EXPORT void str_decode_percent (const char * str, int len, char * out)
00088 {
00089     if (len < 0)
00090         len = INT_MAX;
00091 
00092     while (len --)
00093     {
00094         char c = * str ++;
00095         if (! c)
00096             break;
00097 
00098         if (c == '%' && len >= 2 && str[0] && str[1])
00099         {
00100             c = (FROM_HEX (str[0]) << 4) | FROM_HEX (str[1]);
00101             str += 2;
00102             len -= 2;
00103         }
00104 
00105         * out ++ = c;
00106     }
00107 
00108     * out = 0;
00109 }
00110 
00111 /* Percent-encodes up to <len> bytes of <str> to <out>, which must be large
00112  * enough to hold the encoded string (i.e., (3 * len + 1) bytes).  If <len> is
00113  * negative, decodes all of <str>. */
00114 
00115 EXPORT void str_encode_percent (const char * str, int len, char * out)
00116 {
00117     if (len < 0)
00118         len = INT_MAX;
00119 
00120     while (len --)
00121     {
00122         char c = * str ++;
00123         if (! c)
00124             break;
00125 
00126         if (IS_LEGAL (c))
00127             * out ++ = c;
00128         else
00129         {
00130             * out ++ = '%';
00131             * out ++ = TO_HEX ((unsigned char) c >> 4);
00132             * out ++ = TO_HEX (c & 0xF);
00133         }
00134     }
00135 
00136     * out = 0;
00137 }
00138 
00139 /* Like g_filename_to_uri, but converts the filename from the system locale to
00140  * UTF-8 before percent-encoding.  On Windows, replaces '\' with '/' and adds a
00141  * leading '/'. */
00142 
00143 EXPORT char * filename_to_uri (const char * name)
00144 {
00145     char * utf8 = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
00146     if (! utf8)
00147     {
00148         fprintf (stderr, "Cannot convert filename from system locale: %s\n", name);
00149         return NULL;
00150     }
00151 
00152 #ifdef _WIN32
00153     string_replace_char (utf8, '\\', '/');
00154 #endif
00155     char enc[3 * strlen (utf8) + 1];
00156     str_encode_percent (utf8, -1, enc);
00157 
00158     g_free (utf8);
00159 
00160 #ifdef _WIN32
00161     return g_strdup_printf ("file:///%s", enc);
00162 #else
00163     return g_strdup_printf ("file://%s", enc);
00164 #endif
00165 }
00166 
00167 /* Like g_filename_from_uri, but converts the filename from UTF-8 to the system
00168  * locale after percent-decoding.  On Windows, strips the leading '/' and
00169  * replaces '/' with '\'. */
00170 
00171 EXPORT char * uri_to_filename (const char * uri)
00172 {
00173 #ifdef _WIN32
00174     g_return_val_if_fail (! strncmp (uri, "file:///", 8), NULL);
00175     char buf[strlen (uri + 8) + 1];
00176     str_decode_percent (uri + 8, -1, buf);
00177 #else
00178     g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL);
00179     char buf[strlen (uri + 7) + 1];
00180     str_decode_percent (uri + 7, -1, buf);
00181 #endif
00182 #ifdef _WIN32
00183     string_replace_char (buf, '/', '\\');
00184 #endif
00185 
00186     char * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL);
00187     if (! name)
00188         fprintf (stderr, "Cannot convert filename to system locale: %s\n", buf);
00189 
00190     return name;
00191 }
00192 
00193 /* Formats a URI for human-readable display.  Percent-decodes and, for file://
00194  * URI's, converts to filename format, but in UTF-8. */
00195 
00196 EXPORT char * uri_to_display (const char * uri)
00197 {
00198     if (! strncmp (uri, "cdda://?", 8))
00199         return g_strdup_printf (_("Audio CD, track %s"), uri + 8);
00200 
00201     char buf[strlen (uri) + 1];
00202 
00203 #ifdef _WIN32
00204     if (! strncmp (uri, "file:///", 8))
00205     {
00206         str_decode_percent (uri + 8, -1, buf);
00207         string_replace_char (buf, '/', '\\');
00208     }
00209 #else
00210     if (! strncmp (uri, "file://", 7))
00211         str_decode_percent (uri + 7, -1, buf);
00212 #endif
00213     else
00214         str_decode_percent (uri, -1, buf);
00215 
00216     return g_strdup (buf);
00217 }
00218 
00219 EXPORT void uri_parse (const char * uri, const char * * base_p, const char * * ext_p,
00220  const char * * sub_p, int * isub_p)
00221 {
00222     const char * end = uri + strlen (uri);
00223     const char * base, * ext, * sub, * c;
00224     int isub = 0;
00225     char junk;
00226 
00227     if ((c = strrchr (uri, '/')))
00228         base = c + 1;
00229     else
00230         base = end;
00231 
00232     if ((c = strrchr (base, '?')) && sscanf (c + 1, "%d%c", & isub, & junk) == 1)
00233         sub = c;
00234     else
00235         sub = end;
00236 
00237     char buf[sub - base + 1];
00238     memcpy (buf, base, sub - base);
00239     buf[sub - base] = 0;
00240 
00241     if ((c = strrchr (buf, '.')))
00242         ext = base + (c - buf);
00243     else
00244         ext = sub;
00245 
00246     if (base_p)
00247         * base_p = base;
00248     if (ext_p)
00249         * ext_p = ext;
00250     if (sub_p)
00251         * sub_p = sub;
00252     if (isub_p)
00253         * isub_p = isub;
00254 }
00255 
00256 /* Like strcasecmp, but orders numbers correctly (2 before 10). */
00257 /* Non-ASCII characters are treated exactly as is. */
00258 /* Handles NULL gracefully. */
00259 
00260 EXPORT int string_compare (const char * ap, const char * bp)
00261 {
00262     if (ap == NULL)
00263         return (bp == NULL) ? 0 : -1;
00264     if (bp == NULL)
00265         return 1;
00266 
00267     unsigned char a = * ap ++, b = * bp ++;
00268     for (; a || b; a = * ap ++, b = * bp ++)
00269     {
00270         if (a > '9' || b > '9' || a < '0' || b < '0')
00271         {
00272             if (a <= 'Z' && a >= 'A')
00273                 a += 'a' - 'A';
00274             if (b <= 'Z' && b >= 'A')
00275                 b += 'a' - 'A';
00276 
00277             if (a > b)
00278                 return 1;
00279             if (a < b)
00280                 return -1;
00281         }
00282         else
00283         {
00284             int x = a - '0';
00285             for (; (a = * ap) <= '9' && a >= '0'; ap ++)
00286                 x = 10 * x + (a - '0');
00287 
00288             int y = b - '0';
00289             for (; (b = * bp) >= '0' && b <= '9'; bp ++)
00290                 y = 10 * y + (b - '0');
00291 
00292             if (x > y)
00293                 return 1;
00294             if (x < y)
00295                 return -1;
00296         }
00297     }
00298 
00299     return 0;
00300 }
00301 
00302 /* Decodes percent-encoded strings, then compares then with string_compare. */
00303 
00304 EXPORT int string_compare_encoded (const char * ap, const char * bp)
00305 {
00306     if (ap == NULL)
00307         return (bp == NULL) ? 0 : -1;
00308     if (bp == NULL)
00309         return 1;
00310 
00311     unsigned char a = * ap ++, b = * bp ++;
00312     for (; a || b; a = * ap ++, b = * bp ++)
00313     {
00314         if (a == '%' && ap[0] && ap[1])
00315         {
00316             a = (FROM_HEX (ap[0]) << 4) | FROM_HEX (ap[1]);
00317             ap += 2;
00318         }
00319         if (b == '%' && bp[0] && bp[1])
00320         {
00321             b = (FROM_HEX (bp[0]) << 4) | FROM_HEX (bp[1]);
00322             bp += 2;
00323         }
00324 
00325         if (a > '9' || b > '9' || a < '0' || b < '0')
00326         {
00327             if (a <= 'Z' && a >= 'A')
00328                 a += 'a' - 'A';
00329             if (b <= 'Z' && b >= 'A')
00330                 b += 'a' - 'A';
00331 
00332             if (a > b)
00333                 return 1;
00334             if (a < b)
00335                 return -1;
00336         }
00337         else
00338         {
00339             int x = a - '0';
00340             for (; (a = * ap) <= '9' && a >= '0'; ap ++)
00341                 x = 10 * x + (a - '0');
00342 
00343             int y = b - '0';
00344             for (; (b = * bp) >= '0' && b <= '9'; bp ++)
00345                 y = 10 * y + (b - '0');
00346 
00347             if (x > y)
00348                 return 1;
00349             if (x < y)
00350                 return -1;
00351         }
00352     }
00353 
00354     return 0;
00355 }
00356 
00357 EXPORT char *
00358 str_replace_fragment(char *s, int size, const char *old, const char *new)
00359 {
00360     char *ptr = s;
00361     int left = strlen(s);
00362     int avail = size - (left + 1);
00363     int oldlen = strlen(old);
00364     int newlen = strlen(new);
00365     int diff = newlen - oldlen;
00366 
00367     while (left >= oldlen)
00368     {
00369         if (strncmp(ptr, old, oldlen))
00370         {
00371             left--;
00372             ptr++;
00373             continue;
00374         }
00375 
00376         if (diff > avail)
00377             break;
00378 
00379         if (diff != 0)
00380             memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen);
00381 
00382         memcpy(ptr, new, newlen);
00383         ptr += newlen;
00384         left -= oldlen;
00385     }
00386 
00387     return s;
00388 }
00389 
00390 /*
00391  * Routines to convert numbers between string and binary representations.
00392  *
00393  * Goals:
00394  *
00395  *  - Accuracy, meaning that we can convert back and forth between string and
00396  *    binary without the number changing slightly each time.
00397  *  - Consistency, meaning that we get the same results no matter what
00398  *    architecture or locale we have to deal with.
00399  *  - Readability, meaning that the number one is rendered "1", not "1.000".
00400  *
00401  * Values are limited between -1,000,000,000 and 1,000,000,000 (inclusive) and
00402  * have an accuracy of 6 decimal places.
00403  */
00404 
00405 EXPORT bool_t string_to_int (const char * string, int * addr)
00406 {
00407     bool_t neg = (string[0] == '-');
00408     if (neg)
00409         string ++;
00410 
00411     int val = 0;
00412     char c;
00413 
00414     while ((c = * string ++))
00415     {
00416         if (c < '0' || c > '9' || val > 100000000)
00417             goto ERR;
00418 
00419         val = val * 10 + (c - '0');
00420     }
00421 
00422     if (val > 1000000000)
00423         goto ERR;
00424 
00425     * addr = neg ? -val : val;
00426     return TRUE;
00427 
00428 ERR:
00429     return FALSE;
00430 }
00431 
00432 EXPORT bool_t string_to_double (const char * string, double * addr)
00433 {
00434     bool_t neg = (string[0] == '-');
00435     if (neg)
00436         string ++;
00437 
00438     const char * p = strchr (string, '.');
00439     int i, f;
00440 
00441     if (p)
00442     {
00443         char buf[11];
00444         int len;
00445 
00446         len = p - string;
00447         if (len > 10)
00448             goto ERR;
00449 
00450         memcpy (buf, string, len);
00451         buf[len] = 0;
00452 
00453         if (! string_to_int (buf, & i))
00454             goto ERR;
00455 
00456         len = strlen (p + 1);
00457         if (len > 6)
00458             goto ERR;
00459 
00460         memcpy (buf, p + 1, len);
00461         memset (buf + len, '0', 6 - len);
00462         buf[6] = 0;
00463 
00464         if (! string_to_int (buf, & f))
00465             goto ERR;
00466     }
00467     else
00468     {
00469         if (! string_to_int (string, & i))
00470             goto ERR;
00471 
00472         f = 0;
00473     }
00474 
00475     double val = i + (double) f / 1000000;
00476     if (val > 1000000000)
00477         goto ERR;
00478 
00479     * addr = neg ? -val : val;
00480     return TRUE;
00481 
00482 ERR:
00483     return FALSE;
00484 }
00485 
00486 EXPORT char * int_to_string (int val)
00487 {
00488     g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
00489     return g_strdup_printf ("%d", val);
00490 }
00491 
00492 EXPORT char * double_to_string (double val)
00493 {
00494     g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
00495 
00496     bool_t neg = (val < 0);
00497     if (neg)
00498         val = -val;
00499 
00500     int i = floor (val);
00501     int f = round ((val - i) * 1000000);
00502 
00503     if (f == 1000000)
00504     {
00505         i ++;
00506         f = 0;
00507     }
00508 
00509     char * s = neg ? g_strdup_printf ("-%d.%06d", i, f) : g_strdup_printf ("%d.%06d", i, f);
00510 
00511     char * c = s + strlen (s);
00512     while (* (c - 1) == '0')
00513         c --;
00514     if (* (c - 1) == '.')
00515         c --;
00516     * c = 0;
00517 
00518     return s;
00519 }
00520 
00521 EXPORT bool_t string_to_double_array (const char * string, double * array, int count)
00522 {
00523     char * * split = g_strsplit (string, ",", -1);
00524     if (g_strv_length (split) != count)
00525         goto ERR;
00526 
00527     for (int i = 0; i < count; i ++)
00528     {
00529         if (! string_to_double (split[i], & array[i]))
00530             goto ERR;
00531     }
00532 
00533     g_strfreev (split);
00534     return TRUE;
00535 
00536 ERR:
00537     g_strfreev (split);
00538     return FALSE;
00539 }
00540 
00541 EXPORT char * double_array_to_string (const double * array, int count)
00542 {
00543     char * * split = g_malloc0 (sizeof (char *) * (count + 1));
00544 
00545     for (int i = 0; i < count; i ++)
00546     {
00547         split[i] = double_to_string (array[i]);
00548         if (! split[i])
00549             goto ERR;
00550     }
00551 
00552     char * string = g_strjoinv (",", split);
00553     g_strfreev (split);
00554     return string;
00555 
00556 ERR:
00557     g_strfreev (split);
00558     return NULL;
00559 }