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
wchar_strlen(const wchar_t * str)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*
wchar_strchr(const wchar_t * str,char ch)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
wchar_strcasecmp(const wchar_t * a,const wchar_t * b)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
wchar_strncasecmp(const wchar_t * a,const wchar_t * b,size_t n)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*
wchar_strdup(const wchar_t * str)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*
gettext_to_wchar(const char * str)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*
gettext_to_wchar(const char * str)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*
wchar_to_str(const wchar_t * str,size_t count)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*
wchar_to_str(const wchar_t * str,size_t count)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
print_wchar(const wchar_t * str,size_t count)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*
str_list_alloc()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
str_list_destroy(StrList * list)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
str_list_destroy_node(StrList * list)227 str_list_destroy_node (StrList* list)
228 {
229 free ((wchar_t*) list->str);
230 free (list);
231 }
232
233 StrList*
str_list_duplicate_node(const StrList * node)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*
str_list_duplicate(const StrList * list)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*
str_list_join(StrList * a,StrList * b)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*
_str_list_append(StrList * list,const wchar_t * str)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*
str_list_append(StrList * list,const char * str)284 str_list_append (StrList* list, const char* str)
285 {
286 return _str_list_append (list, gettext_to_wchar (str));
287 }
288
289 StrList*
str_list_append_unique(StrList * list,const char * str)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*
str_list_insert(StrList * list,const char * str)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*
str_list_create(const char * first,...)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*
str_list_create_unique(const char * first,...)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*
str_list_convert_node(const StrList * list)352 str_list_convert_node (const StrList* list)
353 {
354 return wchar_to_str (list->str, 0);
355 }
356
357 char*
str_list_convert(const StrList * list)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
str_list_print(const StrList * list)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
str_search(const wchar_t * str,int n,wchar_t c)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
is_break_point(wchar_t c)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
is_space(wchar_t c)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
str_list_print_wrap(const StrList * list,int line_length,int offset,int indent)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
_str_list_match_node(const StrList * list,const wchar_t * str)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
str_list_match_node(const StrList * list,const char * str)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
str_list_match_any(const StrList * list,const char * str)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*
str_list_match(const StrList * list,const char * str)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
str_list_length(const StrList * list)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 }
587