cgi-bin/tek/lib/posix.c
author Timm S. Mueller <tmueller@neoscientists.org>
Fri, 24 Oct 2008 01:35:27 +0200
changeset 251 2de5931b723d
parent 241 cgi-bin/tek/os/posix.c@c6c81629f54e
permissions -rw-r--r--
tek.os.posix has been moved to tek.lib.posix, module initialization procedure
simplified, added statvfs() under Linux
     1 
     2 /*
     3 **	tek.lib.posix - tek POSIX library
     4 **	Written by Timm S. Mueller <tmueller at neoscientists.org>
     5 **	See copyright notice in COPYRIGHT
     6 */
     7 
     8 
     9 #include <stdlib.h>
    10 #include <string.h>
    11 #include <sys/types.h>
    12 #include <sys/stat.h>
    13 #include <unistd.h>
    14 #include <dirent.h>
    15 #include <errno.h>
    16 #include <math.h>
    17 #include <time.h>
    18 #include <lua.h>
    19 #include <lualib.h>
    20 #include <lauxlib.h>
    21 
    22 
    23 #define DIRCLASSNAME "dir*"
    24 
    25 
    26 static void
    27 setfield(lua_State *L, const char *attr, const char *key, lua_Integer n)
    28 {
    29 	if (attr == NULL || strcmp(attr, key) == 0)
    30 		lua_pushinteger(L, n);
    31 	if (attr == NULL)
    32 		lua_setfield(L, -2, key);
    33 }
    34 
    35 
    36 static int
    37 setstat(lua_State *L, int result, struct stat *s, const char *attr)
    38 {
    39 	if (result != 0)
    40 	{
    41 		lua_pushnil(L);
    42 		lua_pushstring(L, strerror(errno));
    43 		return 2;
    44 	}
    45 
    46 	if (attr == NULL)
    47 		lua_newtable(L);
    48 
    49 	setfield(L, attr, "dev", s->st_dev);
    50 	setfield(L, attr, "ino", s->st_ino);
    51 
    52 	if (attr == NULL || strcmp(attr, "mode") == 0)
    53 	{
    54 		if (S_ISREG(s->st_mode))
    55 			lua_pushstring(L, "file");
    56 		else if (S_ISDIR(s->st_mode))
    57 			lua_pushstring(L, "directory");
    58 		else if (S_ISLNK(s->st_mode))
    59 			lua_pushstring(L, "link");
    60 		else if (S_ISSOCK(s->st_mode))
    61 			lua_pushstring(L, "socket");
    62 		else if (S_ISFIFO(s->st_mode))
    63 			lua_pushstring(L, "named pipe");
    64 		else if (S_ISCHR(s->st_mode))
    65 			lua_pushstring(L, "char device");
    66 		else if (S_ISBLK(s->st_mode))
    67 			lua_pushstring(L, "block device");
    68 		else
    69 			lua_pushstring(L, "other");
    70 		if (attr == NULL)
    71 			lua_setfield(L, -2, "mode");
    72 	}
    73 
    74 	setfield(L, attr, "nlink", s->st_nlink);
    75 	setfield(L, attr, "uid", s->st_uid);
    76 	setfield(L, attr, "gid", s->st_gid);
    77 	setfield(L, attr, "rdev", s->st_rdev);
    78 	setfield(L, attr, "access", s->st_atime);
    79 	setfield(L, attr, "modification", s->st_mtime);
    80 	setfield(L, attr, "change", s->st_ctime);
    81 	setfield(L, attr, "size", s->st_size);
    82 	setfield(L, attr, "blocks", s->st_blocks);
    83 	setfield(L, attr, "blksize", s->st_blksize);
    84 
    85 	return 1;
    86 }
    87 
    88 
    89 static int
    90 posix_stat(lua_State *L)
    91 {
    92 	const char *path = luaL_checkstring(L, 1);
    93 	const char *attr = luaL_optstring(L, 2, NULL);
    94 	struct stat s;
    95 	return setstat(L, stat(path, &s), &s, attr);
    96 }
    97 
    98 
    99 static int
   100 posix_lstat(lua_State *L)
   101 {
   102 	const char *path = luaL_checkstring(L, 1);
   103 	const char *attr = luaL_optstring(L, 2, NULL);
   104 	struct stat s;
   105 	return setstat(L, lstat(path, &s), &s, attr);
   106 }
   107 
   108 
   109 static int
   110 posix_opendir(lua_State *L)
   111 {
   112 	const char *path = luaL_checkstring(L, 1);
   113 	DIR **pdir = lua_newuserdata(L, sizeof(void *));
   114 	/* s: udata */
   115 	*pdir = opendir(path);
   116 	if (*pdir == NULL)
   117 	{
   118 		lua_pushnil(L);
   119 		lua_pushstring(L, strerror(errno));
   120 		return 2;
   121 	}
   122 	lua_getfield(L, LUA_REGISTRYINDEX, DIRCLASSNAME);
   123 	/* s: udata, meta */
   124 	lua_setmetatable(L, -2);
   125 	/* s: udata */
   126 	return 1;
   127 }
   128 
   129 
   130 static DIR **
   131 getinstptr(lua_State *L, int narg, const char *classname)
   132 {
   133 	DIR **pinst = luaL_checkudata(L, narg, classname);
   134 	if (*pinst) return pinst;
   135 	luaL_argerror(L, narg, "Closed handle");
   136 	return NULL;
   137 }
   138 
   139 
   140 static int
   141 posix_closedir(lua_State *L)
   142 {
   143 	DIR **pdir = getinstptr(L, 1, DIRCLASSNAME);
   144 	closedir(*pdir);
   145 	*pdir = NULL;
   146 	return 0;
   147 }
   148 
   149 
   150 static int
   151 posix_readdir(lua_State *L)
   152 {
   153 	DIR *dir = *getinstptr(L, 1, DIRCLASSNAME);
   154 	struct dirent *de = readdir(dir);
   155 	if (de)
   156 		lua_pushstring(L, de->d_name);
   157 	else
   158 		lua_pushnil(L);
   159 	return 1;
   160 }
   161 
   162 
   163 static int
   164 posix_readlink(lua_State *L)
   165 {
   166 	const char *path = luaL_checkstring(L, 1);
   167 	char buf[PATH_MAX + 1];
   168 	ssize_t len = readlink(path, buf, sizeof(buf) - 1);
   169 	if (len < 0)
   170 	{
   171 		lua_pushnil(L);
   172 		lua_pushstring(L, strerror(errno));
   173 		return 2;
   174 	}
   175 	buf[len] = 0;
   176 	lua_pushstring(L, buf);
   177 	return 1;
   178 }
   179 
   180 
   181 static int
   182 posix_mkdir(lua_State *L)
   183 {
   184 	const char *path = luaL_checkstring(L, 1);
   185 	if (mkdir(path, 0775) == 0)
   186 	{
   187 		lua_pushboolean(L, 1);
   188 		return 1;
   189 	}
   190 	lua_pushnil(L);
   191 	lua_pushstring(L, strerror(errno));
   192 	return 2;
   193 }
   194 
   195 
   196 static int
   197 posix_symlink(lua_State *L)
   198 {
   199 	const char *oldpath = luaL_checkstring(L, 1);
   200 	const char *newpath = luaL_checkstring(L, 2);
   201 	if (symlink(oldpath, newpath) == 0)
   202 	{
   203 		lua_pushboolean(L, 1);
   204 		return 1;
   205 	}
   206 	lua_pushnil(L);
   207 	lua_pushstring(L, strerror(errno));
   208 	return 2;
   209 }
   210 
   211 
   212 static int
   213 resolvepath(const char *src, char *dest)
   214 {
   215 	int len = strlen(src);
   216 	const char *sp = src + len;
   217 	char *dp = dest;
   218 	int dc = 0, slc = 0, eac = 0, wc = 0;
   219 	int i, c;
   220 
   221 	while (len--)
   222 	{
   223 		c = *(--sp);
   224 		switch (c)
   225 		{
   226 			case '/':
   227 				if (dc == 2)
   228 					eac++;
   229 				dc = 0;
   230 				slc = 1;
   231 				wc = 0;
   232 				break;
   233 
   234 			case '.':
   235 				if (slc)
   236 				{
   237 					dc++;
   238 					break;
   239 				}
   240 				/* fallthru: */
   241 
   242 			default:
   243 				if (wc)
   244 					break;
   245 
   246 				if (slc)
   247 				{
   248 					slc = 0;
   249 
   250 					if (eac > 0)
   251 					{
   252 						/* resolve one eatcount */
   253 						eac--;
   254 						/* now wait for next path part */
   255 						wc = 1;
   256 						break;
   257 					}
   258 
   259 					*dp++ = '/';
   260 				}
   261 
   262 				while (dc == 2 || dc == 1)
   263 				{
   264 					*dp++ = '.';
   265 					dc--;
   266 				}
   267 				dc = 0;
   268 
   269 				*dp++ = c;
   270 				break;
   271 		}
   272 	}
   273 
   274 	/* unresolved eatcount */
   275 	if (eac)
   276 		return 0;
   277 
   278 	/* resolve remaining slash */
   279 	if (slc)
   280 		*dp++ = '/';
   281 
   282 	*dp = 0;
   283 
   284 	len = dp - dest;
   285 	for (i = 0; i < len / 2; ++i)
   286 	{
   287 		char t = dest[i];
   288 		dest[i] = dest[len - i - 1];
   289 		dest[len - i - 1] = t;
   290 	}
   291 
   292 	return 1;
   293 }
   294 
   295 
   296 static int
   297 posix_abspath(lua_State *L)
   298 {
   299 	const char *path = luaL_checkstring(L, 1);
   300 	char *pwd = NULL;
   301 	size_t pwdsize = 16;
   302 	size_t len1, len2;
   303 	char *srcpath, *dstpath;
   304 
   305 	for (;;)
   306 	{
   307 		if (path[0] == '/')
   308 		{
   309 			pwd = malloc(2);
   310 			if (pwd == NULL)
   311 				break;
   312 			pwd[0] = '/';
   313 			pwd[1] = 0;
   314 		}
   315 		else
   316 		{
   317 			char *newpwd = realloc(pwd, pwdsize);
   318 			if (newpwd == NULL)
   319 				break;
   320 			pwd = newpwd;
   321 
   322 			if (!getcwd(pwd, pwdsize))
   323 			{
   324 				if (errno == ERANGE)
   325 				{
   326 					pwdsize <<= 1;
   327 					continue;
   328 				}
   329 				lua_pushnil(L);
   330 				lua_pushstring(L, strerror(errno));
   331 				return 2;
   332 			}
   333 		}
   334 
   335 		len1 = strlen(pwd);
   336 		len2 = strlen(path);
   337 		srcpath = malloc(len1 + 1 + len2 + 1);
   338 		dstpath = malloc(len1 + 1 + len2 + 1);
   339 
   340 		if (srcpath && dstpath)
   341 		{
   342 			int res;
   343 
   344 			strcpy(srcpath, pwd);
   345 			free(pwd);
   346 			srcpath[len1] = '/';
   347 			strcpy(srcpath + len1 + 1, path);
   348 			res = resolvepath(srcpath, dstpath);
   349 			free(srcpath);
   350 
   351 			if (res)
   352 			{
   353 				lua_pushstring(L, dstpath);
   354 				free(dstpath);
   355 				return 1;
   356 			}
   357 
   358 			free(dstpath);
   359 			lua_pushnil(L);
   360 			lua_pushstring(L, "Not a valid path");
   361 			return 2;
   362 		}
   363 
   364 		free(srcpath);
   365 		free(dstpath);
   366 		break;
   367 	}
   368 
   369 	free(pwd);
   370 	luaL_error(L, "Out of memory");
   371 	return 0;
   372 }
   373 
   374 
   375 static int
   376 posix_nanosleep(lua_State *L)
   377 {
   378 	struct timespec req;
   379 	lua_Number d = luaL_checknumber(L, 1);
   380 
   381 	req.tv_sec = (time_t) d;
   382 	req.tv_nsec = (long) ((d - req.tv_sec) * 1000000000);
   383 
   384 	if (nanosleep(&req, NULL) == 0)
   385 	{
   386 		lua_pushboolean(L, 1);
   387 		return 1;
   388 	}
   389 
   390 	lua_pushnil(L);
   391 	lua_pushstring(L, strerror(errno));
   392 	return 2;
   393 }
   394 
   395 #if defined(__linux)
   396 #include <sys/statvfs.h>
   397 
   398 static int
   399 posix_statvfs(lua_State *L)
   400 {
   401 	struct statvfs buf;
   402 	const char *path = luaL_checkstring(L, 1);
   403 	if (statvfs(path, &buf) == 0)
   404 	{
   405 		lua_newtable(L);
   406 		lua_pushinteger(L, buf.f_bsize);
   407 		lua_setfield(L, -2, "bsize");
   408 		lua_pushinteger(L, buf.f_blocks);
   409 		lua_setfield(L, -2, "blocks");
   410 		lua_pushinteger(L, buf.f_bfree);
   411 		lua_setfield(L, -2, "bfree");
   412 		lua_pushinteger(L, buf.f_fsid);
   413 		lua_setfield(L, -2, "fsid");
   414 		lua_pushinteger(L, buf.f_namemax);
   415 		lua_setfield(L, -2, "namemax");
   416 		return 1;
   417 	}
   418 	lua_pushnil(L);
   419 	lua_pushstring(L, strerror(errno));
   420 	return 2;
   421 }
   422 
   423 #endif
   424 
   425 static const luaL_Reg libfuncs[] =
   426 {
   427 	{ "stat", posix_stat },
   428 	{ "lstat", posix_lstat },
   429 	{ "opendir", posix_opendir },
   430 	{ "closedir", posix_closedir },
   431 	{ "readdir", posix_readdir },
   432 	{ "mkdir", posix_mkdir },
   433 	{ "readlink", posix_readlink },
   434 	{ "symlink", posix_symlink },
   435 	{ "abspath", posix_abspath },
   436 	{ "nanosleep", posix_nanosleep },
   437 #if defined(__linux)
   438 	{ "statvfs", posix_statvfs },
   439 #endif
   440 	{ NULL, NULL }
   441 };
   442 
   443 
   444 static const luaL_Reg dirmethods[] =
   445 {
   446 	{"read", posix_readdir },
   447 	{"close", posix_closedir },
   448 	{"__gc", posix_closedir },
   449 	{NULL, NULL}
   450 };
   451 
   452 int luaopen_tek_lib_posix(lua_State *L)
   453 {
   454 	luaL_register(L, "tek.lib.posix", libfuncs);
   455 	/* s: lib */
   456 	luaL_newmetatable(L, DIRCLASSNAME);
   457 	/* s: lib, meta */
   458 	lua_pushvalue(L, -1);
   459 	/* s: lib, meta, meta */
   460 	lua_setfield(L, -2, "__index");
   461 	/* s: lib, meta */
   462 	luaL_register(L, NULL, dirmethods);
   463 	/* s: lib, meta */
   464 	lua_pop(L, 2);
   465 	/* s: */
   466 	return 0;
   467 }