gliv-1.7
[gliv.git] / src / str_utils.c
bloba8f5e1de21678ec95df755b3536ce5485021509b
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 <gfc@altern.org>
21 /*********************************
22 * UTF-8 and filenames handling. *
23 *********************************/
25 #include "gliv.h"
27 #include <string.h> /* memcpy(), strlen(), strstr() */
29 /* The returned string should not be freed. */
30 const gchar *add_mnemonic(const gchar * str)
32 static gchar *result = NULL;
33 static gint size = 0;
34 const gchar *ptr = NULL, *end;
35 gint new_size;
37 for (ptr = str; *ptr != '\0'; ptr = g_utf8_next_char(ptr))
38 if (g_unichar_isalnum(g_utf8_get_char(ptr)))
39 break;
41 end = ptr + strlen(ptr);
43 if (*ptr == '\0')
45 * No g_unichar_isalnum() were found,
46 * we add the underscore in front.
48 ptr = str;
50 /* + 2: '_' and '\0'. */
51 new_size = end - str + 2;
52 if (new_size > size) {
53 size = new_size;
54 g_free(result);
56 result = g_new(gchar, size);
59 memcpy(result, str, ptr - str);
60 result[ptr - str] = '_';
61 memcpy(result + (ptr - str) + 1, ptr, end - ptr + 1);
63 return result;
66 static gboolean str_is_ascii(const gchar * str)
68 while (*str != '\0') {
69 if (*str >> 7)
70 return FALSE;
72 str++;
75 return TRUE;
78 /* The returned string should not be freed. */
79 const gchar *locale_to_utf8(const gchar * str)
81 static gchar *result = NULL;
82 GError *err = NULL;
84 if (str_is_ascii(str))
85 return str;
87 g_free(result);
89 result = g_locale_to_utf8(str, -1, NULL, NULL, &err);
90 if (err != NULL) {
91 g_printerr("%s\n", err->message);
92 g_error_free(err);
94 g_free(result);
95 result = NULL;
96 return str;
99 return result;
102 static gboolean is_clean(const gchar * filename)
104 if (filename[0] != '/' && (filename[0] != '.' || filename[1] != '/'))
105 return FALSE;
107 return strstr(filename, "//") == NULL && strstr(filename, "/./") == NULL;
110 gchar *clean_filename(const gchar * filename)
112 gchar *orig_copy, *copy, *ptr, *new, *new_ptr;
113 gint count = 0, len = 0;
114 gboolean absolute;
116 if (is_clean(filename))
117 return g_strdup(filename);
119 /* We work on a copy as we may modify it. */
120 orig_copy = copy = g_strdup(filename);
122 if (copy[0] == '/') {
123 /* Absolute filename. */
124 while (copy[1] == '/')
125 copy++;
127 absolute = TRUE;
128 } else {
129 /* Relative filename. */
130 gboolean finished = FALSE;
132 while (finished == FALSE) {
133 switch (copy[0]) {
134 case '.':
135 if (copy[1] == '/')
136 copy += 2;
137 else
138 finished = TRUE;
139 break;
141 case '/':
142 copy++;
143 break;
145 default:
146 finished = TRUE;
150 absolute = FALSE;
154 * "path1/./path2" is replaced with "path1///path2".
155 * "./" and "//" are counted.
157 for (ptr = copy; *ptr != '\0'; ptr++) {
158 len++;
159 if (ptr[0] == '/')
160 switch (ptr[1]) {
161 case '.':
162 if (ptr[2] == '/') {
163 ptr[1] = '/';
164 count++;
166 break;
168 case '/':
169 count++;
173 if (count == 0) {
174 /* The filename was clean. */
176 if (absolute) {
177 if (orig_copy != copy)
178 /* The filename started with "//". */
179 g_memmove(orig_copy, copy, len + 1);
181 return orig_copy;
184 /* The relative filename just lacked the "./" in the beginning. */
185 new = fast_build_filename(".", copy);
186 g_free(orig_copy);
187 return new;
190 /* We now have to remove the "//". */
192 if (absolute) {
193 new = g_new(gchar, len - count + 1);
194 new_ptr = new;
195 } else {
196 new = g_new(gchar, len - count + 3);
197 new[0] = '.';
198 new[1] = '/';
199 new_ptr = new + 2;
202 for (ptr = copy; *ptr != '\0'; ptr++)
203 if (ptr[0] != '/' || ptr[1] != '/') {
204 *new_ptr = *ptr;
205 new_ptr++;
208 *new_ptr = '\0';
210 g_free(orig_copy);
212 return new;
215 gchar *clean_filename_free(gchar * filename)
217 gchar *clean;
219 if (filename[0] == '\0' || is_clean(filename))
220 clean = filename;
221 else {
222 clean = clean_filename(filename);
223 g_free(filename);
226 return clean;
229 gchar *fast_build_filename(const gchar * dir, const gchar * file)
231 if (dir[0] == '\0')
232 return g_strdup(file);
234 if (file[0] == '\0')
235 return g_strdup(dir);
237 return g_strconcat(dir, "/", file, NULL);
240 gchar *str_escape(const gchar * str)
242 static gboolean *no_problem = NULL;
243 gchar *buffer;
244 gint buffer_length;
245 gchar *escaped_ptr;
247 if (no_problem == NULL) {
248 /* First time. */
249 gint i;
251 no_problem = g_new0(gboolean, 256);
253 no_problem['/'] = TRUE;
255 for (i = '0'; i <= '9'; i++)
256 no_problem[i] = TRUE;
258 for (i = 'A'; i <= 'Z'; i++)
259 no_problem[i] = TRUE;
261 for (i = 'a'; i <= 'z'; i++)
262 no_problem[i] = TRUE;
265 buffer_length = strlen(str) * 2 + 1;
266 buffer = g_new(gchar, buffer_length);
268 escaped_ptr = buffer;
270 while (*str != '\0') {
271 if (no_problem[(gint) (*str)] == FALSE)
272 *escaped_ptr++ = '\\';
274 *escaped_ptr++ = *str++;
277 *escaped_ptr = '\0';
278 return buffer;