/*
 * 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
 */
/***
 Control Terminal I/O.

 Functions and constants for controlling terminal behaviour.

@module posix.termio
*/

#include <config.h>

#include <termios.h>

#include "_helpers.c"


/***
Control characters.
The field names below are not strings, but index constants each
referring to a terminal control character.
@table ccs
@int VINTR interrupt control character
@int VQUIT quit control character
@int WERASE erase control character
@int VKILL kill control character
@int VEOF end-of-file control character
@int VEOL end-of-line control charactor
@int VEOL2 another end-of-line control charactor
@int VMIN 1
@int VTIME 0
@int VSTART xon/xoff start control character
@int VSTOP xon/xoff stop control character
@int VSUSP suspend control character
*/

/***
Terminal attributes.
The constants named below are all available in this submodule's namespace,
as long as they are supported by the underlying system.
@table termios
@int cflag bitwise OR of zero or more of `B0`, `B50`, `B75`, `B110`,
  `B134`, `B150`, `B200`, `B300`, `B600`, `B1200`, `B1800`, `B2400`,
  `B4800`, `B9600`, `B19200`, `B38400`, `B57600`, `B115200`, `CSIZE`,
  `CS5`, `CS6`, `CS7`, `CS8`, `CSTOPB`, `CREAD`, `PARENB`, `PARODD`,
  `HUPCL`, `CLOCAL` and `CRTSCTS`
@int iflag input flags; bitwise OR of zero or more of `IGNBRK`, `BRKINT`,
  `IGNPAR`, `PARMRK`, `INPCK`, `ISTRIP`, `INLCR`, `IGNCR`, `ICRNL`,
  `IXON`, `IXOFF`, `IXANY`, `IMAXBEL` and `IUTF8`
@int lflags local flags; bitwise OR of zero or more of `ISIG`, `ICANON`,
  `ECHO`, `ECHOE`, `ECHOK', 'ECHONL`, `NOFLSH`, `IEXTEN` and `TOSTOP`
@int oflag output flags; bitwise OR of zero or more of `OPOST`, `ONLCR`,
  `OXTABS`, `ONOEOT`, `OCRNL`, `ONOCR`, `ONLRET`, `OFILL`, `NLDLY`,
  `TABDLY`, `CRDLY`, `FFDLY`, `BSDLY`, `VTDLY` and `OFDEL`
@tfield ccs cc array of terminal control characters
*/


/***
Wait for all written output to reach the terminal.
@function tcdrain
@int fd terminal descriptor to act on
@treturn[1] int `0`, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see tcdrain(3)
*/
static int
Ptcdrain(lua_State *L)
{
	int fd = checkint(L, 1);
	checknargs(L, 1);
	return pushresult(L, tcdrain(fd), NULL);
}


/***
Suspend transmission or receipt of data.
@function tcflow
@int fd terminal descriptor to act on
@int action one of `TCOOFF`, `TCOON`, `TCIOFF` or `TCION`
@treturn[1] int `0`, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see tcflow(3)
*/
static int
Ptcflow(lua_State *L)
{
	int fd = checkint(L, 1);
	int action = checkint(L, 2);
	checknargs(L, 2);
	return pushresult(L, tcflow(fd, action), NULL);
}


/***
Discard any data already written but not yet sent to the terminal.
@function tcflush
@int fd terminal descriptor to act on
@int action one of `TCIFLUSH`, `TCOFLUSH`, `TCIOFLUSH`
@treturn[1] int `0`, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@see tcflush(3)
*/
static int
Ptcflush(lua_State *L)
{
	int fd = checkint(L, 1);
	int qs = checkint(L, 2);
	checknargs(L, 2);
	return pushresult(L, tcflush(fd, qs), NULL);
}


/***
Get termios state.
@function tcgetattr
@int fd terminal descriptor
@treturn[1] termios terminal attributes, if successful
@return[2] nil
@treturn[2] string error message
@treturn[2] int errnum
@return error message if failed
@see tcgetattr(3)
@usage
local termios, errmsg = tcgetattr (fd)
*/
static int
Ptcgetattr(lua_State *L)
{
	int r, i;
	struct termios t;
	int fd = checkint(L, 1);

	checknargs(L, 1);
	r = tcgetattr(fd, &t);
	if (r == -1) return pusherror(L, NULL);

	lua_newtable(L);
	pushintegerfield("iflag", t.c_iflag);
	pushintegerfield("oflag", t.c_oflag);
	pushintegerfield("lflag", t.c_lflag);
	pushintegerfield("cflag", t.c_cflag);

	lua_newtable(L);
	for (i=0; i<NCCS; i++)
	{
		lua_pushinteger(L, i);
		lua_pushinteger(L, t.c_cc[i]);
		lua_settable(L, -3);
	}
	lua_setfield(L, -2, "cc");

	return 1;
}


/***
Send a stream of zero valued bits.
@function tcsendbreak
@see tcsendbreak(3)
@int fd terminal descriptor
@int duration if non-zero, stream for some implementation defined time
@return 0 if successful, otherwise nil
@return error message if failed
*/
static int
Ptcsendbreak(lua_State *L)
{
	int fd = checkint(L, 1);
	int duration = checkint(L, 2);
	checknargs(L, 2);
	return pushresult(L, tcsendbreak(fd, duration), NULL);
}


/***
Set termios state.
@function tcsetattr
@int fd terminal descriptor to act on
@int actions bitwise OR of one or more of `TCSANOW`, `TCSADRAIN`,
  `TCSAFLUSH` and `TSASOFT`
@tparam termios a table with fields from iflag, oflag, cflag, lflag and cc,
 each formed by `bor` operations with various posix constants
@return 0 if successful, otherwise nil
@return error message if failed
@see tcsetattr(3)
@usage
ok, errmsg = tcsetattr (fd, 0, { cc = { [P.VTIME] = 0, [P.VMIN] = 1 })
*/
static int
Ptcsetattr(lua_State *L)
{
	struct termios t;
	int i;
	int fd = checkint(L, 1);
	int act = checkint(L, 2);
	luaL_checktype(L, 3, LUA_TTABLE);
	checknargs(L, 3);

	lua_getfield(L, 3, "iflag"); t.c_iflag = optint(L, -1, 0); lua_pop(L, 1);
	lua_getfield(L, 3, "oflag"); t.c_oflag = optint(L, -1, 0); lua_pop(L, 1);
	lua_getfield(L, 3, "cflag"); t.c_cflag = optint(L, -1, 0); lua_pop(L, 1);
	lua_getfield(L, 3, "lflag"); t.c_lflag = optint(L, -1, 0); lua_pop(L, 1);

	lua_getfield(L, 3, "cc");
	for (i=0; i<NCCS; i++)
	{
		lua_pushinteger(L, i);
		lua_gettable(L, -2);
		t.c_cc[i] = optint(L, -1, 0);
		lua_pop(L, 1);
	}

	return pushresult(L, tcsetattr(fd, act, &t), NULL);
}


static const luaL_Reg posix_termio_fns[] =
{
	LPOSIX_FUNC( Ptcdrain		),
	LPOSIX_FUNC( Ptcflow		),
	LPOSIX_FUNC( Ptcflush		),
	LPOSIX_FUNC( Ptcgetattr		),
	LPOSIX_FUNC( Ptcsendbreak	),
	LPOSIX_FUNC( Ptcsetattr		),
	{NULL, NULL}
};


LUALIB_API int
luaopen_posix_termio(lua_State *L)
{
	luaL_register(L, "posix.termio", posix_termio_fns);
	lua_pushliteral(L, "posix.termio for " LUA_VERSION " / " PACKAGE_STRING);
	lua_setfield(L, -2, "version");

	/* tcsetattr */
	LPOSIX_CONST( TCSANOW		);
	LPOSIX_CONST( TCSADRAIN		);
	LPOSIX_CONST( TCSAFLUSH		);

	/* tcflush */
	LPOSIX_CONST( TCIFLUSH		);
	LPOSIX_CONST( TCOFLUSH		);
	LPOSIX_CONST( TCIOFLUSH		);

	/* tcflow() */
	LPOSIX_CONST( TCOOFF		);
	LPOSIX_CONST( TCOON		);
	LPOSIX_CONST( TCIOFF		);
	LPOSIX_CONST( TCION		);

	/* cflag */
#ifdef B0
	LPOSIX_CONST( B0		);
#endif
#ifdef B50
	LPOSIX_CONST( B50		);
#endif
#ifdef B75
	LPOSIX_CONST( B75		);
#endif
#ifdef B110
	LPOSIX_CONST( B110		);
#endif
#ifdef B134
	LPOSIX_CONST( B134		);
#endif
#ifdef B150
	LPOSIX_CONST( B150		);
#endif
#ifdef B200
	LPOSIX_CONST( B200		);
#endif
#ifdef B300
	LPOSIX_CONST( B300		);
#endif
#ifdef B600
	LPOSIX_CONST( B600		);
#endif
#ifdef B1200
	LPOSIX_CONST( B1200		);
#endif
#ifdef B1800
	LPOSIX_CONST( B1800		);
#endif
#ifdef B2400
	LPOSIX_CONST( B2400		);
#endif
#ifdef B4800
	LPOSIX_CONST( B4800		);
#endif
#ifdef B9600
	LPOSIX_CONST( B9600		);
#endif
#ifdef B19200
	LPOSIX_CONST( B19200		);
#endif
#ifdef B38400
	LPOSIX_CONST( B38400		);
#endif
#ifdef B57600
	LPOSIX_CONST( B57600		);
#endif
#ifdef B115200
	LPOSIX_CONST( B115200		);
#endif
#ifdef CSIZE
	LPOSIX_CONST( CSIZE		);
#endif
#ifdef BS5
	LPOSIX_CONST( CS5		);
#endif
#ifdef CS6
	LPOSIX_CONST( CS6		);
#endif
#ifdef CS7
	LPOSIX_CONST( CS7		);
#endif
#ifdef CS8
	LPOSIX_CONST( CS8		);
#endif
#ifdef CSTOPB
	LPOSIX_CONST( CSTOPB		);
#endif
#ifdef CREAD
	LPOSIX_CONST( CREAD		);
#endif
#ifdef PARENB
	LPOSIX_CONST( PARENB		);
#endif
#ifdef PARODD
	LPOSIX_CONST( PARODD		);
#endif
#ifdef HUPCL
	LPOSIX_CONST( HUPCL		);
#endif
#ifdef CLOCAL
	LPOSIX_CONST( CLOCAL		);
#endif
#ifdef CRTSCTS
	LPOSIX_CONST( CRTSCTS		);
#endif

	/* lflag */
#ifdef ISIG
	LPOSIX_CONST( ISIG		);
#endif
#ifdef ICANON
	LPOSIX_CONST( ICANON		);
#endif
#ifdef ECHO
	LPOSIX_CONST( ECHO		);
#endif
#ifdef ECHOE
	LPOSIX_CONST( ECHOE		);
#endif
#ifdef ECHOK
	LPOSIX_CONST( ECHOK		);
#endif
#ifdef ECHONL
	LPOSIX_CONST( ECHONL		);
#endif
#ifdef NOFLSH
	LPOSIX_CONST( NOFLSH		);
#endif
#ifdef IEXTEN
	LPOSIX_CONST( IEXTEN		);
#endif
#ifdef TOSTOP
	LPOSIX_CONST( TOSTOP		);
#endif

	/* iflag */
#ifdef INPCK
	LPOSIX_CONST( INPCK		);
#endif
#ifdef IGNPAR
	LPOSIX_CONST( IGNPAR		);
#endif
#ifdef PARMRK
	LPOSIX_CONST( PARMRK		);
#endif
#ifdef ISTRIP
	LPOSIX_CONST( ISTRIP		);
#endif
#ifdef IXON
	LPOSIX_CONST( IXON		);
#endif
#ifdef IXOFF
	LPOSIX_CONST( IXOFF		);
#endif
#ifdef IXANY
	LPOSIX_CONST( IXANY		);
#endif
#ifdef IGNBRK
	LPOSIX_CONST( IGNBRK		);
#endif
#ifdef BRKINT
	LPOSIX_CONST( BRKINT		);
#endif
#ifdef INLCR
	LPOSIX_CONST( INLCR		);
#endif
#ifdef IGNCR
	LPOSIX_CONST( IGNCR		);
#endif
#ifdef ICRNL
	LPOSIX_CONST( ICRNL		);
#endif
#ifdef IMAXBEL
	LPOSIX_CONST( IMAXBEL		);
#endif

	/* oflag */
#ifdef OPOST
	LPOSIX_CONST( OPOST		);
#endif
#ifdef ONLCR
	LPOSIX_CONST( ONLCR		);
#endif
#ifdef OCRNL
	LPOSIX_CONST( OCRNL		);
#endif
#ifdef ONLRET
	LPOSIX_CONST( ONLRET		);
#endif
#ifdef OFILL
	LPOSIX_CONST( OFILL		);
#endif
#ifdef OFDEL
	LPOSIX_CONST( OFDEL		);
#endif
#ifdef NLDLY
	LPOSIX_CONST( NLDLY		);
#endif
#ifdef NL0
	LPOSIX_CONST( NL0		);
#endif
#ifdef NL1
	LPOSIX_CONST( NL1		);
#endif
#ifdef CRDLY
	LPOSIX_CONST( CRDLY		);
#endif
#ifdef CR0
	LPOSIX_CONST( CR0		);
#endif
#ifdef CR1
	LPOSIX_CONST( CR1		);
#endif
#ifdef CR2
	LPOSIX_CONST( CR2		);
#endif
#ifdef CR3
	LPOSIX_CONST( CR3		);
#endif
#ifdef TABDLY
	LPOSIX_CONST( TABDLY		);
#endif
#ifdef TAB0
	LPOSIX_CONST( TAB0		);
#endif
#ifdef TAB1
	LPOSIX_CONST( TAB1		);
#endif
#ifdef TAB2
	LPOSIX_CONST( TAB2		);
#endif
#ifdef TAB3
	LPOSIX_CONST( TAB3		);
#endif
#ifdef BSDLY
	LPOSIX_CONST( BSDLY		);
#endif
#ifdef BS0
	LPOSIX_CONST( BS0		);
#endif
#ifdef BS1
	LPOSIX_CONST( BS1		);
#endif
#ifdef VTDLY
	LPOSIX_CONST( VTDLY		);
#endif
#ifdef VT0
	LPOSIX_CONST( VT0		);
#endif
#ifdef VT1
	LPOSIX_CONST( VT1		);
#endif
#ifdef FFDLY
	LPOSIX_CONST( FFDLY		);
#endif
#ifdef FF0
	LPOSIX_CONST( FF0		);
#endif
#ifdef FF1
	LPOSIX_CONST( FF1		);
#endif

	/* cc */
#ifdef VINTR
	LPOSIX_CONST( VINTR		);
#endif
#ifdef VQUIT
	LPOSIX_CONST( VQUIT		);
#endif
#ifdef VERASE
	LPOSIX_CONST( VERASE		);
#endif
#ifdef VKILL
	LPOSIX_CONST( VKILL		);
#endif
#ifdef VEOF
	LPOSIX_CONST( VEOF		);
#endif
#ifdef VEOL
	LPOSIX_CONST( VEOL		);
#endif
#ifdef VEOL2
	LPOSIX_CONST( VEOL2		);
#endif
#ifdef VMIN
	LPOSIX_CONST( VMIN		);
#endif
#ifdef VTIME
	LPOSIX_CONST( VTIME		);
#endif
#ifdef VSTART
	LPOSIX_CONST( VSTART		);
#endif
#ifdef VSTOP
	LPOSIX_CONST( VSTOP		);
#endif
#ifdef VSUSP
	LPOSIX_CONST( VSUSP		);
#endif

	/* XSI extensions - don't use these if you care about portability
	 * to strict POSIX conforming machines, such as Mac OS X.
	 */
#ifdef CBAUD
	LPOSIX_CONST( CBAUD		);
#endif
#ifdef EXTA
	LPOSIX_CONST( EXTA		);
#endif
#ifdef EXTB
	LPOSIX_CONST( EXTB		);
#endif
#ifdef DEFECHO
	LPOSIX_CONST( DEFECHO		);
#endif
#ifdef ECHOCTL
	LPOSIX_CONST( ECHOCTL		);
#endif
#ifdef ECHOPRT
	LPOSIX_CONST( ECHOPRT		);
#endif
#ifdef ECHOKE
	LPOSIX_CONST( ECHOKE		);
#endif
#ifdef FLUSHO
	LPOSIX_CONST( FLUSHO		);
#endif
#ifdef PENDIN
	LPOSIX_CONST( PENDIN		);
#endif
#ifdef LOBLK
	LPOSIX_CONST( LOBLK		);
#endif
#ifdef SWTCH
	LPOSIX_CONST( SWTCH		);
#endif
#ifdef VDISCARD
	LPOSIX_CONST( VDISCARD		);
#endif
#ifdef VDSUSP
	LPOSIX_CONST( VDSUSP		);
#endif
#ifdef VLNEXT
	LPOSIX_CONST( VLNEXT		);
#endif
#ifdef VREPRINT
	LPOSIX_CONST( VREPRINT		);
#endif
#ifdef VSTATUS
	LPOSIX_CONST( VSTATUS		);
#endif
#ifdef VWERASE
	LPOSIX_CONST( VWERASE		);
#endif

	return 1;
}
