2022-07-20 22:13:39 +00:00
|
|
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
2023-12-08 03:11:56 +00:00
|
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
2022-07-20 22:13:39 +00:00
|
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
|
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
|
|
|
│ │
|
|
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
|
|
│ │
|
|
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
|
|
#include "libc/calls/struct/stat.h"
|
|
|
|
#include "libc/errno.h"
|
|
|
|
#include "libc/sysv/consts/at.h"
|
|
|
|
#include "libc/sysv/consts/s.h"
|
|
|
|
#include "third_party/lua/lauxlib.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @fileoverview redbean unix path manipulation module
|
|
|
|
*/
|
|
|
|
|
|
|
|
// path.basename(str)
|
|
|
|
// └─→ str
|
|
|
|
static int LuaPathBasename(lua_State *L) {
|
|
|
|
size_t i, n;
|
|
|
|
const char *p;
|
|
|
|
if ((p = luaL_optlstring(L, 1, 0, &n)) && n) {
|
|
|
|
while (n > 1 && p[n - 1] == '/') --n;
|
|
|
|
i = n - 1;
|
|
|
|
while (i && p[i - 1] != '/') --i;
|
|
|
|
lua_pushlstring(L, p + i, n - i);
|
|
|
|
} else {
|
|
|
|
lua_pushlstring(L, ".", 1);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// path.dirname(str)
|
|
|
|
// └─→ str
|
|
|
|
static int LuaPathDirname(lua_State *L) {
|
|
|
|
size_t n;
|
|
|
|
const char *p;
|
|
|
|
if ((p = luaL_optlstring(L, 1, 0, &n)) && n--) {
|
|
|
|
for (; p[n] == '/'; n--) {
|
|
|
|
if (!n) goto ReturnSlash;
|
|
|
|
}
|
|
|
|
for (; p[n] != '/'; n--) {
|
|
|
|
if (!n) goto ReturnDot;
|
|
|
|
}
|
|
|
|
for (; p[n] == '/'; n--) {
|
|
|
|
if (!n) goto ReturnSlash;
|
|
|
|
}
|
|
|
|
lua_pushlstring(L, p, n + 1);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
ReturnDot:
|
|
|
|
lua_pushlstring(L, ".", 1);
|
|
|
|
return 1;
|
|
|
|
ReturnSlash:
|
|
|
|
lua_pushlstring(L, "/", 1);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// path.join(str, ...)
|
|
|
|
// └─→ str
|
|
|
|
static int LuaPathJoin(lua_State *L) {
|
|
|
|
int i, n;
|
|
|
|
size_t z;
|
|
|
|
bool gotstr;
|
|
|
|
const char *c;
|
|
|
|
luaL_Buffer b;
|
|
|
|
bool needslash;
|
|
|
|
if ((n = lua_gettop(L))) {
|
|
|
|
luaL_buffinit(L, &b);
|
|
|
|
gotstr = false;
|
|
|
|
needslash = false;
|
|
|
|
for (i = 1; i <= n; ++i) {
|
|
|
|
if (lua_isnoneornil(L, i)) continue;
|
|
|
|
gotstr = true;
|
|
|
|
c = luaL_checklstring(L, i, &z);
|
|
|
|
if (z) {
|
|
|
|
if (c[0] == '/') {
|
|
|
|
luaL_buffsub(&b, luaL_bufflen(&b));
|
|
|
|
} else if (needslash) {
|
|
|
|
luaL_addchar(&b, '/');
|
|
|
|
}
|
|
|
|
luaL_addlstring(&b, c, z);
|
|
|
|
needslash = c[z - 1] != '/';
|
|
|
|
} else if (needslash) {
|
|
|
|
luaL_addchar(&b, '/');
|
|
|
|
needslash = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (gotstr) {
|
|
|
|
luaL_pushresult(&b);
|
|
|
|
} else {
|
|
|
|
lua_pushnil(L);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
luaL_error(L, "missing argument");
|
2023-06-09 06:44:03 +00:00
|
|
|
__builtin_unreachable();
|
2022-07-20 22:13:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CheckPath(lua_State *L, int type, int flags) {
|
|
|
|
int olderr;
|
|
|
|
struct stat st;
|
|
|
|
const char *path;
|
|
|
|
path = luaL_checkstring(L, 1);
|
|
|
|
olderr = errno;
|
|
|
|
if (fstatat(AT_FDCWD, path, &st, flags) != -1) {
|
|
|
|
lua_pushboolean(L, !type || (st.st_mode & S_IFMT) == type);
|
|
|
|
} else {
|
|
|
|
errno = olderr;
|
|
|
|
lua_pushboolean(L, false);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// path.exists(str)
|
|
|
|
// └─→ bool
|
|
|
|
static int LuaPathExists(lua_State *L) {
|
|
|
|
return CheckPath(L, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// path.isfile(str)
|
|
|
|
// └─→ bool
|
|
|
|
static int LuaPathIsfile(lua_State *L) {
|
|
|
|
return CheckPath(L, S_IFREG, AT_SYMLINK_NOFOLLOW);
|
|
|
|
}
|
|
|
|
|
|
|
|
// path.islink(str)
|
|
|
|
// └─→ bool
|
|
|
|
static int LuaPathIslink(lua_State *L) {
|
|
|
|
return CheckPath(L, S_IFLNK, AT_SYMLINK_NOFOLLOW);
|
|
|
|
}
|
|
|
|
|
|
|
|
// path.isdir(str)
|
|
|
|
// └─→ bool
|
|
|
|
static int LuaPathIsdir(lua_State *L) {
|
|
|
|
return CheckPath(L, S_IFDIR, AT_SYMLINK_NOFOLLOW);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const luaL_Reg kLuaPath[] = {
|
|
|
|
{"basename", LuaPathBasename}, //
|
|
|
|
{"dirname", LuaPathDirname}, //
|
|
|
|
{"exists", LuaPathExists}, //
|
|
|
|
{"isdir", LuaPathIsdir}, //
|
|
|
|
{"isfile", LuaPathIsfile}, //
|
|
|
|
{"islink", LuaPathIslink}, //
|
|
|
|
{"join", LuaPathJoin}, //
|
|
|
|
{0}, //
|
|
|
|
};
|
|
|
|
|
|
|
|
int LuaPath(lua_State *L) {
|
|
|
|
luaL_newlib(L, kLuaPath);
|
|
|
|
return 1;
|
|
|
|
}
|