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 54 int 55 wchar_strlen (const wchar_t* str) 56 { 57 #ifdef ENABLE_NLS 58 return wcslen (str); 59 #else 60 return strlen (str); 61 #endif 62 } 63 64 wchar_t* 65 wchar_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 74 int 75 wchar_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 84 int 85 wchar_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 94 wchar_t* 95 wchar_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 108 static wchar_t* 109 gettext_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 129 error: 130 printf ("Error during translation: %s\n", strerror (errno)); 131 exit (1); 132 } 133 134 #else /* ENABLE_NLS */ 135 136 static wchar_t* 137 gettext_to_wchar (const char* str) 138 { 139 return xstrdup (str); 140 } 141 142 #endif /* !ENABLE_NLS */ 143 144 145 #ifdef ENABLE_NLS 146 static char* 147 wchar_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 178 error: 179 printf ("Error during translation: %s\n", strerror (errno)); 180 exit (1); 181 } 182 183 #else /* ENABLE_NLS */ 184 185 static char* 186 wchar_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 198 static void 199 print_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 206 static StrList* 207 str_list_alloc () 208 { 209 StrList* list; 210 211 list = xmalloc (sizeof (StrList)); 212 list->next = NULL; 213 214 return list; 215 } 216 217 void 218 str_list_destroy (StrList* list) 219 { 220 if (list) { 221 str_list_destroy (list->next); 222 str_list_destroy_node (list); 223 } 224 } 225 226 void 227 str_list_destroy_node (StrList* list) 228 { 229 free ((wchar_t*) list->str); 230 free (list); 231 } 232 233 StrList* 234 str_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 241 StrList* 242 str_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 251 StrList* 252 str_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 266 static 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 283 StrList* 284 str_list_append (StrList* list, const char* str) 285 { 286 return _str_list_append (list, gettext_to_wchar (str)); 287 } 288 289 StrList* 290 str_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 307 StrList* 308 str_list_insert (StrList* list, const char* str) 309 { 310 return str_list_join (str_list_create (str, NULL), list); 311 } 312 313 StrList* 314 str_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 332 StrList* 333 str_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 351 char* 352 str_list_convert_node (const StrList* list) 353 { 354 return wchar_to_str (list->str, 0); 355 } 356 357 char* 358 str_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 382 void 383 str_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 393 static int 394 str_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. 409 14159 :-) 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 415 static int 416 is_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 */ 428 static int 429 is_space (wchar_t c) 430 { 431 #ifdef ENABLE_NLS 432 return c == (wchar_t) btowc(' '); 433 #else 434 return c == ' '; 435 #endif 436 } 437 438 void 439 str_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 508 static 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 518 int 519 str_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 */ 534 int 535 str_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 551 StrList* 552 str_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 576 int 577 str_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 }