1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
25 static int removed
= 0; /* number of items removed */
26 static bool user_abort
;
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';
53 entry
->directory
= false;
55 char *a
= rb
->strchr(entry
->filestring
, '*');
58 entry
->pre
= a
- entry
->filestring
;
59 entry
->post
= rb
->strlen(a
+1);
68 static int find_file_string(const char *file
, char *last_group
)
71 int idx_last_group
= -1;
73 rb
->strcpy(temp
, file
);
74 if (temp
[rb
->strlen(temp
)-1] == '/')
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
)
83 else if (!rb
->strcmp(tidy_types
[i
].filestring
, last_group
))
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] == '<')
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
));
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
] = "";
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
))
120 if (*str
== '\\') /* escape first character ? */
122 unsigned i
= find_file_string(str
, last_group
);
124 tidy_types
[i
].remove
= rb
->strcmp(remove
, "yes");
126 if (i
>= tidy_type_count
)
133 rb
->strcpy(last_group
, str
);
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
)
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
);
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
);
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
)
186 /* special case for path <> "/" */
187 if(rb
->strcmp(path
, "/") != 0)
189 rb
->strcat(path
+ *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
;
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
);
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
)
229 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
232 return PLUGIN_USB_CONNECTED
;
237 struct dirinfo info
= rb
->dir_get_info(dir
, entry
);
238 if (!rmdir
&& !tidy_remove_item(entry
->d_name
, info
.attribute
))
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);
258 tidy_path_remove_entry(path
, old_path_length
, path_length
);
271 static enum plugin_status
tidy_do(void)
273 /* clean disk and display num of items removed */
276 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
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);
288 if (status
== PLUGIN_OK
)
290 rb
->lcd_clear_display();
293 rb
->splash(HZ
, "User aborted");
294 rb
->lcd_clear_display();
296 rb
->splashf(HZ
*2, "Cleaned up %d items", removed
);
301 static enum themable_icons
get_icon(int item
, void * data
)
304 if (tidy_types
[item
].filestring
[0] == '<') /* special type */
306 else if (tidy_types
[item
].remove
)
312 static const char* get_name(int selected_item
, void * data
,
313 char * buffer
, size_t buffer_len
)
316 if (tidy_types
[selected_item
].directory
)
318 rb
->snprintf(buffer
, buffer_len
, "%s/",
319 tidy_types
[selected_item
].filestring
);
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
)
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 >");
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
;
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)
355 struct simplelist_info list
;
357 MENUITEM_STRINGLIST(menu
, "Disktidy Menu", NULL
, "Start Cleaning",
358 "Files to Clean", "Quit");
361 switch(rb
->do_menu(&menu
, &selection
, NULL
, false))
366 return; /* start cleaning */
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
);
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);
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");
396 /* this is the plugin entry point */
397 enum plugin_status
plugin_start(const 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");
409 if (tidy_loaded_and_changed
)
412 return user_abort
? PLUGIN_OK
: tidy_do();