From a841079f51aefedf04915930f54b15cc5af4cccc Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 28 Jul 1999 15:11:09 +0000 Subject: [PATCH] r33: Changed to using cursor to highlight panel items instead of selecting them. DND with panel items is now partly supported. --- ROX-Filer/src/dnd.c | 184 +++++++++++++++++++++++++++++++++++--------- ROX-Filer/src/filer.c | 38 +++++---- ROX-Filer/src/gui_support.c | 37 ++++++++- ROX-Filer/src/gui_support.h | 2 + ROX-Filer/src/main.c | 2 + ROX-Filer/src/menu.c | 17 +++- ROX-Filer/src/options.c | 18 ++--- ROX-Filer/src/options.h | 2 +- ROX-Filer/src/support.c | 5 ++ ROX-Filer/src/support.h | 1 + 10 files changed, 234 insertions(+), 72 deletions(-) diff --git a/ROX-Filer/src/dnd.c b/ROX-Filer/src/dnd.c index 14f465b2..42b6b976 100644 --- a/ROX-Filer/src/dnd.c +++ b/ROX-Filer/src/dnd.c @@ -40,6 +40,7 @@ GdkAtom text_uri_list; GdkAtom application_octet_stream; /* Static prototypes */ +static char *get_dest_path(FilerWindow *filer_window, GdkDragContext *context); static void create_uri_list(GString *string, Collection *collection, FilerWindow *filer_window); @@ -51,6 +52,13 @@ static gboolean drag_drop(GtkWidget *widget, guint time); static gboolean provides(GdkDragContext *context, GdkAtom target); static void set_xds_prop(GdkDragContext *context, char *text); +static gboolean drag_motion(GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); +static void drag_leave(GtkWidget *widget, + GdkDragContext *context); static void drag_data_received(GtkWidget *widget, GdkDragContext *context, gint x, @@ -83,6 +91,15 @@ void dnd_init() FALSE); } +static char *get_dest_path(FilerWindow *filer_window, GdkDragContext *context) +{ + char *dest_path; + + dest_path = g_dataset_get_data(context, "drop_dest_path"); + + return dest_path ? dest_path : filer_window->path; +} + /* Set the XdndDirectSave0 property on the source window for this context */ static void set_xds_prop(GdkDragContext *context, char *text) { @@ -192,7 +209,7 @@ static GSList *uri_list_to_gslist(char *uri_list) if (!linebreak || linebreak[1] != 10) { - report_error("uri_list_to_gslist", + delayed_error("uri_list_to_gslist", "Incorrect or missing line break " "in text/uri-list data"); return list; @@ -300,7 +317,7 @@ void drag_selection(Collection *collection, context = gtk_drag_begin(widget, target_list, - GDK_ACTION_COPY, + GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK, (event->state & GDK_BUTTON1_MASK) ? 1 : 2, (GdkEvent *) event); g_dataset_set_data(context, "filer_window", filer_window); @@ -359,7 +376,7 @@ void drag_data_get(GtkWidget *widget, g_string_free(string, FALSE); break; default: - report_error("drag_data_get", + delayed_error("drag_data_get", "Internal error - bad info type\n"); break; } @@ -386,7 +403,7 @@ static gboolean load_file(char *pathname, char **data_out, long *length_out) if (!file) { - report_error("Opening file for DND", g_strerror(errno)); + delayed_error("Opening file for DND", g_strerror(errno)); return FALSE; } @@ -401,7 +418,7 @@ static gboolean load_file(char *pathname, char **data_out, long *length_out) if (ferror(file)) { - report_error("Loading file for DND", + delayed_error("Loading file for DND", g_strerror(errno)); g_free(buffer); } @@ -413,7 +430,7 @@ static gboolean load_file(char *pathname, char **data_out, long *length_out) } } else - report_error("Loading file for DND", + delayed_error("Loading file for DND", "Can't allocate memory for buffer to " "transfer this file"); @@ -437,18 +454,82 @@ void drag_set_dest(GtkWidget *widget, FilerWindow *filer_window) }; gtk_drag_dest_set(widget, - GTK_DEST_DEFAULT_MOTION, + 0, /* GTK_DEST_DEFAULT_MOTION, */ target_table, sizeof(target_table) / sizeof(*target_table), GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE); + gtk_signal_connect(GTK_OBJECT(widget), "drag_motion", + GTK_SIGNAL_FUNC(drag_motion), filer_window); + gtk_signal_connect(GTK_OBJECT(widget), "drag_leave", + GTK_SIGNAL_FUNC(drag_leave), filer_window); gtk_signal_connect(GTK_OBJECT(widget), "drag_drop", GTK_SIGNAL_FUNC(drag_drop), filer_window); gtk_signal_connect(GTK_OBJECT(widget), "drag_data_received", GTK_SIGNAL_FUNC(drag_data_received), filer_window); } +/* Called during the drag when the mouse is in a widget registered + * as a drop target. Returns TRUE if we can accept the drop. + */ +static gboolean drag_motion(GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + FilerWindow *filer_window; + int item; + + filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window"); + g_return_val_if_fail(filer_window != NULL, TRUE); + + if (gtk_drag_get_source_widget(context) == widget) + { + /* Ignore drags within a single window */ + return FALSE; + } + + if (filer_window->panel == FALSE) + { + gdk_drag_status(context, context->suggested_action, time); + return TRUE; + } + + item = collection_get_item(filer_window->collection, x, y); + + if (item != filer_window->collection->cursor_item && item != -1) + { + FileItem *fileitem = (FileItem *) + filer_window->collection->items[item].data; + + panel_set_timeout(NULL, 0); + + g_dataset_set_data_full(context, "drop_dest_path", + g_strdup(make_path(filer_window->path, + fileitem->leafname)->str), + g_free); + } + collection_set_cursor_item(filer_window->collection, item); + + gdk_drag_status(context, context->suggested_action, time); + return TRUE; +} + +/* Remove panel highlights */ +static void drag_leave(GtkWidget *widget, + GdkDragContext *context) +{ + FilerWindow *filer_window; + + filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window"); + g_return_if_fail(filer_window != NULL); + + panel_set_timeout(NULL, 0); + collection_set_cursor_item(filer_window->collection, -1); +} + /* User has tried to drop some data on us. Decide what format we would * like the data in. */ @@ -462,6 +543,7 @@ static gboolean drag_drop(GtkWidget *widget, char *leafname = NULL; FilerWindow *filer_window; GdkAtom target; + char *dest_path; filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window"); g_return_val_if_fail(filer_window != NULL, TRUE); @@ -473,7 +555,14 @@ static gboolean drag_drop(GtkWidget *widget, return TRUE; } - if (provides(context, XdndDirectSave0)) + dest_path = g_dataset_get_data(context, "drop_dest_path"); + + if (((!dest_path) && filer_window->panel) + || !((dest_path = filer_window->path))) + { + error = "Bad drop on panel"; + } + else if (provides(context, XdndDirectSave0)) { leafname = get_xds_prop(context); if (leafname) @@ -493,7 +582,7 @@ static gboolean drag_drop(GtkWidget *widget, uri = g_string_new(NULL); g_string_sprintf(uri, "file://%s%s", our_host_name(), - make_path(filer_window->path, + make_path(dest_path, leafname)->str); set_xds_prop(context, uri->str); g_string_free(uri, TRUE); @@ -518,7 +607,7 @@ static gboolean drag_drop(GtkWidget *widget, { gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */ - report_error("ROX-Filer", error); + delayed_error("ROX-Filer", error); } else gtk_drag_get_data(widget, context, target, time); @@ -558,7 +647,7 @@ static void drag_data_received(GtkWidget *widget, break; default: gtk_drag_finish(context, FALSE, FALSE, time); - report_error("drag_data_received", "Unknown target"); + delayed_error("drag_data_received", "Unknown target"); break; } } @@ -620,7 +709,7 @@ static void got_data_xds_reply(GtkWidget *widget, } if (error) - report_error("ROX-Filer", error); + delayed_error("ROX-Filer", error); } static void got_data_raw(GtkWidget *widget, @@ -632,18 +721,21 @@ static void got_data_raw(GtkWidget *widget, char *leafname; int fd; char *error = NULL; + char *dest_path; filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window"); g_return_if_fail(filer_window != NULL); leafname = g_dataset_get_data(context, "leafname"); - if (!leafname) leafname = "UntitledData"; + + dest_path = get_dest_path(filer_window, context); - fd = open(make_path(filer_window->path, leafname)->str, + fd = open(make_path(dest_path, leafname)->str, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, - S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH); + S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH); if (fd == -1) error = g_strerror(errno); @@ -665,7 +757,7 @@ static void got_data_raw(GtkWidget *widget, if (provides(context, XdndDirectSave0)) set_xds_prop(context, ""); gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */ - report_error("Error saving file", error); + delayed_error("Error saving file", error); } else gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */ @@ -687,17 +779,17 @@ static void got_uri_list(GtkWidget *widget, char **argv = NULL; /* Command to exec, or NULL */ GSList *next_uri; gboolean send_reply = TRUE; - + char *dest_path; + filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window"); g_return_if_fail(filer_window != NULL); + dest_path = get_dest_path(filer_window, context); + uri_list = uri_list_to_gslist(selection_data->data); if (!uri_list) - { - /* No URIs in the list! */ error = "No URIs in the text/uri-list (nothing to do!)"; - } else if ((!uri_list->next) && (!get_local_path(uri_list->data))) { /* There is one URI in the list, and it's not on the local @@ -725,16 +817,31 @@ static void got_uri_list(GtkWidget *widget, else { int local_files = 0; - const char *start_args[] = {"xterm", "-wf", - "-e", "cp", "-Riva"}; + const char *start_args[] = {"xterm", "-wf", "-e"}; int argc = sizeof(start_args) / sizeof(char *); next_uri = uri_list; argv = g_malloc(sizeof(start_args) + - sizeof(char *) * (g_slist_length(uri_list) + 2)); + sizeof(char *) * (g_slist_length(uri_list) + 4)); memcpy(argv, start_args, sizeof(start_args)); + if (context->action == GDK_ACTION_MOVE) + { + argv[argc++] = "mv"; + argv[argc++] = "-iv"; + } + else if (context->action == GDK_ACTION_LINK) + { + argv[argc++] = "ln"; + argv[argc++] = "-vis"; + } + else + { + argv[argc++] = "cp"; + argv[argc++] = "-Riva"; + } + /* Either one local URI, or a list. If anything in the list * isn't local then we are stuck. */ @@ -768,7 +875,7 @@ static void got_uri_list(GtkWidget *widget, } else { - argv[argc++] = filer_window->path; + argv[argc++] = dest_path; argv[argc++] = NULL; } } @@ -776,23 +883,24 @@ static void got_uri_list(GtkWidget *widget, if (error) { gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */ - report_error("Error getting file list", error); + delayed_error("Error getting file list", error); } else if (send_reply) - gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */ - - if (argv) { - int child; /* Child process ID */ - - child = spawn(argv); - if (child) - g_hash_table_insert(child_to_filer, - (gpointer) child, filer_window); - else - report_error("ROX-Filer", "Failed to fork() child " - "process"); - g_free(argv); + gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */ + if (argv) + { + int child; /* Child process ID */ + + child = spawn(argv); + if (child) + g_hash_table_insert(child_to_filer, + (gpointer) child, filer_window); + else + delayed_error("ROX-Filer", + "Failed to fork() child process"); + g_free(argv); + } } next_uri = uri_list; diff --git a/ROX-Filer/src/filer.c b/ROX-Filer/src/filer.c index 52c29aa4..8462f8da 100644 --- a/ROX-Filer/src/filer.c +++ b/ROX-Filer/src/filer.c @@ -30,6 +30,7 @@ #include "mount.h" #define MAX_ICON_HEIGHT 42 +#define PANEL_BORDER 2 FilerWindow *window_with_focus = NULL; @@ -282,20 +283,20 @@ static void add_item(FilerWindow *filer_window, char *leafname) static gboolean test_point(Collection *collection, int point_x, int point_y, - CollectionItem *item, + CollectionItem *colitem, int width, int height) { - FileItem *fileitem = (FileItem *) item->data; + FileItem *item = (FileItem *) colitem->data; GdkFont *font = GTK_WIDGET(collection)->style->font; int text_height = font->ascent + font->descent; int x_off = ABS(point_x - (width >> 1)); + int image_y = MAX(0, MAX_ICON_HEIGHT - item->pix_height); - if (x_off <= (fileitem->pix_width >> 1) + 2 && - point_y < height - text_height - 2 && - point_y > 6) + if (x_off <= (item->pix_width >> 1) + 2 && + point_y >= image_y && point_y <= image_y + item->pix_height) return TRUE; - if (x_off <= (fileitem->text_width >> 1) + 2 && + if (x_off <= (item->text_width >> 1) + 2 && point_y > height - text_height - 2) return TRUE; @@ -438,15 +439,14 @@ static int sort_by_name(const void *item1, const void *item2) static gint clear_panel_hilight(gpointer data) { - collection_clear_selection(panel_with_timeout->collection); + collection_set_cursor_item(panel_with_timeout->collection, -1); panel_with_timeout = NULL; return FALSE; } /* It is possible to highlight an item briefly on a panel by calling this - * function. If anything happens to the selection state of the panel you - * should call this with filer_window set to NULL to remove the timeout. + * function. */ void panel_set_timeout(FilerWindow *filer_window, gulong msec) { @@ -461,7 +461,7 @@ void panel_set_timeout(FilerWindow *filer_window, gulong msec) { panel_with_timeout = filer_window; panel_timeout = gtk_timeout_add(msec, - clear_panel_hilight, NULL); + clear_panel_hilight, NULL); } } @@ -480,9 +480,9 @@ void open_item(Collection *collection, if (filer_window->panel) { panel_set_timeout(NULL, 0); - collection_select_item(collection, item_number); + collection_set_cursor_item(collection, item_number); gdk_flush(); - panel_set_timeout(filer_window, 300); + panel_set_timeout(filer_window, 200); } switch (item->base_type) @@ -649,16 +649,22 @@ void filer_opendir(char *path, gboolean panel, Side panel_side) if (panel_side == TOP || panel_side == BOTTOM) { - int y = panel_side == TOP ? 0 : sheight - iheight; + int height = iheight + PANEL_BORDER; + int y = panel_side == TOP + ? -PANEL_BORDER + : sheight - height - PANEL_BORDER; - gtk_widget_set_usize(collection, swidth, iheight); + gtk_widget_set_usize(collection, swidth, height); gtk_widget_set_uposition(win, 0, y); } else { - int x = panel_side == LEFT ? 0 : swidth - iwidth; + int width = iwidth + PANEL_BORDER; + int x = panel_side == LEFT + ? -PANEL_BORDER + : swidth - width - PANEL_BORDER; - gtk_widget_set_usize(collection, iwidth, sheight); + gtk_widget_set_usize(collection, width, sheight); gtk_widget_set_uposition(win, x, 0); } diff --git a/ROX-Filer/src/gui_support.c b/ROX-Filer/src/gui_support.c index 110729ef..1ea335d2 100644 --- a/ROX-Filer/src/gui_support.c +++ b/ROX-Filer/src/gui_support.c @@ -61,8 +61,6 @@ int get_choice(char *title, va_list ap; int choice_return; - va_start(ap, number_of_buttons); - dialog = gtk_window_new(GTK_WINDOW_DIALOG); gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); gtk_window_set_title(GTK_WINDOW(dialog), title); @@ -88,6 +86,8 @@ int get_choice(char *title, text_container, TRUE, TRUE, 0); + va_start(ap, number_of_buttons); + for (i = 0; i < number_of_buttons; i++) { button = gtk_button_new_with_label(va_arg(ap, char *)); @@ -159,3 +159,36 @@ void make_panel_window(GdkWindow *window) WIN_STATE_STICKY | WIN_STATE_HIDDEN | WIN_STATE_FIXED_POSITION | WIN_STATE_ARRANGE_IGNORE); } + +gint hide_dialog_event(GtkWidget *widget, GdkEvent *event, gpointer window) +{ + gtk_widget_hide((GtkWidget *) window); + + return TRUE; +} + +static gboolean error_idle_cb(gpointer data) +{ + char **error = (char **) data; + + report_error(error[0], error[1]); + g_free(error[0]); + g_free(error[1]); + error[0] = error[1] = NULL; + + return FALSE; +} + +/* Display an error next time we are idle */ +void delayed_error(char *title, char *error) +{ + static char *delayed_error_data[2] = {NULL, NULL}; + + g_return_if_fail(error != NULL); + g_return_if_fail(delayed_error_data[1] == NULL); + + delayed_error_data[0] = g_strdup(title); + delayed_error_data[1] = g_strdup(error); + + gtk_idle_add(error_idle_cb, delayed_error_data); +} diff --git a/ROX-Filer/src/gui_support.h b/ROX-Filer/src/gui_support.h index 92bad549..d26a1cc8 100644 --- a/ROX-Filer/src/gui_support.h +++ b/ROX-Filer/src/gui_support.h @@ -22,5 +22,7 @@ int get_choice(char *title, void report_error(char *title, char *message); void set_cardinal_property(GdkWindow *window, GdkAtom prop, guint32 value); void make_panel_window(GdkWindow *window); +gint hide_dialog_event(GtkWidget *widget, GdkEvent *event, gpointer window); +void delayed_error(char *title, char *error); #endif /* _GUI_SUPPORT_H */ diff --git a/ROX-Filer/src/main.c b/ROX-Filer/src/main.c index cacaf165..c39a0753 100644 --- a/ROX-Filer/src/main.c +++ b/ROX-Filer/src/main.c @@ -20,6 +20,7 @@ #include "dnd.h" #include "options.h" #include "choices.h" +#include "newdir.h" /* XXX: Maybe we shouldn't do so much work in a signal handler? */ static void child_died(int signum) @@ -61,6 +62,7 @@ int main(int argc, char **argv) filer_init(); mount_init(); options_init(); + newdir_init(); signal(SIGCHLD, child_died); diff --git a/ROX-Filer/src/menu.c b/ROX-Filer/src/menu.c index 98a3a6c2..9ac45203 100644 --- a/ROX-Filer/src/menu.c +++ b/ROX-Filer/src/menu.c @@ -17,6 +17,7 @@ #include "gui_support.h" #include "options.h" #include "choices.h" +#include "newdir.h" #define C_ "" @@ -33,6 +34,7 @@ static void refresh(gpointer data, guint action, GtkWidget *widget); static void mount(gpointer data, guint action, GtkWidget *widget); static void delete(gpointer data, guint action, GtkWidget *widget); static void show_options(gpointer data, guint action, GtkWidget *widget); +static void new_directory(gpointer data, guint action, GtkWidget *widget); static void xterm_here(gpointer data, guint action, GtkWidget *widget); static void open_parent(gpointer data, guint action, GtkWidget *widget); @@ -70,7 +72,7 @@ static GtkItemFactoryEntry filer_menu_def[] = { {"/Select All", C_"A", NULL, 0, NULL}, {"/Clear Selection", C_"Z", NULL, 0, NULL}, {"/Options...", NULL, show_options, 0, NULL}, -{"/New directory", NULL, NULL, 0, NULL}, +{"/New directory", NULL, new_directory, 0, NULL}, {"/Xterm here", NULL, xterm_here, 0, NULL}, {"/Open parent", NULL, open_parent, 0, NULL}, }; @@ -86,8 +88,8 @@ static GtkItemFactoryEntry panel_menu_def[] = { {"/Display/Sort by Date", NULL, NULL, 0, "/Display/Sort by Name"}, {"/Display/Sort by Size", NULL, NULL, 0, "/Display/Sort by Name"}, {"/Display/Separator", NULL, NULL, 0, ""}, -{"/Display/Show Hidden", C_"H", NULL, 0, ""}, -{"/Display/Refresh", C_"L", refresh, 0, NULL}, +{"/Display/Show Hidden", NULL, NULL, 0, ""}, +{"/Display/Refresh", NULL, refresh, 0, NULL}, {"/File", NULL, NULL, 0, ""}, {"/File/Delete", NULL, NULL, 0, NULL}, {"/Open as directory", NULL, open_as_dir, 0, NULL}, @@ -318,7 +320,14 @@ static void show_options(gpointer data, guint action, GtkWidget *widget) { g_return_if_fail(window_with_focus != NULL); - options_edit(window_with_focus); + options_show(window_with_focus); +} + +static void new_directory(gpointer data, guint action, GtkWidget *widget) +{ + g_return_if_fail(window_with_focus != NULL); + + newdir_show(window_with_focus); } static void xterm_here(gpointer data, guint action, GtkWidget *widget) diff --git a/ROX-Filer/src/options.c b/ROX-Filer/src/options.c index f38cce58..d447cb77 100644 --- a/ROX-Filer/src/options.c +++ b/ROX-Filer/src/options.c @@ -10,12 +10,11 @@ #include #include +#include "gui_support.h" #include "options.h" static GtkWidget *window; -static gint hide_options(GtkWidget *options, gpointer data); - void options_init() { GtkWidget *tl_vbox, *scrolled_area, *vbox; @@ -25,7 +24,7 @@ void options_init() window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "ROX-Filer options"); gtk_signal_connect(GTK_OBJECT(window), "delete_event", - GTK_SIGNAL_FUNC(hide_options), NULL); + GTK_SIGNAL_FUNC(hide_dialog_event), window); gtk_container_set_border_width(GTK_CONTAINER(window), 4); gtk_window_set_default_size(GTK_WINDOW(window), 400, 400); @@ -60,16 +59,13 @@ void options_init() button = gtk_button_new_with_label("Cancel"); gtk_box_pack_start(GTK_BOX(actions), button, FALSE, TRUE, 0); + gtk_signal_connect_object(GTK_OBJECT(button), "clicked", + GTK_SIGNAL_FUNC(gtk_widget_hide), GTK_OBJECT(window)); } -static gint hide_options(GtkWidget *options, gpointer data) -{ - gtk_widget_hide(window); - - return TRUE; -} - -void options_edit(FilerWindow *filer_window) +void options_show(FilerWindow *filer_window) { + if (GTK_WIDGET_MAPPED(window)) + gtk_widget_hide(window); gtk_widget_show_all(window); } diff --git a/ROX-Filer/src/options.h b/ROX-Filer/src/options.h index dccc8ed3..12653914 100644 --- a/ROX-Filer/src/options.h +++ b/ROX-Filer/src/options.h @@ -13,6 +13,6 @@ /* Prototypes */ void options_init(void); -void options_edit(FilerWindow *filer_window); +void options_show(FilerWindow *filer_window); #endif /* _OPTIONS_H */ diff --git a/ROX-Filer/src/support.c b/ROX-Filer/src/support.c index 23569038..413405b3 100644 --- a/ROX-Filer/src/support.c +++ b/ROX-Filer/src/support.c @@ -108,3 +108,8 @@ int spawn_in(char **argv, char *dir) return child; } +void debug_free_string(void *data) +{ + g_print("Freeing string '%s'\n", (char *) data); + g_free(data); +} diff --git a/ROX-Filer/src/support.h b/ROX-Filer/src/support.h index ba2aae84..609d7a72 100644 --- a/ROX-Filer/src/support.h +++ b/ROX-Filer/src/support.h @@ -15,5 +15,6 @@ GString *make_path(char *dir, char *leaf); char *our_host_name(); int spawn(char **argv); int spawn_in(char **argv, char *dir); +void debug_free_string(void *data); #endif /* _SUPPORT_H */ -- 2.11.4.GIT