/*
 *
 *
 *    tty.cc
 *
 *    C++ source code for class my::tty member functions and/or static member
 *    data.
 *
 *    =========================================================================
 *
 *    Copyright (C) 1999-2003 by Matt Roberts, KK5JY.
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation; either
 *    version 2.1 of the License, or (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *    =========================================================================
 *
 *
 */


#include <my++/tty.h>


/*
 *
 *   option handling routines
 *
 */

void my::tty::set_options (const my::termios &t, actiontype a) {
	int action = 0;
	switch (a) {
		case apply_now: action = TCSANOW; break;
		case flush_first: action = TCSAFLUSH; break;
		case drain_first: action = TCSADRAIN; break;
		default: throw tty::exception(this, EBADF);
	}
	::termios call_ios;
	t.copy_to (call_ios);
	if (::tcsetattr (fd, action, &call_ios) < 0)
		throw tty::exception(this, errno);
}

void my::tty::get_options (my::termios &t) {
	::termios call_ios;
	if (::tcgetattr (fd, &call_ios) < 0)
		throw tty::exception(this, errno);
	t = call_ios;
}


/*
 *
 *   buffer handling options
 *
 */

void my::tty::drain (void) {
	if (fd < 0)
		throw tty::exception (this, EBADF);
	if (::tcdrain (fd) < 0)
		throw tty::exception (this, errno);
}

void my::tty::flush (flushtype f) {
	int action = 0;

	switch (f) {
		case flush_in: action = TCIFLUSH; break;
		case flush_out: action = TCOFLUSH; break;
		case flush_io: action = TCIOFLUSH; break;
		default: throw tty::exception(this, EBADF);
	}

	if (::tcflush (fd, action) < 0)
		throw tty::exception (this, errno);
}

void my::tty::flow (my::tty::flowtype f) {
	int action = 0;

	if (fd < 0)
		throw tty::exception (this, EBADF);

	switch (f) {
		case start:
			action = TCOOFF;
			break;
		case send_start:
			action = TCIOFF;
			break;
		case stop:
			action = TCOON;
			break;
		case send_stop:
			action = TCION;
			break;
		default:
			throw tty::exception (this, EBADF);
	}

	if (::tcflow (fd, action) < 0)
		throw tty::exception(this, errno);
}

void my::tty::send_break (int intervals) {
	if (fd < 0)
		throw tty::exception (this, EBADF);
	if (::tcsendbreak (fd, intervals) < 0)
		throw tty::exception (this, errno);
}

void my::tty::set_pgrp (pid_t p_gid) {
	// implement me -- FIXME
}

/*
 *
 *  open/close
 *
 */

void my::tty::close (void) throw() {
	if (fd >= 0) {
		if (!dont_save)
			set_options(old_options);
		::close(fd);
		fd = (-1);
	}
}

void my::tty::open (const std::string &name, int modes) {
	int open_flags = 0;
	if ((modes & device::read) && (modes & device::write)) {
		open_flags = O_RDWR;
	} else {
		if (! ((modes & device::read) || (modes & device::write))) {
			open_flags = O_RDWR;
		} else {
			if (modes & device::read)
				open_flags = O_RDONLY;
			if (modes & device::write)
				open_flags = O_WRONLY;
		}
	}

	if (modes & device::not_ctl_tty)
		open_flags |= O_NOCTTY;
#ifdef O_NOFOLLOW
	if (modes & device::not_symlink)
		open_flags |= O_NOFOLLOW;
#endif
	if (modes & device::nonblocking)
#ifdef O_NONBLOCK
		open_flags |= O_NONBLOCK;
#else
	open_flags |= O_NDELAY;
#endif
	if (modes & device::synchronous)
		modes |= O_SYNC;

	// first, close the fd if it is open
	close();
	fd = ::open (name.c_str(), open_flags);
	if (fd < 0)
		throw tty::exception (this, errno);
	get_options (old_options);
}


/*
 *
 *   I/O routines
 *
 */

ssize_t my::tty::read_vp (void *buf, size_t len) {
	return ::read (fd, buf, len);
}

ssize_t my::tty::write_vp (const void *buf, size_t len) {
	return ::write (fd, buf, len);
}


/*
 *
 *   public ctors
 *
 */

my::tty::tty (const std::string &filename, const my::termios &opts, int o_modes) : fd(-1), dont_save(false) {
	open (filename, o_modes);
	set_options(opts);
}

my::tty::tty (const std::string &filename, const my::termios &opts) : fd(-1), dont_save(false) {
	open (filename, 0);
	set_options(opts);
}

my::tty::tty (const std::string &filename) : fd(-1), dont_save(false) {
	open (filename, 0);
}

my::tty::tty (const std::string &filename, int o_modes) : fd(-1), dont_save(false) {
	open (filename, o_modes);
}

my::tty::tty (void) : fd(-1), dont_save(false) {
	/* noop */
}


/*
 *
 *   public dtor
 *
 */

my::tty::~tty (void) throw(){
	close();
}
