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