Prepare new maemo release
[maemo-rb.git] / apps / plugins / disktidy.c
blob621238acfbb71ef93db363bf720c18bd44f0c591
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 */
26 static bool user_abort;
28 struct tidy_type {
29 char filestring[64];
30 int pre;
31 int post;
32 bool directory;
33 bool remove;
34 } tidy_types[64];
36 static size_t tidy_type_count;
38 bool tidy_loaded_and_changed = false;
40 #define DEFAULT_FILES PLUGIN_APPS_DATA_DIR "/disktidy.config"
41 #define CUSTOM_FILES PLUGIN_APPS_DATA_DIR "/disktidy_custom.config"
43 static void add_item(const char* name, int index)
45 struct tidy_type *entry = &tidy_types[index];
46 rb->strcpy(entry->filestring, name);
47 if (name[rb->strlen(name)-1] == '/')
49 entry->directory = true;
50 entry->filestring[rb->strlen(name)-1] = '\0';
52 else
53 entry->directory = false;
55 char *a = rb->strchr(entry->filestring, '*');
56 if (a)
58 entry->pre = a - entry->filestring;
59 entry->post = rb->strlen(a+1);
61 else
63 entry->pre = -1;
64 entry->post = -1;
68 static int find_file_string(const char *file, char *last_group)
70 char temp[MAX_PATH];
71 int idx_last_group = -1;
72 bool folder = false;
73 rb->strcpy(temp, file);
74 if (temp[rb->strlen(temp)-1] == '/')
76 folder = true;
77 temp[rb->strlen(temp)-1] = '\0';
80 for (unsigned i = 0; i < tidy_type_count; i++)
81 if (!rb->strcmp(tidy_types[i].filestring, temp) && folder == tidy_types[i].directory)
82 return i;
83 else if (!rb->strcmp(tidy_types[i].filestring, last_group))
84 idx_last_group = i;
86 if (file[0] == '<' || idx_last_group == -1)
87 return tidy_type_count;
90 /* not found, so insert it into its group */
91 for (unsigned i=idx_last_group; i<tidy_type_count; i++)
92 if (tidy_types[i].filestring[0] == '<')
94 idx_last_group = i;
95 break;
98 /* shift items up one */
99 for (int i=tidy_type_count;i>idx_last_group;i--)
100 rb->memcpy(&tidy_types[i], &tidy_types[i-1], sizeof(struct tidy_type));
102 tidy_type_count++;
103 add_item(file, idx_last_group+1);
104 return idx_last_group+1;
107 static void tidy_load_file(const char* file)
109 int fd = rb->open(file, O_RDONLY);
110 char buf[MAX_PATH], *str, *remove;
111 char last_group[MAX_PATH] = "";
112 if (fd < 0)
113 return;
115 while ((tidy_type_count < sizeof(tidy_types) / sizeof(tidy_types[0])) && rb->read_line(fd, buf, MAX_PATH))
117 if (!rb->settings_parseline(buf, &str, &remove))
118 continue;
120 if (*str == '\\') /* escape first character ? */
121 str++;
122 unsigned i = find_file_string(str, last_group);
124 tidy_types[i].remove = rb->strcmp(remove, "yes");
126 if (i >= tidy_type_count)
128 i = tidy_type_count;
129 add_item(str, i);
130 tidy_type_count++;
132 if (str[0] == '<')
133 rb->strcpy(last_group, str);
135 rb->close(fd);
138 static bool match(struct tidy_type *tidy_type, const char *string, int len)
140 char *pattern = tidy_type->filestring;
142 if (tidy_type->pre < 0) /* no '*', just compare. */
143 return !rb->strcmp(pattern, string);
145 /* pattern is too long for the string. avoid 'ab*bc' matching 'abc'. */
146 if (len < tidy_type->pre + tidy_type->post)
147 return false;
149 /* pattern has '*', compare former part of '*' to the begining of
150 the string and compare next part of '*' to the end of string. */
151 return !rb->strncmp(pattern, string, tidy_type->pre) &&
152 !rb->strcmp(pattern + tidy_type->pre + 1, string + len - tidy_type->post);
155 static bool tidy_remove_item(const char *item, int attr)
157 for (struct tidy_type *t = &tidy_types[0]; t < &tidy_types[tidy_type_count]; t++)
158 if (match(t, item, rb->strlen(item)))
159 return t->remove && ((!!(attr&ATTR_DIRECTORY)) == t->directory);
161 return false;
164 static void tidy_lcd_status(const char *name)
166 /* display status text */
167 rb->lcd_clear_display();
168 rb->lcd_puts(0, 0, "Working ...");
169 rb->lcd_puts(0, 1, name);
170 #ifdef HAVE_LCD_BITMAP
171 rb->lcd_putsf(0, 2, "Cleaned up %d items", removed);
172 #endif
173 rb->lcd_update();
176 static 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 static 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 static enum plugin_status tidy_clean(char *path, int *path_length, bool rmdir)
210 int old_path_length = *path_length;
212 tidy_lcd_status(path);
214 DIR *dir = rb->opendir(path);
215 if (!dir)
216 return PLUGIN_ERROR;
218 struct dirent *entry;
219 while ((entry = rb->readdir(dir)))
221 /* check for user input and usb connect */
222 int button = rb->get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
223 if (button == ACTION_STD_CANCEL)
225 rb->closedir(dir);
226 user_abort = true;
227 return PLUGIN_OK;
229 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
231 rb->closedir(dir);
232 return PLUGIN_USB_CONNECTED;
235 rb->yield();
237 struct dirinfo info = rb->dir_get_info(dir, entry);
238 if (!rmdir && !tidy_remove_item(entry->d_name, info.attribute))
239 continue;
241 /* get absolute path, returns an error if path is too long */
242 if(!tidy_path_append_entry(path, entry, path_length))
243 continue; /* silent error */
245 if (info.attribute & ATTR_DIRECTORY)
247 /* dir ignore "." and ".." */
248 if (rb->strcmp(entry->d_name, ".") && rb->strcmp(entry->d_name, ".."))
249 tidy_clean(path, path_length, true);
251 else
253 removed++;
254 rb->remove(path);
257 /* restore path */
258 tidy_path_remove_entry(path, old_path_length, path_length);
260 rb->closedir(dir);
262 if (rmdir)
264 removed++;
265 rb->rmdir(path);
268 return PLUGIN_OK;
271 static enum plugin_status tidy_do(void)
273 /* clean disk and display num of items removed */
274 char path[MAX_PATH];
276 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
277 rb->cpu_boost(true);
278 #endif
280 rb->strcpy(path, "/");
281 int path_length = rb->strlen(path);
282 enum plugin_status status = tidy_clean(path, &path_length, false);
284 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
285 rb->cpu_boost(false);
286 #endif
288 if (status == PLUGIN_OK)
290 rb->lcd_clear_display();
291 if (user_abort)
293 rb->splash(HZ, "User aborted");
294 rb->lcd_clear_display();
296 rb->splashf(HZ*2, "Cleaned up %d items", removed);
298 return status;
301 static enum themable_icons get_icon(int item, void * data)
303 (void)data;
304 if (tidy_types[item].filestring[0] == '<') /* special type */
305 return Icon_Folder;
306 else if (tidy_types[item].remove)
307 return Icon_Cursor;
308 else
309 return Icon_NOICON;
312 static const char* get_name(int selected_item, void * data,
313 char * buffer, size_t buffer_len)
315 (void)data;
316 if (tidy_types[selected_item].directory)
318 rb->snprintf(buffer, buffer_len, "%s/",
319 tidy_types[selected_item].filestring);
320 return buffer;
322 return tidy_types[selected_item].filestring;
325 static int list_action_callback(int action, struct gui_synclist *lists)
327 if (action != ACTION_STD_OK)
328 return action;
330 unsigned selection = rb->gui_synclist_get_sel_pos(lists);
331 if (tidy_types[selection].filestring[0] == '<')
333 bool all = !rb->strcmp(tidy_types[selection].filestring, "< ALL >");
334 bool none= !rb->strcmp(tidy_types[selection].filestring, "< NONE >");
336 if (all || none)
338 for (unsigned i=0; i<tidy_type_count; i++)
339 if (tidy_types[i].filestring[0] != '<')
340 tidy_types[i].remove = all;
342 else /* toggle all untill the next <> */
343 while (++selection < tidy_type_count && tidy_types[selection].filestring[0] != '<')
344 tidy_types[selection].remove = !tidy_types[selection].remove;
346 else
347 tidy_types[selection].remove = !tidy_types[selection].remove;
348 tidy_loaded_and_changed = true;
349 return ACTION_REDRAW;
352 static void tidy_lcd_menu(void)
354 int selection = 0;
355 struct simplelist_info list;
357 MENUITEM_STRINGLIST(menu, "Disktidy Menu", NULL, "Start Cleaning",
358 "Files to Clean", "Quit");
360 for(;;)
361 switch(rb->do_menu(&menu, &selection, NULL, false))
363 default:
364 user_abort = true;
365 case 0:
366 return; /* start cleaning */
368 case 1:
369 rb->simplelist_info_init(&list, "Files to Clean", tidy_type_count, NULL);
370 list.get_icon = get_icon;
371 list.get_name = get_name;
372 list.action_callback = list_action_callback;
373 rb->simplelist_show_list(&list);
374 break;
378 /* Creates a file and writes information about what files to
379 delete and what to keep to it.
381 static void save_config(void)
383 int fd = rb->creat(CUSTOM_FILES, 0666);
384 if (fd < 0)
385 return;
387 for (unsigned i=0; i<tidy_type_count; i++)
388 rb->fdprintf(fd, "%s%s%s: %s\n",
389 tidy_types[i].filestring[0] == '#' ? "\\" : "",
390 tidy_types[i].filestring,
391 tidy_types[i].directory ? "/" : "",
392 tidy_types[i].remove ? "yes" : "no");
393 rb->close(fd);
396 /* this is the plugin entry point */
397 enum plugin_status plugin_start(const void* parameter)
399 (void)parameter;
401 tidy_load_file(DEFAULT_FILES);
402 tidy_load_file(CUSTOM_FILES);
403 if (tidy_type_count == 0)
405 rb->splash(3*HZ, "Missing disktidy.config file");
406 return PLUGIN_ERROR;
408 tidy_lcd_menu();
409 if (tidy_loaded_and_changed)
410 save_config();
412 return user_abort ? PLUGIN_OK : tidy_do();