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

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dir.h>
#include <errno.h>
#include <pc.h>

#include "config.h"
#include "sysdep.h"

#define BIOSKEY i386_bioskey
int Abort_Char = 7;

#include <dos.h>

unsigned int i386_bioskey(int f)
{
   union REGS in, out;
   in.h.ah = f;
   int86(0x16, &in, &out);
   return(out.x.ax);
}

int PC_Alt_Char = 27;

/* Here I also map keys to edt keys */
unsigned char sys_getkey()
{
#if 0
   char *keypad_scan = 
      "\x4e\x53\x52\x4f\x50\x51\x4b\x4c\x4d\x47\x48\x49\x37\x4a";
   char *edt_chars = "lnpqrstuvwxyRS";
   int bra = 'O', P = 'P', keypad, weird;
   char *p;
#endif
   char *normal = "!@#$%^&*()-=\t*\0177QWERTYUIOP[]\r*ASDFGHJKL;'`*\\ZXCVBNM<>/";
   int wit = 300;
   unsigned int c, c1, shft;
   unsigned int i;

   if (!kbhit()) while (!input_pending(&wit))
     {
	if (Display_Time)
	  {
	     Window->trashed = 1;
	     update((Line *) NULL, 0, 1);
	  }
     }
   
   i = (unsigned int) BIOSKEY(0);
   c = i & 0xFF;
   c1 = i >> 8;
   shft = BIOSKEY(2) & 0xF;
#if 0   
   keypad = (c1 > 0x36) && NumLock_Is_Gold;
   /* allow Shift-keypad to give non edt keypad chars */
   keypad = keypad && !(*shift & 0x3);
   
   /* This is for the damned enter and slash keys */
   weird = keypad && ((c1 == 0xE0) && ((c == 0xD) || (c == 0x2F)));
   
   keypad = keypad && (c != 0xE0);  /* excludes small keypad */
#endif
   if ((c == 8) && ((i >> 8) == 0x0e)) c =127;
   else if ((c == 32) && (shft & 0x04))    /* ^ space = ^@ */
     {
	c = 3;
	ungetkey((int *) &c);
	c = 0;
     }
#if 0
   else if (weird || (keypad && (NULL != (p = strchr(keypad_scan, c1)))))
     {
	if (c1 == 0xE0) if (c == 0x2f) c = 'Q'; else c = 'M';
	else c = (unsigned int) edt_chars[(int) (p - keypad_scan)];
	ungetkey((int *) &c);
	ungetkey((int *) &bra);
	c = 27;
     }
#endif
   else if ((!c) || ((c == 0xe0) && (c1 > 0x43)))
     {
#if 0
	if (NumLock_Is_Gold && (c1 == 0x3B))
	  {
	     ungetkey(&P); ungetkey(&bra); c = 27;
	  } 
	/* if key is 5 of keypad, get another char */
	else if ((c1 == 'L') && bios_key_f && (c == 0)) return(sys_getkey());
	else 
	  {
#endif
	     if ((shft == 0x8) && (c == 0) && PC_Alt_Char)
	       {
		  if ((c1 >= 14) && (c1 <= 53))
		    {
		       c1 = (unsigned int) normal[c1];
		       c = PC_Alt_Char;
		    }
		  else if ((c1 >= 120) && (c1 <= 131))
		    {
		       c1 = (unsigned int) normal[c1 - 120];
		       c = PC_Alt_Char;
		    }
		  else if (c1 == 165) /* tab */
		    {
		       c1 = (unsigned int) normal[c1 - 165 + 12];
		       c = PC_Alt_Char;
		    }
	       }
	     else c = 0;
	     ungetkey((int *) &c1);
#if 0
	  }
#endif
     }
   return(c);
}





int input_pending(int *tsecs)
{
   int count = *tsecs * 5;
   if (Batch || INPUT_BUFFER_LEN) return(INPUT_BUFFER_LEN);
   
   if (count)
     {
	while(count > 0)
	  {
	     delay(20);		       /* 20 ms or 1/50 sec */
	     if (kbhit()) break;
	     count--;
	  }
	return(count);
     }
   else if (INPUT_BUFFER_LEN) return(1); 
   return kbhit();
}

/*  This is to get the size of the terminal  */
void get_term_dimensions(int *cols, int *rows)
{
   *rows = ScreenRows();
   *cols = ScreenCols();
}

/* 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));
}



/* 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 *dum1, short *dum2)
{
   struct stat buf;
   int m;
   *dum1 = *dum2 = 0;
   if (what)
     {
	chmod(file, *mode);
	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;

/* 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);
}

static int cbreak;

void reset_tty()
{   
   setcbrk(cbreak);
}
void init_tty()
{   
   cbreak = getcbrk();
   setcbrk(0);
}

char *expand_filename(char *file)
{
   register char *p;
   char *p1, *mark, *last, slash = '\\', ch = 0;
   static char work[256];

   *work = 0;
#if 0   
   p = file + strlen(file);
   while (p > file)
     {
	if (*p-- == ':') 
	  {
	     drive = *p;
	     break;
	  }
     }
#endif
   /* 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++ != 0) if (*p == '/') *p = slash;
   p--;
   mark = p;			       /* mark points at null char */
   last = &ch;

   while(p != file) if (*p-- == ':')
     {
	/* if (p < file) break; */
	p1 = file;
	if (*(p + 2) != slash)  /* change c:file.dat to c:\file.dat */
	  {
	     while(*mark != ':')
	       {
		  *(mark + 1) = *mark;       /* copies null char as well */
		  mark--;
	       }
	     *(mark + 1) = slash;
	  }
	/* p points at drive, p1 = file so shift left */
	if (p != p1)
	  {
	     while(*p != 0) *p1++ = *p++;
	     *p1 = 0;
	  }
	/* start file past the drive spec */
	while (*file) if (*file++ == slash) 
	 {
	    file--;
	    /* last = file - 1; */
	    break;
	 }
	break;
     }

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

   p = p1 = file;

   while(*p != 0)
     {
	if ((*p == '.') && (*(p + 1) == '.'))
	  {
	     p += 2;
	     if ((*p == slash) && (*last == slash))
	       {
		  while ((--last >= file) && (*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;
   p = work + strlen(work);
   while ((p >= work) && (*p != slash))
     {
	*p = (*p <= 'Z') && (*p >= 'A') ? *p + 32 : *p;
	p--;
     } 
   while (p >= work)
      {
	 *p = (*p >= 'a') && (*p <= 'z') ? *p - 32 : *p;
	p--;
      }
   return(work);
}

void i386_getcwd(char *buf, int n)
{
   union REGS in, out;
   register char ch;
   
   in.h.ah = 0x19;
   int86(0x21, &in, &out);
   ch = 'A' + out.h.al;
   *buf++ = ch;
   *buf++ = ':';
   getcwd(buf, n - 2);
   while ((ch = *buf) != 0) 
     {
	if (ch == '/') *buf = '\\';
	buf++;
     }
}


static struct ffblk Dos_DTA;
static char Dos_Dir[256];

#define lcase(x) if (((x) >= 'A') && ((x) <= 'Z')) (x) |= 0x20

void dta_fixup_name(char *file)
{
   int dir;
   char *p, name[13];

   strcpy(file, Dos_Dir);
   strcpy(name, Dos_DTA.ff_name);
   dir = (Dos_DTA.ff_attrib & FA_DIREC);
   p = name;
   while (*p)
     {
	lcase(*p);
	p++;
     }
   strcat(file, name);
   if (dir) strcat(file, "\\");
}



int sys_findfirst(char *thefile)
{
   char *f, the_path[256], *file, *f1;

   file = expand_filename(thefile);
   f1 = f = extract_file(file);
   strcpy (Dos_Dir, file);
   
   
   Dos_Dir[(int) (f - file)] = 0;

   strcpy(the_path, file);
   
   while (*f1 && (*f1 != '*')) f1++;
   if (! *f1)
     {
	while (*f && (*f != '.')) f++;
	if (*f) strcat(the_path, "*"); else strcat(the_path, "*.*");
     }
   
   if (findfirst(the_path, &Dos_DTA, FA_RDONLY | FA_DIREC)) return (0);
   
   dta_fixup_name(file);
   strcpy(thefile, file);
   return(1);
}

int sys_findnext(char *file)
{
   if (findnext(&Dos_DTA)) return(0);
   dta_fixup_name(file);
   return(1);
}

void sys_suspend()
{
   system(getenv("COMSPEC"));
}

/* This routine is called from S-Lang inner interpreter.  It serves
   as a poor mans version of an interrupt 9 handler */

void i386_check_kbd()
{
   int ch;
   while (kbhit()) 
     {
	if ((ch = sys_getkey()) == Abort_Char) SLang_Error = USER_BREAK;
	ungetkey(&ch);
     }
}

