1 /* $Id$ */
2 /* Copyright (c) 2008-2015 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS Desktop Browser */
4 /* Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the authors nor the names of the contributors may be
16  *    used to endorse or promote products derived from this software without
17  *    specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 
30 
31 
32 #include <stdio.h>
33 #include <libintl.h>
34 
35 
36 /* macros */
37 #ifndef _
38 # define _(string) gettext(string)
39 #endif
40 #define min(a, b) ((a) < (b)) ? (a) : (b)
41 
42 
43 /* prototypes */
44 #ifdef COMMON_CONFIG_FILENAME
45 static String * _common_config_filename(String const * name);
46 #endif
47 
48 #ifdef COMMON_DND
49 static int _common_drag_data_received(GdkDragContext * context,
50 		GtkSelectionData * seldata, char const * dest);
51 #endif
52 
53 #ifdef COMMON_EXEC
54 static int _common_exec(char const * program, char const * flags, GList * args);
55 #endif
56 
57 #ifdef COMMON_GET_ABSOLUTE_PATH
58 static char * _common_get_absolute_path(char const * path);
59 #endif
60 
61 #ifdef COMMON_SIZE
62 static char const * _common_size(off_t size);
63 #endif
64 
65 #ifdef COMMON_SYMLINK
66 static int _common_symlink(GtkWidget * window, char const * cur);
67 #endif
68 
69 
70 /* functions */
71 #ifdef COMMON_CONFIG_FILENAME
_common_config_filename(String const * name)72 static String * _common_config_filename(String const * name)
73 {
74 	char const * homedir;
75 
76 	if((homedir = getenv("HOME")) == NULL)
77 		homedir = g_get_home_dir();
78 	return string_new_append(homedir, "/", name, NULL);
79 }
80 #endif
81 
82 
83 #ifdef COMMON_DND
84 /* common_drag_data_received */
_common_drag_data_received(GdkDragContext * context,GtkSelectionData * seldata,char const * dest)85 static int _common_drag_data_received(GdkDragContext * context,
86 		GtkSelectionData * seldata, char const * dest)
87 {
88 	int ret = 0;
89 	size_t len;
90 	size_t i;
91 	GList * selection = NULL;
92 	char * p;
93 	GdkDragAction action;
94 #ifdef DEBUG
95 	GList * s;
96 #endif
97 
98 #if GTK_CHECK_VERSION(2, 14, 0)
99 	if(gtk_selection_data_get_length(seldata) <= 0
100 			|| gtk_selection_data_get_data(seldata) == NULL)
101 		return 0;
102 	len = gtk_selection_data_get_length(seldata);
103 #else
104 	if(seldata->length <= 0 || seldata->data == NULL)
105 		return 0;
106 	len = seldata->length;
107 #endif
108 	for(i = 0; i < len; i += strlen(p) + 1)
109 	{
110 #if GTK_CHECK_VERSION(2, 14, 0)
111 		p = (char *)gtk_selection_data_get_data(seldata);
112 		p = &p[i];
113 #else
114 		p = (char *)&seldata->data[i];
115 #endif
116 		selection = g_list_append(selection, p);
117 	}
118 #if GTK_CHECK_VERSION(2, 22, 0)
119 	action = gdk_drag_context_get_suggested_action(context);
120 #else
121 	action = context->suggested_action;
122 #endif
123 #ifdef DEBUG
124 	fprintf(stderr, "%s%s%s%s%s", "DEBUG: ", action == GDK_ACTION_COPY
125 			? _("copying") : _("moving"), _(" to \""), dest,
126 			"\":\n");
127 	for(s = selection; s != NULL; s = s->next)
128 		fprintf(stderr, "DEBUG: \"%s\"\n", (char const *)s->data);
129 #else
130 	selection = g_list_append(selection, (char *)dest); /* XXX */
131 	if(action == GDK_ACTION_COPY)
132 		ret = _common_exec("copy", "-iR", selection);
133 	else if(action == GDK_ACTION_MOVE)
134 		ret = _common_exec("move", "-i", selection);
135 #endif
136 	g_list_free(selection);
137 	return ret;
138 }
139 #endif /* COMMON_DND */
140 
141 
142 #ifdef COMMON_EXEC
143 /* common_exec */
_common_exec(char const * program,char const * flags,GList * args)144 static int _common_exec(char const * program, char const * flags, GList * args)
145 {
146 	int ret = 0;
147 	unsigned long i = (flags != NULL) ? 3 : 2;
148 	char ** argv = NULL;
149 	GList * a;
150 	char ** p;
151 	GError * error = NULL;
152 
153 	if(args == NULL)
154 		return 0;
155 	for(a = args; a != NULL; a = a->next)
156 	{
157 		if(a->data == NULL)
158 			continue;
159 		if((p = realloc(argv, sizeof(*argv) * (i + 2))) == NULL)
160 			break;
161 		argv = p;
162 		argv[i++] = a->data;
163 	}
164 	if(a != NULL)
165 	{
166 		free(argv);
167 		return -error_set_code(1, "%s: %s", program, strerror(errno));
168 	}
169 	if(argv == NULL)
170 		return 0;
171 #ifdef DEBUG
172 	argv[0] = strdup("echo");
173 #else
174 	argv[0] = strdup(program);
175 #endif
176 	if(argv[0] == NULL)
177 	{
178 		free(argv);
179 		return -error_set_code(1, "%s: %s", program, strerror(errno));
180 	}
181 	argv[i] = NULL;
182 	i = 0;
183 	if(flags != NULL)
184 		argv[++i] = strdup(flags); /* XXX may fail too */
185 	argv[i + 1] = "--";
186 	if(g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
187 				NULL, &error) != TRUE)
188 	{
189 		ret = error_set_code(1, "%s", error->message);
190 		g_error_free(error);
191 	}
192 	free(argv[0]);
193 	if(flags != NULL)
194 		free(argv[i]);
195 	return ret;
196 }
197 #endif /* COMMON_EXEC */
198 
199 
200 #ifdef COMMON_GET_ABSOLUTE_PATH
201 /* common_get_absolute_path */
_common_get_absolute_path(char const * path)202 static char * _common_get_absolute_path(char const * path)
203 {
204 	char * p;
205 	char * cur;
206 	size_t i;
207 
208 	if(path == NULL)
209 		return NULL;
210 	if(g_path_is_absolute(path))
211 	{
212 		if((p = strdup(path)) == NULL)
213 			return NULL;
214 	}
215 	else
216 	{
217 		cur = g_get_current_dir();
218 		p = g_build_filename(cur, path, NULL);
219 		g_free(cur);
220 	}
221 	/* replace "/./" by "/" */
222 	for(i = strlen(p); (cur = strstr(p, "/./")) != NULL; i = strlen(p))
223 		memmove(cur, &cur[2], (p + i) - (cur + 1));
224 	/* replace "//" by "/" */
225 	for(i = strlen(p); (cur = strstr(p, "//")) != NULL; i = strlen(p))
226 		memmove(cur, &cur[1], (p + i) - (cur));
227 	/* remove single dots at the end of the address */
228 	i = strlen(p);
229 	if(i >= 2 && strcmp(&p[i - 2], "/.") == 0)
230 		p[i - 1] = '\0';
231 	/* trim slashes in the end if relevant */
232 	if(string_compare(p, "/") != 0)
233 		string_rtrim(p, "/");
234 #ifdef DEBUG
235 	fprintf(stderr, "DEBUG: %s(\"%s\") => \"%s\"\n", __func__, path, p);
236 #endif
237 	return p;
238 }
239 #endif /* COMMON_GET_ABSOLUTE_PATH */
240 
241 
242 #ifdef COMMON_SIZE
243 /* common_size */
_common_size(off_t size)244 static char const * _common_size(off_t size)
245 {
246 	static char buf[16];
247 	double sz = size;
248 	char * unit;
249 
250 	if(sz < 1024)
251 	{
252 		snprintf(buf, sizeof(buf), "%.0f %s", sz, _("bytes"));
253 		return buf;
254 	}
255 	else if((sz /= 1024) < 1024)
256 		unit = N_("kB");
257 	else if((sz /= 1024) < 1024)
258 		unit = N_("MB");
259 	else if((sz /= 1024) < 1024)
260 		unit = N_("GB");
261 	else
262 	{
263 		sz /= 1024;
264 		unit = N_("TB");
265 	}
266 	snprintf(buf, sizeof(buf), "%.1f %s", sz, _(unit));
267 	return buf;
268 }
269 #endif
270 
271 
272 #ifdef COMMON_SYMLINK
273 /* common_symlink */
_common_symlink(GtkWidget * window,char const * cur)274 static int _common_symlink(GtkWidget * window, char const * cur)
275 {
276 	static char const * newsymlink = NULL;
277 	int ret = 0;
278 	size_t len;
279 	char * path;
280 	GtkWidget * dialog;
281 	GtkWidget * hbox;
282 	GtkWidget * widget;
283 	char const * to = NULL;
284 
285 	if(newsymlink == NULL)
286 		newsymlink = _("New symbolic link");
287 	len = strlen(cur) + strlen(newsymlink) + 2;
288 	if((path = malloc(len)) == NULL)
289 		return 1;
290 	snprintf(path, len, "%s/%s", cur, newsymlink);
291 	dialog = gtk_dialog_new_with_buttons(newsymlink,
292 			(window != NULL) ? GTK_WINDOW(window) : NULL,
293 			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
294 			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
295 			GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
296 	if(window == NULL)
297 		gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
298 #if GTK_CHECK_VERSION(3, 0, 0)
299 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
300 #else
301 	hbox = gtk_hbox_new(FALSE, 0);
302 #endif
303 	widget = gtk_label_new(_("Destination:"));
304 	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 4);
305 	widget = gtk_entry_new();
306 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 4);
307 	gtk_widget_show_all(hbox);
308 #if GTK_CHECK_VERSION(2, 14, 0)
309 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(
310 						dialog))), hbox, TRUE, TRUE, 4);
311 #else
312 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE,
313 			4);
314 #endif
315 	if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
316 		to = gtk_entry_get_text(GTK_ENTRY(widget));
317 	if(to != NULL && strlen(to) > 0 && symlink(to, path) != 0)
318 		ret = 1;
319 	gtk_widget_destroy(dialog);
320 	free(path);
321 	return ret;
322 }
323 #endif /* COMMON_SYMLINK */
324