Skip to content

Instantly share code, notes, and snippets.

@katlogic
Created June 14, 2014 14:57
Show Gist options
  • Save katlogic/a989dfe1fc4062a9ad13 to your computer and use it in GitHub Desktop.
Save katlogic/a989dfe1fc4062a9ad13 to your computer and use it in GitHub Desktop.
#define DEF_ERR(_) \
_(EPERM)_(ENOENT)_(ESRCH)_(EINTR)_(EIO)_(ENXIO)_(E2BIG)_(ENOEXEC) \
_(EBADF)_(ECHILD)_(EAGAIN)_(ENOMEM)_(EACCESS)_(EFAULT)_(ENOTBLK) \
_(EBUSY)_(EEXIST)_(EXDEV)_(ENODEV)_(ENOTDIR)_(EISDIR)_(EINVAL) \
_(ENFILE)_(EMFILE)_(ENOTTY)_(ETXTBSY)_(EFBIG)_(ENOSPC)_(ESPIPE) \
_(EROFS)_(EMLINK)_(EPIPE)_(EDOM)_(ENAMETOOLONG)_(ENOSYS)_(ELOOP) \
_(EAGAIN)
#define DEF_SYMS(_) \
_(SEEK_SET)_(SEEK_CUR)_(SEEK_END) \
_(F_OK)_(R_OK)_(W_OK)_(X_OK) \
_(S_IFMT)_(S_IFSOCK)_(S_IFLNK)_(S_IFREG) \
_(S_IFBLK)_(S_IFDIR)_(S_IFCHR)_(S_IFIFO) \
_(S_ISUID)_(S_ISGID)_(S_ISVTX)_(S_IRWXU) \
_(S_IRUSR)_(S_IWUSR)_(S_IXUSR)_(S_IRWXG) \
_(S_IRGRP)_(S_IWGRP)_(S_IXGRP)_(S_IRWXO) \
_(S_IROTH)_(S_IWOTH)_(S_IXOTH) \
_(AT_SYMLINK_NOFOLLOW)_(AT_REMOVEDIR) \
_(AT_SYMLINK_FOLLOW)_(AT_EACCESS) \
_(AT_NO_AUTOMOUNT)_(AT_EMPTY_PATH) \
_(O_SEARCH)_(O_EXEC)_(O_PATH) \
_(O_ACCMODE)_(O_RDONLY)_(O_WRONLY) \
_(O_RDWR)_(F_RDLCK)_(F_WRLCK)_(F_UNLCK) \
_(FD_CLOEXEC) \
_(POSIX_FADV_NORMAL)_(POSIX_FADV_RANDOM) \
_(POSIX_FADV_SEQUENTIAL)_(POSIX_FADV_WILLNEED) \
_(POSIX_FADV_DONTNEED)_(POSIX_FADV_NOREUSE) \
_(F_ULOCK)_(F_LOCK)_(F_TLOCK)_(F_TEST) \
_(F_SETLEASE)_(F_GETLEASE)_(F_NOTIFY) \
_(F_CANCELLK)_(F_SETPIPE_SZ)_(F_GETPIPE_SZ) \
_(DN_ACCESS)_(DN_MODIFY)_(DN_CREATE) \
_(DN_DELETE)_(DN_RENAME)_(DN_ATTRIB) \
_(DN_MULTISHOT) \
_(RLIMIT_CORE)_(RLIMIT_CPU)_(RLIMIT_DATA) \
_(RLIMIT_FSIZE)_(RLIMIT_NOFILE)_(RLIMIT_STACK) \
_(RLIMIT_AS) \
_(FNM_PATHNAME)_(FNM_NOESCAPE)_(FNM_PERIOD) \
_(FNM_LEADING_DIR)_(FNM_CASEFOLD)_(FNM_FILE_NAME) \
_(FNM_NOMATCH)
/* Mini-libiberty */
#ifndef HAVE_LIBIBERTY
static const char *strerrno(int err)
{
#define SWITCH_AND_RET(e) case e: return #e;
static char buf[16];
switch (err) {
DEF_ERR(SWITCH_AND_RET)
default:
sprintf(buf, "E%d", err);
return buf;
}
}
#endif
/* Template for errors. */
#define PUSHERR \
lua_pushnil(L); \
lua_pushstring(L, strerror(err)); \
lua_pushinteger(err); \
lua_pushstring(strerrno(err)); \
return 4;
#define DOERR { \
int err = errno; \
PUSHERR
}
/* Tamplate for most APIs. */
#define CHECK(fn, ...) \
int res = fn(__VA_ARGS__) \
if (res < 0) \
DOERR;
#define API(n, ...) \
static int fs_##n(lua_State *L) { \
HEADER \
CHECK(n, __VA_ARGS__) \
FOOTER \
}
/*
* Functions returning result (if no error)
*/
#define HEADER
#define FOOTER lua_puhsinteger(L, res); return 1;
API(access, STR, OPTINT(F_OK))
API(alarm, INT)
API(acct, STR)
API(chdir, STR)
API(chown, STR, INT, INT)
API(close, INT)
API(faccessat, INT, STR, INT, OPTINT(F_OK))
API(fchdir, INT)
API(fchown, INT, INT, INT)
API(fchownat, INT, STR, INT, INT, OPTINT(0))
API(fdatasync, INT)
API(fsync, INT)
API(ftruncate, INT, NUM)
API(lchown, STR, INT, INT)
API(link, STR, STR)
API(linkat, INT, STR, INT, STR, OPTINT(0))
API(lseek, INT, NUM, OPTINT(SEEK_END))
API(rename, STR, STR)
API(renameat, INT, STR, INT, STR)
API(rmdir, STR)
API(setegid, INT)
API(seteuid, INT)
API(setgid, INT)
API(setpgid, INT, INT)
API(setregid, INT, INT)
API(setresgid, INT, INT, INT)
API(setresuid, INT, INT, INT)
API(setreuid, INT, INT)
API(setuid, INT)
API(sleep, INT)
API(usleep, INT)
API(symlink, STR, STR)
API(symlinkat, STR, INT, STR)
API(tcsetpgrp, INT, INT)
API(truncate, STR, NUM)
API(chmod, STR, INT)
API(fchmod, INT, INT)
API(fchmodat, INT, STR, INT, OPTINT(0))
API(lchmod, STR, INT)
API(unlink, STR)
API(mkdir, STR, OPTINT(0755))
API(mkdirat, INT, STR, OPTINT(0755))
API(mkfifo, STR, OPTINT(0644))
API(mkfifoat, INT, STR, OPTINT(0644))
API(mknod, STR, OPTINT(0644)|S_IFIFO, OPTINT(0))
API(mknodat, INT, STR, OPTINT(0644)|S_IFIFO, OPTINT(0))
API(grantpt, INT)
API(unlockpt, INT)
API(dup, INT)
API(dup2, INT, INT)
API(getpgid, INT)
API(getsid, OPTINT(0))
API(isatty, OPTINT(0))
API(nice, OPINT(20))
API(open, STR, OPTINT(O_RDONLY), OPTINT(0755))
API(openat, INT, STR, OPTINT(O_RDONLY), OPTINT(0755))
API(posix_openpt, OPTINT(O_RDWR))
API(tcgetpgrp, INT)
API(umask)
API(getegid,)
API(geteuid,)
API(getpgrp,)
API(getpid,)
API(getppid,)
API(getuid,)
API(setpgrp,)
API(setsid,)
/*
* Functions returning string (with buffer as input)
*/
#define HEADER char buf[PATH_MAX+1];
#define FOOTER buf[PATH_MAX] = 0; lua_pushstring(L, buf); return 1
#define CHECK(fn, realfn, ...) \
const char *res = realfn(__VA_ARGS__) \
if (!res) \
DOERR;
API(getcwd, buf, PATH_MAX)
API(basename, strcpy(buf, STR))
API(dirname, strcpy(buf, STR))
API(readlink, STR, buf, PATH_MAX)
API(realpath, STR, buf)
API(readlinkat, INT, STR, buf, PATH_MAX)
API(mkstemp, strcpy(buf, STR))
API(mkdtemp, strcpy(buf, STR))
API(mkstemps, strcpy(buf, STR), INT)
API(tmpnam, strcpy(buf, STR))
API(ctermid,)
/* Slightly renamed */
#undef CHECK
#define CHECK(fn, realfn, ...) \
const char *res = realfn(__VA_ARGS__) \
if (!res) \
DOERR;
API(ttyname, ttyname_r, INT, buf, sizeof(buf))
API(ptsname, ptsname_r, INT, buf, sizeof(buf))
API(getlogin, getlogin_r, buf, sizeof(buf))
/*
* Get file/fs status.
*/
#undef HEADER
#undef FOOTER
#define HEADER struct stat st;
#define FOOTER return stat_convert(L, &st);
#define PUSH lua_pushvalue(L, -1); \
lua_setfield(L, -3, #PUSHER_STRUCT "_" #n);lua_rawseti(L, -2, ++counter);
#define PUSH_NUMS(n) lua_pushnumber(L, \
PUSHER_STRUCT->##PUSHER_STRUCT##_##n); PUSH
#define PUSH_STRINGS(n) lua_pushstring(L, \
PUSHER_STRUCT->##PUSHER_STRUCT##_##n); PUSH
#define ST_FIELDS(N) \
N(dev) N(ino) N(mode) N(nlink) N(uid) \
N(gid) N(rdev) N(size) N(blksize)N(blocks) \
N(atime)N(mtime)N(ctime)
static int stat_convert(lua_State *L, const struct stat *st)
{
/* Poop. */
int counter = 0;
lua_createtable(L, 14, 14)
#define PUSHER_STRUCT st
ST_FIELDS(PUSH_NUMS)
#undef PUSHER_STRUCT
return 1;
}
API(stat, STR)
API(fstat, INT)
API(lstat, INT)
#define F_FIELDS(N) \
N(bsize)N(frsize) N(blocks) N(bfree)N(bavail) \
N(files)N(ffree) N(favail) N(fsid) N(flag) \
N(fnamemax)
static int statvfs_convert(lua_State *L, const struct statvfs *f)
{
/* Poop. */
int counter = 0;
lua_createtable(L, 13, 13)
#define PUSHER_STRUCT f
F_FIELDS(PUSH_NUMS)
#undef PUSHER_STRUCT
return 1;
}
#undef FOOTER
#define FOOTER return statvfs_convert(L, &st);
API(statvfs, STR)
API(fstatvfs, INT)
/*
* Miscalleneous functions which don't fit the macro templates very well.
*/
#undef API
#define API(n) \
static int fs_##n(lua_State *L)
API(pause) { pause(); return 0; }
API(sync) { sync(); return 0; }
API(abort) { abort(); return 0; }
/*
* File IO.
*/
API(pipe) {
int p[2];
CHECK(pipe, p);
lua_pushinteger(p[0]);
lua_pushinteger(p[1]);
return 2;
}
API(pread) {
int fd = luaL_checkint(L, 1);
ssize_t got;
size_t pfxsz = 0;
const char *pfx = lua_tolstring(L, 2, &pfxsz);
size_t nbyte = luaL_optint(L, 3, 4096);
char buf[nbyte+pfxsz]; /* C99 */
lua_Number off = luaL_optnumber(L, 4, -1);
if (pfx)
memcpy(buf, pfx, pfxsz);
if (off >= 0) {
got = pread(fd, buf + pfxsz, nbyte, off);
} else {
got = read(fd, buf + pfxsz, nbyte);
}
if (got < 0)
DOERR;
lua_pushlstring(L, buf, pfxsz + got);
lua_pushnumber(L, got);
return 2;
}
API(read) {
lua_settop(L, 3);
return fs_pread(L);
}
API(pwrite) {
int fd = luaL_checkint(L, 1);
ssize_t got;
size_t nbyte = 0;
const char *buf = lua_checklstring(L, 2, &pfxsz);
nbyte = luaL_optint(L, 3, nbyte);
lua_Number off = luaL_optnumber(L, 4, -1);
if (off >= 0) {
got = pwrite(fd, buf, nbyte, off);
} else {
got = read(fd, buf, nbyte);
}
if (got < 0)
DOERR;
lua_pushnumber(L, got);
return 1;
}
API(write) {
lua_settop(L, 3);
return fs_pwrite(L);
}
API(fileno) {
FILE *f = *(FILE**) luaL_checkudata(L, 1, LUA_FILEHANDLE);
lua_pushinteger(L, fileno(f));
return 1;
}
/*
* Time handling.
*/
API(gettimeofday)
{
struct timeval tv;
gettimeofday(&tv, NULL);
lua_pushinteger(L, tv.tv_sec);
lua_pushinteger(L, tv.tv_usec);
return 2;
}
API(time)
{
lua_pushinteger(L, time(NULL));
return 1;
}
#define TM_FIELDS(N) \
N(sec) N(min) N(hour)N(mday)N(mon) \
N(year) N(wday) N(yday)N(isdst)
static int tm2tab(lua_State *L, struct tm *tm)
{
int counter = 0;
if (!tm)
DOERR;
lua_createtable(L, 10, 10)
#define PUSHER_STRUCT tm
TM_FIELDS(PUSH_NUMS)
#undef PUSHER_STRUCT
return 1;
}
static struct tm *tab2tm(lua_State *L, int idx, struct tm *tm)
{
#define GET_TM(n) \
lua_getfield(L, idx, "tm_" #n); tm->n = lua_tointeger(L, -1); lua_pop(L, 1);
TM_FIELDS(GET_TM)
}
API(localtime)
{
time_t t = luaL_checkint(L, 1);
struct tm tm;
return tm2tab(L, localtime_r(&t, &tm));
}
API(gmtime)
{
time_t t = luaL_checkint(L, 1);
struct tm tm;
return tm2tab(L, gmtime_r(&t, &tm));
}
API(strftime)
{
char buf[BUFSZ];
struct tm tm;
if (lua_isniL(L, 2)) {
t = time(NULL);
localtime_r(&t, &tm);
} else {
tab2tm(L, 1, &tm);
}
lua_pushlstring(L, buf,
strftime(buf, sizeof(buf), luaL_checkstring(L, 1), &tm));
return 1;
}
/*
* Access to passwd/group files.
*/
#define PW_FIELDS(S,N) \
S(name) S(passwd) N(uid) N(gid) \
S(gecos) S(dir) S(shell)
static int pw2tab(lua_State *L, struct passwd **pwp, int xerr)
{
int counter = 0;
struct passwd *pw = *pwp;
if (xerr < 0)
DOERR;
if (!pw)
PUSHERR;
lua_createtable(L, 8, 8);
#define PUSHER_STRUCT pw
PW_FIELDS(PUSH_NUMS, PUSH_STRINGS)
#undef PUSHER_STRUCT
return 1;
}
API(getpwnam)
{
struct passwd pw, *pwp = NULL;
char bufs[BUFSZ];
return pw2tab(L, &pwp, xerr,
getpwnam_r(luaL_checkstring(L, 1),
&pw, bufs, sizeof(bufs), &pwp));
}
API(getpwuid)
{
struct passwd pw, *pwp = NULL;
char bufs[BUFSZ];
return pw2tab(L, &pwp, xerr,
getpwuid_r(luaL_checkint(L, 1),
&pw, bufs, sizeof(bufs), &pwp));
}
static int gr2tab(lua_State *L, struct group **grp, int xerr)
{
int counter = 0;
struct group *gr = *pwp;
int i;
if (xerr < 0)
DOERR;
if (!gr)
PUSHERR;
lua_newtable(L, 0, 0);
lua_pushstring(L, gr->gr_name); lua_setfield(L, -2, "gr_name");
lua_pushstring(L, gr->gr_passwd); lua_setfield(L, -2, "gr_passwd");
lua_pushinteger(L, gr->gr_grgid); lua_setfield(L, -2, "gr_grgid");
lua_newtable(L)
for (i = 0; gr->gr_mem[i]; i++) { // Table of members.
lua_pushstring(L, gr->gr_mem[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "gr_mem");
return 1;
}
API(getgrnam)
{
struct group gr, *grp = NULL;
char bufs[BUFSZ];
return gr2tab(L, &grp, xerr,
getgrnam_r(luaL_checkstring(L, 1),
&gr, bufs, sizeof(bufs), &grp));
}
API(getgruid)
{
struct group gr, *grp = NULL;
char bufs[BUFSZ];
return gr2tab(L, &grp, xerr,
getgruid_r(luaL_checkint(L, 1),
&gr, bufs, sizeof(bufs), &grp));
}
API(getgroups) {
int i;
gid_t grouplist[NGROUPS_MAX+1];
CHECK(getgroups, NGROUPS_MAX, grouplist);
lua_createtable(L, i, 0);
for (i = 0; i < res; i++) {
lua_pushinteger(L, grouplist[i]);
lua_rawseti(L, -2, i+1);
}
return 1;
}
#define IMPLEMENTS(_) \
_(strftime)_(gmtime)_(localtime)_(time)_(gettimeofday)_(fileno) \
_(write)_(pwrite)_(read)_(pread)_(pipe)_(getgroups)_(abort) \
_(sync)_(pause)_(lstat)_(fstat)_(stat)_(ptsname)_(ttyname) \
_(tmpname)_(mkstemps)_(mkdtemp)_(mkstemp)_(readlinkat) \
_(realpath)_(readlink)_(dirname)_(basename)_(getcwd) \
_(access)_(alarm)_(acct)_(chdir)_(chown)_(close) \
_(faccessat)_(fchdir)_(fchown)_(fchownat)_(fdatasync) \
_(fsync)_(ftruncate)_(lchown)_(link)_(linkat)_(lseek) \
_(rename)_(renameat)_(rmdir)_(setegid)_(seteuid) \
_(setgid)_(setpgid)_(setregid)_(setresgid)_(setresuid) \
_(setuid)_(sleep)_(usleep)_(symlink)_(symlinkat) \
_(tcsetpgrp)_(truncate)_(chmod)_(fchmod)_(fchmodat) \
_(lchmod)_(unlink)_(mkdir)_(mkdirat)_(mkfifo)_(mkfifoat) \
_(mknod)_(mknodat)_(grantpt)_(unlockpt)_(dup)_(dup2) \
_(getpgid)_(getsid)_(isatty)_(nice)_(open)_(openat) \
_(posix_openpt)_(tcgetpgrp)_(umask) \
_(getegid)_(geteuid)_(getpgrp)_(getpid)_(getppid) \
_(getuid)_(setpgrp)_(setsid)_(getpwnam)
#define REG(n) \
{ #n, fs_##n },
static const luaL_Reg[] = {
IMPLEMENTS(REG)
{ NULL, NULL }
};
int luaopen_unistd()
{
lua_newtable(L);
#define PUSH_CONST(n) lua_pushnumber(L, n) lua_setfield(L, -2, #n)
DEF_ERR(PUSH_CONSTS)
DEF_SYMS(PUSH_CONSTS)
luaL_register(L, NULL, unistd_reg);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment