disktidy: handles user abort properly.
[maemo-rb.git] / apps / plugins / disktidy.c
blob23bf0d18c94f65ac88ce8acefc6582b8aaabe036
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 David Dent
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include "errno.h"
25 static int removed = 0; /* number of items removed */
27 /* function return values */
28 enum tidy_return
30 TIDY_RETURN_OK = 0,
31 TIDY_RETURN_ERROR = 1,
32 TIDY_RETURN_USB = 2,
33 TIDY_RETURN_ABORT = 3,
36 #define MAX_TYPES 64
37 struct tidy_type {
38 char filestring[64];
39 int pre;
40 int post;
41 bool directory;
42 bool remove;
43 } tidy_types[MAX_TYPES];
44 int tidy_type_count;
45 bool tidy_loaded_and_changed = false;
47 #define DEFAULT_FILES PLUGIN_APPS_DIR "/disktidy.config"
48 #define CUSTOM_FILES PLUGIN_APPS_DIR "/disktidy_custom.config"
49 void add_item(const char* name, int index)
51 char *a;
52 rb->strcpy(tidy_types[index].filestring, name);
53 if (name[rb->strlen(name)-1] == '/')
55 tidy_types[index].directory = true;
56 tidy_types[index].filestring[rb->strlen(name)-1] = '\0';
58 else
59 tidy_types[index].directory = false;
60 a = rb->strchr(name, '*');
61 if (a)
63 tidy_types[index].pre = a - name;
64 tidy_types[index].post = rb->strlen(a+1);
66 else
68 tidy_types[index].pre = -1;
69 tidy_types[index].post = -1;
72 static int find_file_string(const char *file, char *last_group)
74 char temp[MAX_PATH];
75 int i = 0, idx_last_group = -1;
76 bool folder = false;
77 rb->strcpy(temp, file);
78 if (temp[rb->strlen(temp)-1] == '/')
80 folder = true;
81 temp[rb->strlen(temp)-1] = '\0';
83 while (i<tidy_type_count)
85 if (!rb->strcmp(tidy_types[i].filestring, temp) &&
86 folder == tidy_types[i].directory)
87 return i;
88 else if (!rb->strcmp(tidy_types[i].filestring, last_group))
89 idx_last_group = i;
90 i++;
92 /* not found, so insert it into its group */
93 if (file[0] != '<' && idx_last_group != -1)
95 for (i=idx_last_group; i<tidy_type_count; i++)
97 if (tidy_types[i].filestring[0] == '<')
99 idx_last_group = i;
100 break;
103 /* shift items up one */
104 for (i=tidy_type_count;i>idx_last_group;i--)
106 rb->memcpy(&tidy_types[i], &tidy_types[i-1], sizeof(struct tidy_type));
108 tidy_type_count++;
109 add_item(file, idx_last_group+1);
110 return idx_last_group+1;
112 return i;
115 bool tidy_load_file(const char* file)
117 int fd = rb->open(file, O_RDONLY), i;
118 char buf[MAX_PATH], *str, *remove;
119 char last_group[MAX_PATH] = "";
120 bool new;
121 if (fd < 0)
122 return false;
123 while ((tidy_type_count < MAX_TYPES) &&
124 rb->read_line(fd, buf, MAX_PATH))
126 if (rb->settings_parseline(buf, &str, &remove))
128 i = find_file_string(str, last_group);
129 new = (i >= tidy_type_count);
130 if (!rb->strcmp(remove, "yes"))
131 tidy_types[i].remove = true;
132 else tidy_types[i].remove = false;
133 if (new)
135 i = tidy_type_count;
136 add_item(str, i);
137 tidy_type_count++;
139 if (str[0] == '<')
140 rb->strcpy(last_group, str);
143 rb->close(fd);
144 return true;
147 static bool match(struct tidy_type *tidy_type, char *string, int len)
149 char *pattern = tidy_type->filestring;
150 if (tidy_type->pre < 0)
152 /* no '*', just compare. */
153 return (rb->strcmp(pattern, string) == 0);
155 /* pattern is too long for the string. avoid 'ab*bc' matching 'abc'. */
156 if (len < tidy_type->pre + tidy_type->post)
157 return false;
158 /* pattern has '*', compare former part of '*' to the begining of
159 the string and compare next part of '*' to the end of string. */
160 return (rb->strncmp(pattern, string, tidy_type->pre) == 0 &&
161 rb->strcmp(pattern + tidy_type->pre + 1,
162 string + len - tidy_type->post) == 0);
165 bool tidy_remove_item(char *item, int attr)
167 int i;
168 int len;
169 bool ret = false;
170 len = rb->strlen(item);
171 for (i=0; ret == false && i < tidy_type_count; i++)
173 if (match(&tidy_types[i], item, len))
175 if (!tidy_types[i].remove)
176 return false;
177 if (attr&ATTR_DIRECTORY)
178 ret = tidy_types[i].directory;
179 else ret = true;
182 return ret;
185 void tidy_lcd_status(const char *name)
187 /* display status text */
188 rb->lcd_clear_display();
189 rb->lcd_puts(0, 0, "Working ...");
190 rb->lcd_puts(0, 1, name);
191 #ifdef HAVE_LCD_BITMAP
192 rb->lcd_putsf(0, 2, "Cleaned up %d items", removed);
193 #endif
194 rb->lcd_update();
197 int tidy_path_append_entry(char *path, struct dirent *entry, int *path_length)
199 int name_len = rb->strlen(entry->d_name);
200 /* for the special case of path="/" this is one bigger but it's not a problem */
201 int new_length = *path_length + name_len + 1;
203 /* check overflow (keep space for trailing zero) */
204 if(new_length >= MAX_PATH)
205 return 0;
207 /* special case for path <> "/" */
208 if(rb->strcmp(path, "/") != 0)
210 rb->strcat(path + *path_length, "/");
211 (*path_length)++;
213 /* strcat is unsafe but the previous check normally avoid any problem */
214 /* use path_length to optimise */
216 rb->strcat(path + *path_length, entry->d_name);
217 *path_length += name_len;
219 return 1;
222 void tidy_path_remove_entry(char *path, int old_path_length, int *path_length)
224 path[old_path_length] = '\0';
225 *path_length = old_path_length;
228 /* path is assumed to be array of size MAX_PATH */
229 enum tidy_return tidy_removedir(char *path, int *path_length)
231 /* delete directory */
232 struct dirent *entry;
233 enum tidy_return status = TIDY_RETURN_OK;
234 int button;
235 DIR *dir;
236 int old_path_length = *path_length;
238 /* display status text */
239 tidy_lcd_status(path);
241 rb->yield();
243 dir = rb->opendir(path);
244 if (dir)
246 while((status == TIDY_RETURN_OK) && ((entry = rb->readdir(dir)) != 0))
247 /* walk directory */
249 /* check for user input and usb connect */
250 button = rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
251 if (button == ACTION_STD_CANCEL)
253 rb->closedir(dir);
254 return TIDY_RETURN_ABORT;
256 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
258 rb->closedir(dir);
259 return TIDY_RETURN_USB;
262 rb->yield();
264 /* get absolute path */
265 /* returns an error if path is too long */
266 if(!tidy_path_append_entry(path, entry, path_length))
267 /* silent error */
268 continue;
270 struct dirinfo info = rb->dir_get_info(dir, entry);
271 if (info.attribute & ATTR_DIRECTORY)
273 /* dir ignore "." and ".." */
274 if ((rb->strcmp(entry->d_name, ".") != 0) && \
275 (rb->strcmp(entry->d_name, "..") != 0))
277 status = tidy_removedir(path, path_length);
280 else
282 /* file */
283 removed++;
284 rb->remove(path);
287 /* restore path */
288 tidy_path_remove_entry(path, old_path_length, path_length);
290 rb->closedir(dir);
291 /* rmdir */
292 removed++;
293 rb->rmdir(path);
295 else
297 status = TIDY_RETURN_ERROR;
299 return status;
302 /* path is assumed to be array of size MAX_PATH */
303 enum tidy_return tidy_clean(char *path, int *path_length)
305 /* deletes junk files and dirs left by system */
306 struct dirent *entry;
307 enum tidy_return status = TIDY_RETURN_OK;
308 int button;
309 DIR *dir;
310 int old_path_length = *path_length;
312 /* display status text */
313 tidy_lcd_status(path);
315 rb->yield();
317 dir = rb->opendir(path);
318 if (dir)
320 while((status == TIDY_RETURN_OK) && ((entry = rb->readdir(dir)) != 0))
321 /* walk directory */
323 struct dirinfo info = rb->dir_get_info(dir, entry);
324 /* check for user input and usb connect */
325 button = rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
326 if (button == ACTION_STD_CANCEL)
328 rb->closedir(dir);
329 return TIDY_RETURN_ABORT;
331 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
333 rb->closedir(dir);
334 return TIDY_RETURN_USB;
337 rb->yield();
339 if (info.attribute & ATTR_DIRECTORY)
341 /* directory ignore "." and ".." */
342 if ((rb->strcmp(entry->d_name, ".") != 0) && \
343 (rb->strcmp(entry->d_name, "..") != 0))
345 /* get absolute path */
346 /* returns an error if path is too long */
347 if(!tidy_path_append_entry(path, entry, path_length))
348 /* silent error */
349 continue;
351 if (tidy_remove_item(entry->d_name, info.attribute))
353 /* delete dir */
354 status = tidy_removedir(path, path_length);
356 else
358 /* dir not deleted so clean it */
359 status = tidy_clean(path, path_length);
362 /* restore path */
363 tidy_path_remove_entry(path, old_path_length, path_length);
366 else
368 /* file */
369 if (tidy_remove_item(entry->d_name, info.attribute))
371 /* get absolute path */
372 /* returns an error if path is too long */
373 if(!tidy_path_append_entry(path, entry, path_length))
374 /* silent error */
375 continue;
377 removed++; /* increment removed files counter */
378 /* delete file */
379 rb->remove(path);
381 /* restore path */
382 tidy_path_remove_entry(path, old_path_length, path_length);
386 rb->closedir(dir);
387 return status;
389 else
391 return TIDY_RETURN_ERROR;
395 enum tidy_return tidy_do(void)
397 /* clean disk and display num of items removed */
398 enum tidy_return status;
399 char path[MAX_PATH];
400 int path_length;
402 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
403 rb->cpu_boost(true);
404 #endif
406 rb->strcpy(path, "/");
407 path_length = rb->strlen(path);
408 status = tidy_clean(path, &path_length);
410 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
411 rb->cpu_boost(false);
412 #endif
414 if ((status == TIDY_RETURN_OK) || (status == TIDY_RETURN_ABORT))
416 rb->lcd_clear_display();
417 if (status == TIDY_RETURN_ABORT)
419 rb->splash(HZ, "User aborted");
420 rb->lcd_clear_display();
422 rb->splashf(HZ*2, "Cleaned up %d items", removed);
424 return status;
427 enum themable_icons get_icon(int item, void * data)
429 (void)data;
430 if (tidy_types[item].filestring[0] == '<') /* special type */
431 return Icon_Folder;
432 else if (tidy_types[item].remove)
433 return Icon_Cursor;
434 else
435 return Icon_NOICON;
438 static const char* get_name(int selected_item, void * data,
439 char * buffer, size_t buffer_len)
441 (void)data;
442 if (tidy_types[selected_item].directory)
444 rb->snprintf(buffer, buffer_len, "%s/",
445 tidy_types[selected_item].filestring);
446 return buffer;
448 return tidy_types[selected_item].filestring;
451 int list_action_callback(int action, struct gui_synclist *lists)
453 if (action == ACTION_STD_OK)
455 int selection = rb->gui_synclist_get_sel_pos(lists);
456 if (tidy_types[selection].filestring[0] == '<')
458 int i;
459 if (!rb->strcmp(tidy_types[selection].filestring, "< ALL >"))
461 for (i=0; i<tidy_type_count; i++)
463 if (tidy_types[i].filestring[0] != '<')
464 tidy_types[i].remove = true;
467 else if (!rb->strcmp(tidy_types[selection].filestring, "< NONE >"))
469 for (i=0; i<tidy_type_count; i++)
471 if (tidy_types[i].filestring[0] != '<')
472 tidy_types[i].remove = false;
475 else /* toggle all untill the next <> */
477 selection++;
478 while (selection < tidy_type_count &&
479 tidy_types[selection].filestring[0] != '<')
481 tidy_types[selection].remove = !tidy_types[selection].remove;
482 selection++;
486 else
487 tidy_types[selection].remove = !tidy_types[selection].remove;
488 tidy_loaded_and_changed = true;
489 return ACTION_REDRAW;
491 return action;
494 enum tidy_return tidy_lcd_menu(void)
496 int selection = 0;
497 enum tidy_return status = TIDY_RETURN_OK;
498 bool menu_quit = false;
500 MENUITEM_STRINGLIST(menu, "Disktidy Menu", NULL,
501 "Start Cleaning", "Files to Clean",
502 "Quit");
504 while (!menu_quit)
506 switch(rb->do_menu(&menu, &selection, NULL, false))
508 case 0:
509 menu_quit = true; /* start cleaning */
510 break;
512 case 1:
514 struct simplelist_info list;
515 rb->simplelist_info_init(&list, "Files to Clean",
516 tidy_type_count, NULL);
517 list.get_icon = get_icon;
518 list.get_name = get_name;
519 list.action_callback = list_action_callback;
520 rb->simplelist_show_list(&list);
522 break;
524 default:
525 status = TIDY_RETURN_ABORT; /* exit plugin */
526 menu_quit = true;
527 break;
530 return status;
533 /* this is the plugin entry point */
534 enum plugin_status plugin_start(const void* parameter)
536 enum tidy_return status;
537 (void)parameter;
539 tidy_type_count = 0;
540 tidy_load_file(DEFAULT_FILES);
541 tidy_load_file(CUSTOM_FILES);
542 if (tidy_type_count == 0)
544 rb->splash(3*HZ, "Missing disktidy.config file");
545 return PLUGIN_ERROR;
547 status = tidy_lcd_menu();
548 if (tidy_loaded_and_changed)
550 int fd = rb->creat(CUSTOM_FILES, 0666);
551 int i;
552 if (fd >= 0)
554 for(i=0;i<tidy_type_count;i++)
556 rb->fdprintf(fd, "%s%c: %s\n", tidy_types[i].filestring,
557 tidy_types[i].directory?'/':'\0',
558 tidy_types[i].remove?"yes":"no");
560 rb->close(fd);
563 if (status == TIDY_RETURN_ABORT)
564 return PLUGIN_OK;
565 while (true)
567 status = tidy_do();
569 switch (status)
571 case TIDY_RETURN_OK:
572 return PLUGIN_OK;
573 case TIDY_RETURN_ERROR:
574 return PLUGIN_ERROR;
575 case TIDY_RETURN_USB:
576 return PLUGIN_USB_CONNECTED;
577 case TIDY_RETURN_ABORT:
578 return PLUGIN_OK;
582 if (rb->default_event_handler(rb->button_get(false)) == SYS_USB_CONNECTED)
583 return PLUGIN_USB_CONNECTED;
585 rb->yield();
587 return PLUGIN_OK;