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