/*
 * POSIX library for Lua 5.1, 5.2 & 5.3.
 * (c) Gary V. Vaughan <gary@vaughan.pe>, 2013-2015
 * (c) Reuben Thomas <rrt@sc3d.org> 2010-2013
 * (c) Natanael Copa <natanael.copa@gmail.com> 2008-2010
 * Clean up and bug fixes by Leo Razoumov <slonik.az@gmail.com> 2006-10-11
 * Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br> 07 Apr 2006 23:17:49
 * Based on original by Claudio Terra for Lua 3.x.
 * With contributions by Roberto Ierusalimschy.
 * With documentation from Steve Donovan 2012
 */
/***
 Standard Posix Library functions.

@module posix.stdlib
*/

#include <config.h>

#include <fcntl.h>	/* for open(2) */
#include <stdlib.h>

#include "_helpers.c"


/***
Abort the program immediately.
@function abort
@see abort(3)
*/
static int
Pabort(lua_State *L)
{
	checknargs(L, 0);
	abort();
	return 0; /* Avoid a compiler warning (or possibly cause one
		     if the compiler's too clever, sigh). */
}


/***
Get value of environment variable, or _all_ variables.
@function getenv
@see getenv(3)
@string[opt] name if nil, get all
@return value if name given, otherwise a name-indexed table of values.
@usage for a,b in pairs(posix.getenv()) do print(a, b) end
*/
static int
Pgetenv(lua_State *L)
{
	checknargs(L, 1);
	if (lua_isnoneornil(L, 1))
	{
		extern char **environ;
		char **e;
		lua_newtable(L);
		for (e=environ; *e!=NULL; e++)
		{
			char *s=*e;
			char *eq=strchr(s, '=');
			if (eq==NULL)		/* will this ever happen? */
			{
				lua_pushstring(L, s);
				lua_pushboolean(L, 1);
			}
			else
			{
				lua_pushlstring(L, s, eq-s);
				lua_pushstring(L, eq+1);
			}
			lua_settable(L, -3);
		}
	}
	else
		lua_pushstring(L, getenv(optstring(L, 1,
			"lua_isnoneornil prevents this happening")));
	return 1;
}


/***
Grant access to a slave pseudoterminal
@function grantpt
@int fd descriptor returned by openpt
@treturn[1] int `0`, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see grantpt(3)
@see openpt
@see ptsname
@see unlockpt
*/
static int
Pgrantpt(lua_State *L)
{
	int fd=checkint(L, 1);
	checknargs(L, 1);
	return pushresult(L, grantpt(fd), "grantpt");
}


/***
Create a unique temporary directory.
@function mkdtemp
@string templ pattern that ends in six 'X' characters
@treturn[1] string path to directory, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see mkdtemp(3)
*/
static int
Pmkdtemp(lua_State *L)
{
#if defined LPOSIX_2008_COMPLIANT
	const char *path = luaL_checkstring(L, 1);
	size_t path_len = strlen(path) + 1;
	void *ud;
	lua_Alloc lalloc;
	char *tmppath;
	char *r;
	checknargs(L, 1);
	lalloc = lua_getallocf(L, &ud);

	if ((tmppath = lalloc(ud, NULL, 0, path_len)) == NULL)
		return pusherror(L, "lalloc");
	strcpy(tmppath, path);

	if ((r = mkdtemp(tmppath)))
		lua_pushstring(L, tmppath);
	lalloc(ud, tmppath, path_len, 0);
	return (r == NULL) ? pusherror(L, path) : 1;
#else
	return binding_notimplemented(L, "mkdtemp", "C");
#endif
}


/***
Create a unique temporary file.
@function mkstemp
@string templ pattern that ends in six 'X' characters
@treturn[1] int open file descriptor
@treturn[2] string path to file, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see mkstemp(3)
@usage P.mkstemp 'wooXXXXXX'
*/
static int
Pmkstemp(lua_State *L)
{
	const char *path = luaL_checkstring(L, 1);
	size_t path_len = strlen(path) + 1;
	void *ud;
	lua_Alloc lalloc;
	char *tmppath;
	int r;
	checknargs(L, 1);
	lalloc = lua_getallocf(L, &ud);

	if ((tmppath = lalloc(ud, NULL, 0, path_len)) == NULL)
		return pusherror(L, "lalloc");
	strcpy(tmppath, path);
	r = mkstemp(tmppath);

	if (r != -1)
	{
		lua_pushinteger(L, r);
		lua_pushstring(L, tmppath);
	}

	lalloc(ud, tmppath, path_len, 0);
	return (r == -1) ? pusherror(L, path) : 2;
}


/***
Open a pseudoterminal.
@function openpt
@int oflags bitwise OR of zero or more of `O_RDWR` and `O_NOCTTY`
@return[1] file descriptor of pseudoterminal, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see posix_openpt(3)
@see grantpt
@see ptsname
@see unlockpt
*/
static int
Popenpt(lua_State *L)
{
	int flags = checkint(L, 1);
	checknargs(L, 1);
	/* The name of the pseudo-device is specified by POSIX */
	return pushresult(L, open("/dev/ptmx", flags), NULL);
}


/***
Get the name of a slave pseudo-terminal
@function ptsname
@int fd descriptor returned by @{openpt}
@return[1] path name of the slave terminal device, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see ptsname(3)
@see grantpt
@see unlockpt
*/
static int
Pptsname(lua_State *L)
{
	int fd=checkint(L, 1);
	const char* slave;
	checknargs(L, 1);
	slave = ptsname(fd);
	if (!slave)
		return pusherror(L, "getptsname");
	return pushstringresult(slave);
}


/***
Find canonicalized absolute pathname.
@function realpath
@string path file to act on
@treturn[1] string canonicalized absolute path, if successful
@return[2] nil
@treturn[2] string error messag
@treturn[2] int errnum
@see realpath(3)
*/
static int
Prealpath(lua_State *L)
{
	char *s;
	checknargs(L, 1);
	if ((s = realpath(luaL_checkstring(L, 1), NULL)) == NULL)
		return pusherror(L, "realpath");
	lua_pushstring(L, s);
	free(s);
	return 1;
}


/***
Set an environment variable for this process.
(Child processes will inherit this)
@function setenv
@string name
@string[opt] value (maybe nil, meaning 'unset')
@param[opt] overwrite non-nil prevents overwriting a variable
@treturn[1] int `0`, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see setenv(3)
*/
static int
Psetenv(lua_State *L)
{
	const char *name=luaL_checkstring(L, 1);
	const char *value=optstring(L, 2, NULL);
	checknargs(L, 3);
	if (value==NULL)
	{
		unsetenv(name);
		return pushresult(L, 0, NULL);
	}
	else
	{
		int overwrite=lua_isnoneornil(L, 3) || lua_toboolean(L, 3);
		return pushresult(L, setenv(name,value,overwrite), NULL);
	}
}


/***
Unlock a pseudoterminal master/slave pair
@function unlockpt
@int fd descriptor returned by openpt
@treturn[1] int `0`, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see unlockpt(3)
@see openpt
@see ptsname
@see grantpt
*/
static int
Punlockpt(lua_State *L)
{
	int fd=checkint(L, 1);
	checknargs(L, 1);
	return pushresult(L, unlockpt(fd), "unlockpt");
}


static const luaL_Reg posix_stdlib_fns[] =
{
	LPOSIX_FUNC( Pabort		),
	LPOSIX_FUNC( Pgetenv		),
	LPOSIX_FUNC( Pgrantpt		),
	LPOSIX_FUNC( Pmkdtemp		),
	LPOSIX_FUNC( Pmkstemp		),
	LPOSIX_FUNC( Popenpt		),
	LPOSIX_FUNC( Pptsname		),
	LPOSIX_FUNC( Prealpath		),
	LPOSIX_FUNC( Psetenv		),
	LPOSIX_FUNC( Punlockpt		),
	{NULL, NULL}
};


LUALIB_API int
luaopen_posix_stdlib(lua_State *L)
{
	luaL_register(L, "posix.stdlib", posix_stdlib_fns);
	lua_pushliteral(L, "posix.stdlib for " LUA_VERSION " / " PACKAGE_STRING);
	lua_setfield(L, -2, "version");

	return 1;
}
