Merged older cs.po file with newest pot file.
[gliv/czech_localization.git] / src / str_utils.c
blob1b5f17c5d22f659439382c615432e542e63ded44
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /*******************************************
22 * UTF-8, filenames and mnemonics handling *
23 *******************************************/
25 #include <string.h> /* strstr(), strchr(), memcpy(), strcpy() */
26 #include <stdlib.h> /* getenv() */
28 #include "gliv.h"
29 #include "str_utils.h"
31 /* If you can't stand string manipulations in C, just don't look there */
33 gint common_prefix_length(const gchar * str1, const gchar * str2)
35 const gchar *orig_str1 = str1;
37 if (NOT_ALIGNED(str1) == NOT_ALIGNED(str2)) {
38 /* We can align both pointers. */
39 while (NOT_ALIGNED(str1) && *str1 == *str2 && *str1 != '\0') {
40 str1++;
41 str2++;
44 if (*str1 != '\0' && *str1 == *str2) {
45 /* Both pointers are now aligned. */
46 gulong *long_ptr1 = (gulong *) str1, *long_ptr2 = (gulong *) str2;
48 /* Skip identical blocks in the paths. */
49 while (!HAS_ZERO(*long_ptr1) && *long_ptr1 == *long_ptr2) {
50 long_ptr1++;
51 long_ptr2++;
54 str1 = (const gchar *) long_ptr1;
55 str2 = (const gchar *) long_ptr2;
59 /* Skip identical characters in the paths. */
60 while (*str1 != '\0' && *str1 == *str2) {
61 str1++;
62 str2++;
65 return str1 - orig_str1;
68 #define CHECK_BYTE(ptr) \
69 do { \
70 if (*ptr == '\0') \
71 return TRUE; \
73 if (*ptr >> 7) \
74 return FALSE; \
76 ptr++; \
77 } while (0)
79 static gboolean str_is_ascii(const gchar * str)
81 gulong *long_ptr, long_int;
83 for (;;) {
84 if (*str == '\0')
85 return TRUE;
87 if (*str >> 7)
88 return FALSE;
90 if (!NOT_ALIGNED(str))
91 /* Aligned. */
92 break;
94 str++;
97 long_ptr = (gulong *) str;
99 for (;;) {
100 long_int = *long_ptr;
101 if (HAS_ZERO(long_int)) {
102 /* A '\0' has been detected. */
103 const gchar *char_ptr = (const gchar *) long_ptr;
104 CHECK_BYTE(char_ptr);
105 CHECK_BYTE(char_ptr);
106 CHECK_BYTE(char_ptr);
107 CHECK_BYTE(char_ptr);
108 if (sizeof(gulong) > 4) {
109 /* 64-bit */
110 CHECK_BYTE(char_ptr);
111 CHECK_BYTE(char_ptr);
112 CHECK_BYTE(char_ptr);
113 CHECK_BYTE(char_ptr);
115 } else if (long_int & LONG_MASK(0x80808080L, 0x8080808080808080L))
116 return FALSE;
118 long_ptr++;
120 return TRUE;
123 /* From glib. */
124 static gboolean have_broken_filenames(void)
126 static gboolean initialized = FALSE;
127 static gboolean broken;
129 if (initialized)
130 return broken;
132 broken = getenv("G_BROKEN_FILENAMES") != NULL;
133 initialized = TRUE;
135 return broken;
138 /* The returned string should not be freed. */
139 const gchar *filename_to_utf8(const gchar * str)
141 static GStaticPrivate result_key = G_STATIC_PRIVATE_INIT;
142 gchar **result;
143 GError *err = NULL;
145 if (!have_broken_filenames() || g_get_charset(NULL) || str_is_ascii(str))
146 return str;
148 result = g_static_private_get(&result_key);
149 if (result == NULL) {
150 /* First time in this thread. */
151 result = g_new(gchar *, 1);
152 g_static_private_set(&result_key, result, g_free);
153 *result = NULL;
156 g_free(*result);
158 *result = g_filename_to_utf8(str, -1, NULL, NULL, &err);
159 if (err != NULL) {
160 g_printerr("%s\n", err->message);
161 g_error_free(err);
163 g_free(*result);
164 *result = NULL;
165 return str;
168 return *result;
171 static gboolean starts_dotslash(const gchar * str)
173 return str[0] == '.' && str[1] == '/';
176 static gboolean is_clean(const gchar * filename)
178 if (filename[0] != '/' && !starts_dotslash(filename))
179 return FALSE;
181 return strstr(filename, "//") == NULL && strstr(filename, "/./") == NULL;
184 static gint count_errors(gchar * str, gint * len)
186 gint count = 0;
187 gchar *prev_slash = str, *next_slash = NULL;
190 * "path1/./path2" is replaced with "path1///path2".
191 * "./" and "//" are counted.
193 while ((next_slash = strchr(prev_slash + !!next_slash, '/')) != NULL) {
194 *len += next_slash - prev_slash;
196 switch (next_slash[1]) {
197 case '.':
198 if (next_slash[2] == '/') {
199 next_slash[1] = '/';
200 count++;
202 break;
204 case '/':
205 count++;
208 prev_slash = next_slash;
211 *len += strlen(prev_slash);
212 return count;
215 static gchar *remove_double_slash(gchar * str, gint new_len)
217 gchar *new, *new_ptr;
218 gchar *prev_slash = str, *next_slash = NULL;
220 if (str[0] == '/' || starts_dotslash(str)) {
221 new = g_new(gchar, new_len);
222 new_ptr = new;
223 } else {
224 new = g_new(gchar, new_len + 2);
225 new[0] = '.';
226 new[1] = '/';
227 new_ptr = new + 2;
230 while ((next_slash = strchr(prev_slash + !!next_slash, '/')) != NULL) {
231 memcpy(new_ptr, prev_slash, next_slash - prev_slash);
232 new_ptr += next_slash - prev_slash;
234 while (next_slash[1] == '/')
235 next_slash++;
237 prev_slash = next_slash;
240 strcpy(new_ptr, prev_slash);
242 return new;
245 gchar *clean_filename(const gchar * filename)
247 gchar *orig_copy, *copy, *new;
248 gint count = 0, len = 0;
249 gboolean absolute, finished = FALSE;
251 if (is_clean(filename))
252 return g_strdup(filename);
254 /* We work on a copy as we may modify it. */
255 orig_copy = copy = g_strdup(filename);
257 absolute = (copy[0] == '/');
258 while (finished == FALSE) {
259 switch (copy[0]) {
260 case '.':
261 if (copy[1] == '/')
262 copy += 2;
263 else
264 finished = TRUE;
265 break;
267 case '/':
268 copy++;
269 break;
271 default:
272 finished = TRUE;
276 if (absolute)
277 /* We keep the last leading '/' for absolute filenames. */
278 copy--;
279 else if (copy - orig_copy >= 2) {
280 /* We add a './' for relative filenames. */
281 copy -= 2;
282 copy[0] = '.';
283 copy[1] = '/';
286 /* Count the '//' and '/./'. */
287 count = count_errors(copy, &len);
289 if (count == 0) {
290 /* The filename was clean. */
292 if (absolute) {
293 if (orig_copy != copy)
294 /* The filename started with "//". */
295 g_memmove(orig_copy, copy, len + 1);
297 return orig_copy;
300 if (starts_dotslash(copy)) {
301 if (orig_copy != copy)
302 g_memmove(orig_copy, copy, len + 1);
303 new = orig_copy;
304 } else {
305 /* The relative filename just lacked the "./" in the beginning. */
306 new = g_strconcat("./", copy, NULL);
307 g_free(orig_copy);
310 return new;
313 /* We now have to remove the "//". */
314 new = remove_double_slash(copy, len - count + 1);
316 g_free(orig_copy);
318 return new;