4 * ROX-Filer, filer for the ROX desktop project
5 * By Thomas Leonard, <tal197@ecs.soton.ac.uk>.
8 /* action.c - code for handling the filer action windows.
9 * These routines generally fork() and talk to us via pipes.
16 #include <sys/types.h>
24 #include "gui_support.h"
28 static GdkColor red
= {0, 0xffff, 0, 0};
30 typedef struct _GUIside GUIside
;
31 typedef void ActionChild(gpointer data
);
32 typedef void ForDirCB(char *path
);
36 int from_child
; /* File descriptor */
38 int input_tag
; /* gdk_input_add() */
39 GtkWidget
*log
, *window
, *actions
;
40 int child
; /* Process ID */
44 /* These don't need to be in a structure because we fork() before
47 static int from_parent
= 0;
48 static FILE *to_parent
= NULL
;
49 static gboolean quiet
= FALSE
;
50 static GString
*message
= NULL
;
51 static char *action_dest
= NULL
;
52 static void (*action_do_func
)(char *source
, char *dest
);
54 /* Static prototypes */
55 static gboolean
send();
56 static gboolean
send_error();
57 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
59 /* Called when the child sends us a message */
60 static void message_from_child(gpointer data
,
62 GdkInputCondition condition
)
65 GUIside
*gui_side
= (GUIside
*) data
;
66 GtkWidget
*log
= gui_side
->log
;
68 if (read_exact(source
, buf
, 4))
74 message_len
= strtol(buf
, NULL
, 16);
75 buffer
= g_malloc(message_len
+ 1);
76 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
78 buffer
[message_len
] = '\0';
80 gtk_widget_set_sensitive(gui_side
->actions
,
82 else if (*buffer
== '+')
84 refresh_dirs(buffer
+ 1);
88 else if (*buffer
== '!')
91 gtk_text_insert(GTK_TEXT(log
),
93 *buffer
== '!' ? &red
: NULL
,
95 buffer
+ 1, message_len
- 1);
99 g_print("Child died in the middle of a message.\n");
102 /* The child is dead */
105 fclose(gui_side
->to_child
);
106 close(gui_side
->from_child
);
107 gdk_input_remove(gui_side
->input_tag
);
109 if (gui_side
->errors
)
112 report
= g_string_new(NULL
);
113 g_string_sprintf(report
, "There %s %d error%s.\n",
114 gui_side
->errors
== 1 ? "was" : "were",
116 gui_side
->errors
== 1 ? "" : "s");
117 gtk_text_insert(GTK_TEXT(log
), NULL
, &red
, NULL
,
118 report
->str
, report
->len
);
120 g_string_free(report
, TRUE
);
123 gtk_widget_destroy(gui_side
->window
);
126 static void for_dir_contents(char *dir
, ForDirCB
*cb
)
130 GSList
*list
= NULL
, *next
;
139 while ((ent
= readdir(d
)))
141 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
142 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
144 list
= g_slist_append(list
, g_strdup(make_path(dir
,
156 cb((char *) next
->data
);
157 g_string_sprintf(message
, "+%s", dir
);
167 /* Read this many bytes into the buffer. TRUE on success. */
168 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
173 got
= read(source
, buffer
, len
);
182 /* Send 'message' to our parent process. TRUE on success. */
183 static gboolean
send()
188 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
190 sprintf(len_buffer
, "%04x", message
->len
);
191 fwrite(len_buffer
, 1, 4, to_parent
);
192 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
194 return len
== message
->len
;
197 static gboolean
send_error()
199 g_string_sprintf(message
, "!ERROR: %s\n", g_strerror(errno
));
203 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
207 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
208 g_return_if_fail(text
!= NULL
);
209 fputc(*text
, gui_side
->to_child
);
210 fflush(gui_side
->to_child
);
212 gtk_widget_set_sensitive(gui_side
->actions
, FALSE
);
215 /* Get one char from fd. Quit on error. */
216 static char reply(int fd
)
221 len
= read(fd
, &retval
, 1);
225 fprintf(stderr
, "read() error: %s\n", g_strerror(errno
));
226 _exit(1); /* Parent died? */
230 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
232 GUIside
*gui_side
= (GUIside
*) data
;
236 kill(gui_side
->child
, SIGTERM
);
237 fclose(gui_side
->to_child
);
238 close(gui_side
->from_child
);
239 gdk_input_remove(gui_side
->input_tag
);
244 if (--number_of_windows
< 1)
248 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
249 * (NULL on failure). The child calls func().
251 static GUIside
*start_action(gpointer data
, ActionChild
*func
)
253 int filedes
[4]; /* 0 and 2 are for reading */
256 GtkWidget
*vbox
, *button
, *control
;
260 report_error("ROX-Filer", g_strerror(errno
));
264 if (pipe(filedes
+ 2))
268 report_error("ROX-Filer", g_strerror(errno
));
276 report_error("ROX-Filer", g_strerror(errno
));
279 /* We are the child */
280 message
= g_string_new(NULL
);
283 to_parent
= fdopen(filedes
[1], "wb");
284 from_parent
= filedes
[2];
289 /* We are the parent */
292 gui_side
= g_malloc(sizeof(GUIside
));
293 gui_side
->from_child
= filedes
[0];
294 gui_side
->to_child
= fdopen(filedes
[3], "wb");
295 gui_side
->log
= NULL
;
296 gui_side
->child
= child
;
297 gui_side
->errors
= 0;
299 gui_side
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
300 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 500, 100);
301 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
302 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
304 vbox
= gtk_vbox_new(FALSE
, 4);
305 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
307 gui_side
->log
= gtk_text_new(NULL
, NULL
);
308 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->log
, TRUE
, TRUE
, 0);
310 gui_side
->actions
= gtk_hbox_new(TRUE
, 4);
311 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->actions
, FALSE
, TRUE
, 0);
313 button
= gtk_button_new_with_label("Always");
314 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "A");
315 gtk_box_pack_start(GTK_BOX(gui_side
->actions
), button
, TRUE
, TRUE
, 0);
316 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
317 button_reply
, gui_side
);
318 button
= gtk_button_new_with_label("Yes");
319 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
320 gtk_box_pack_start(GTK_BOX(gui_side
->actions
), button
, TRUE
, TRUE
, 0);
321 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
322 button_reply
, gui_side
);
323 button
= gtk_button_new_with_label("No");
324 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
325 gtk_box_pack_start(GTK_BOX(gui_side
->actions
), button
, TRUE
, TRUE
, 0);
326 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
327 button_reply
, gui_side
);
328 gtk_widget_set_sensitive(gui_side
->actions
, FALSE
);
330 control
= gtk_hbox_new(TRUE
, 4);
331 gtk_box_pack_start(GTK_BOX(vbox
), control
, FALSE
, TRUE
, 0);
333 button
= gtk_button_new_with_label("Suspend");
334 gtk_box_pack_start(GTK_BOX(control
), button
, TRUE
, TRUE
, 0);
336 button
= gtk_button_new_with_label("Resume");
337 gtk_widget_set_sensitive(button
, FALSE
);
338 gtk_box_pack_start(GTK_BOX(control
), button
, TRUE
, TRUE
, 0);
340 button
= gtk_button_new_with_label("Abort");
341 gtk_box_pack_start(GTK_BOX(control
), button
, TRUE
, TRUE
, 0);
342 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
343 gtk_widget_destroy
, GTK_OBJECT(gui_side
->window
));
345 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
353 /* ACTIONS ON ONE ITEM */
355 /* These may call themselves recursively, or ask questions, etc */
357 static void do_delete(char *path
)
363 if (lstat(path
, &info
))
369 write_prot
= access(path
, W_OK
) != 0;
370 if (quiet
== 0 || write_prot
)
372 g_string_sprintf(message
, "?Delete %s'%s'?\n",
373 write_prot
? "WRITE-PROTECTED " : " ",
376 rep
= reply(from_parent
);
382 if (S_ISDIR(info
.st_mode
))
385 safe_path
= g_strdup(path
);
386 g_string_sprintf(message
, "'Scanning in '%s'\n", safe_path
);
388 for_dir_contents(safe_path
, do_delete
);
389 if (rmdir(safe_path
))
395 g_string_assign(message
, "'Directory deleted\n");
399 else if (unlink(path
) == 0)
401 g_string_sprintf(message
, "'Deleted '%s'\n", path
);
408 static void do_copy(char *path
, char *dest
)
413 leaf
= strrchr(path
, '/');
415 leaf
= path
; /* Error? */
419 dest_path
= make_path(dest
, leaf
)->str
;
421 if (access(dest_path
, F_OK
) == 0)
424 g_string_sprintf(message
, "?'%s' already exists - overwrite?\n",
428 rep
= reply(from_parent
);
435 g_string_sprintf(message
, "cp -a %s %s", path
, dest_path
);
436 if (system(message
->str
) == 0)
437 g_string_sprintf(message
, "'Copied %s as %s\n",
440 g_string_sprintf(message
, "!ERROR: Failed to copy %s as %s\n",
445 static void do_move(char *path
, char *dest
)
450 leaf
= strrchr(path
, '/');
452 leaf
= path
; /* Error? */
456 dest_path
= make_path(dest
, leaf
)->str
;
458 if (access(dest_path
, F_OK
) == 0)
461 g_string_sprintf(message
, "?'%s' already exists - overwrite?\n",
465 rep
= reply(from_parent
);
472 g_string_sprintf(message
, "mv -f %s %s", path
, dest_path
);
473 if (system(message
->str
) == 0)
475 g_string_sprintf(message
, "+%s", path
);
476 g_string_truncate(message
, leaf
- path
);
478 g_string_sprintf(message
, "'Moved %s as %s\n",
482 g_string_sprintf(message
, "!ERROR: Failed to move %s as %s\n",
487 static void do_link(char *path
, char *dest
)
492 leaf
= strrchr(path
, '/');
494 leaf
= path
; /* Error? */
498 dest_path
= make_path(dest
, leaf
)->str
;
500 if (symlink(path
, dest_path
))
504 g_string_sprintf(message
, "'Symlinked %s as %s\n",
510 /* CHILD MAIN LOOPS */
512 /* After forking, the child calls one of these functions */
514 static void delete_cb(gpointer data
)
516 FilerWindow
*filer_window
= (FilerWindow
*) data
;
517 Collection
*collection
= filer_window
->collection
;
519 int left
= collection
->number_selected
;
525 if (!collection
->items
[i
].selected
)
527 item
= (FileItem
*) collection
->items
[i
].data
;
528 do_delete(make_path(filer_window
->path
, item
->leafname
)->str
);
529 g_string_sprintf(message
, "+%s", filer_window
->path
);
534 g_string_sprintf(message
, "'\nDone\n");
539 static void list_cb(gpointer data
)
541 GSList
*paths
= (GSList
*) data
;
545 action_do_func((char *) paths
->data
, action_dest
);
546 g_string_sprintf(message
, "+%s", action_dest
);
552 g_string_sprintf(message
, "'\nDone\n");
557 /* EXTERNAL INTERFACE */
559 /* Deletes all selected items in the window */
560 void action_delete(FilerWindow
*filer_window
)
563 Collection
*collection
;
565 collection
= window_with_focus
->collection
;
567 if (collection
->number_selected
< 1)
569 report_error("ROX-Filer", "You need to select some items "
574 gui_side
= start_action(filer_window
, delete_cb
);
579 gtk_widget_show_all(gui_side
->window
);
582 void action_copy(GSList
*paths
, char *dest
)
587 action_do_func
= do_copy
;
588 gui_side
= start_action(paths
, list_cb
);
593 gtk_widget_show_all(gui_side
->window
);
596 void action_move(GSList
*paths
, char *dest
)
601 action_do_func
= do_move
;
602 gui_side
= start_action(paths
, list_cb
);
607 gtk_widget_show_all(gui_side
->window
);
610 void action_link(GSList
*paths
, char *dest
)
615 action_do_func
= do_link
;
616 gui_side
= start_action(paths
, list_cb
);
621 gtk_widget_show_all(gui_side
->window
);