strlist.c revision 9663:ace9a2ac3683
1/* 2 parted - a frontend to libparted 3 Copyright (C) 1999, 2000, 2001, 2007 Free Software Foundation, Inc. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17*/ 18 19#include <config.h> 20 21#include <parted/debug.h> 22 23#include <ctype.h> 24#include <errno.h> 25#include <stdarg.h> 26#include <stdio.h> 27#include <stdlib.h> 28#include <string.h> 29#include <limits.h> 30#include "xalloc.h" 31 32#ifdef ENABLE_NLS 33 34#undef __USE_GNU 35#define __USE_GNU 36 37#include <wchar.h> 38#include <wctype.h> 39 40#else /* ENABLE_NLS */ 41 42#ifdef wchar_t 43#undef wchar_t 44#endif 45 46#define wchar_t char 47 48#endif /* !ENABLE_NLS */ 49 50#include "strlist.h" 51 52#define MIN(a,b) ( (a<b)? a : b ) 53 54int 55wchar_strlen (const wchar_t* str) 56{ 57#ifdef ENABLE_NLS 58 return wcslen (str); 59#else 60 return strlen (str); 61#endif 62} 63 64wchar_t* 65wchar_strchr (const wchar_t* str, char ch) 66{ 67#ifdef ENABLE_NLS 68 return wcschr (str, ch); 69#else 70 return strchr (str, ch); 71#endif 72} 73 74int 75wchar_strcasecmp (const wchar_t* a, const wchar_t* b) 76{ 77#ifdef ENABLE_NLS 78 return wcscasecmp (a, b); 79#else 80 return strcasecmp (a, b); 81#endif 82} 83 84int 85wchar_strncasecmp (const wchar_t* a, const wchar_t* b, size_t n) 86{ 87#ifdef ENABLE_NLS 88 return wcsncasecmp (a, b, n); 89#else 90 return strncasecmp (a, b, n); 91#endif 92} 93 94wchar_t* 95wchar_strdup (const wchar_t* str) 96{ 97#ifdef ENABLE_NLS 98 return wcsdup (str); 99#else 100 return xstrdup (str); 101#endif 102} 103 104/* converts a string from the encoding in the gettext catalogues to wide 105 * character strings (of type wchar_t*). 106 */ 107#ifdef ENABLE_NLS 108static wchar_t* 109gettext_to_wchar (const char* str) 110{ 111 int count; 112 wchar_t* result; 113 size_t status; 114 mbstate_t ps; 115 116 count = strlen (str) + 1; 117 result = malloc (count * sizeof (wchar_t)); 118 if (!result) 119 goto error; 120 121 memset(&ps, 0, sizeof (ps)); 122 status = mbsrtowcs(result, &str, count, &ps); 123 if (status == (size_t) -1) 124 goto error; 125 126 result = realloc (result, (wcslen (result) + 1) * sizeof (wchar_t)); 127 return result; 128 129error: 130 printf ("Error during translation: %s\n", strerror (errno)); 131 exit (1); 132} 133 134#else /* ENABLE_NLS */ 135 136static wchar_t* 137gettext_to_wchar (const char* str) 138{ 139 return xstrdup (str); 140} 141 142#endif /* !ENABLE_NLS */ 143 144 145#ifdef ENABLE_NLS 146static char* 147wchar_to_str (const wchar_t* str, size_t count) 148{ 149 char* result; 150 char* out_buf; 151 size_t status; 152 mbstate_t ps; 153 size_t i; 154 155 if (count == 0 || wcslen(str) < count) 156 count = wcslen (str); 157 158 out_buf = result = malloc ((count + 1) * MB_LEN_MAX); 159 if (!result) 160 goto error; 161 162 memset(&ps, 0, sizeof(ps)); 163 164 for (i = 0; i < count; i++) { 165 status = wcrtomb (out_buf, str[i], &ps); 166 if (status == (size_t) -1) 167 goto error; 168 out_buf += status; 169 } 170 171 status = wcrtomb (out_buf, 0, &ps); 172 if (status == (size_t) -1) 173 goto error; 174 175 result = realloc (result, strlen (result) + 1); 176 return result; 177 178error: 179 printf ("Error during translation: %s\n", strerror (errno)); 180 exit (1); 181} 182 183#else /* ENABLE_NLS */ 184 185static char* 186wchar_to_str (const wchar_t* str, size_t count) 187{ 188 char* result; 189 190 result = xstrdup (str); 191 if (count && count < strlen (result)) 192 result [count] = 0; 193 return result; 194} 195 196#endif /* !ENABLE_NLS */ 197 198static void 199print_wchar (const wchar_t* str, size_t count) 200{ 201 char* tmp = wchar_to_str (str, count); 202 printf ("%s", tmp); 203 free (tmp); 204} 205 206static StrList* 207str_list_alloc () 208{ 209 StrList* list; 210 211 list = xmalloc (sizeof (StrList)); 212 list->next = NULL; 213 214 return list; 215} 216 217void 218str_list_destroy (StrList* list) 219{ 220 if (list) { 221 str_list_destroy (list->next); 222 str_list_destroy_node (list); 223 } 224} 225 226void 227str_list_destroy_node (StrList* list) 228{ 229 free ((wchar_t*) list->str); 230 free (list); 231} 232 233StrList* 234str_list_duplicate_node (const StrList* node) 235{ 236 StrList* result = str_list_alloc (); 237 result->str = wchar_strdup (node->str); 238 return result; 239} 240 241StrList* 242str_list_duplicate (const StrList* list) 243{ 244 if (list) 245 return str_list_join (str_list_duplicate_node (list), 246 str_list_duplicate (list->next)); 247 else 248 return NULL; 249} 250 251StrList* 252str_list_join (StrList* a, StrList* b) 253{ 254 StrList* walk; 255 256 for (walk = a; walk && walk->next; walk = walk->next); 257 258 if (walk) { 259 walk->next = b; 260 return a; 261 } else { 262 return b; 263 } 264} 265 266static StrList* 267_str_list_append (StrList* list, const wchar_t* str) 268{ 269 StrList* walk; 270 271 if (list) { 272 for (walk = list; walk->next; walk = walk->next); 273 walk->next = str_list_alloc (); 274 walk = walk->next; 275 } else { 276 walk = list = str_list_alloc (); 277 } 278 walk->str = str; 279 280 return list; 281} 282 283StrList* 284str_list_append (StrList* list, const char* str) 285{ 286 return _str_list_append (list, gettext_to_wchar (str)); 287} 288 289StrList* 290str_list_append_unique (StrList* list, const char* str) 291{ 292 StrList* walk; 293 wchar_t* new_str = gettext_to_wchar (str); 294 295 for (walk=list; walk; walk=walk->next) { 296 if (walk->str) { 297 if (wchar_strcasecmp (new_str, walk->str) == 0) { 298 free (new_str); 299 return list; 300 } 301 } 302 } 303 304 return _str_list_append (list, new_str); 305} 306 307StrList* 308str_list_insert (StrList* list, const char* str) 309{ 310 return str_list_join (str_list_create (str, NULL), list); 311} 312 313StrList* 314str_list_create (const char* first, ...) 315{ 316 va_list args; 317 char* str; 318 StrList* list; 319 320 list = str_list_append (NULL, first); 321 322 if (first) { 323 va_start (args, first); 324 while ( (str = va_arg (args, char*)) ) 325 str_list_append (list, str); 326 va_end (args); 327 } 328 329 return list; 330} 331 332StrList* 333str_list_create_unique (const char* first, ...) 334{ 335 va_list args; 336 char* str; 337 StrList* list; 338 339 list = str_list_append (NULL, first); 340 341 if (first) { 342 va_start (args, first); 343 while ( (str = va_arg (args, char*)) ) 344 str_list_append_unique (list, str); 345 va_end (args); 346 } 347 348 return list; 349} 350 351char* 352str_list_convert_node (const StrList* list) 353{ 354 return wchar_to_str (list->str, 0); 355} 356 357char* 358str_list_convert (const StrList* list) 359{ 360 const StrList* walk; 361 int pos = 0; 362 int length = 1; 363 char* str = xstrdup (""); 364 365 for (walk = list; walk; walk = walk->next) { 366 if (walk->str) { 367 char* tmp = wchar_to_str (walk->str, 0); 368 369 length += strlen (tmp); 370 371 str = realloc (str, length); 372 strcpy (str + pos, tmp); 373 374 pos = length - 1; 375 free (tmp); 376 } 377 } 378 379 return str; 380} 381 382void 383str_list_print (const StrList* list) 384{ 385 const StrList* walk; 386 387 for (walk=list; walk; walk=walk->next) { 388 if (walk->str) 389 print_wchar (walk->str, 0); 390 } 391} 392 393static int 394str_search (const wchar_t* str, int n, wchar_t c) 395{ 396 int i; 397 398 for (i=0; i<n; i++) 399 if (str [i] == c) 400 return i; 401 return -1; 402} 403 404 405/* Japanese don't leave spaces between words, so ALL Japanese characters 406 * are treated as delimiters. Note: since the translations should already 407 * be properly formatted (eg: spaces after commas), there should be no 408 * need to include them. Best not to avoid side effects, like 3. 40914159 :-) 410 * FIXME: how do we exclude "." and "(" ? 411 * FIXME: glibc doesn't like umlaute. i.e. \"o (TeX notation), which should 412 * look like: � 413 */ 414 415static int 416is_break_point (wchar_t c) 417{ 418#ifdef ENABLE_NLS 419 return !iswalnum (c) && !iswpunct (c); 420#else 421 return !isalnum (c) && !ispunct (c); 422#endif 423} 424 425/* NOTE: this should not return '\n' as a space, because explicit '\n' may 426 * be placed inside strings. 427 */ 428static int 429is_space (wchar_t c) 430{ 431#ifdef ENABLE_NLS 432 return c == (wchar_t) btowc(' '); 433#else 434 return c == ' '; 435#endif 436} 437 438void 439str_list_print_wrap (const StrList* list, int line_length, int offset, 440 int indent) 441{ 442 const StrList* walk; 443 const wchar_t* str; 444 int str_len; 445 int cut_right; 446 int cut_left; 447 int line_left; 448 int search_result; 449 int line_break; 450 451 PED_ASSERT (line_length - indent > 10, return); 452 453 line_left = line_length - offset; 454 455 for (walk=list; walk; walk=walk->next) { 456 if (!walk->str) 457 continue; 458 str = walk->str; 459 str_len = wchar_strlen (str); 460 461 while (line_left < str_len || wchar_strchr (str, '\n')) { 462 line_break = 0; 463 464 cut_left = MIN (line_left - 1, str_len - 1); 465 466 /* we can have a space "over", but not a comma */ 467 if (cut_left < str_len 468 && is_space (str [cut_left + 1])) 469 cut_left++; 470 471 while (cut_left && !is_break_point (str [cut_left])) 472 cut_left--; 473 while (cut_left && is_space (str [cut_left])) 474 cut_left--; 475 476 /* str [cut_left] is either the end of a word, or a 477 * Japanese character, or the start of a blank line. 478 */ 479 480 search_result = str_search (str, cut_left + 1, '\n'); 481 if (search_result != -1) { 482 cut_left = search_result - 1; 483 line_break = 1; 484 } 485 486 for (cut_right = cut_left + (line_break ? 2 : 1); 487 cut_right < str_len && is_space (str [cut_right]); 488 cut_right++); 489 490 if (cut_left > 0) 491 print_wchar (str, cut_left + 1); 492 493 str += cut_right; 494 str_len -= cut_right; 495 line_left = line_length - indent; 496 497 if (walk->next || *str) 498 printf ("\n%*s", indent, ""); 499 else if (line_break) 500 putchar ('\n'); 501 } 502 503 print_wchar (str, 0); 504 line_left -= wchar_strlen (str); 505 } 506} 507 508static int 509_str_list_match_node (const StrList* list, const wchar_t* str) 510{ 511 if (wchar_strcasecmp (list->str, str) == 0) 512 return 2; 513 if (wchar_strncasecmp (list->str, str, wchar_strlen (str)) == 0) 514 return 1; 515 return 0; 516} 517 518int 519str_list_match_node (const StrList* list, const char* str) 520{ 521 wchar_t* wc_str = gettext_to_wchar (str); /* FIXME */ 522 int status; 523 524 status = _str_list_match_node (list, wc_str); 525 free (wc_str); 526 527 return status; 528} 529 530/* returns: 2 for full match 531 1 for partial match 532 0 for no match 533 */ 534int 535str_list_match_any (const StrList* list, const char* str) 536{ 537 const StrList* walk; 538 int best_status = 0; 539 wchar_t* wc_str = gettext_to_wchar (str); 540 541 for (walk = list; walk; walk = walk->next) { 542 int this_status = _str_list_match_node (walk, wc_str); 543 if (this_status > best_status) 544 best_status = this_status; 545 } 546 547 free (wc_str); 548 return best_status; 549} 550 551StrList* 552str_list_match (const StrList* list, const char* str) 553{ 554 const StrList* walk; 555 const StrList* partial_match = NULL; 556 int ambiguous = 0; 557 wchar_t* wc_str = gettext_to_wchar (str); 558 559 for (walk = list; walk; walk = walk->next) { 560 switch (_str_list_match_node (walk, wc_str)) { 561 case 2: 562 free (wc_str); 563 return (StrList*) walk; 564 565 case 1: 566 if (partial_match) 567 ambiguous = 1; 568 partial_match = walk; 569 } 570 } 571 572 free (wc_str); 573 return ambiguous ? NULL : (StrList*) partial_match; 574} 575 576int 577str_list_length (const StrList* list) 578{ 579 int length = 0; 580 const StrList* walk; 581 582 for (walk = list; walk; walk = walk->next) 583 length++; 584 585 return length; 586} 587