Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 }