/*
 *  Copyright (c) 1992, 1993 John E. Davis  (davis@amy.tch.harvard.edu)
 *  All Rights Reserved.
 */

#include <stdio.h>
#include "config.h"
#include "sysdep.h"

#include <signal.h>
/* sequent support thanks to Kenneth Lorber <keni@oasys.dt.navy.mil> */
/* SYSV (SYSV ISC R3.2 v3.0) provided by iain.lea@erlm.siemens.de */

#ifndef sequent
# include <stdlib.h>
#endif
#include <sys/time.h>
#include <sys/ioctl.h>
#ifndef sequent
# include <unistd.h>
# include <termios.h>
#endif
#ifdef SYSV
# include <sys/termio.h>
# include <sys/stream.h>
# include <sys/ptem.h>
# include <sys/tty.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

#define TTY_DESCR 2
#ifdef sequent
struct ttystuff
  {
      struct tchars t;
      struct ltchars lt;
      struct sgttyb s;
  };
struct ttystuff OLDTTY;
#else
struct termios OLDTTY;
#endif

     /* this next works on ultrix for setting termios */
#ifdef TCGETS
#define GET_TERMIOS(fd, x) ioctl(fd, TCGETS, x)
#define SET_TERMIOS(fd, x) ioctl(fd, TCSETS, x)
#else
# ifdef sequent
#  define X(x,m)  &(((struct ttystuff*)(x))->m)
#  define GET_TERMIOS(fd, x)	\
	if(ioctl(fd, TIOCGETC, X(x,t))<0 || \
	ioctl(fd, TIOCGLTC, X(x,lt))<0 || \
	ioctl(fd, TIOCGETP, X(x,s))<0)exit_error("Can't get terminal info")
#  define SET_TERMIOS(fd, x)	\
	if(ioctl(fd, TIOCSETC, X(x,t))<0 || \
	ioctl(fd, TIOCSLTC, X(x,lt))<0 || \
	ioctl(fd, TIOCSETP, X(x,s))<0)exit_error("Can't set terminal info")
# else
#  define GET_TERMIOS(fd, x) tcgetattr(fd, x)
#  define SET_TERMIOS(fd, x) tcsetattr(fd, TCSANOW, x)
# endif
#endif

static int Baud_Rates[20] = 
{
   0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 
   9600, 19200, 38400, 0, 0, 0, 0
};

static int tty_inited = 0;

void init_tty()
{
#ifdef sequent
    struct ttystuff newtty;
#else
    struct termios newtty;
#endif
   tty_inited = 1;
   if (Batch) return;
   enable_cursor_keys();
   GET_TERMIOS(TTY_DESCR, &OLDTTY);
   GET_TERMIOS(TTY_DESCR, &newtty);
#ifdef sequent
   newtty.s.sg_flags &= ~(ECHO);
   newtty.s.sg_flags &= ~(CRMOD);
   newtty.t.t_eofc = 1;
   newtty.t.t_intrc = Abort_Char;	/* ^G */
   newtty.t.t_quitc = 255;
   newtty.lt.t_suspc = 255;   /* to ignore ^Z */
   newtty.lt.t_duspc = 255;    /* to ignore ^Y */
   newtty.lt.t_lnextc = 255;
   newtty.s.sg_flags |= CBREAK;		/* do I want cbreak or raw????? */
#else
   
   
   if (Output_Rate == 0)
     {
/* Note:  if this generates an compiler error, simply remove 
   the statement */
	Output_Rate = cfgetospeed (&newtty);
	
	
	Output_Rate = (Output_Rate > 0) && (Output_Rate < 19) ?
	              Baud_Rates[Output_Rate] / 10 : 4096;
     }
   
   newtty.c_iflag &= ~(ECHO | INLCR | ICRNL);
   newtty.c_oflag &= ~ONLCR;	       /* do not map newline to cr/newline on out */
   if (Flow_Control == 0) newtty.c_iflag &= ~IXON;
   newtty.c_cc[VMIN] = 1;
   newtty.c_cc[VTIME] = 0;
   newtty.c_cc[VEOF] = 1;
   newtty.c_lflag = ISIG | NOFLSH;
   newtty.c_cc[VINTR] = Abort_Char;   /* ^G */
   newtty.c_cc[VQUIT] = 255;
   newtty.c_cc[VSUSP] = 255;   /* to ignore ^Z */
#ifdef VSWTCH
   newtty.c_cc[VSWTCH] = 255;   /* to ignore who knows what */
#endif
#endif /*sequent*/
   SET_TERMIOS(TTY_DESCR, &newtty);
}

void reset_tty()
{
   if (Batch) return;
/*   send_string_to_term("\033>"); */  /* Enable the Numeric Keypad */

   if (!tty_inited) return;
   SET_TERMIOS(TTY_DESCR, &OLDTTY);
   /* This statement ensures init_tty will not try to change output_rate 
      (when coming back from suspension) */
   if (Output_Rate == 0) Output_Rate = -1;
}

unsigned char sys_getkey()
{
   int n = 450;
   unsigned char c;
   if (KeyBoard_Quit) return((int) Abort_Char);
   /* sleep for 45 second and try again */
   while (!KeyBoard_Quit && !input_pending(&n))
     {
	/* update status line incase user is displaying time */
	if (Display_Time)
	  {
	     Window->trashed = 1;
	     update((Line *) NULL, 0, 1);
	  }
     }
   if (KeyBoard_Quit) return(Abort_Char);
   if ((read(TTY_DESCR, &c, 1) < 0) && !KeyBoard_Quit)
     {
	fprintf(stderr,"getkey():  read failed.\n");
	reset_tty();
	exit(1);
     }
   /* only way for keyboard quit to be non zero is if ^G recived and sigint processed */
   if (KeyBoard_Quit) c = Abort_Char;
   KeyBoard_Quit = 0;

   return(c);
}
  
#ifndef FD_SET
#define FD_SET(fd, tthis) *(tthis) = 1 << fd
#define FD_ZERO(tthis)    *(tthis) = 0
typedef fd_set int;
#endif

int input_pending(int *tsecs)
{
   struct timeval wait;
   fd_set readfd, writefd, exceptfd;
   long usecs, secs;
   int fd = 2, ret;

   if (INPUT_BUFFER_LEN || Batch) return(INPUT_BUFFER_LEN);

   secs = *tsecs / 10;
   usecs = (*tsecs % 10) * 100000;
   wait.tv_sec = secs;
   wait.tv_usec = usecs;
   
   FD_SET(fd, &readfd);   /* readfd = 1 << fd; */
   FD_ZERO(&writefd);
   FD_ZERO(&exceptfd);
   
   ret = select(fd + 1, &readfd, &writefd, &exceptfd, &wait);
   return(ret);
}

/*  This is to get the size of the terminal  */
void get_term_dimensions(int *cols, int *rows)
{
#ifdef TIOCGWINSZ
   struct winsize wind_struct;

   ioctl(2,TIOCGWINSZ,&wind_struct);
   *cols = (int) wind_struct.ws_col;
   *rows = (int) wind_struct.ws_row;
   if (*rows <= 0) *rows = Screen_Height;
   if (*cols <= 0) *cols = Screen_Width;
#else
   *rows = Screen_Height;
   *cols = Screen_Width;
#endif
}

/* returns 0 on failure, 1 on sucess */
int sys_delete_file(char *filename)
{
    return(1 + unlink(filename));
}

int sys_rename(char *from, char *to)
{
    return(rename(from,to));
}

#ifdef SIGTSTP
extern void sig_sys_spawn_cmd(int);
extern int Signal_Sys_Spawn_Flag;      /* used if something else sends stop */
#endif


void sys_suspend(void)
{
   signal (SIGTSTP, SIG_DFL);
   if (Signal_Sys_Spawn_Flag) kill(0, SIGSTOP); else kill(0, SIGTSTP);
   signal (SIGTSTP, sig_sys_spawn_cmd);   
}

/* returns 0 if file does not exist, 1 if it is not a dir, 2 if it is */
int sys_chmod(char *file, int what, int *mode, short *uid, short *gid)
{
   struct stat buf;
   int m;

   if (what)
     {
	chmod(file, *mode);
	chown(file, (uid_t) *uid, (uid_t) *gid);
	return(0);
     }

   if (stat(file, &buf) < 0) switch (errno)
     {
	case EACCES: return(-1); /* es = "Access denied."; break; */
	case ENOENT: return(0);  /* ms = "File does not exist."; */
	case ENOTDIR: return(-2); /* es = "Invalid Path."; */
	default: return(-3); /* "stat: unknown error."; break;*/
     }

   m = buf.st_mode;
   *uid = buf.st_uid;
   *gid = buf.st_gid;
   
/* AIX requires this */
#ifdef _S_IFDIR
#ifndef S_IFDIR
#define S_IFDIR _S_IFDIR
#endif
#endif

   *mode = m & 0777;

   if (m & S_IFDIR) return (2);
   return(1);
}

unsigned long sys_file_mod_time(char *file)
{
   struct stat buf;

   if (stat(file, &buf) < 0) return(0);
   return((unsigned long) buf.st_mtime);
}

