OSDN Git Service

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