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

/*  This file is the interface to system specific files */

#ifdef AIX
#define _ALL_SOURCE
#define POSIX
#endif

#ifdef POSIX
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#endif

#include <stdio.h>
#include <string.h>
#include "config.h"
#include "buffer.h"
#include "sysdep.h"
#include "display.h"
#include "file.h"
#include "screen.h"
#include "misc.h"
#include "mem.h"
#include "slang.h"
#include "hooks.h"

int Screen_Height = 24;
int Screen_Width = 80;

/* These are hooks for porting to other systems */

int (*X_Read_Hook) (void);
int (*X_Input_Pending_Hook) (void);
void (*X_Get_Term_Size_Hook)(int *, int *);
int (*X_Init_Term_Hook) (void);
void (*X_Reset_Term_Hook) (void);

volatile int Input_Buffer_Active;      /* routines that touch input buffer
					* should NOT do anything while this is
					* true.  Then, they should set it. 
					*/
extern char *get_cwd(void);

#ifdef msdos
int Abort_Char = 34;		       /* scan code for G (control) */
#include <dir.h>
#include "ibmpc.c"
#endif

int Ignore_User_Abort = 1;	       /* Abort char triggers S-Lang error */

unsigned char KeyBoard_Xlate[256] = 
{
   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 
 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 
 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 
 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 
 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 
 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 
 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 
 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 
 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
 248, 249, 250, 251, 252, 253, 254, 255
};




#ifdef __os2__
   int Abort_Char = 7;		       /* scan code for G (control) */
#  if !defined (_MSC_VER) && !defined (__EMX__)
#    include <dir.h>
#  endif
#  include "os2.c"
#endif

#ifdef VMS
int Abort_Char = 7;
#include "vms.c"
#endif

#ifdef __GO32__
#include "i386.c"
#else
#ifdef unix
int Abort_Char = 7;
int Flow_Control = 1;		       /* controls XON/XOFF */
#include "unix.c"
#endif
#endif

int INPUT_BUFFER_LEN = 0;
unsigned char INPUT_BUFFER[MAX_INPUT_BUFFER_LEN];

void map_character(int *fromp, int *top)
{
   int from = *fromp, to = *top;
   if ((from > 255) || (to > 255) || (from < 0) || (to < 0)) return;
   KeyBoard_Xlate[from] = to;
}


/* if input char arrives with hi bit set, it is replaced by 2 characters:
 *   Meta_Char + char with hi bit off.  If Meta_Char is -1, then return 
 * full 8 bits which self inserts */
      
/* By default, 8 bit chars self insert. */	 
int Meta_Char = -1;
#if defined(unix) || defined(VMS)
int DEC_8Bit_Hack = 64;
#else
int DEC_8Bit_Hack = 0;
#endif

int my_getkey()
{
   char buf[10];
   int i, imax;
   unsigned char ch;
   
   if (Batch)
     {
	fgets(buf, 9 ,stdin);
	return (int) *buf;
     }
   
   if (!INPUT_BUFFER_LEN)
     {
	/* if (Batch) ch = (unsigned char) getc(stdin); else ch = sys_getkey(); */
	ch = (unsigned char) KeyBoard_Xlate[sys_getkey()];
	if (ch & 0x80)
	  {
	     i = (ch & 0x7F);
	     if ((i < ' ') && DEC_8Bit_Hack)
	       {
		  i += DEC_8Bit_Hack;
		  ungetkey((int *) &i);
		  ch = 27;
	       }
	     else if (Meta_Char != -1)
	       {
		  ungetkey((int *) &i);
		  ch = Meta_Char;		       /* escape char */
	       }
	  }
	return((int) ch);
     }
   
   ch = INPUT_BUFFER[0];
   if ((ch & 0x80) && ((Meta_Char != -1) || ((ch < 160) && DEC_8Bit_Hack)))
     {
	ch = (ch & 0x7F);
	if ((ch < ' ') && DEC_8Bit_Hack) 
	  {
	     ch += DEC_8Bit_Hack;
	     i = 27;
	  }
	else i = Meta_Char;

	INPUT_BUFFER[0] = ch;
	return ((int) (unsigned int) i);
     }
   while (Input_Buffer_Active);
   Input_Buffer_Active = 1;

   INPUT_BUFFER_LEN--;
   imax = INPUT_BUFFER_LEN;
   
   MEMCPY ((char *) INPUT_BUFFER, (char *) (INPUT_BUFFER + 1), imax);
   /* for (i = 0; i < imax; i++)
     {
	INPUT_BUFFER[i] = INPUT_BUFFER[i + 1];
     } */

   Input_Buffer_Active = 0;
   return((int) ch);
}


void ungetkey_string(char *s, int n)
{
   /* int i; */
   register unsigned char *bmax, *b, *b1;
   if (INPUT_BUFFER_LEN > MAX_INPUT_BUFFER_LEN - 3 - n) return;

   while (Input_Buffer_Active);
   Input_Buffer_Active = 1;
   b = INPUT_BUFFER;
   bmax = b + (INPUT_BUFFER_LEN - 1);
   b1 = bmax + n;
   while (bmax >= b) *b1-- = *bmax--;
   /* 
    for (i = INPUT_BUFFER_LEN - 1; i >= 0; i--)
      {
	 b[i + n] = b[i];
      } */
   bmax = b + n;
   while (b < bmax) *b++ = (unsigned char) *s++;
   INPUT_BUFFER_LEN += n;
   Input_Buffer_Active = 0;
}

void buffer_keystring(char *s, int n)
{
   while (Input_Buffer_Active);
   Input_Buffer_Active = 1;

   if (n + INPUT_BUFFER_LEN > MAX_INPUT_BUFFER_LEN - 3) return;
   
   MEMCPY ((char *) INPUT_BUFFER + INPUT_BUFFER_LEN, s, n);
   INPUT_BUFFER_LEN += n;
   Input_Buffer_Active = 0;
}

void ungetkey(int *ci)
{
   char ch;
   ch = (char) *ci;
   ungetkey_string(&ch, 1);
}

void flush_input()
{
   int quit = KeyBoard_Quit;
   INPUT_BUFFER_LEN = 0;
   KeyBoard_Quit = 0;
#ifdef msdos
   while (input_pending(&Number_Zero)) if (!my_getkey()) my_getkey();
#else
#ifdef __os2__
   sys_flush_input();
#endif
   while (input_pending(&Number_Zero)) my_getkey();
#endif
   KeyBoard_Quit = quit;
}

#include <time.h>

unsigned long sys_time(void)
{
   return((unsigned long) time((time_t *) 0));
}

char *get_time()
{
    time_t clock;
    char *the_time;

    clock = time((time_t *) 0);
    the_time = (char *) ctime(&clock);
    /* returns the form Sun Sep 16 01:03:52 1985\n\0 */
    the_time[24] = '\0';
    return(the_time);
}

#ifndef pc_system
#ifdef unix

/* use berkeley interface
#include <sys/dir.h>
*/

static char Unix_Dir[256];
static char Unix_File[256];
static int File_Len;

#ifdef sequent
# include <sys/dir.h>
# define NEED_D_NAMLEN
#else
# include <dirent.h>
#endif

static DIR *Dirp;

int sys_findnext(char *file)
{
#ifdef NEED_D_NAMLEN
#define dirent direct
#endif

   struct dirent *dp;

   while (1)
     {
	if (NULL == (dp = readdir(Dirp))) return(0);
#ifdef NEED_D_NAMLEN
	dp->d_name[dp->d_namlen] = 0;
#endif
	if (!strncmp(Unix_File, dp->d_name, File_Len)) break;
     }
   strcpy(file, Unix_Dir); strcat(file, dp->d_name);
   if (2 == file_status(file)) strcat(file, "/");
   return(1);
}

/* this routine returns a Static pointer which is considered volatile */
char *expand_filename(char *file)
{
   char *p, *p1, slash = '/', *home, ch = 0, *last;
   static char work[500];
   char workp[256];

    /* This converts ~/ to $HOME/ */
   int n;

   *work = 0;
   /* This bit removes any // combinations.  */
   p = file + strlen(file);
   while (p > file)
     {
	if ((*p == slash) && (*(p - 1) == slash))
	  {
	     file = p;
	     break;
	  }
	p--;
     }
   
   if (*file != slash) strcpy(work, get_cwd());  /* assume relative dir */
   strcat(work, file);
   file = work;

   p = file;
   while(*p) if (*p++ == '~') break;

   if (*p == '/')
     {
	/* at some point, I need to make this more generic for other users */
	if (NULL != (home = (char *) getenv("HOME")))
	  {
	     strcpy(workp, p);
	     strcpy(work, home);
	     n = strlen(home) - 1;
	     if (home[n] == '/') work[n] = 0;
	     strcat(work, workp);
	  }
     }

   /* remove ../ and ./ stuff */

   p = p1 = file;
   last = &ch;
   while(*p != 0)
     {
	if ((*p == '.') && (*(p + 1) == '.'))
	  {
	     p += 2;
	     if ((*p == slash) && (*last == slash))
	       {
		  while ((--last >= work) && (*last != slash));
		  p1 = last + 1;
	       }
	     else
	       {
		  *p1++ = '.'; *p1++ = '.'; *p1++ = *p;
	       }
	  }
	else if (*p == '.')
	  {
	     p++;
	     if ((*p == slash) && (*last == slash)); /* nil */
	     else
	       {
		  *p1++ = '.';  
		  *p1++ = *p;
	       }
	  }
	else if ((*p == slash) && (*(p + 1) == slash))
	  {
	     /* // is a signal that we go back to root */
	     p1 = file;
	     *p1++ = slash;
	     p++;
	  }
	else (*p1++ = *p);
	 
	last = p1 - 1;
	if (*p != 0) p++;
     }
   *p1 = 0;
   return(work);
}

int sys_findfirst(char *the_file)
{
   char *f, *file;

   file = expand_filename(the_file);
   f = extract_file(file);

   strcpy (Unix_Dir, file);
   strcpy (Unix_File, f);
   File_Len = strlen(f);

   Unix_Dir[(int) (f - file)] = 0;

   if (Dirp != NULL) closedir(Dirp);

   if (NULL == (Dirp = (DIR *) opendir(Unix_Dir))) return(0);
   strcpy(the_file, file);
   return sys_findnext(the_file);
}

#endif
#endif
/* msdos_and_i386 */

/* given a canonical filename, return pointer to its name */
char *extract_file(char *file)
{
   char *f;
#ifdef VMS
   char slash = ']';
#endif
#ifdef pc_system
   char slash = '\\';
#else
#ifdef unix
   char slash = '/';
#endif
#endif
   f = file + strlen(file);
   while (f-- > file)
     {
	if (
#ifdef __os2__
	    strchr("\\/:", *f)
#else
	    *f == slash
#endif
	    )
        return(++f);
     }
   
   return(file);
}


#ifdef sequent
char *my_strstr(char *a, char *b)
{
   register char *bb, *aa, *amax;
   
   if (*b == 0) return(a);
   
   bb = b; while (*bb) bb++;
   aa = a; while (*aa++);
   
   amax = aa - (bb - b);
   
   while (a < amax)
     {
	bb = b;
	while ((a < amax) && (*a != *bb)) a++;
	if (a == amax) return((char *) NULL);
	
	aa = a;
	while (*aa && (*aa == *bb)) aa++, bb++;
	if (! *bb) return(a);
   
	a++;
     }
   return((char *) NULL);
}

#endif

#ifndef VMS
/* Remove trailing slash if not required by mkdir, rmdir, chdir. */
char *deslash(char *dir)
{
   int n;
 
   if ((n = strlen(dir)) > 1) 
     {
	n--;
#if defined(pc_system) || defined(__os2__)
	if (((dir[n] == '/' || dir[n] == '\\')) && (dir[n - 1] != ':')) dir[n] = 0;
#else
	if (dir[n] == '/') dir[n] = 0;
#endif
     }
   return(dir);
}
#endif


int make_directory(char *path)
{
   char work[256];
   
   strcpy(work, path);
   
#ifndef VMS
   deslash(work);
#endif

#if defined (msdos) || (defined (__os2__) && !defined (__EMX__))
   return !mkdir(work);
#else
   return !mkdir(work, 0777);
#endif
}

int delete_directory(char *path)
{
   char work[256];
   
   strcpy(work, path);
#ifndef VMS
   deslash(work);
#endif   
   return !rmdir(work);
}

 /* ch_dir routine added during OS/2 port in order to
    simplify script writing. */
 
int ch_dir(char *path)
{
#if defined(pc_system) || defined(__os2__)
   char work[256];
  
   strcpy(work, path);
   return chdir(deslash(work));
#else   
   return chdir(path);
#endif
}

/* generate a random number */
int make_random_number(int *seed, int *max)
{
   if (*seed == -1)		       /* generate seed */
     {
#ifndef unix
	srand((unsigned int)(time(0) & getpid()));
#else
#ifdef _HPUX_SOURCE
        srand48((int)(time(0) & getpid()));
#else
        srandom((int)(time(0) & getpid()));
#endif   
#endif   
     }
   else if (*seed != 0)
     {
#ifndef unix
	srand(*seed);
#else
#ifdef _HPUX_SOURCE
        srand48(*seed);
#else
        srandom(*seed);
#endif   
#endif   
     }

#ifndef unix
   return (int) rand() % *max;
#else
#ifdef _HPUX_SOURCE
   return (int) lrand48() % *max;
#else
   return (int) random() % *max;
#endif
#endif   
}

#ifndef __GO32__
#ifdef unix
/* if non-zero, Flow control is enabled */
void enable_flow_control(int *mode)
{
   /* This kills X windows.  For the time being, work around it as follows */
   if (X_Init_Term_Hook != NULL) return;
   Flow_Control = *mode;
   reset_tty();
   init_tty();
}
#endif
#endif

#if defined(pc_system)

/* This routine converts  C:\  --> C:\ and C:\subdir\  -> C:\subdir */
char *msdos_pinhead_fix_dir(char *f)
{
   static char file[256];
   register char ch;
   int n;
   
   if (*f == 0) return f;
   strncpy (file, f, 255); file[255] = 0;
   f = file;
   /* skip past colon */
   while (((ch = *f) != 0) && (ch != ':')) f++;
   
   if (ch == 0)			       /* no colon */
     {
	n = (int) (f - file);
	f = file;
     }
   else
     {
	f++;
	n = strlen (f);
     }
   if (n == 0) 
     {
	*f++ = '\\'; *f = 0;
	return file;
     }
   if ((n == 1) && (*f == '\\')) return file;
   
   f += n - 1;
   if (*f == '\\') *f = 0;
   return file;
}
#endif
