-
Notifications
You must be signed in to change notification settings - Fork 3.1k
file: list now takes optional pattern for filtering #2452
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
P.S. please be gentle, but please do review this for sanity and proper stack handling; it's only my second or third time programming against the Lua FFI. |
docs/en/modules/file.md
Outdated
| a Lua table which contains the {file name: file size} pairs | ||
| a Lua table which contains all {file name: file size} pairs, if no pattern | ||
| given. If a pattern is given, only those file names matching the pattern | ||
| (interpreted as a traditional Lua pattern, not, say, a UNIX shell glob) will be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Linking to https://www.lua.org/pil/20.2.html or http://lua-users.org/wiki/PatternsTutorial here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. 👍
c417644 to
c20d02a
Compare
| lua_pushstring( L, stat.name ); | ||
| lua_pushvalue( L, -4 ); | ||
| /* Call transforms stack from T P F F N P to T P F Result */ | ||
| pcres = lua_pcall( L, 2, 1, 0 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't do a lua_pcall(), in this case just do a plain lua_call() is more appropriate. If the app programmer can't use a valid pattern then the correct thing to do is to let lstrlib.c throw a Lua error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that's not sufficient. Lua will longjmp over our activation frame and not give us the opportunity to call vfs_closedir, which might imply a leak of a directory iterator. If not in SPIFFS or FATFS, some day, all the same.
| /* If we're doing a match, push the Pattern and Function */ | ||
| if (pattern != emptystr) { | ||
| lua_pushstring( L, pattern ); | ||
| /* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why push this here? It was the call arg, so is already on the stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough. I suppose luaL_checkoptstring just returns what's already on the stack.
| int res = 1; | ||
|
|
||
| pattern = luaL_optlstring(L, 1, emptystr, NULL); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just use NULL as the default and this works fine. It also makes the following code easier. Also it is often just easier to fix the stack length so a lua_settop(L,1); will add the add the nil is missing and trim excess args so that you can now use base relative rather than top relative addressing. A lot easier to follow, IMO.
app/modules/file.c
Outdated
| lua_pop( L, 1 ); | ||
| } else { | ||
| /* Stack just holds result table at the top; add result */ | ||
| lua_pushinteger( L, stat.size ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these to push/setfield calls can be folded easily
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I debated doing that. I can, if you like.
|
Nathaniel, just out of interest, here was my attempt. Does this make sense to you? I know the manual talks as if it is a stack, but sometimes its just a lot simpler to base relative addressing. static int file_list( lua_State* L ) {
vfs_dir *dir;
const char *pattern;
struct vfs_stat stat;
/* make sure the stack contains exactly one string or nil */
lua_settop(L, 1);
pattern = luaL_optstring(L, 1, NULL); /* pattern at 1 */
if ( (dir = vfs_pendir("")) == NULL) {
return 0;
}
/* Allocate result table T */
lua_newtable( L ); /* table at 2 */
/* If we're doing a match, push the string.match function */
if (pattern) {
luaL_getmetafield( L, 1, "match" ); /* function at 3 */
}
while (vfs_readdir(dir, &stat) == VFS_RES_OK) {
if (pattern) {
lua_settop( L, 3 );
/* string.match(stat_name, pattern */
lua_pushvalue( L, 3 ); /* function at 4 */
lua_pushstring( L, stat.name ); /* filename at 5 */
lua_pushvalue( L, 1 ); /* pattern at 6 */
lua_call( L, 2, 1 ); /* result at 4 */
if (lua_isnil( L, -1 )) {
continue;
}
}
lua_pushinteger( L, stat.size );
lua_setfield( L, 2, stat.name );
}
lua_remove( L, 1 ); /* remove pattern at 1, so table now at 1 */
vfs_closedir(dir);
return 1;
} |
|
@TerryE Thanks for that. I reworked mine in light of yours and re-ran the tests I enumerated above. |
c20d02a to
b83ecf7
Compare
|
@nwf, it takes a bit to get your head around using the API, but once you do, it is really quite potent. The more of us the merrier. Are you using the gdb stub? It's really quite good. This is one time that I use esplorer, because I am cycling reboot/tweak and start script in explorer and then toogle into the xtensa gdb. Also note the |
| lua_pushvalue( L, 3 ); | ||
| lua_pushstring( L, stat.name ); | ||
| lua_pushvalue( L, 1 ); | ||
| pcres = lua_pcall( L, 2, 1, 0 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really the wrong thing to do. You should just use a lua_call() and let the lua error system do its stuff. So for example if you do something like:
for name, len in pairs(files.list('[a-f]%')) do`you want it to throw a malformed pattern (ends with '%') rather than a bad argument #1 to 'pairs' (table expected, got number). 9 times out of 10 the pattern is programmed, and on the 10th time, the programmer can always wrap the file.list(filter) in a pcall()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I said above, I do not believe that longjump-ing out of file.list() is correct, as that will not call vfs_closedir() on its way out. I've reworked the patch to use lua_error to re-throw the exception.
Thanks to @TerryE for many useful suggestions
b83ecf7 to
0343283
Compare
|
Sorry, missed that. In which case you don't need the goto. Just do the closedir and throw the error inline in the pcall status test code. |
|
@TerryE The goto's gone; mind taking another look? |
See #2440.
devbranch rather than formaster.docs/en/*.This adds an optional pattern to
file.list()to filter results before handing them to Lua. This optimizes the relatively common pattern ofinto the more concise, faster, and less-memory-consuming
(neglecting the possibility that pattern might raise an error. If that happens,
file.list(pattern)will now returnnil, errorstr, as typical).This has been tested by running comprehensive test cases: