Scripting: Allow recursive 'for'

This commit is contained in:
d0k3 2018-01-18 02:23:46 +01:00
parent 3684db2fe7
commit 5034e99832
2 changed files with 82 additions and 19 deletions

View File

@ -41,6 +41,8 @@
#define _SKIP_TO_NEXT 3 #define _SKIP_TO_NEXT 3
#define _SKIP_TO_FOR 4 #define _SKIP_TO_FOR 4
#define _MAX_FOR_DEPTH 16
#define VAR_BUFFER (SCRIPT_BUFFER + SCRIPT_BUFFER_SIZE - VAR_BUFFER_SIZE) #define VAR_BUFFER (SCRIPT_BUFFER + SCRIPT_BUFFER_SIZE - VAR_BUFFER_SIZE)
// macros for textviewer // macros for textviewer
@ -130,7 +132,7 @@ Gm9ScriptCmd cmd_list[] = {
{ CMD_ID_ELIF , _CMD_ELIF , 1, 0 }, { CMD_ID_ELIF , _CMD_ELIF , 1, 0 },
{ CMD_ID_ELSE , _CMD_ELSE , 0, 0 }, { CMD_ID_ELSE , _CMD_ELSE , 0, 0 },
{ CMD_ID_END , _CMD_END , 0, 0 }, { CMD_ID_END , _CMD_END , 0, 0 },
{ CMD_ID_FOR , _CMD_FOR , 2, 0 }, { CMD_ID_FOR , _CMD_FOR , 2, _FLG('r') },
{ CMD_ID_NEXT , _CMD_NEXT , 0, 0 }, { CMD_ID_NEXT , _CMD_NEXT , 0, 0 },
{ CMD_ID_GOTO , "goto" , 1, 0 }, { CMD_ID_GOTO , "goto" , 1, 0 },
{ CMD_ID_LABELSEL, "labelsel", 2, 0 }, { CMD_ID_LABELSEL, "labelsel", 2, 0 },
@ -508,6 +510,7 @@ u32 get_flag(char* str, u32 len, char* err_str) {
else if (strncmp(str, "--legit", len) == 0) flag_char = 'l'; else if (strncmp(str, "--legit", len) == 0) flag_char = 'l';
else if (strncmp(str, "--no_cancel", len) == 0) flag_char = 'n'; else if (strncmp(str, "--no_cancel", len) == 0) flag_char = 'n';
else if (strncmp(str, "--optional", len) == 0) flag_char = 'o'; else if (strncmp(str, "--optional", len) == 0) flag_char = 'o';
else if (strncmp(str, "--recursive", len) == 0) flag_char = 'r';
else if (strncmp(str, "--silent", len) == 0) flag_char = 's'; else if (strncmp(str, "--silent", len) == 0) flag_char = 's';
else if (strncmp(str, "--unequal", len) == 0) flag_char = 'u'; else if (strncmp(str, "--unequal", len) == 0) flag_char = 'u';
else if (strncmp(str, "--overwrite", len) == 0) flag_char = 'w'; else if (strncmp(str, "--overwrite", len) == 0) flag_char = 'w';
@ -671,29 +674,43 @@ char* find_label(const char* label, const char* last_found) {
return NULL; return NULL;
} }
bool for_handler(char* path, const char* dir, const char* pattern) { bool for_handler(char* path, const char* dir, const char* pattern, bool recursive) {
static DIR fdir; static DIR fdir[_MAX_FOR_DEPTH];
static DIR* dp = NULL; static DIR* dp = NULL;
static char ldir[256]; static char ldir[256];
static char lpattern[64]; static char lpattern[64];
static bool rec = false;
if (!path && !dir && !pattern) { if (!path && !dir && !pattern) { // close all dirs
if (dp) fvx_closedir(dp); while (dp >= fdir) fvx_closedir(dp--);
dp = NULL; dp = NULL;
return true; return true;
} }
if (dir) { if (dir) { // open a dir
snprintf(lpattern, 64, pattern); snprintf(lpattern, 64, pattern);
snprintf(ldir, 256, dir); snprintf(ldir, 256, dir);
if (dp) return false; // <- this should never happen if (dp) return false; // <- this should never happen
if (fvx_opendir(&fdir, dir) != FR_OK) if (fvx_opendir(&fdir[0], dir) != FR_OK)
return false; return false;
dp = &fdir; dp = &fdir[0];
} else if (dp) { rec = recursive;
} else if (dp) { // traverse dir
FILINFO fno; FILINFO fno;
if ((fvx_preaddir(dp, &fno, lpattern) != FR_OK) || !*(fno.fname)) *path = '\0'; while ((fvx_preaddir(dp, &fno, lpattern) != FR_OK) || !*(fno.fname)) {
else snprintf(path, 256, "%s/%.254s", ldir, fno.fname); *path = '\0';
if (dp == fdir) return true;
fvx_closedir(dp--);
char* slash = strrchr(ldir, '/');
if (!slash) return false;
*slash = '\0';
}
snprintf(path, 256, "%s/%.254s", ldir, fno.fname);
if (rec && (fno.fattrib & AM_DIR) && (dp - fdir < _MAX_FOR_DEPTH - 1)) {
if (fvx_opendir(++dp, path) != FR_OK) dp--;
else strncpy(ldir, path, 255);
}
} else return false; } else return false;
return true; return true;
@ -833,7 +850,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
if (err_str) snprintf(err_str, _ERR_STR_LEN, "'for' inside 'for'"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "'for' inside 'for'");
syntax_error = true; syntax_error = true;
return false; return false;
} else if (!for_handler(NULL, argv[0], argv[1])) { } else if (!for_handler(NULL, argv[0], argv[1], flags & _FLG('r'))) {
if (err_str) snprintf(err_str, _ERR_STR_LEN, "dir not found"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "dir not found");
skip_state = _SKIP_TO_NEXT; skip_state = _SKIP_TO_NEXT;
ret = false; ret = false;
@ -854,9 +871,9 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
if (err_str) snprintf(err_str, _ERR_STR_LEN, "forpath error"); if (err_str) snprintf(err_str, _ERR_STR_LEN, "forpath error");
ret = false; ret = false;
} else { } else {
if (!for_handler(var, NULL, NULL)) *var = '\0'; if (!for_handler(var, NULL, NULL, false)) *var = '\0';
if (!*var) { if (!*var) {
for_handler(NULL, NULL, NULL); // finish for_handler for_handler(NULL, NULL, NULL, false); // finish for_handler
for_ptr = NULL; for_ptr = NULL;
skip_state = 0; skip_state = 0;
} else { } else {
@ -1708,7 +1725,7 @@ bool ExecuteGM9Script(const char* path_script) {
ifcnt = 0; // jumping into conditional block is unexpected/unsupported ifcnt = 0; // jumping into conditional block is unexpected/unsupported
jump_ptr = NULL; jump_ptr = NULL;
for_ptr = NULL; for_ptr = NULL;
for_handler(NULL, NULL, NULL); for_handler(NULL, NULL, NULL, false);
} else { } else {
ptr = line_end + 1; ptr = line_end + 1;
lno++; lno++;
@ -1721,7 +1738,7 @@ bool ExecuteGM9Script(const char* path_script) {
return false; return false;
} else if (for_ptr) { } else if (for_ptr) {
ShowPrompt(false, "%s\nend of script: unresolved 'for'", path_str); ShowPrompt(false, "%s\nend of script: unresolved 'for'", path_str);
for_handler(NULL, NULL, NULL); for_handler(NULL, NULL, NULL, false);
return false; return false;
} }

View File

@ -1,11 +1,16 @@
# GodMode9 "Spaghetti code sample" # GodMode9 "Spaghetti code sample"
# Tutorial script - read / run this to learn how it works # Tutorial script - read / run this to learn how it works
# last changed: 20171229 # last changed: 20180118
# author: d0k3 # author: d0k3
# quick preview mode as default, otherwise slow
set PREVIEW_MODE quick
# choose example to try # choose example to try
labelsel "Choose example" spaghetti_* labelsel -o -s "Choose example" spaghetti_*
goto outside
# if-else-elif-end sample code # if-else-elif-end sample code
@ -61,6 +66,47 @@ goto outside
@chooser @chooser
labelsel -o -s "Choose preview mode" choice_* labelsel -o -s "Choose preview mode" choice_*
goto chooser goto outside
# for-next sample code
@spaghetti_fornext_sample
echo "This will display entries\nfrom your SD card root."
if ask "Use recursive 'for'?"
for -r 0: *
# check type of current entry
set TYPE "File"
if isdir $[FORPATH]
set TYPE "Dir"
end
# output on screen
if not ask "$[TYPE]: $[FORPATH]\nShow next entry?"
goto outside
end
next
else
for 0: * # everything inside the loop pretty much identical to above
# check type of current entry
set TYPE "File"
if isdir $[FORPATH]
set TYPE "Dir"
end
# output on screen
if not ask "$[TYPE]: $[FORPATH]\nShow next entry?"
goto outside
end
if not exist $[FORPATH]
echo "WTF" # can never happen here, just a demonstration
end
next
end
goto outside
@outside @outside