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 */
27 /* function return values */
31 TIDY_RETURN_ERROR
= 1,
33 TIDY_RETURN_ABORT
= 3,
43 } tidy_types
[MAX_TYPES
];
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
)
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';
61 entry
->directory
= false;
62 a
= rb
->strchr(entry
->filestring
, '*');
65 entry
->pre
= a
- entry
->filestring
;
66 entry
->post
= rb
->strlen(a
+1);
75 static int find_file_string(const char *file
, char *last_group
)
78 int i
= 0, idx_last_group
= -1;
80 rb
->strcpy(temp
, file
);
81 if (temp
[rb
->strlen(temp
)-1] == '/')
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
)
91 else if (!rb
->strcmp(tidy_types
[i
].filestring
, last_group
))
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] == '<')
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
));
112 add_item(file
, idx_last_group
+1);
113 return idx_last_group
+1;
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
] = "";
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;
143 rb
->strcpy(last_group
, str
);
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
)
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
)
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
)
180 if (attr
&ATTR_DIRECTORY
)
181 ret
= tidy_types
[i
].directory
;
183 ret
= !tidy_types
[i
].directory
;
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
);
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
)
211 /* special case for path <> "/" */
212 if(rb
->strcmp(path
, "/") != 0)
214 rb
->strcat(path
+ *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
;
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
;
243 int old_path_length
= *path_length
;
245 /* display status text */
246 tidy_lcd_status(path
);
250 dir
= rb
->opendir(path
);
253 while((status
== TIDY_RETURN_OK
) && ((entry
= rb
->readdir(dir
)) != 0))
256 /* check for user input and usb connect */
257 button
= rb
->get_action(CONTEXT_STD
, TIMEOUT_NOBLOCK
);
258 if (button
== ACTION_STD_CANCEL
)
261 return TIDY_RETURN_ABORT
;
263 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
266 return TIDY_RETURN_USB
;
271 /* get absolute path */
272 /* returns an error if path is too long */
273 if(!tidy_path_append_entry(path
, entry
, path_length
))
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
);
295 tidy_path_remove_entry(path
, old_path_length
, path_length
);
304 status
= TIDY_RETURN_ERROR
;
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
;
317 int old_path_length
= *path_length
;
319 /* display status text */
320 tidy_lcd_status(path
);
324 dir
= rb
->opendir(path
);
327 while((status
== TIDY_RETURN_OK
) && ((entry
= rb
->readdir(dir
)) != 0))
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
)
336 return TIDY_RETURN_ABORT
;
338 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
341 return TIDY_RETURN_USB
;
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
))
358 if (tidy_remove_item(entry
->d_name
, info
.attribute
))
361 status
= tidy_removedir(path
, path_length
);
365 /* dir not deleted so clean it */
366 status
= tidy_clean(path
, path_length
);
370 tidy_path_remove_entry(path
, old_path_length
, path_length
);
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
))
384 removed
++; /* increment removed files counter */
386 if (rb
->remove(path
) != 0)
387 DEBUGF("Could not delete file %s\n", path
);
390 tidy_path_remove_entry(path
, old_path_length
, path_length
);
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
;
410 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
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);
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
);
435 enum themable_icons
get_icon(int item
, void * data
)
438 if (tidy_types
[item
].filestring
[0] == '<') /* special type */
440 else if (tidy_types
[item
].remove
)
446 static const char* get_name(int selected_item
, void * data
,
447 char * buffer
, size_t buffer_len
)
450 if (tidy_types
[selected_item
].directory
)
452 rb
->snprintf(buffer
, buffer_len
, "%s/",
453 tidy_types
[selected_item
].filestring
);
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] == '<')
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 <> */
486 while (selection
< tidy_type_count
&&
487 tidy_types
[selection
].filestring
[0] != '<')
489 tidy_types
[selection
].remove
= !tidy_types
[selection
].remove
;
495 tidy_types
[selection
].remove
= !tidy_types
[selection
].remove
;
496 tidy_loaded_and_changed
= true;
497 return ACTION_REDRAW
;
502 enum tidy_return
tidy_lcd_menu(void)
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",
514 switch(rb
->do_menu(&menu
, &selection
, NULL
, false))
517 menu_quit
= true; /* start cleaning */
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
);
533 status
= TIDY_RETURN_ABORT
; /* exit plugin */
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
)
550 fd
= rb
->creat(file_name
, 0666);
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");
565 DEBUGF("Could not create file %s\n", file_name
);
571 /* this is the plugin entry point */
572 enum plugin_status
plugin_start(const void* parameter
)
574 enum tidy_return status
;
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");
585 status
= tidy_lcd_menu();
586 if (tidy_loaded_and_changed
)
588 save_config(CUSTOM_FILES
);
590 if (status
== TIDY_RETURN_ABORT
)
600 case TIDY_RETURN_ERROR
:
602 case TIDY_RETURN_USB
:
603 return PLUGIN_USB_CONNECTED
;
604 case TIDY_RETURN_ABORT
:
609 if (rb
->default_event_handler(rb
->button_get(false)) == SYS_USB_CONNECTED
)
610 return PLUGIN_USB_CONNECTED
;