1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/cgi-bin/tek/lib/posix.c Wed Feb 24 14:44:46 2010 +0100
1.3 @@ -0,0 +1,467 @@
1.4 +
1.5 +/*
1.6 +** tek.lib.posix - tek POSIX library
1.7 +** Written by Timm S. Mueller <tmueller at neoscientists.org>
1.8 +** See copyright notice in COPYRIGHT
1.9 +*/
1.10 +
1.11 +
1.12 +#include <stdlib.h>
1.13 +#include <string.h>
1.14 +#include <sys/types.h>
1.15 +#include <sys/stat.h>
1.16 +#include <unistd.h>
1.17 +#include <dirent.h>
1.18 +#include <errno.h>
1.19 +#include <math.h>
1.20 +#include <time.h>
1.21 +#include <lua.h>
1.22 +#include <lualib.h>
1.23 +#include <lauxlib.h>
1.24 +
1.25 +
1.26 +#define DIRCLASSNAME "dir*"
1.27 +
1.28 +
1.29 +static void
1.30 +setfield(lua_State *L, const char *attr, const char *key, lua_Integer n)
1.31 +{
1.32 + if (attr == NULL || strcmp(attr, key) == 0)
1.33 + lua_pushinteger(L, n);
1.34 + if (attr == NULL)
1.35 + lua_setfield(L, -2, key);
1.36 +}
1.37 +
1.38 +
1.39 +static int
1.40 +setstat(lua_State *L, int result, struct stat *s, const char *attr)
1.41 +{
1.42 + if (result != 0)
1.43 + {
1.44 + lua_pushnil(L);
1.45 + lua_pushstring(L, strerror(errno));
1.46 + return 2;
1.47 + }
1.48 +
1.49 + if (attr == NULL)
1.50 + lua_newtable(L);
1.51 +
1.52 + setfield(L, attr, "dev", s->st_dev);
1.53 + setfield(L, attr, "ino", s->st_ino);
1.54 +
1.55 + if (attr == NULL || strcmp(attr, "mode") == 0)
1.56 + {
1.57 + if (S_ISREG(s->st_mode))
1.58 + lua_pushstring(L, "file");
1.59 + else if (S_ISDIR(s->st_mode))
1.60 + lua_pushstring(L, "directory");
1.61 + else if (S_ISLNK(s->st_mode))
1.62 + lua_pushstring(L, "link");
1.63 + else if (S_ISSOCK(s->st_mode))
1.64 + lua_pushstring(L, "socket");
1.65 + else if (S_ISFIFO(s->st_mode))
1.66 + lua_pushstring(L, "named pipe");
1.67 + else if (S_ISCHR(s->st_mode))
1.68 + lua_pushstring(L, "char device");
1.69 + else if (S_ISBLK(s->st_mode))
1.70 + lua_pushstring(L, "block device");
1.71 + else
1.72 + lua_pushstring(L, "other");
1.73 + if (attr == NULL)
1.74 + lua_setfield(L, -2, "mode");
1.75 + }
1.76 +
1.77 + setfield(L, attr, "nlink", s->st_nlink);
1.78 + setfield(L, attr, "uid", s->st_uid);
1.79 + setfield(L, attr, "gid", s->st_gid);
1.80 + setfield(L, attr, "rdev", s->st_rdev);
1.81 + setfield(L, attr, "access", s->st_atime);
1.82 + setfield(L, attr, "modification", s->st_mtime);
1.83 + setfield(L, attr, "change", s->st_ctime);
1.84 + setfield(L, attr, "size", s->st_size);
1.85 + setfield(L, attr, "blocks", s->st_blocks);
1.86 + setfield(L, attr, "blksize", s->st_blksize);
1.87 +
1.88 + return 1;
1.89 +}
1.90 +
1.91 +
1.92 +static int
1.93 +posix_stat(lua_State *L)
1.94 +{
1.95 + const char *path = luaL_checkstring(L, 1);
1.96 + const char *attr = luaL_optstring(L, 2, NULL);
1.97 + struct stat s;
1.98 + return setstat(L, stat(path, &s), &s, attr);
1.99 +}
1.100 +
1.101 +
1.102 +static int
1.103 +posix_lstat(lua_State *L)
1.104 +{
1.105 + const char *path = luaL_checkstring(L, 1);
1.106 + const char *attr = luaL_optstring(L, 2, NULL);
1.107 + struct stat s;
1.108 + return setstat(L, lstat(path, &s), &s, attr);
1.109 +}
1.110 +
1.111 +
1.112 +static int
1.113 +posix_opendir(lua_State *L)
1.114 +{
1.115 + const char *path = luaL_checkstring(L, 1);
1.116 + DIR **pdir = lua_newuserdata(L, sizeof(void *));
1.117 + /* s: udata */
1.118 + *pdir = opendir(path);
1.119 + if (*pdir == NULL)
1.120 + {
1.121 + lua_pushnil(L);
1.122 + lua_pushstring(L, strerror(errno));
1.123 + return 2;
1.124 + }
1.125 + lua_getfield(L, LUA_REGISTRYINDEX, DIRCLASSNAME);
1.126 + /* s: udata, meta */
1.127 + lua_setmetatable(L, -2);
1.128 + /* s: udata */
1.129 + return 1;
1.130 +}
1.131 +
1.132 +
1.133 +static DIR **
1.134 +getinstptr(lua_State *L, int narg, const char *classname)
1.135 +{
1.136 + DIR **pinst = luaL_checkudata(L, narg, classname);
1.137 + if (*pinst) return pinst;
1.138 + luaL_argerror(L, narg, "Closed handle");
1.139 + return NULL;
1.140 +}
1.141 +
1.142 +
1.143 +static int
1.144 +posix_closedir(lua_State *L)
1.145 +{
1.146 + DIR **pdir = getinstptr(L, 1, DIRCLASSNAME);
1.147 + closedir(*pdir);
1.148 + *pdir = NULL;
1.149 + return 0;
1.150 +}
1.151 +
1.152 +
1.153 +static int
1.154 +posix_readdir(lua_State *L)
1.155 +{
1.156 + DIR *dir = *getinstptr(L, 1, DIRCLASSNAME);
1.157 + struct dirent *de = readdir(dir);
1.158 + if (de)
1.159 + lua_pushstring(L, de->d_name);
1.160 + else
1.161 + lua_pushnil(L);
1.162 + return 1;
1.163 +}
1.164 +
1.165 +
1.166 +static int
1.167 +posix_readlink(lua_State *L)
1.168 +{
1.169 + const char *path = luaL_checkstring(L, 1);
1.170 + char buf[PATH_MAX + 1];
1.171 + ssize_t len = readlink(path, buf, sizeof(buf) - 1);
1.172 + if (len < 0)
1.173 + {
1.174 + lua_pushnil(L);
1.175 + lua_pushstring(L, strerror(errno));
1.176 + return 2;
1.177 + }
1.178 + buf[len] = 0;
1.179 + lua_pushstring(L, buf);
1.180 + return 1;
1.181 +}
1.182 +
1.183 +
1.184 +static int
1.185 +posix_mkdir(lua_State *L)
1.186 +{
1.187 + const char *path = luaL_checkstring(L, 1);
1.188 + if (mkdir(path, 0775) == 0)
1.189 + {
1.190 + lua_pushboolean(L, 1);
1.191 + return 1;
1.192 + }
1.193 + lua_pushnil(L);
1.194 + lua_pushstring(L, strerror(errno));
1.195 + return 2;
1.196 +}
1.197 +
1.198 +
1.199 +static int
1.200 +posix_symlink(lua_State *L)
1.201 +{
1.202 + const char *oldpath = luaL_checkstring(L, 1);
1.203 + const char *newpath = luaL_checkstring(L, 2);
1.204 + if (symlink(oldpath, newpath) == 0)
1.205 + {
1.206 + lua_pushboolean(L, 1);
1.207 + return 1;
1.208 + }
1.209 + lua_pushnil(L);
1.210 + lua_pushstring(L, strerror(errno));
1.211 + return 2;
1.212 +}
1.213 +
1.214 +
1.215 +static int
1.216 +resolvepath(const char *src, char *dest)
1.217 +{
1.218 + int len = strlen(src);
1.219 + const char *sp = src + len;
1.220 + char *dp = dest;
1.221 + int dc = 0, slc = 0, eac = 0, wc = 0;
1.222 + int i, c;
1.223 +
1.224 + while (len--)
1.225 + {
1.226 + c = *(--sp);
1.227 + switch (c)
1.228 + {
1.229 + case '/':
1.230 + if (dc == 2)
1.231 + eac++;
1.232 + dc = 0;
1.233 + slc = 1;
1.234 + wc = 0;
1.235 + break;
1.236 +
1.237 + case '.':
1.238 + if (slc)
1.239 + {
1.240 + dc++;
1.241 + break;
1.242 + }
1.243 + /* fallthru: */
1.244 +
1.245 + default:
1.246 + if (wc)
1.247 + break;
1.248 +
1.249 + if (slc)
1.250 + {
1.251 + slc = 0;
1.252 +
1.253 + if (eac > 0)
1.254 + {
1.255 + /* resolve one eatcount */
1.256 + eac--;
1.257 + /* now wait for next path part */
1.258 + wc = 1;
1.259 + break;
1.260 + }
1.261 +
1.262 + *dp++ = '/';
1.263 + }
1.264 +
1.265 + while (dc == 2 || dc == 1)
1.266 + {
1.267 + *dp++ = '.';
1.268 + dc--;
1.269 + }
1.270 + dc = 0;
1.271 +
1.272 + *dp++ = c;
1.273 + break;
1.274 + }
1.275 + }
1.276 +
1.277 + /* unresolved eatcount */
1.278 + if (eac)
1.279 + return 0;
1.280 +
1.281 + /* resolve remaining slash */
1.282 + if (slc)
1.283 + *dp++ = '/';
1.284 +
1.285 + *dp = 0;
1.286 +
1.287 + len = dp - dest;
1.288 + for (i = 0; i < len / 2; ++i)
1.289 + {
1.290 + char t = dest[i];
1.291 + dest[i] = dest[len - i - 1];
1.292 + dest[len - i - 1] = t;
1.293 + }
1.294 +
1.295 + return 1;
1.296 +}
1.297 +
1.298 +
1.299 +static int
1.300 +posix_abspath(lua_State *L)
1.301 +{
1.302 + const char *path = luaL_checkstring(L, 1);
1.303 + char *pwd = NULL;
1.304 + size_t pwdsize = 16;
1.305 + size_t len1, len2;
1.306 + char *srcpath, *dstpath;
1.307 +
1.308 + for (;;)
1.309 + {
1.310 + if (path[0] == '/')
1.311 + {
1.312 + pwd = malloc(2);
1.313 + if (pwd == NULL)
1.314 + break;
1.315 + pwd[0] = '/';
1.316 + pwd[1] = 0;
1.317 + }
1.318 + else
1.319 + {
1.320 + char *newpwd = realloc(pwd, pwdsize);
1.321 + if (newpwd == NULL)
1.322 + break;
1.323 + pwd = newpwd;
1.324 +
1.325 + if (!getcwd(pwd, pwdsize))
1.326 + {
1.327 + if (errno == ERANGE)
1.328 + {
1.329 + pwdsize <<= 1;
1.330 + continue;
1.331 + }
1.332 + lua_pushnil(L);
1.333 + lua_pushstring(L, strerror(errno));
1.334 + return 2;
1.335 + }
1.336 + }
1.337 +
1.338 + len1 = strlen(pwd);
1.339 + len2 = strlen(path);
1.340 + srcpath = malloc(len1 + 1 + len2 + 1);
1.341 + dstpath = malloc(len1 + 1 + len2 + 1);
1.342 +
1.343 + if (srcpath && dstpath)
1.344 + {
1.345 + int res;
1.346 +
1.347 + strcpy(srcpath, pwd);
1.348 + free(pwd);
1.349 + srcpath[len1] = '/';
1.350 + strcpy(srcpath + len1 + 1, path);
1.351 + res = resolvepath(srcpath, dstpath);
1.352 + free(srcpath);
1.353 +
1.354 + if (res)
1.355 + {
1.356 + lua_pushstring(L, dstpath);
1.357 + free(dstpath);
1.358 + return 1;
1.359 + }
1.360 +
1.361 + free(dstpath);
1.362 + lua_pushnil(L);
1.363 + lua_pushstring(L, "Not a valid path");
1.364 + return 2;
1.365 + }
1.366 +
1.367 + free(srcpath);
1.368 + free(dstpath);
1.369 + break;
1.370 + }
1.371 +
1.372 + free(pwd);
1.373 + luaL_error(L, "Out of memory");
1.374 + return 0;
1.375 +}
1.376 +
1.377 +
1.378 +static int
1.379 +posix_nanosleep(lua_State *L)
1.380 +{
1.381 + struct timespec req;
1.382 + lua_Number d = luaL_checknumber(L, 1);
1.383 +
1.384 + req.tv_sec = (time_t) d;
1.385 + req.tv_nsec = (long) ((d - req.tv_sec) * 1000000000);
1.386 +
1.387 + if (nanosleep(&req, NULL) == 0)
1.388 + {
1.389 + lua_pushboolean(L, 1);
1.390 + return 1;
1.391 + }
1.392 +
1.393 + lua_pushnil(L);
1.394 + lua_pushstring(L, strerror(errno));
1.395 + return 2;
1.396 +}
1.397 +
1.398 +#if defined(__linux)
1.399 +#include <sys/statvfs.h>
1.400 +
1.401 +static int
1.402 +posix_statvfs(lua_State *L)
1.403 +{
1.404 + struct statvfs buf;
1.405 + const char *path = luaL_checkstring(L, 1);
1.406 + if (statvfs(path, &buf) == 0)
1.407 + {
1.408 + lua_newtable(L);
1.409 + lua_pushinteger(L, buf.f_bsize);
1.410 + lua_setfield(L, -2, "bsize");
1.411 + lua_pushinteger(L, buf.f_blocks);
1.412 + lua_setfield(L, -2, "blocks");
1.413 + lua_pushinteger(L, buf.f_bfree);
1.414 + lua_setfield(L, -2, "bfree");
1.415 + lua_pushinteger(L, buf.f_fsid);
1.416 + lua_setfield(L, -2, "fsid");
1.417 + lua_pushinteger(L, buf.f_namemax);
1.418 + lua_setfield(L, -2, "namemax");
1.419 + return 1;
1.420 + }
1.421 + lua_pushnil(L);
1.422 + lua_pushstring(L, strerror(errno));
1.423 + return 2;
1.424 +}
1.425 +
1.426 +#endif
1.427 +
1.428 +static const luaL_Reg libfuncs[] =
1.429 +{
1.430 + { "stat", posix_stat },
1.431 + { "lstat", posix_lstat },
1.432 + { "opendir", posix_opendir },
1.433 + { "closedir", posix_closedir },
1.434 + { "readdir", posix_readdir },
1.435 + { "mkdir", posix_mkdir },
1.436 + { "readlink", posix_readlink },
1.437 + { "symlink", posix_symlink },
1.438 + { "abspath", posix_abspath },
1.439 + { "nanosleep", posix_nanosleep },
1.440 +#if defined(__linux)
1.441 + { "statvfs", posix_statvfs },
1.442 +#endif
1.443 + { NULL, NULL }
1.444 +};
1.445 +
1.446 +
1.447 +static const luaL_Reg dirmethods[] =
1.448 +{
1.449 + {"read", posix_readdir },
1.450 + {"close", posix_closedir },
1.451 + {"__gc", posix_closedir },
1.452 + {NULL, NULL}
1.453 +};
1.454 +
1.455 +int luaopen_tek_lib_posix(lua_State *L)
1.456 +{
1.457 + luaL_register(L, "tek.lib.posix", libfuncs);
1.458 + /* s: lib */
1.459 + luaL_newmetatable(L, DIRCLASSNAME);
1.460 + /* s: lib, meta */
1.461 + lua_pushvalue(L, -1);
1.462 + /* s: lib, meta, meta */
1.463 + lua_setfield(L, -2, "__index");
1.464 + /* s: lib, meta */
1.465 + luaL_register(L, NULL, dirmethods);
1.466 + /* s: lib, meta */
1.467 + lua_pop(L, 2);
1.468 + /* s: */
1.469 + return 0;
1.470 +}