1 /*
2 * Copyright (C) 2008 Giuseppe Torelli - <colossus73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA.
18 */
19
20 #include "config.h"
21 #include <dirent.h>
22 #include <string.h>
23 #include "string_utils.h"
24 #include "support.h"
25 #include "utf8-fnmatch.h"
26
27 #ifndef HAVE_MKDTEMP
28 #include <errno.h>
29 #include <glib/gstdio.h>
30 #endif
31
32 #ifndef HAVE_STRCASESTR
33 #include <ctype.h>
34 #endif
35
36 #ifndef HAVE_MKDTEMP
mkdtemp(gchar * tmpl)37 gchar *mkdtemp (gchar *tmpl)
38 {
39 static const gchar LETTERS[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
40 static guint64 value = 0;
41 guint64 v;
42 gint len;
43 gint i, j;
44
45 len = strlen (tmpl);
46 if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX") != 0)
47 {
48 errno = EINVAL;
49 return NULL;
50 }
51
52 value += ((guint64) time (NULL)) ^ getpid ();
53
54 for (i = 0; i < TMP_MAX; ++i, value += 7777)
55 {
56 /* fill in the random bits */
57 for (j = 0, v = value; j < 6; ++j)
58 tmpl[(len - 6) + j] = LETTERS[v % 62]; v /= 62;
59
60 /* try to create the directory */
61 if (g_mkdir (tmpl, 0700) == 0)
62 return tmpl;
63 else if (errno != EEXIST)
64 return NULL;
65 }
66
67 errno = EEXIST;
68 return NULL;
69 }
70 #endif
71
72 #ifndef HAVE_STRCASESTR
73 /*
74 * case-insensitive version of strstr()
75 */
strcasestr(const char * haystack,const char * needle)76 const char *strcasestr(const char *haystack, const char *needle)
77 {
78 const char *h;
79 const char *n;
80
81 h = haystack;
82 n = needle;
83 while (*haystack)
84 {
85 if (tolower((unsigned char) *h) == tolower((unsigned char) *n))
86 {
87 h++;
88 n++;
89 if (!*n)
90 return haystack;
91 } else {
92 h = ++haystack;
93 n = needle;
94 }
95 }
96 return NULL;
97 }
98 #endif
99
100 /* This function is from File-Roller code */
count_chars_to_escape(const char * str,const char * meta_chars)101 static int count_chars_to_escape (const char *str, const char *meta_chars)
102 {
103 int meta_chars_n = strlen (meta_chars);
104 const char *s;
105 int n = 0;
106
107 for (s = str; *s != 0; s++)
108 {
109 int i;
110 for (i = 0; i < meta_chars_n; i++)
111 if (*s == meta_chars[i])
112 {
113 n++;
114 break;
115 }
116 }
117 return n;
118 }
119 /* End code from File-Roller */
120
xa_escape_common_chars(const char * str,const char * meta_chars,const char prefix,const char postfix)121 static char *xa_escape_common_chars (const char *str, const char *meta_chars, const char prefix, const char postfix)
122 {
123 int meta_chars_n = strlen (meta_chars);
124 char *escaped;
125 int i, new_l, extra_chars = 0;
126 const char *s;
127 char *t;
128
129 if (str == NULL)
130 return NULL;
131
132 if (prefix)
133 extra_chars++;
134 if (postfix)
135 extra_chars++;
136
137 new_l = strlen (str) + (count_chars_to_escape (str, meta_chars) * extra_chars);
138 escaped = g_malloc0 (new_l + 1);
139
140 s = str;
141 t = escaped;
142 while (*s) {
143 gboolean is_bad = FALSE;
144 for (i = 0; (i < meta_chars_n) && !is_bad; i++)
145 is_bad = (*s == meta_chars[i]);
146 if (is_bad && prefix)
147 *t++ = prefix;
148 *t++ = *s++;
149 if (is_bad && postfix)
150 *t++ = postfix;
151 }
152 *t = 0;
153
154 return escaped;
155 }
156
xa_strip_current_working_dir_from_path(gchar * working_dir,gchar * filename)157 static gchar *xa_strip_current_working_dir_from_path (gchar *working_dir, gchar *filename)
158 {
159 gchar *slash;
160 int len = 0;
161
162 if (working_dir == NULL)
163 return filename;
164 len = strlen(working_dir)+1;
165 slash = g_strrstr(filename,"/");
166 if (slash == NULL || ! g_path_is_absolute(filename))
167 return filename;
168
169 return filename+len;
170 }
171
xa_remove_slash_from_path(gchar * path)172 static void xa_remove_slash_from_path (gchar *path)
173 {
174 size_t len = strlen(path);
175
176 if (len > 0 && path[len - 1] == '/')
177 path[len - 1] = 0;
178 }
179
xa_escape_bad_chars(const gchar * string,const gchar * pattern)180 gchar *xa_escape_bad_chars (const gchar *string, const gchar *pattern)
181 {
182 return xa_escape_common_chars (string, pattern, '\\', 0);
183 }
184
xa_remove_level_from_path(const gchar * path)185 gchar *xa_remove_level_from_path (const gchar *path)
186 {
187 gchar *local_path;
188 gchar *_local_path;
189
190 if (path[strlen(path)-1] == '/')
191 {
192 _local_path = g_strndup(path,strlen(path)-1);
193 local_path = g_path_get_dirname (_local_path);
194 g_free(_local_path);
195 }
196 else
197 local_path = g_path_get_dirname (path);
198 return local_path;
199 }
200
xa_set_window_title(GtkWidget * window,gchar * title)201 void xa_set_window_title (GtkWidget *window,gchar *title)
202 {
203 gchar *x = NULL;
204 gchar *slash= NULL;
205
206 if (title)
207 {
208 slash = g_strrstr (title , "/");
209 if (slash)
210 slash++;
211 }
212 if (!slash)
213 slash = title;
214
215 if (title == NULL)
216 x = g_strconcat(PACKAGE_NAME, " ", PACKAGE_VERSION, NULL);
217 else
218 x = g_strconcat(slash, " - ", PACKAGE_NAME, " ", PACKAGE_VERSION, NULL);
219 gtk_window_set_title (GTK_WINDOW (window),x);
220 g_free (x);
221 }
222
match_patterns(char ** patterns,const char * string,int flags)223 gboolean match_patterns (char **patterns,const char *string,int flags)
224 {
225 int i;
226 int result;
227
228 if (patterns[0] == NULL)
229 return TRUE;
230
231 if (string == NULL)
232 return FALSE;
233
234 result = FNM_NOMATCH;
235 i = 0;
236 while ((result != 0) && (patterns[i] != NULL))
237 {
238 result = g_utf8_fnmatch (patterns[i],string,flags);
239 i++;
240 }
241 return (result == 0);
242 }
243
xa_collect_filenames(XArchive * archive,GSList * in)244 GSList *xa_collect_filenames (XArchive *archive, GSList *in)
245 {
246 GSList *list = in, *out = NULL;
247 gchar *basename, *name;
248
249 while (list)
250 {
251 if (archive->location_path)
252 {
253 if (archive->do_full_path)
254 {
255 name = g_strconcat(archive->location_path, list->data, NULL);
256 out = g_slist_append(out, name);
257 }
258 else
259 {
260 basename = xa_strip_current_working_dir_from_path(archive->child_dir ? archive->child_dir : archive->working_dir, list->data);
261 name = g_strconcat(archive->location_path, basename, NULL);
262 out = g_slist_append(out, name);
263 }
264 }
265 else
266 {
267 if (archive->do_full_path)
268 out = g_slist_append(out, g_strdup(list->data));
269 else
270 {
271 basename = xa_strip_current_working_dir_from_path(archive->child_dir ? archive->child_dir : archive->working_dir, list->data);
272 out = g_slist_append(out, g_strdup(basename));
273 }
274 }
275
276 list = list->next;
277 }
278
279 return out;
280 }
281
xa_slist_copy(GSList * list)282 GSList *xa_slist_copy(GSList *list)
283 {
284 GSList *x,*y = NULL;
285 x = list;
286
287 while (x)
288 {
289 y = g_slist_prepend(y,g_strdup(x->data));
290 x = x->next;
291 }
292 return g_slist_reverse(y);
293 }
294
xa_recurse_local_directory(gchar * path,GSList ** list,gboolean recurse)295 void xa_recurse_local_directory (gchar *path, GSList **list, gboolean recurse)
296 {
297 DIR *dir;
298 struct dirent *dirlist;
299 gchar *fullname = NULL,*basename = NULL;
300 gboolean is_dir;
301
302 dir = opendir(path);
303 is_dir = g_file_test(path,G_FILE_TEST_IS_DIR);
304 if (is_dir)
305 *list = g_slist_prepend(*list,g_strdup(path));
306 if (dir == NULL)
307 {
308 if (is_dir == FALSE)
309 {
310 basename = g_path_get_basename(path);
311 *list = g_slist_prepend(*list,basename);
312 return;
313 }
314 }
315
316 while ((dirlist = readdir(dir)))
317 {
318 if (strcmp(dirlist->d_name,".") == 0 || strcmp(dirlist->d_name,"..") == 0)
319 continue;
320 fullname = g_strconcat (path,"/",dirlist->d_name,NULL);
321 is_dir = g_file_test(fullname,G_FILE_TEST_IS_DIR);
322 if ( ! is_dir)
323 *list = g_slist_prepend(*list,fullname);
324 if (recurse && is_dir)
325 xa_recurse_local_directory(fullname, list, recurse);
326 }
327 closedir(dir);
328 }
329
xa_quote_filenames(GSList * file_list,const gchar * escape,gboolean slash)330 GString *xa_quote_filenames (GSList *file_list, const gchar *escape, gboolean slash)
331 {
332 GString *files;
333 GSList *list;
334
335 files = g_string_new("");
336 list= file_list;
337
338 while (list)
339 {
340 gchar *shellname, *escaped = NULL;
341
342 if (!slash)
343 xa_remove_slash_from_path(list->data);
344
345 if (escape)
346 escaped = xa_escape_bad_chars(list->data, "\\");
347
348 shellname = g_shell_quote(escaped ? escaped : list->data);
349
350 g_free(escaped);
351
352 if (escape)
353 escaped = xa_escape_bad_chars(shellname, escape);
354
355 g_string_prepend(files, escaped ? escaped : shellname);
356 g_string_prepend_c(files, ' ');
357
358 g_free(escaped);
359 g_free(shellname);
360
361 list = list->next;
362 }
363
364 g_slist_free_full(file_list, g_free);
365
366 return files;
367 }
368
xa_quote_shell_command(const gchar * string,gboolean unescaped)369 gchar *xa_quote_shell_command (const gchar *string, gboolean unescaped)
370 {
371 gchar *unquoted = NULL, *quoted, *escaped;
372
373 if (!unescaped)
374 unquoted = g_shell_unquote(string, NULL);
375
376 quoted = g_shell_quote(unquoted ? unquoted : string);
377
378 escaped = xa_escape_bad_chars(quoted, "\"");
379
380 g_free(quoted);
381 g_free(unquoted);
382
383 return escaped;
384 }
385
xa_collect_files_in_dir(const gchar * directory)386 GString *xa_collect_files_in_dir (const gchar *directory)
387 {
388 size_t offset;
389 GSList *stack;
390 GString *files = g_string_new("");
391
392 offset = strlen(directory) + 1;
393 stack = g_slist_append(NULL, g_strdup(directory));
394
395 while (stack)
396 {
397 gchar *file;
398
399 file = stack->data;
400 stack = g_slist_delete_link(stack, stack);
401
402 if (g_file_test(file, G_FILE_TEST_IS_DIR))
403 {
404 GDir *dir;
405 const gchar *name;
406
407 dir = g_dir_open(file, 0, NULL);
408
409 if (dir)
410 {
411 while ((name = g_dir_read_name(dir)))
412 stack = g_slist_prepend(stack, g_strconcat(file, "/", name, NULL));
413
414 g_dir_close(dir);
415 }
416 }
417 else
418 {
419 gchar *quoted = g_shell_quote(file + offset);
420
421 files = g_string_append_c(files, ' ');
422 files = g_string_append(files, quoted);
423
424 g_free(quoted);
425 }
426
427 g_free(file);
428 }
429
430 g_slist_free(stack);
431
432 return files;
433 }
434
xa_set_max_width_chars_ellipsize(const gchar * string,gint n,PangoEllipsizeMode mode)435 gchar *xa_set_max_width_chars_ellipsize (const gchar *string, gint n, PangoEllipsizeMode mode)
436 {
437 static gchar *ellipsized;
438 glong len;
439
440 len = g_utf8_strlen(string, -1);
441
442 if (len <= n)
443 return (gchar *) string;
444
445 g_free(ellipsized);
446 ellipsized = g_malloc0(strlen(string) + 2);
447
448 switch (mode)
449 {
450 case PANGO_ELLIPSIZE_NONE:
451
452 g_utf8_strncpy(ellipsized, string, n);
453
454 break;
455
456 case PANGO_ELLIPSIZE_START:
457
458 strcpy(ellipsized, "…");
459
460 while (len > n - 1)
461 {
462 string = g_utf8_next_char(string);
463 len--;
464 }
465
466 g_utf8_strncpy(ellipsized + 3, string, n - 1);
467
468 break;
469
470 case PANGO_ELLIPSIZE_END:
471
472 g_utf8_strncpy(ellipsized, string, n - 1);
473 strcat(ellipsized, "…");
474
475 break;
476
477 default:
478 break;
479 }
480
481 return ellipsized;
482 }
483