/*
 *	binkleyforce -- unix FTN mailer project
 *	
 *	Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
 *	
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *	
 *	$Id: io_unix_modem.c,v 1.2 2002/11/21 21:22:55 kst Exp $
 */

#include "includes.h"
#include "confread.h"
#include "logger.h"
#include "util.h"
#include "io.h"

/*
 * Get verbal device name, remove "/dev/" part and replace all '/'
 * characters by the '-' character. Returned value must be freed.
 */
char *port_get_name(const char *devname)
{
	char *yield = NULL;
	
	if( !devname )
		yield = xstrcpy("unknown");
	else if( !strncmp(devname, "/dev/", 5) )
		yield = xstrcpy(devname + 5);
	else
		yield = xstrcpy(devname);

	string_replchar(yield, '/', '-');

	return yield;
}

#ifndef MODEM_WATCH_CARRIER
static RETSIGTYPE linedrop(int sig)
{
	if( tty_online )
	{
		log("carrier lost");
		tty_hangup = TRUE;
	}
	else
	{
		log("terminating on signal %d", sig);
		tty_abort = TRUE;
	}
}
#endif

static RETSIGTYPE interrupt(int sig)
{
	tty_abort = TRUE;
	log("terminating on signal %d", sig);
}

/*
 *  Open modem device $port, save old termios to $oldtio
 */
int port_open(const s_modemport *port, int detach, TIO *oldtio)
{
	int fd;
	
	ASSERT(port != NULL);
	
	DEB((D_MODEM, "openport: open device \"%s\" locked at %ld",
		port->name, (long)port->speed));
	
	if( (fd = open(port->name, O_RDWR | O_NONBLOCK)) < 0 )
		{ logerr("cannot open device \"%s\"", port->name); return 1; }

	/* make new fd == stdin if it isn't already */
	if( fd > 0 )
	{
		if( detach )
			(void)close(0);
		if( dup(fd) != 0 )
			{ logerr("cannot make device \"%s\" stdin", port->name); return(1); }
	}
	
	/* make stdout and stderr, too */
	if( detach )
		{ (void)close(1); (void)close(2); }
	
	if( dup(0) != 1 )
		{ logerr("cannot dup stdin to stdout"); return(1); }
	
	if( dup(0) != 2 )
		{ logerr("cannot dup stdin to stderr"); return(1); }
	
	if( fd > 2 )
		(void)close(fd);

	/* Set port modes (LOCAL mode) */
	if( port_init(0, port->speed, oldtio, TRUE) )
		return(1);
	
	/* Make it controlling tty */
	if( detach )
		tio_ctty(0);
	
	/*
	 * Switch off stdio buffering. We are
	 * not going to use streamed I/O, but..
	 */
	setbuf(stdin,  (char *)NULL);
	setbuf(stdout, (char *)NULL);
	setbuf(stderr, (char *)NULL);
	
	return(0);
}

/*
 *  Set terminal options as required. Don't forget that settings of
 *  the file descriptor copies made with dup() are shared.
 */
int port_init(int fd, int portspeed, TIO *oldtio, bool local)
{
	TIO tio;
	
	/* Install signal handlers */
#ifdef MODEM_WATCH_CARRIER
	signal(SIGHUP, interrupt);
#else
	signal(SIGHUP, linedrop);
#endif
	signal(SIGINT, interrupt);
	signal(SIGTERM, interrupt);
	
	/*
	 * Set descriptor I/O to the non-blocking mode
	 */
	if( fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1 )
	{
		logerr("cannot set non-blocking mode for stdin");
		return -1;
	}
	
	if( isatty(fd) )
	{
		/* Report to tty I/O routines that we are using modem */
		tty_modem = 1;
	
		if( tio_get(fd, &tio) == -1 )
		{
			logerr("initport error: cannot get terminal settings");
			return -1;
		}
		
		/*
		 * Save old settings. We will restore them on exit.
		 */
		if( oldtio )
			*oldtio = tio;
		
		/* Set "raw" mode */
		tio_set_raw_mode(&tio);

		/* Set hardware flow control */
		if( tio_set_flow_control(fd, &tio, FLOW_HARD) == -1 )
			return -1;
		
		/* Should we ignore DCD? */
#ifdef MODEM_WATCH_CARRIER
		tio_set_local(&tio, TRUE);
#else
		tio_set_local(&tio, local);
#endif
		
		/* Set port speed */
		if( portspeed )
		{
			/* set bit rate */
			tio_set_speed(&tio, portspeed);
		}
	
		if( tio_set(fd, &tio) == -1 )
		{
			logerr("initport error: cannot set terminal settings");
			return -1;
		}
	}

	return 0;
}

int port_carrier(int fd, bool online)
{
	tty_online = online;

#ifndef MODEM_WATCH_CARRIER
	if( isatty(fd) )
	{
		TIO tio;
		
		if( tio_get(fd, &tio) == -1 )
		{
			logerr("port_carrier error: cannot get terminal settings");
			return -1;
		}
		
		tio_set_local(&tio, online ? FALSE : TRUE);
			
		if( tio_set(fd, &tio) == -1 )
		{
			logerr("port_carrier error: cannot set terminal settings");
			return -1;
		}
	}
#endif

	return 0;
}

/*
 *  Restore old terminal settings
 */
int port_deinit(int fd, TIO *oldtio)
{
	if( tio_set(fd, oldtio) == -1 )
	{
		logerr("deinitport error: cannot restore terminal settings");
		return -1;
	}
	
	return 0;
}

/*
 *  Close port
 */
void port_close(void)
{
	DEB((D_MODEM, "closeport: close modem port"));
	/* Do nothing */
}

