xref: /onnv-gate/usr/src/cmd/parted/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 
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