| /* |
| parted - a frontend to libparted |
| Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <config.h> |
| |
| #include <parted/debug.h> |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <limits.h> |
| |
| #ifdef ENABLE_NLS |
| |
| #undef __USE_GNU |
| #define __USE_GNU |
| |
| #include <wchar.h> |
| #include <wctype.h> |
| |
| #else /* ENABLE_NLS */ |
| |
| #ifdef wchar_t |
| #undef wchar_t |
| #endif |
| |
| #define wchar_t char |
| |
| #endif /* !ENABLE_NLS */ |
| |
| #include "strlist.h" |
| |
| #define MIN(a,b) ( (a<b)? a : b ) |
| |
| int |
| wchar_strlen (const wchar_t* str) |
| { |
| #ifdef ENABLE_NLS |
| return wcslen (str); |
| #else |
| return strlen (str); |
| #endif |
| } |
| |
| wchar_t* |
| wchar_strchr (const wchar_t* str, char ch) |
| { |
| #ifdef ENABLE_NLS |
| return wcschr (str, ch); |
| #else |
| return strchr (str, ch); |
| #endif |
| } |
| |
| int |
| wchar_strcasecmp (const wchar_t* a, const wchar_t* b) |
| { |
| #ifdef ENABLE_NLS |
| return wcscasecmp (a, b); |
| #else |
| return strcasecmp (a, b); |
| #endif |
| } |
| |
| int |
| wchar_strncasecmp (const wchar_t* a, const wchar_t* b, size_t n) |
| { |
| #ifdef ENABLE_NLS |
| return wcsncasecmp (a, b, n); |
| #else |
| return strncasecmp (a, b, n); |
| #endif |
| } |
| |
| wchar_t* |
| wchar_strdup (const wchar_t* str) |
| { |
| #ifdef ENABLE_NLS |
| return wcsdup (str); |
| #else |
| return strdup (str); |
| #endif |
| } |
| |
| /* converts a string from the encoding in the gettext catalogues to wide |
| * character strings (of type wchar_t*). |
| */ |
| #ifdef ENABLE_NLS |
| static wchar_t* |
| gettext_to_wchar (const char* str) |
| { |
| int count; |
| wchar_t* result; |
| size_t status; |
| mbstate_t ps; |
| |
| count = strlen (str) + 1; |
| result = malloc (count * sizeof (wchar_t)); |
| if (!result) |
| goto error; |
| |
| memset(&ps, 0, sizeof (ps)); |
| status = mbsrtowcs(result, &str, count, &ps); |
| if (status == (size_t) -1) |
| goto error; |
| |
| result = realloc (result, (wcslen (result) + 1) * sizeof (wchar_t)); |
| return result; |
| |
| error: |
| printf ("Error during translation: %s\n", strerror (errno)); |
| exit (1); |
| } |
| |
| #else /* ENABLE_NLS */ |
| |
| static wchar_t* |
| gettext_to_wchar (const char* str) |
| { |
| return strdup (str); |
| } |
| |
| #endif /* !ENABLE_NLS */ |
| |
| |
| #ifdef ENABLE_NLS |
| static char* |
| wchar_to_str (const wchar_t* str, size_t count) |
| { |
| char* result; |
| char* out_buf; |
| size_t status; |
| mbstate_t ps; |
| size_t i; |
| |
| if (count == 0 || wcslen(str) < count) |
| count = wcslen (str); |
| |
| out_buf = result = malloc ((count + 1) * MB_LEN_MAX); |
| if (!result) |
| goto error; |
| |
| memset(&ps, 0, sizeof(ps)); |
| |
| for (i = 0; i < count; i++) { |
| status = wcrtomb (out_buf, str[i], &ps); |
| if (status == (size_t) -1) |
| goto error; |
| out_buf += status; |
| } |
| |
| status = wcrtomb (out_buf, 0, &ps); |
| if (status == (size_t) -1) |
| goto error; |
| |
| result = realloc (result, strlen (result) + 1); |
| return result; |
| |
| error: |
| printf ("Error during translation: %s\n", strerror (errno)); |
| exit (1); |
| } |
| |
| #else /* ENABLE_NLS */ |
| |
| static char* |
| wchar_to_str (const wchar_t* str, size_t count) |
| { |
| char* result; |
| |
| result = strdup (str); |
| if (count && count < strlen (result)) |
| result [count] = 0; |
| return result; |
| } |
| |
| #endif /* !ENABLE_NLS */ |
| |
| static void |
| print_wchar (const wchar_t* str, size_t count) |
| { |
| char* tmp = wchar_to_str (str, count); |
| printf ("%s", tmp); |
| free (tmp); |
| } |
| |
| static StrList* |
| str_list_alloc () |
| { |
| StrList* list; |
| |
| list = (StrList*) malloc (sizeof (StrList)); |
| list->next = NULL; |
| |
| return list; |
| } |
| |
| void |
| str_list_destroy (StrList* list) |
| { |
| if (list) { |
| str_list_destroy (list->next); |
| str_list_destroy_node (list); |
| } |
| } |
| |
| void |
| str_list_destroy_node (StrList* list) |
| { |
| free ((wchar_t*) list->str); |
| free (list); |
| } |
| |
| StrList* |
| str_list_duplicate_node (const StrList* node) |
| { |
| StrList* result = str_list_alloc (); |
| result->str = wchar_strdup (node->str); |
| return result; |
| } |
| |
| StrList* |
| str_list_duplicate (const StrList* list) |
| { |
| if (list) |
| return str_list_join (str_list_duplicate_node (list), |
| str_list_duplicate (list->next)); |
| else |
| return NULL; |
| } |
| |
| StrList* |
| str_list_join (StrList* a, StrList* b) |
| { |
| StrList* walk; |
| |
| for (walk = a; walk && walk->next; walk = walk->next); |
| |
| if (walk) { |
| walk->next = b; |
| return a; |
| } else { |
| return b; |
| } |
| } |
| |
| static StrList* |
| _str_list_append (StrList* list, const wchar_t* str) |
| { |
| StrList* walk; |
| |
| if (list) { |
| for (walk = list; walk->next; walk = walk->next); |
| walk->next = str_list_alloc (); |
| walk = walk->next; |
| } else { |
| walk = list = str_list_alloc (); |
| } |
| walk->str = str; |
| |
| return list; |
| } |
| |
| StrList* |
| str_list_append (StrList* list, const char* str) |
| { |
| return _str_list_append (list, gettext_to_wchar (str)); |
| } |
| |
| StrList* |
| str_list_append_unique (StrList* list, const char* str) |
| { |
| StrList* walk; |
| wchar_t* new_str = gettext_to_wchar (str); |
| |
| for (walk=list; walk; walk=walk->next) { |
| if (walk->str) { |
| if (wchar_strcasecmp (new_str, walk->str) == 0) { |
| free (new_str); |
| return list; |
| } |
| } |
| } |
| |
| return _str_list_append (list, new_str); |
| } |
| |
| StrList* |
| str_list_insert (StrList* list, const char* str) |
| { |
| return str_list_join (str_list_create (str, NULL), list); |
| } |
| |
| StrList* |
| str_list_create (const char* first, ...) |
| { |
| va_list args; |
| char* str; |
| StrList* list; |
| |
| list = str_list_append (NULL, first); |
| |
| if (first) { |
| va_start (args, first); |
| while ( (str = va_arg (args, char*)) ) |
| str_list_append (list, str); |
| va_end (args); |
| } |
| |
| return list; |
| } |
| |
| StrList* |
| str_list_create_unique (const char* first, ...) |
| { |
| va_list args; |
| char* str; |
| StrList* list; |
| |
| list = str_list_append (NULL, first); |
| |
| if (first) { |
| va_start (args, first); |
| while ( (str = va_arg (args, char*)) ) |
| str_list_append_unique (list, str); |
| va_end (args); |
| } |
| |
| return list; |
| } |
| |
| char* |
| str_list_convert_node (const StrList* list) |
| { |
| return wchar_to_str (list->str, 0); |
| } |
| |
| char* |
| str_list_convert (const StrList* list) |
| { |
| const StrList* walk; |
| int pos = 0; |
| int length = 1; |
| char* str = strdup (""); |
| |
| for (walk = list; walk; walk = walk->next) { |
| if (walk->str) { |
| char* tmp = wchar_to_str (walk->str, 0); |
| |
| length += strlen (tmp); |
| |
| str = realloc (str, length); |
| strcpy (str + pos, tmp); |
| |
| pos = length - 1; |
| free (tmp); |
| } |
| } |
| |
| return str; |
| } |
| |
| void |
| str_list_print (const StrList* list) |
| { |
| const StrList* walk; |
| |
| for (walk=list; walk; walk=walk->next) { |
| if (walk->str) |
| print_wchar (walk->str, 0); |
| } |
| } |
| |
| static char* |
| get_spaces (int space_count) |
| { |
| char* str; |
| int i; |
| |
| str = malloc (space_count + 1); |
| for (i = 0; i < space_count; i++) |
| str [i] = ' '; |
| str [i] = 0; |
| |
| return str; |
| } |
| |
| static int |
| str_search (const wchar_t* str, int n, wchar_t c) |
| { |
| int i; |
| |
| for (i=0; i<n; i++) |
| if (str [i] == c) |
| return i; |
| return -1; |
| } |
| |
| |
| /* Japanese don't leave spaces between words, so ALL Japanese characters |
| * are treated as delimiters. Note: since the translations should already |
| * be properly formatted (eg: spaces after commas), there should be no |
| * need to include them. Best not to avoid side effects, like 3. |
| 14159 :-) |
| * FIXME: how do we exclude "." and "(" ? |
| * FIXME: glibc doesn't like umlaute. i.e. \"o (TeX notation), which should |
| * look like: รถ |
| */ |
| |
| static int |
| is_break_point (wchar_t c) |
| { |
| #ifdef ENABLE_NLS |
| return !iswalnum (c) && !iswpunct (c); |
| #else |
| return !isalnum (c) && !ispunct (c); |
| #endif |
| } |
| |
| /* NOTE: this should not return '\n' as a space, because explicit '\n' may |
| * be placed inside strings. |
| */ |
| static int |
| is_space (wchar_t c) |
| { |
| #ifdef ENABLE_NLS |
| return c == (wchar_t) btowc(' '); |
| #else |
| return c == ' '; |
| #endif |
| } |
| |
| void |
| str_list_print_wrap (const StrList* list, int line_length, int offset, |
| int indent) |
| { |
| const StrList* walk; |
| const wchar_t* str; |
| int str_len; |
| int cut_right; |
| int cut_left; |
| int line_left; |
| char* spaces; |
| int search_result; |
| int line_break; |
| |
| PED_ASSERT (line_length - indent > 10, return); |
| |
| spaces = get_spaces (indent); |
| line_left = line_length - offset; |
| |
| for (walk=list; walk; walk=walk->next) { |
| if (!walk->str) |
| continue; |
| str = walk->str; |
| str_len = wchar_strlen (str); |
| |
| while (line_left < str_len || wchar_strchr (str, '\n')) { |
| line_break = 0; |
| |
| cut_left = MIN (line_left - 1, str_len - 1); |
| |
| /* we can have a space "over", but not a comma */ |
| if (cut_left < str_len |
| && is_space (str [cut_left + 1])) |
| cut_left++; |
| |
| while (cut_left && !is_break_point (str [cut_left])) |
| cut_left--; |
| while (cut_left && is_space (str [cut_left])) |
| cut_left--; |
| |
| /* str [cut_left] is either the end of a word, or a |
| * Japanese character, or the start of a blank line. |
| */ |
| |
| search_result = str_search (str, cut_left + 1, '\n'); |
| if (search_result != -1) { |
| cut_left = search_result - 1; |
| line_break = 1; |
| } |
| |
| for (cut_right = cut_left + (line_break ? 2 : 1); |
| cut_right < str_len && is_space (str [cut_right]); |
| cut_right++); |
| |
| if (cut_left > 0) |
| print_wchar (str, cut_left + 1); |
| |
| str += cut_right; |
| str_len -= cut_right; |
| line_left = line_length - indent; |
| |
| if (walk->next || *str) |
| printf ("\n%s", spaces); |
| else if (line_break) |
| putchar ('\n'); |
| } |
| |
| print_wchar (str, 0); |
| line_left -= wchar_strlen (str); |
| } |
| |
| free (spaces); |
| } |
| |
| static int |
| _str_list_match_node (const StrList* list, const wchar_t* str) |
| { |
| if (wchar_strcasecmp (list->str, str) == 0) |
| return 2; |
| if (wchar_strncasecmp (list->str, str, wchar_strlen (str)) == 0) |
| return 1; |
| return 0; |
| } |
| |
| int |
| str_list_match_node (const StrList* list, const char* str) |
| { |
| wchar_t* wc_str = gettext_to_wchar (str); /* FIXME */ |
| int status; |
| |
| status = _str_list_match_node (list, wc_str); |
| free (wc_str); |
| |
| return status; |
| } |
| |
| /* returns: 2 for full match |
| 1 for partial match |
| 0 for no match |
| */ |
| int |
| str_list_match_any (const StrList* list, const char* str) |
| { |
| const StrList* walk; |
| int best_status = 0; |
| wchar_t* wc_str = gettext_to_wchar (str); |
| |
| for (walk = list; walk; walk = walk->next) { |
| int this_status = _str_list_match_node (walk, wc_str); |
| if (this_status > best_status) |
| best_status = this_status; |
| } |
| |
| free (wc_str); |
| return best_status; |
| } |
| |
| StrList* |
| str_list_match (const StrList* list, const char* str) |
| { |
| const StrList* walk; |
| const StrList* partial_match = NULL; |
| int ambiguous = 0; |
| wchar_t* wc_str = gettext_to_wchar (str); |
| |
| for (walk = list; walk; walk = walk->next) { |
| switch (_str_list_match_node (walk, wc_str)) { |
| case 2: |
| free (wc_str); |
| return (StrList*) walk; |
| |
| case 1: |
| if (partial_match) |
| ambiguous = 1; |
| partial_match = walk; |
| } |
| } |
| |
| free (wc_str); |
| return ambiguous ? NULL : (StrList*) partial_match; |
| } |
| |
| int |
| str_list_length (const StrList* list) |
| { |
| int length = 0; |
| const StrList* walk; |
| |
| for (walk = list; walk; walk = walk->next) |
| length++; |
| |
| return length; |
| } |
| |