426 lines
9.3 KiB
C
426 lines
9.3 KiB
C
/*
|
|
* nixio - Linux I/O library for lua
|
|
*
|
|
* Copyright (C) 2009 Steven Barth <steven@midlink.org>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "nixio.h"
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/param.h>
|
|
|
|
|
|
static int nixio_open(lua_State *L) {
|
|
const char *filename = luaL_checklstring(L, 1, NULL);
|
|
int flags;
|
|
|
|
if (lua_isnoneornil(L, 2)) {
|
|
flags = O_RDONLY;
|
|
} else if (lua_isnumber(L, 2)) {
|
|
flags = lua_tointeger(L, 2);
|
|
} else if (lua_isstring(L, 2)) {
|
|
const char *str = lua_tostring(L, 2);
|
|
if (!strcmp(str, "r")) {
|
|
flags = O_RDONLY;
|
|
} else if (!strcmp(str, "r+")) {
|
|
flags = O_RDWR;
|
|
} else if (!strcmp(str, "w")) {
|
|
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
|
} else if (!strcmp(str, "w+")) {
|
|
flags = O_RDWR | O_CREAT | O_TRUNC;
|
|
} else if (!strcmp(str, "a")) {
|
|
flags = O_WRONLY | O_CREAT | O_APPEND;
|
|
} else if (!strcmp(str, "a+")) {
|
|
flags = O_RDWR | O_CREAT | O_APPEND;
|
|
} else {
|
|
return luaL_argerror(L, 2, "supported values: r, r+, w, w+, a, a+");
|
|
}
|
|
} else {
|
|
return luaL_argerror(L, 2, "open flags or string expected");
|
|
}
|
|
|
|
int fd;
|
|
|
|
do {
|
|
fd = open(filename, flags, nixio__check_mode(L, 3, 0666));
|
|
} while (fd == -1 && errno == EINTR);
|
|
if (fd == -1) {
|
|
return nixio__perror(L);
|
|
}
|
|
|
|
int *udata = lua_newuserdata(L, sizeof(int));
|
|
if (!udata) {
|
|
return luaL_error(L, "out of memory");
|
|
}
|
|
|
|
*udata = fd;
|
|
|
|
luaL_getmetatable(L, NIXIO_FILE_META);
|
|
lua_setmetatable(L, -2);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int nixio_open_flags(lua_State *L) {
|
|
int mode = 0;
|
|
const int j = lua_gettop(L);
|
|
for (int i=1; i<=j; i++) {
|
|
const char *flag = luaL_checkstring(L, i);
|
|
if (!strcmp(flag, "append")) {
|
|
mode |= O_APPEND;
|
|
} else if (!strcmp(flag, "creat")) {
|
|
mode |= O_CREAT;
|
|
} else if (!strcmp(flag, "excl")) {
|
|
mode |= O_EXCL;
|
|
} else if (!strcmp(flag, "nonblock") || !strcmp(flag, "ndelay")) {
|
|
#ifndef __WINNT__
|
|
mode |= O_NONBLOCK;
|
|
#endif
|
|
} else if (!strcmp(flag, "sync")) {
|
|
#ifndef __WINNT__
|
|
mode |= O_SYNC;
|
|
#endif
|
|
} else if (!strcmp(flag, "trunc")) {
|
|
mode |= O_TRUNC;
|
|
} else if (!strcmp(flag, "rdonly")) {
|
|
mode |= O_RDONLY;
|
|
} else if (!strcmp(flag, "wronly")) {
|
|
mode |= O_WRONLY;
|
|
} else if (!strcmp(flag, "rdwr")) {
|
|
mode |= O_RDWR;
|
|
} else {
|
|
return luaL_argerror(L, i, "supported values: append, creat, "
|
|
"excl, nonblock, ndelay, sync, trunc");
|
|
}
|
|
}
|
|
lua_pushinteger(L, mode);
|
|
return 1;
|
|
}
|
|
|
|
static int nixio_dup(lua_State *L) {
|
|
int oldfd = nixio__checkfd(L, 1);
|
|
int newfd = (lua_gettop(L) > 1) ? nixio__checkfd(L, 2) : -1;
|
|
int stat = (newfd == -1) ? dup(oldfd) : dup2(oldfd, newfd);
|
|
|
|
if (stat == -1) {
|
|
return nixio__perror(L);
|
|
} else {
|
|
if (newfd == -1) {
|
|
int *udata = lua_newuserdata(L, sizeof(int));
|
|
if (!udata) {
|
|
return luaL_error(L, "out of memory");
|
|
}
|
|
|
|
*udata = stat;
|
|
luaL_getmetatable(L, NIXIO_FILE_META);
|
|
lua_setmetatable(L, -2);
|
|
} else {
|
|
lua_pushvalue(L, 2);
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int nixio_pipe(lua_State *L) {
|
|
int pipefd[2], *udata;
|
|
if (pipe(pipefd)) {
|
|
return nixio__perror(L);
|
|
}
|
|
|
|
luaL_getmetatable(L, NIXIO_FILE_META);
|
|
udata = lua_newuserdata(L, sizeof(int));
|
|
if (!udata) {
|
|
return luaL_error(L, "out of memory");
|
|
}
|
|
|
|
*udata = pipefd[0];
|
|
lua_pushvalue(L, -2);
|
|
lua_setmetatable(L, -2);
|
|
|
|
|
|
udata = lua_newuserdata(L, sizeof(int));
|
|
if (!udata) {
|
|
return luaL_error(L, "out of memory");
|
|
}
|
|
|
|
*udata = pipefd[1];
|
|
lua_pushvalue(L, -3);
|
|
lua_setmetatable(L, -2);
|
|
|
|
return 2;
|
|
}
|
|
|
|
static int nixio_file_write(lua_State *L) {
|
|
int fd = nixio__checkfd(L, 1);
|
|
size_t len;
|
|
ssize_t sent;
|
|
const char *data = luaL_checklstring(L, 2, &len);
|
|
|
|
if (lua_gettop(L) > 2) {
|
|
int offset = luaL_optint(L, 3, 0);
|
|
if (offset) {
|
|
if (offset < len) {
|
|
data += offset;
|
|
len -= offset;
|
|
} else {
|
|
len = 0;
|
|
}
|
|
}
|
|
|
|
unsigned int wlen = luaL_optint(L, 4, len);
|
|
if (wlen < len) {
|
|
len = wlen;
|
|
}
|
|
}
|
|
|
|
do {
|
|
sent = write(fd, data, len);
|
|
} while(sent == -1 && errno == EINTR);
|
|
if (sent >= 0) {
|
|
lua_pushinteger(L, sent);
|
|
return 1;
|
|
} else {
|
|
return nixio__perror(L);
|
|
}
|
|
}
|
|
|
|
static int nixio_file_read(lua_State *L) {
|
|
int fd = nixio__checkfd(L, 1);
|
|
char buffer[NIXIO_BUFFERSIZE];
|
|
uint req = luaL_checkinteger(L, 2);
|
|
int readc;
|
|
|
|
/* We limit the readsize to NIXIO_BUFFERSIZE */
|
|
req = (req > NIXIO_BUFFERSIZE) ? NIXIO_BUFFERSIZE : req;
|
|
|
|
do {
|
|
readc = read(fd, buffer, req);
|
|
} while (readc == -1 && errno == EINTR);
|
|
|
|
if (readc < 0) {
|
|
return nixio__perror(L);
|
|
} else {
|
|
lua_pushlstring(L, buffer, readc);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
#ifdef __linux__
|
|
|
|
static int nixio_file_numexp(lua_State *L) {
|
|
int fd = nixio__checkfd(L, 1);
|
|
uint64_t numexp;
|
|
int readc;
|
|
|
|
do {
|
|
readc = read(fd, &numexp, sizeof(uint64_t));
|
|
} while (readc == -1 && errno == EINTR);
|
|
|
|
if (readc < 0) {
|
|
return nixio__perror(L);
|
|
} else {
|
|
lua_pushnumber(L, (lua_Number)numexp);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static int nixio_file_seek(lua_State *L) {
|
|
int fd = nixio__checkfd(L, 1);
|
|
off_t len = (off_t)nixio__checknumber(L, 2);
|
|
int whence;
|
|
const char *whstr = luaL_optlstring(L, 3, "set", NULL);
|
|
if (!strcmp(whstr, "set")) {
|
|
whence = SEEK_SET;
|
|
} else if (!strcmp(whstr, "cur")) {
|
|
whence = SEEK_CUR;
|
|
} else if (!strcmp(whstr, "end")) {
|
|
whence = SEEK_END;
|
|
} else {
|
|
return luaL_argerror(L, 3, "supported values: set, cur, end");
|
|
}
|
|
len = lseek(fd, len, whence);
|
|
if (len == -1) {
|
|
return nixio__perror(L);
|
|
} else {
|
|
nixio__pushnumber(L, len);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int nixio_file_tell(lua_State *L) {
|
|
int fd = nixio__checkfd(L, 1);
|
|
off_t pos = lseek(fd, 0, SEEK_CUR);
|
|
if (pos < 0) {
|
|
return nixio__perror(L);
|
|
} else {
|
|
nixio__pushnumber(L, pos);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int nixio_file_stat(lua_State *L) {
|
|
nixio_stat_t buf;
|
|
if (fstat(nixio__checkfd(L, 1), &buf)) {
|
|
return nixio__perror(L);
|
|
} else {
|
|
nixio__push_stat(L, &buf);
|
|
if (lua_isstring(L, 2)) {
|
|
lua_getfield(L, -1, lua_tostring(L, 2));
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int nixio_file_sync(lua_State *L) {
|
|
int fd = nixio__checkfd(L, 1);
|
|
int stat;
|
|
#if (!defined BSD && !defined __WINNT__)
|
|
int dataonly = lua_toboolean(L, 2);
|
|
do {
|
|
stat = (dataonly) ? fdatasync(fd) : fsync(fd);
|
|
} while (stat == -1 && errno == EINTR);
|
|
return nixio__pstatus(L, !stat);
|
|
#else
|
|
do {
|
|
stat = fsync(fd);
|
|
} while (stat == -1 && errno == EINTR);
|
|
return nixio__pstatus(L, !stat);
|
|
#endif
|
|
}
|
|
|
|
static int nixio_file_lock(lua_State *L) {
|
|
int fd = nixio__checkfd(L, 1);
|
|
const char *flag = luaL_checkstring(L, 2);
|
|
off_t len = (off_t)nixio__optnumber(L, 3, 0);
|
|
int stat;
|
|
|
|
int cmd = 0;
|
|
if (!strcmp(flag, "lock")) {
|
|
cmd = F_LOCK;
|
|
} else if (!strcmp(flag, "tlock")) {
|
|
cmd = F_TLOCK;
|
|
} else if (!strcmp(flag, "ulock")) {
|
|
cmd = F_ULOCK;
|
|
} else if (!strcmp(flag, "test")) {
|
|
cmd = F_TEST;
|
|
} else {
|
|
return luaL_argerror(L, 2,
|
|
"supported values: lock, tlock, ulock, test");
|
|
}
|
|
|
|
do {
|
|
stat = lockf(fd, cmd, len);
|
|
} while (stat == -1 && errno == EINTR);
|
|
|
|
return nixio__pstatus(L, !stat);
|
|
}
|
|
|
|
static int nixio_file_close(lua_State *L) {
|
|
int *fdp = luaL_checkudata(L, 1, NIXIO_FILE_META);
|
|
luaL_argcheck(L, *fdp != -1, 1, "invalid file object");
|
|
int res;
|
|
do {
|
|
res = close(*fdp);
|
|
} while (res == -1 && errno == EINTR);
|
|
*fdp = -1;
|
|
return nixio__pstatus(L, !res);
|
|
}
|
|
|
|
static int nixio_file__gc(lua_State *L) {
|
|
int *fdp = luaL_checkudata(L, 1, NIXIO_FILE_META);
|
|
int res;
|
|
if (*fdp > 2) {
|
|
do {
|
|
res = close(*fdp);
|
|
} while (res == -1 && errno == EINTR);
|
|
*fdp = -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* string representation
|
|
*/
|
|
static int nixio_file__tostring(lua_State *L) {
|
|
lua_pushfstring(L, "nixio file %d", nixio__tofd(L, 1));
|
|
return 1;
|
|
}
|
|
|
|
/* method table */
|
|
static const luaL_reg M[] = {
|
|
{"write", nixio_file_write},
|
|
{"read", nixio_file_read},
|
|
#ifdef __linux__
|
|
{"numexp", nixio_file_numexp},
|
|
#endif
|
|
{"tell", nixio_file_tell},
|
|
{"seek", nixio_file_seek},
|
|
{"stat", nixio_file_stat},
|
|
{"sync", nixio_file_sync},
|
|
{"lock", nixio_file_lock},
|
|
{"close", nixio_file_close},
|
|
{"__gc", nixio_file__gc},
|
|
{"__tostring", nixio_file__tostring},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
/* module table */
|
|
static const luaL_reg R[] = {
|
|
{"dup", nixio_dup},
|
|
{"open", nixio_open},
|
|
{"open_flags", nixio_open_flags},
|
|
{"pipe", nixio_pipe},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
void nixio_open_file(lua_State *L) {
|
|
luaL_register(L, NULL, R);
|
|
|
|
luaL_newmetatable(L, NIXIO_FILE_META);
|
|
luaL_register(L, NULL, M);
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
int *uin = lua_newuserdata(L, sizeof(int));
|
|
int *uout = lua_newuserdata(L, sizeof(int));
|
|
int *uerr = lua_newuserdata(L, sizeof(int));
|
|
|
|
if (!uin || !uout || !uerr) {
|
|
luaL_error(L, "out of memory");
|
|
}
|
|
|
|
*uin = STDIN_FILENO;
|
|
*uout = STDOUT_FILENO;
|
|
*uerr = STDERR_FILENO;
|
|
|
|
for (int i = -4; i < -1; i++) {
|
|
lua_pushvalue(L, -4);
|
|
lua_setmetatable(L, i);
|
|
}
|
|
|
|
lua_setfield(L, -5, "stderr");
|
|
lua_setfield(L, -4, "stdout");
|
|
lua_setfield(L, -3, "stdin");
|
|
lua_setfield(L, -2, "meta_file");
|
|
}
|