RaaA: Fix write locations of plugins
[maemo-rb.git] / apps / plugins / disktidy.c
blobd42a62d469e143c4a814a9c9873d0227cfdaa1aa
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_DATA_DIR "/disktidy.config"
48 #define CUSTOM_FILES PLUGIN_APPS_DATA_DIR "/disktidy_custom.config"
50 void add_item(const char* name, int index)
52 char *a;
53 struct tidy_type *entry = tidy_types + index;
54 rb->strcpy(entry->filestring, name);
55 if (name[rb->strlen(name)-1] == '/')
57 entry->directory = true;
58 entry->filestring[rb->strlen(name)-1] = '\0';
60 else
61 entry->directory = false;
62 a = rb->strchr(entry->filestring, '*');
63 if (a)
65 entry->pre = a - entry->filestring;
66 entry->post = rb->strlen(a+1);
68 else
70 entry->pre = -1;
71 entry->post = -1;
75 static int find_file_string(const char *file, char *last_group)
77 char temp[MAX_PATH];
78 int i = 0, idx_last_group = -1;
79 bool folder = false;
80 rb->strcpy(temp, file);
81 if (temp[rb->strlen(temp)-1] == '/')
83 folder = true;
84 temp[rb->strlen(temp)-1] = '\0';
86 while (i<tidy_type_count)
88 if (!rb->strcmp(tidy_types[i].filestring, temp) &&
89 folder == tidy_types[i].directory)
90 return i;
91 else if (!rb->strcmp(tidy_types[i].filestring, last_group))
92 idx_last_group = i;
93 i++;
95 /* not found, so insert it into its group */
96 if (file[0] != '<' && idx_last_group != -1)
98 for (i=idx_last_group; i<tidy_type_count; i++)
100 if (tidy_types[i].filestring[0] == '<')
102 idx_last_group = i;
103 break;
106 /* shift items up one */
107 for (i=tidy_type_count;i>idx_last_group;i--)
109 rb->memcpy(&tidy_types[i], &tidy_types[i-1], sizeof(struct tidy_type));
111 tidy_type_count++;
112 add_item(file, idx_last_group+1);
113 return idx_last_group+1;
115 return i;
118 bool tidy_load_file(const char* file)
120 int fd = rb->open(file, O_RDONLY), i;
121 char buf[MAX_PATH], *str, *remove;
122 char last_group[MAX_PATH] = "";
123 bool new;
124 if (fd < 0)
125 return false;
126 while ((tidy_type_count < MAX_TYPES) &&
127 rb->read_line(fd, buf, MAX_PATH))
129 if (rb->settings_parseline(buf, &str, &remove))
131 i = find_file_string(str, last_group);
132 new = (i >= tidy_type_count);
133 if (!rb->strcmp(remove, "yes"))
134 tidy_types[i].remove = true;
135 else tidy_types[i].remove = false;
136 if (new)
138 i = tidy_type_count;
139 add_item(str, i);
140 tidy_type_count++;
142 if (str[0] == '<')
143 rb->strcpy(last_group, str);
146 rb->close(fd);
147 return true;
150 static bool match(struct tidy_type *tidy_type, char *string, int len)
152 char *pattern = tidy_type->filestring;
153 if (tidy_type->pre < 0)
155 /* no '*', just compare. */
156 return (rb->strcmp(pattern, string) == 0);
158 /* pattern is too long for the string. avoid 'ab*bc' matching 'abc'. */
159 if (len < tidy_type->pre + tidy_type->post)
160 return false;
161 /* pattern has '*', compare former part of '*' to the begining of
162 the string and compare next part of '*' to the end of string. */
163 return (rb->strncmp(pattern, string, tidy_type->pre) == 0 &&
164 rb->strcmp(pattern + tidy_type->pre + 1,
165 string + len - tidy_type->post) == 0);
168 bool tidy_remove_item(char *item, int attr)
170 int i;
171 int len;
172 bool ret = false;
173 len = rb->strlen(item);
174 for (i=0; ret == false && i < tidy_type_count; i++)
176 if (match(&tidy_types[i], item, len))
178 if (!tidy_types[i].remove)
179 return false;
180 if (attr&ATTR_DIRECTORY)
181 ret = tidy_types[i].directory;
182 else
183 ret = !tidy_types[i].directory;
186 return ret;
189 void tidy_lcd_status(const char *name)
191 /* display status text */
192 rb->lcd_clear_display();
193 rb->lcd_puts(0, 0, "Working ...");
194 rb->lcd_puts(0, 1, name);
195 #ifdef HAVE_LCD_BITMAP
196 rb->lcd_putsf(0, 2, "Cleaned up %d items", removed);
197 #endif
198 rb->lcd_update();
201 int tidy_path_append_entry(char *path, struct dirent *entry, int *path_length)
203 int name_len = rb->strlen(entry->d_name);
204 /* for the special case of path="/" this is one bigger but it's not a problem */
205 int new_length = *path_length + name_len + 1;
207 /* check overflow (keep space for trailing zero) */
208 if(new_length >= MAX_PATH)
209 return 0;
211 /* special case for path <> "/" */
212 if(rb->strcmp(path, "/") != 0)
214 rb->strcat(path + *path_length, "/");
215 (*path_length)++;
217 /* strcat is unsafe but the previous check normally avoid any problem */
218 /* use path_length to optimise */
220 rb->strcat(path + *path_length, entry->d_name);
221 *path_length += name_len;
223 return 1;
226 void tidy_path_remove_entry(char *path, int old_path_length, int *path_length)
228 path[old_path_length] = '\0';
229 *path_length = old_path_length;
232 /* Removes the directory specified by 'path'. This includes recursively
233 removing all files and directories in that directory.
234 path is assumed to be array of size MAX_PATH.
236 enum tidy_return tidy_removedir(char *path, int *path_length)
238 /* delete directory */
239 struct dirent *entry;
240 enum tidy_return status = TIDY_RETURN_OK;
241 int button;
242 DIR *dir;
243 int old_path_length = *path_length;
245 /* display status text */
246 tidy_lcd_status(path);
248 rb->yield();
250 dir = rb->opendir(path);
251 if (dir)
253 while((status == TIDY_RETURN_OK) && ((entry = rb->readdir(dir)) != 0))
254 /* walk directory */
256 /* check for user input and usb connect */
257 button = rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
258 if (button == ACTION_STD_CANCEL)
260 rb->closedir(dir);
261 return TIDY_RETURN_ABORT;
263 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
265 rb->closedir(dir);
266 return TIDY_RETURN_USB;
269 rb->yield();
271 /* get absolute path */
272 /* returns an error if path is too long */
273 if(!tidy_path_append_entry(path, entry, path_length))
274 /* silent error */
275 continue;
277 struct dirinfo info = rb->dir_get_info(dir, entry);
278 if (info.attribute & ATTR_DIRECTORY)
280 /* dir ignore "." and ".." */
281 if ((rb->strcmp(entry->d_name, ".") != 0) && \
282 (rb->strcmp(entry->d_name, "..") != 0))
284 status = tidy_removedir(path, path_length);
287 else
289 /* file */
290 removed++;
291 rb->remove(path);
294 /* restore path */
295 tidy_path_remove_entry(path, old_path_length, path_length);
297 rb->closedir(dir);
298 /* rmdir */
299 removed++;
300 rb->rmdir(path);
302 else
304 status = TIDY_RETURN_ERROR;
306 return status;
309 /* path is assumed to be array of size MAX_PATH */
310 enum tidy_return tidy_clean(char *path, int *path_length)
312 /* deletes junk files and dirs left by system */
313 struct dirent *entry;
314 enum tidy_return status = TIDY_RETURN_OK;
315 int button;
316 DIR *dir;
317 int old_path_length = *path_length;
319 /* display status text */
320 tidy_lcd_status(path);
322 rb->yield();
324 dir = rb->opendir(path);
325 if (dir)
327 while((status == TIDY_RETURN_OK) && ((entry = rb->readdir(dir)) != 0))
328 /* walk directory */
330 struct dirinfo info = rb->dir_get_info(dir, entry);
331 /* check for user input and usb connect */
332 button = rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
333 if (button == ACTION_STD_CANCEL)
335 rb->closedir(dir);
336 return TIDY_RETURN_ABORT;
338 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
340 rb->closedir(dir);
341 return TIDY_RETURN_USB;
344 rb->yield();
346 if (info.attribute & ATTR_DIRECTORY)
348 /* directory ignore "." and ".." */
349 if ((rb->strcmp(entry->d_name, ".") != 0) && \
350 (rb->strcmp(entry->d_name, "..") != 0))
352 /* get absolute path */
353 /* returns an error if path is too long */
354 if(!tidy_path_append_entry(path, entry, path_length))
355 /* silent error */
356 continue;
358 if (tidy_remove_item(entry->d_name, info.attribute))
360 /* delete dir */
361 status = tidy_removedir(path, path_length);
363 else
365 /* dir not deleted so clean it */
366 status = tidy_clean(path, path_length);
369 /* restore path */
370 tidy_path_remove_entry(path, old_path_length, path_length);
373 else
375 /* file */
376 if (tidy_remove_item(entry->d_name, info.attribute))
378 /* get absolute path */
379 /* returns an error if path is too long */
380 if(!tidy_path_append_entry(path, entry, path_length))
381 /* silent error */
382 continue;
384 removed++; /* increment removed files counter */
385 /* delete file */
386 if (rb->remove(path) != 0)
387 DEBUGF("Could not delete file %s\n", path);
389 /* restore path */
390 tidy_path_remove_entry(path, old_path_length, path_length);
394 rb->closedir(dir);
395 return status;
397 else
399 return TIDY_RETURN_ERROR;
403 enum tidy_return tidy_do(void)
405 /* clean disk and display num of items removed */
406 enum tidy_return status;
407 char path[MAX_PATH];
408 int path_length;
410 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
411 rb->cpu_boost(true);
412 #endif
414 rb->strcpy(path, "/");
415 path_length = rb->strlen(path);
416 status = tidy_clean(path, &path_length);
418 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
419 rb->cpu_boost(false);
420 #endif
422 if ((status == TIDY_RETURN_OK) || (status == TIDY_RETURN_ABORT))
424 rb->lcd_clear_display();
425 if (status == TIDY_RETURN_ABORT)
427 rb->splash(HZ, "User aborted");
428 rb->lcd_clear_display();
430 rb->splashf(HZ*2, "Cleaned up %d items", removed);
432 return status;
435 enum themable_icons get_icon(int item, void * data)
437 (void)data;
438 if (tidy_types[item].filestring[0] == '<') /* special type */
439 return Icon_Folder;
440 else if (tidy_types[item].remove)
441 return Icon_Cursor;
442 else
443 return Icon_NOICON;
446 static const char* get_name(int selected_item, void * data,
447 char * buffer, size_t buffer_len)
449 (void)data;
450 if (tidy_types[selected_item].directory)
452 rb->snprintf(buffer, buffer_len, "%s/",
453 tidy_types[selected_item].filestring);
454 return buffer;
456 return tidy_types[selected_item].filestring;
459 int list_action_callback(int action, struct gui_synclist *lists)
461 if (action == ACTION_STD_OK)
463 int selection = rb->gui_synclist_get_sel_pos(lists);
464 if (tidy_types[selection].filestring[0] == '<')
466 int i;
467 if (!rb->strcmp(tidy_types[selection].filestring, "< ALL >"))
469 for (i=0; i<tidy_type_count; i++)
471 if (tidy_types[i].filestring[0] != '<')
472 tidy_types[i].remove = true;
475 else if (!rb->strcmp(tidy_types[selection].filestring, "< NONE >"))
477 for (i=0; i<tidy_type_count; i++)
479 if (tidy_types[i].filestring[0] != '<')
480 tidy_types[i].remove = false;
483 else /* toggle all untill the next <> */
485 selection++;
486 while (selection < tidy_type_count &&
487 tidy_types[selection].filestring[0] != '<')
489 tidy_types[selection].remove = !tidy_types[selection].remove;
490 selection++;
494 else
495 tidy_types[selection].remove = !tidy_types[selection].remove;
496 tidy_loaded_and_changed = true;
497 return ACTION_REDRAW;
499 return action;
502 enum tidy_return tidy_lcd_menu(void)
504 int selection = 0;
505 enum tidy_return status = TIDY_RETURN_OK;
506 bool menu_quit = false;
508 MENUITEM_STRINGLIST(menu, "Disktidy Menu", NULL,
509 "Start Cleaning", "Files to Clean",
510 "Quit");
512 while (!menu_quit)
514 switch(rb->do_menu(&menu, &selection, NULL, false))
516 case 0:
517 menu_quit = true; /* start cleaning */
518 break;
520 case 1:
522 struct simplelist_info list;
523 rb->simplelist_info_init(&list, "Files to Clean",
524 tidy_type_count, NULL);
525 list.get_icon = get_icon;
526 list.get_name = get_name;
527 list.action_callback = list_action_callback;
528 rb->simplelist_show_list(&list);
530 break;
532 default:
533 status = TIDY_RETURN_ABORT; /* exit plugin */
534 menu_quit = true;
535 break;
538 return status;
541 /* Creates a file and writes information about what files to
542 delete and what to keep to it.
543 Returns true iff the file was successfully created.
545 static bool save_config(const char *file_name)
547 int fd, i;
548 bool result;
550 fd = rb->creat(file_name, 0666);
551 result = (fd >= 0);
553 if (result)
555 for (i=0; i<tidy_type_count; i++)
557 rb->fdprintf(fd, "%s%s: %s\n", tidy_types[i].filestring,
558 tidy_types[i].directory ? "/" : "",
559 tidy_types[i].remove ? "yes" : "no");
561 rb->close(fd);
563 else
565 DEBUGF("Could not create file %s\n", file_name);
568 return result;
571 /* this is the plugin entry point */
572 enum plugin_status plugin_start(const void* parameter)
574 enum tidy_return status;
575 (void)parameter;
577 tidy_type_count = 0;
578 tidy_load_file(DEFAULT_FILES);
579 tidy_load_file(CUSTOM_FILES);
580 if (tidy_type_count == 0)
582 rb->splash(3*HZ, "Missing disktidy.config file");
583 return PLUGIN_ERROR;
585 status = tidy_lcd_menu();
586 if (tidy_loaded_and_changed)
588 save_config(CUSTOM_FILES);
590 if (status == TIDY_RETURN_ABORT)
591 return PLUGIN_OK;
592 while (true)
594 status = tidy_do();
596 switch (status)
598 case TIDY_RETURN_OK:
599 return PLUGIN_OK;
600 case TIDY_RETURN_ERROR:
601 return PLUGIN_ERROR;
602 case TIDY_RETURN_USB:
603 return PLUGIN_USB_CONNECTED;
604 case TIDY_RETURN_ABORT:
605 return PLUGIN_OK;
609 if (rb->default_event_handler(rb->button_get(false)) == SYS_USB_CONNECTED)
610 return PLUGIN_USB_CONNECTED;
612 rb->yield();
614 return PLUGIN_OK;