/* $Id: delete.c,v 1.15 2007/12/10 01:50:57 khorben Exp $ */ /* Copyright (c) 2007 Pierre Pronchery */ /* This file is part of DeforaOS Desktop Browser */ /* Browser is not free software; you can redistribute it and/or modify it * under the terms of the Creative Commons Attribution-NonCommercial-ShareAlike * 3.0 Unported as published by the Creative Commons organization. * * Browser is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the Creative Commons Attribution-NonCommercial- * ShareAlike 3.0 Unported license for more details. * * You should have received a copy of the Creative Commons Attribution- * NonCommercial-ShareAlike 3.0 along with Browser; if not, browse to * http://creativecommons.org/licenses/by-nc-sa/3.0/ */ #include #include #include #include #include #include #include #include /* types */ typedef int Prefs; #define PREFS_f 0x1 #define PREFS_i 0x2 #define PREFS_R 0x4 /* Delete */ /* types */ typedef struct _Delete { Prefs * prefs; unsigned int filec; char ** filev; unsigned int cur; GtkWidget * window; GtkWidget * label; GtkWidget * progress; } Delete; /* functions */ static void _delete_refresh(Delete * delete); /* callbacks */ static void _delete_on_closex(GtkWidget * widget, GdkEvent * event, gpointer data); static gboolean _delete_idle(gpointer data); static int _delete(Prefs * prefs, int filec, char * filev[]) { static Delete delete; GtkWidget * vbox; GtkWidget * hbox; GtkWidget * widget; delete.prefs = prefs; delete.filec = filec; delete.filev = filev; delete.cur = 0; delete.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(delete.window), "Delete file(s)"); g_signal_connect(G_OBJECT(delete.window), "delete_event", G_CALLBACK( _delete_on_closex), NULL); vbox = gtk_vbox_new(FALSE, 4); delete.label = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(vbox), delete.label, TRUE, TRUE, 4); delete.progress = gtk_progress_bar_new(); gtk_box_pack_start(GTK_BOX(vbox), delete.progress, TRUE, TRUE, 4); hbox = gtk_hbox_new(FALSE, 4); widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL); gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, FALSE, 4); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 4); gtk_container_set_border_width(GTK_CONTAINER(delete.window), 4); gtk_container_add(GTK_CONTAINER(delete.window), vbox); g_idle_add(_delete_idle, &delete); _delete_refresh(&delete); gtk_widget_show_all(delete.window); return 0; } static void _delete_refresh(Delete * delete) { char buf[256]; double fraction; snprintf(buf, sizeof(buf), "Deleting file: %s", delete->filev[delete->cur]); gtk_label_set_text(GTK_LABEL(delete->label), buf); snprintf(buf, sizeof(buf), "File %u of %u", delete->cur, delete->filec); fraction = (double)(delete->cur) / (double)delete->filec; gtk_progress_bar_set_text(GTK_PROGRESS_BAR(delete->progress), buf); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(delete->progress), fraction); } static int _delete_error(Delete * delete, char const * message, int ret) { GtkWidget * dialog; dialog = gtk_message_dialog_new(GTK_WINDOW(delete->window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s: %s", message, strerror(errno)); gtk_window_set_title(GTK_WINDOW(dialog), "Error"); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return ret; } static void _delete_on_closex(GtkWidget * widget, GdkEvent * event, gpointer data) { gtk_main_quit(); } static int _idle_do(Delete * delete, char const * filename); static gboolean _delete_idle(gpointer data) { Delete * delete = data; _idle_do(delete, delete->filev[delete->cur]); delete->cur++; if(delete->cur == delete->filec) { gtk_main_quit(); return FALSE; } _delete_refresh(delete); return TRUE; } static int _idle_ask_recursive(Delete * delete, char const * filename); static int _idle_ask(Delete * delete, char const * filename); static int _idle_do_recursive(Delete * delete, char const * filename); static int _idle_do(Delete * delete, char const * filename) { struct stat st; if(lstat(filename, &st) != 0 && errno == ENOENT) { if(!(*(delete->prefs) & PREFS_f)) return _delete_error(delete, filename, 1); return 0; } if(S_ISDIR(st.st_mode)) { if(!(*(delete->prefs) & PREFS_R)) { errno = EISDIR; return _delete_error(delete, filename, 1); } else if((*(delete->prefs) & PREFS_f) || _idle_ask_recursive(delete, filename) == 0) return _idle_do_recursive(delete, filename); } else if((*(delete->prefs) & PREFS_f) || _idle_ask(delete, filename) == 0) if(unlink(filename) != 0) return _delete_error(delete, filename, 1); return 0; } static int _idle_ask_recursive(Delete * delete, char const * filename) { int ret; GtkWidget * dialog; dialog = gtk_message_dialog_new(GTK_WINDOW(delete->window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s is a directory\nRecursively delete?", filename); gtk_window_set_title(GTK_WINDOW(dialog), "Question"); gtk_dialog_add_button(GTK_DIALOG(dialog), "Yes to all", 1); ret = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if(ret == 1) { *(delete->prefs) = (*(delete->prefs) & ~PREFS_i) | PREFS_f; return 0; } return ret == GTK_RESPONSE_YES ? 0 : 1; } static int _idle_ask(Delete * delete, char const * filename) { int ret; GtkWidget * dialog; dialog = gtk_message_dialog_new(GTK_WINDOW(delete->window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s will be permanently deleted\nContinue?", filename); gtk_window_set_title(GTK_WINDOW(dialog), "Question"); gtk_dialog_add_button(GTK_DIALOG(dialog), "Yes to all", 1); ret = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if(ret == 1) { *(delete->prefs) = (*(delete->prefs) & ~PREFS_i) | PREFS_f; return 0; } return ret == GTK_RESPONSE_YES ? 0 : 1; } /* FIXME does not update or redraw the dialog */ static int _idle_do_recursive(Delete * delete, char const * filename) { int ret = 0; DIR * dir; struct dirent * de; size_t len = strlen(filename) + 2; char * path; char * p; if((dir = opendir(filename)) == NULL) return _delete_error(delete, filename, 1); if((path = malloc(len)) == NULL) { closedir(dir); return _delete_error(delete, filename, 1); } sprintf(path, "%s/", filename); while((de = readdir(dir)) != NULL) { if(de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) continue; if((p = realloc(path, len + strlen(de->d_name))) == NULL) break; path = p; strcpy(&path[len - 1], de->d_name); ret |= _idle_do(delete, path); } free(path); closedir(dir); if(de != NULL) return _delete_error(delete, filename, 1); if(rmdir(filename) != 0) /* FIXME confirm */ return _delete_error(delete, filename, 1); return ret; } /* usage */ static int _usage(void) { fputs("Usage: delete [-fiRr] file...\n\ -f Do not prompt for confirmation or output error messages\n\ -i Prompt for confirmation\n\ -R Remove file hierarchies\n\ -r Equivalent to -R\n", stderr); return 1; } /* main */ int main(int argc, char * argv[]) { Prefs prefs; int o; memset(&prefs, 0, sizeof(prefs)); gtk_init(&argc, &argv); while((o = getopt(argc, argv, "fiRr")) != -1) switch(o) { case 'f': prefs -= prefs & PREFS_i; prefs |= PREFS_f; break; case 'i': prefs -= prefs & PREFS_f; prefs |= PREFS_i; break; case 'R': case 'r': prefs |= PREFS_R; break; default: return _usage(); } if(optind == argc) return _usage(); _delete(&prefs, argc - optind, &argv[optind]); gtk_main(); return 0; }