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 }