/*
 *  Copyright (c) 1992, 1994 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_DESC 2
static int Read_FD = TTY_DESC;

#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", 0)
#  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", 0)
# else
#  define GET_TERMIOS(fd, x) tcgetattr(fd, x)
#  define SET_TERMIOS(fd, x) tcsetattr(fd, TCSAFLUSH, 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

   if (Batch) return;
   tty_inited = 1;
   if (X_Init_Term_Hook != NULL)
     {
	Read_FD = (*X_Init_Term_Hook) ();
	return;
     }
   
   enable_cursor_keys();
   GET_TERMIOS(Read_FD, &OLDTTY);
   GET_TERMIOS(Read_FD, &newtty);
#ifdef sequent
   newtty.s.sg_flags &= ~(ECHO);
   newtty.s.sg_flags &= ~(CRMOD);
/*   if (Flow_Control == 0) newtty.s.sg_flags &= ~IXON; */
   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_dsuspc = 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(Read_FD, &newtty);
}

void reset_tty()
{
   if (Batch) return;
   if (!tty_inited) return;
   if (X_Init_Term_Hook != NULL)
     {
	if (X_Reset_Term_Hook != NULL) (*X_Reset_Term_Hook) ();
	return;
     }
   
   SET_TERMIOS(Read_FD, &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;
   int count = 10;
   unsigned char c;
   if (KeyBoard_Quit) return((int) Abort_Char);
#ifndef XJED
   /* sleep for 45 second and try again */
   while (!KeyBoard_Quit && !input_pending(&n))
     {
	/* update status line incase user is displaying time */
	if (Display_Time)
	  {
	     JWindow->trashed = 1;
	     update((Line *) NULL, 0, 1);
	  }
     }
#endif
   if (KeyBoard_Quit) return(Abort_Char);

   if (X_Read_Hook != NULL) return (c = X_Read_Hook ());
   
   while (count-- && (read(Read_FD, &c, 1) < 0) && !KeyBoard_Quit) sleep(1);
   
   if (count <= 0)
     {
	exit_error ("getkey(): read failed", 0);
     }

   /* 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 int fd_set;
#endif

int input_pending(int *tsecs)
{
   struct timeval wait;
   long usecs, secs;
   int ret;
   fd_set read_fd;

   if (INPUT_BUFFER_LEN || Batch) return(INPUT_BUFFER_LEN);
   
   
   secs = *tsecs / 10;
   usecs = (*tsecs % 10) * 100000;
   wait.tv_sec = secs;
   wait.tv_usec = usecs;
   
   if (X_Input_Pending_Hook != NULL) 
     {
	if ((*X_Input_Pending_Hook) ()) return 1;
     }
   
   FD_SET(Read_FD, &read_fd);
   ret = select(Read_FD + 1, &read_fd, NULL, NULL, &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;

   if (X_Get_Term_Size_Hook == NULL)
     {
	ioctl(2,TIOCGWINSZ,&wind_struct);
	*cols = (int) wind_struct.ws_col;
	*rows = (int) wind_struct.ws_row;
     }
   else (*X_Get_Term_Size_Hook)(cols, rows);

   if (*rows <= 0) *rows = Screen_Height;
   if (*cols <= 0) *cols = Screen_Width;
#else
   if (X_Get_Term_Size_Hook == NULL)
     {
	*rows = Screen_Height;
	*cols = Screen_Width;
     }
   else (*X_Get_Term_Size_Hook)(cols, rows);
#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);
}

