FS#10738: fix disktidy plugin crash when dealing with very long file/directory names.
[kugel-rb.git] / apps / plugins / disktidy.c
blob3d2685f4ad08250879b8a9cf47633d73cd25bfe2
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"
24 PLUGIN_HEADER
26 /* function return values */
27 enum tidy_return
29 TIDY_RETURN_OK = 0,
30 TIDY_RETURN_ERROR = 1,
31 TIDY_RETURN_USB = 2,
32 TIDY_RETURN_ABORT = 3,
35 #define MAX_TYPES 64
36 struct tidy_type {
37 char filestring[64];
38 bool directory;
39 bool remove;
40 } tidy_types[MAX_TYPES];
41 int tidy_type_count;
42 bool tidy_loaded_and_changed = false;
44 #define DEFAULT_FILES PLUGIN_APPS_DIR "/disktidy.config"
45 #define CUSTOM_FILES PLUGIN_APPS_DIR "/disktidy_custom.config"
46 void add_item(const char* name, int index)
48 rb->strcpy(tidy_types[index].filestring, name);
49 if (name[rb->strlen(name)-1] == '/')
51 tidy_types[index].directory = true;
52 tidy_types[index].filestring[rb->strlen(name)-1] = '\0';
54 else
55 tidy_types[index].directory = false;
57 static int find_file_string(const char *file, char *last_group)
59 char temp[MAX_PATH];
60 int i = 0, idx_last_group = -1;
61 bool folder = false;
62 rb->strcpy(temp, file);
63 if (temp[rb->strlen(temp)-1] == '/')
65 folder = true;
66 temp[rb->strlen(temp)-1] = '\0';
68 while (i<tidy_type_count)
70 if (!rb->strcmp(tidy_types[i].filestring, temp) &&
71 folder == tidy_types[i].directory)
72 return i;
73 else if (!rb->strcmp(tidy_types[i].filestring, last_group))
74 idx_last_group = i;
75 i++;
77 /* not found, so insert it into its group */
78 if (file[0] != '<' && idx_last_group != -1)
80 for (i=idx_last_group; i<tidy_type_count; i++)
82 if (tidy_types[i].filestring[0] == '<')
84 idx_last_group = i;
85 break;
88 /* shift items up one */
89 for (i=tidy_type_count;i>idx_last_group;i--)
91 rb->strcpy(tidy_types[i].filestring, tidy_types[i-1].filestring);
92 tidy_types[i].directory = tidy_types[i-1].directory;
93 tidy_types[i].remove = tidy_types[i-1].remove;
95 tidy_type_count++;
96 add_item(file, idx_last_group+1);
97 return idx_last_group+1;
99 return i;
102 bool tidy_load_file(const char* file)
104 int fd = rb->open(file, O_RDONLY), i;
105 char buf[MAX_PATH], *str, *remove;
106 char last_group[MAX_PATH] = "";
107 bool new;
108 if (fd < 0)
109 return false;
110 while ((tidy_type_count < MAX_TYPES) &&
111 rb->read_line(fd, buf, MAX_PATH))
113 if (rb->settings_parseline(buf, &str, &remove))
115 i = find_file_string(str, last_group);
116 new = (i >= tidy_type_count);
117 if (!rb->strcmp(remove, "yes"))
118 tidy_types[i].remove = true;
119 else tidy_types[i].remove = false;
120 if (new)
122 i = tidy_type_count;
123 add_item(str, i);
124 tidy_type_count++;
126 if (str[0] == '<')
127 rb->strcpy(last_group, str);
130 rb->close(fd);
131 return true;
134 bool tidy_remove_item(char *item, int attr)
136 int i;
137 char *file;
138 bool ret = false, rem = false;
139 for (i=0; ret == false && i < tidy_type_count; i++)
141 file = tidy_types[i].filestring;
142 if (file[rb->strlen(file)-1] == '*')
144 if (!rb->strncmp(file, item, rb->strlen(file)-1))
145 rem = true;
147 else if (!rb->strcmp(file, item))
148 rem = true;
149 if (rem)
151 if (!tidy_types[i].remove)
152 return false;
153 if (attr&ATTR_DIRECTORY)
154 ret = tidy_types[i].directory;
155 else ret = true;
158 return ret;
161 void tidy_lcd_status(const char *name, int *removed)
163 char text[24]; /* "Cleaned up nnnnn items" */
165 /* display status text */
166 rb->lcd_clear_display();
167 rb->lcd_puts(0, 0, "Working ...");
168 rb->lcd_puts(0, 1, name);
169 rb->snprintf(text, 24, "Cleaned up %d items", *removed);
170 #ifdef HAVE_LCD_BITMAP
171 rb->lcd_puts(0, 2, text);
172 #endif
173 rb->lcd_update();
176 int tidy_path_append_entry(char *path, struct dirent *entry, int *path_length)
178 int name_len = rb->strlen(entry->d_name);
179 /* for the special case of path="/" this is one bigger but it's not a problem */
180 int new_length = *path_length + name_len + 1;
182 /* check overflow (keep space for trailing zero) */
183 if(new_length >= MAX_PATH)
184 return 0;
186 /* special case for path <> "/" */
187 if(rb->strcmp(path, "/") != 0)
189 rb->strcat(path + *path_length, "/");
190 (*path_length)++;
192 /* strcat is unsafe but the previous check normally avoid any problem */
193 /* use path_length to optimise */
195 rb->strcat(path + *path_length, entry->d_name);
196 *path_length += name_len;
198 return 1;
201 void tidy_path_remove_entry(char *path, int old_path_length, int *path_length)
203 path[old_path_length] = '\0';
204 *path_length = old_path_length;
207 /* path is assumed to be array of size MAX_PATH */
208 enum tidy_return tidy_removedir(char *path, int *path_length, int *removed)
210 /* delete directory */
211 struct dirent *entry;
212 enum tidy_return status = TIDY_RETURN_OK;
213 int button;
214 DIR *dir;
215 int old_path_length = *path_length;
217 /* display status text */
218 tidy_lcd_status(path, removed);
220 rb->yield();
222 dir = rb->opendir(path);
223 if (dir)
225 while((status == TIDY_RETURN_OK) && ((entry = rb->readdir(dir)) != 0))
226 /* walk directory */
228 /* check for user input and usb connect */
229 button = rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
230 if (button == ACTION_STD_CANCEL)
232 rb->closedir(dir);
233 return TIDY_RETURN_ABORT;
235 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
237 rb->closedir(dir);
238 return TIDY_RETURN_USB;
241 rb->yield();
243 /* get absolute path */
244 /* returns an error if path is too long */
245 if(!tidy_path_append_entry(path, entry, path_length))
246 /* silent error */
247 continue;
249 if (entry->attribute & ATTR_DIRECTORY)
251 /* dir ignore "." and ".." */
252 if ((rb->strcmp(entry->d_name, ".") != 0) && \
253 (rb->strcmp(entry->d_name, "..") != 0))
255 tidy_removedir(path, path_length, removed);
258 else
260 /* file */
261 *removed += 1;
262 rb->remove(path);
265 /* restore path */
266 tidy_path_remove_entry(path, old_path_length, path_length);
268 rb->closedir(dir);
269 /* rmdir */
270 *removed += 1;
271 rb->rmdir(path);
273 else
275 status = TIDY_RETURN_ERROR;
277 return status;
280 /* path is assumed to be array of size MAX_PATH */
281 enum tidy_return tidy_clean(char *path, int *path_length, int *removed)
283 /* deletes junk files and dirs left by system */
284 struct dirent *entry;
285 enum tidy_return status = TIDY_RETURN_OK;
286 int button;
287 int del; /* has the item been deleted */
288 DIR *dir;
289 int old_path_length = *path_length;
291 /* display status text */
292 tidy_lcd_status(path, removed);
294 rb->yield();
296 dir = rb->opendir(path);
297 if (dir)
299 while((status == TIDY_RETURN_OK) && ((entry = rb->readdir(dir)) != 0))
300 /* walk directory */
302 /* check for user input and usb connect */
303 button = rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
304 if (button == ACTION_STD_CANCEL)
306 rb->closedir(dir);
307 return TIDY_RETURN_ABORT;
309 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
311 rb->closedir(dir);
312 return TIDY_RETURN_USB;
315 rb->yield();
317 if (entry->attribute & ATTR_DIRECTORY)
319 /* directory ignore "." and ".." */
320 if ((rb->strcmp(entry->d_name, ".") != 0) && \
321 (rb->strcmp(entry->d_name, "..") != 0))
323 del = 0;
325 /* get absolute path */
326 /* returns an error if path is too long */
327 if(!tidy_path_append_entry(path, entry, path_length))
328 /* silent error */
329 continue;
331 if (tidy_remove_item(entry->d_name, entry->attribute))
333 /* delete dir */
334 tidy_removedir(path, path_length, removed);
335 del = 1;
338 if (del == 0)
340 /* dir not deleted so clean it */
341 status = tidy_clean(path, path_length, removed);
344 /* restore path */
345 tidy_path_remove_entry(path, old_path_length, path_length);
348 else
350 /* file */
351 del = 0;
352 if (tidy_remove_item(entry->d_name, entry->attribute))
354 /* get absolute path */
355 /* returns an error if path is too long */
356 if(!tidy_path_append_entry(path, entry, path_length))
357 /* silent error */
358 continue;
360 *removed += 1; /* increment removed files counter */
361 /* delete file */
362 rb->remove(path);
363 del = 1;
365 /* restore path */
366 tidy_path_remove_entry(path, old_path_length, path_length);
370 rb->closedir(dir);
371 return status;
373 else
375 return TIDY_RETURN_ERROR;
379 enum tidy_return tidy_do(void)
381 /* clean disk and display num of items removed */
382 int removed = 0;
383 enum tidy_return status;
384 char text[24]; /* "Cleaned up nnnnn items" */
385 char path[MAX_PATH];
386 int path_length;
388 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
389 rb->cpu_boost(true);
390 #endif
392 rb->strcpy(path, "/");
393 path_length = rb->strlen(path);
394 status = tidy_clean(path, &path_length, &removed);
396 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
397 rb->cpu_boost(false);
398 #endif
400 if ((status == TIDY_RETURN_OK) || (status == TIDY_RETURN_ABORT))
402 rb->lcd_clear_display();
403 rb->snprintf(text, 24, "Cleaned up %d items", removed);
404 if (status == TIDY_RETURN_ABORT)
406 rb->splash(HZ, "User aborted");
407 rb->lcd_clear_display();
409 rb->splash(HZ*2, text);
411 return status;
414 enum themable_icons get_icon(int item, void * data)
416 (void)data;
417 if (tidy_types[item].filestring[0] == '<') /* special type */
418 return Icon_Folder;
419 else if (tidy_types[item].remove)
420 return Icon_Cursor;
421 else
422 return Icon_NOICON;
425 static const char* get_name(int selected_item, void * data,
426 char * buffer, size_t buffer_len)
428 (void)data;
429 if (tidy_types[selected_item].directory)
431 rb->snprintf(buffer, buffer_len, "%s/",
432 tidy_types[selected_item].filestring);
433 return buffer;
435 return tidy_types[selected_item].filestring;
438 int list_action_callback(int action, struct gui_synclist *lists)
440 if (action == ACTION_STD_OK)
442 int selection = rb->gui_synclist_get_sel_pos(lists);
443 if (tidy_types[selection].filestring[0] == '<')
445 int i;
446 if (!rb->strcmp(tidy_types[selection].filestring, "< ALL >"))
448 for (i=0; i<tidy_type_count; i++)
450 if (tidy_types[i].filestring[0] != '<')
451 tidy_types[i].remove = true;
454 else if (!rb->strcmp(tidy_types[selection].filestring, "< NONE >"))
456 for (i=0; i<tidy_type_count; i++)
458 if (tidy_types[i].filestring[0] != '<')
459 tidy_types[i].remove = false;
462 else /* toggle all untill the next <> */
464 selection++;
465 while (selection < tidy_type_count &&
466 tidy_types[selection].filestring[0] != '<')
468 tidy_types[selection].remove = !tidy_types[selection].remove;
469 selection++;
473 else
474 tidy_types[selection].remove = !tidy_types[selection].remove;
475 tidy_loaded_and_changed = true;
476 return ACTION_REDRAW;
478 return action;
481 enum tidy_return tidy_lcd_menu(void)
483 int selection = 0;
484 enum tidy_return status = TIDY_RETURN_OK;
485 bool menu_quit = false;
487 MENUITEM_STRINGLIST(menu, "Disktidy Menu", NULL,
488 "Start Cleaning", "Files to Clean",
489 "Quit");
491 while (!menu_quit)
493 switch(rb->do_menu(&menu, &selection, NULL, false))
495 case 0:
496 menu_quit = true; /* start cleaning */
497 break;
499 case 1:
501 bool show_icons = rb->global_settings->show_icons;
502 struct simplelist_info list;
503 rb->simplelist_info_init(&list, "Files to Clean",
504 tidy_type_count, NULL);
505 list.get_icon = get_icon;
506 list.get_name = get_name;
507 list.action_callback = list_action_callback;
508 rb->simplelist_show_list(&list);
509 rb->global_settings->show_icons = show_icons;
511 break;
513 default:
514 status = TIDY_RETURN_ABORT; /* exit plugin */
515 menu_quit = true;
516 break;
519 return status;
522 /* this is the plugin entry point */
523 enum plugin_status plugin_start(const void* parameter)
525 enum tidy_return status;
526 (void)parameter;
528 tidy_type_count = 0;
529 tidy_load_file(DEFAULT_FILES);
530 tidy_load_file(CUSTOM_FILES);
531 if (tidy_type_count == 0)
533 rb->splash(3*HZ, "Missing disktidy.config file");
534 return PLUGIN_ERROR;
536 status = tidy_lcd_menu();
537 if (tidy_loaded_and_changed)
539 int fd = rb->creat(CUSTOM_FILES);
540 int i;
541 if (fd >= 0)
543 for(i=0;i<tidy_type_count;i++)
545 rb->fdprintf(fd, "%s%c: %s\n", tidy_types[i].filestring,
546 tidy_types[i].directory?'/':'\0',
547 tidy_types[i].remove?"yes":"no");
549 rb->close(fd);
552 if (status == TIDY_RETURN_ABORT)
553 return PLUGIN_OK;
554 while (true)
556 status = tidy_do();
558 switch (status)
560 case TIDY_RETURN_OK:
561 return PLUGIN_OK;
562 case TIDY_RETURN_ERROR:
563 return PLUGIN_ERROR;
564 case TIDY_RETURN_USB:
565 return PLUGIN_USB_CONNECTED;
566 case TIDY_RETURN_ABORT:
567 return PLUGIN_OK;
571 if (rb->default_event_handler(rb->button_get(false)) == SYS_USB_CONNECTED)
572 return PLUGIN_USB_CONNECTED;
574 rb->yield();
576 return PLUGIN_OK;