From 522bfe5be1212f356acbbd7d566092f8bd8d0748 Mon Sep 17 00:00:00 2001 From: TSaaristo Date: Mon, 11 Dec 2017 23:04:51 +0200 Subject: lua+js: implement utils.file_info() This commit introduces mp.utils.file_info() for querying information on file paths, implemented for both Lua and Javascript. The function takes a file path as an argument and returns a Lua table / JS object upon success. The table/object will contain the values: mode, size, atime, mtime, ctime and the convenience booleans is_file, is_dir. On error, the Lua side will return `nil, error` and the Javascript side will return `undefined` (and mark the last error). This feature utilizes the already existing cross-platform `mp_stat()` function. --- DOCS/man/javascript.rst | 2 ++ DOCS/man/lua.rst | 28 ++++++++++++++++++++++++++++ player/javascript.c | 36 ++++++++++++++++++++++++++++++++++++ player/lua.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+) diff --git a/DOCS/man/javascript.rst b/DOCS/man/javascript.rst index 995899d07b..419f473e2c 100644 --- a/DOCS/man/javascript.rst +++ b/DOCS/man/javascript.rst @@ -168,6 +168,8 @@ Otherwise, where the Lua APIs return ``nil`` on error, JS returns ``undefined``. ``mp.utils.readdir(path [, filter])`` (LE) +``mp.utils.file_info(path)`` (LE) + ``mp.utils.split_path(path)`` ``mp.utils.join_path(p1, p2)`` diff --git a/DOCS/man/lua.rst b/DOCS/man/lua.rst index 831981db06..5fe7507049 100644 --- a/DOCS/man/lua.rst +++ b/DOCS/man/lua.rst @@ -591,6 +591,34 @@ strictly part of the guaranteed API. On error, ``nil, error`` is returned. +``utils.file_info(path)`` + Stats the given path for information and returns a table with the + following entries: + + ``mode`` + protection bits (on Windows, always 755 (octal) for directories + and 644 (octal) for files) + ``size`` + size in bytes + ``atime`` + time of last access + ``mtime`` + time of last modification + ``ctime`` + time of last metadata change (Linux) / time of creation (Windows) + ``is_file`` + Whether ``path`` is a regular file (boolean) + ``is_dir`` + Whether ``path`` is a directory (boolean) + + ``mode`` and ``size`` are integers. + Timestamps (``atime``, ``mtime`` and ``ctime``) are integer seconds since + the Unix epoch (Unix time). + The booleans ``is_file`` and ``is_dir`` are provided as a convenience; + they can be and are derived from ``mode``. + + On error (eg. path does not exist), ``nil, error`` is returned. + ``utils.split_path(path)`` Split a path into directory component and filename component, and return them. The first return value is always the directory. The second return diff --git a/player/javascript.c b/player/javascript.c index 14c1872d67..3de900bdb1 100644 --- a/player/javascript.c +++ b/player/javascript.c @@ -836,6 +836,41 @@ static void script_readdir(js_State *J, void *af) } } +static void script_file_info(js_State *J) +{ + const char *path = js_tostring(J, 1); + + struct stat statbuf; + if (stat(path, &statbuf) != 0) { + push_failure(J, "Cannot stat path"); + return; + } + // Clear last error + set_last_error(jctx(J), 0, NULL); + + const char * stat_names[] = { + "mode", "size", + "atime", "mtime", "ctime", NULL + }; + const double stat_values[] = { + statbuf.st_mode, + statbuf.st_size, + statbuf.st_atime, + statbuf.st_mtime, + statbuf.st_ctime + }; + // Create an object and add all fields + push_nums_obj(J, stat_names, stat_values); + + // Convenience booleans + js_pushboolean(J, S_ISREG(statbuf.st_mode)); + js_setproperty(J, -2, "is_file"); + + js_pushboolean(J, S_ISDIR(statbuf.st_mode)); + js_setproperty(J, -2, "is_dir"); +} + + static void script_split_path(js_State *J) { const char *p = js_tostring(J, 1); @@ -1255,6 +1290,7 @@ static const struct fn_entry main_fns[] = { static const struct fn_entry utils_fns[] = { AF_ENTRY(readdir, 2), + FN_ENTRY(file_info, 1), FN_ENTRY(split_path, 1), AF_ENTRY(join_path, 2), AF_ENTRY(get_user_path, 1), diff --git a/player/lua.c b/player/lua.c index cf754b661b..e084624ccf 100644 --- a/player/lua.c +++ b/player/lua.c @@ -1085,6 +1085,48 @@ static int script_readdir(lua_State *L) return 1; } +static int script_file_info(lua_State *L) +{ + const char *path = luaL_checkstring(L, 1); + + struct stat statbuf; + if (stat(path, &statbuf) != 0) { + lua_pushnil(L); + lua_pushstring(L, "error"); + return 2; + } + + lua_newtable(L); // Result stat table + + const char * stat_names[] = { + "mode", "size", + "atime", "mtime", "ctime", NULL + }; + const unsigned int stat_values[] = { + statbuf.st_mode, + statbuf.st_size, + statbuf.st_atime, + statbuf.st_mtime, + statbuf.st_ctime + }; + + // Add all fields + for (int i = 0; stat_names[i]; i++) { + lua_pushinteger(L, stat_values[i]); + lua_setfield(L, -2, stat_names[i]); + } + + // Convenience booleans + lua_pushboolean(L, S_ISREG(statbuf.st_mode)); + lua_setfield(L, -2, "is_file"); + + lua_pushboolean(L, S_ISDIR(statbuf.st_mode)); + lua_setfield(L, -2, "is_dir"); + + // Return table + return 1; +} + static int script_split_path(lua_State *L) { const char *p = luaL_checkstring(L, 1); @@ -1291,6 +1333,7 @@ static const struct fn_entry main_fns[] = { static const struct fn_entry utils_fns[] = { FN_ENTRY(readdir), + FN_ENTRY(file_info), FN_ENTRY(split_path), FN_ENTRY(join_path), FN_ENTRY(subprocess), -- cgit v1.2.3