#include <termios.h>
#include <sys/time.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
#include <limits.h>

static char *last_line = NULL;

int lnblnk_(char *c, int c_len);
char *readline(char *prompt);
void add_history(char *line_read);
/*
int usleep(time_t delay);
*/

void sys_rd_line_(char *cmd, int *retlen, char *prompt, int clen, int plen)
{
  char *line_read, p[64];
  
  assert(plen < sizeof(p));
  strncpy(p,prompt,plen); p[plen] = '\0';
  if (last_line == NULL) { last_line =malloc(1); last_line[0] = '\0';};

  line_read = readline(p);

  if (line_read)
  {  
    if (*line_read && strcmp(last_line, line_read)!=0)
      add_history (line_read);
    free (last_line);
    strncpy(cmd, line_read, clen);
    *retlen=strlen(line_read);
    last_line = line_read;
    if (*retlen>clen) *retlen=clen;
  } else {
    *retlen=-1;
  }
}


void intcatch(int sig)
{ printf("\nuse quit (normally ctrl-\\) to interrupt\n");
}

int called=0; /* env is valid only if called==1 */
jmp_buf env;

void (*inthdl)(int sig);
void (*errhdl)(void);

void sighdl(int sig)
{ if (called) longjmp(env,sig);
}

void sys_err_hdl_(void errhdl0())
{ errhdl=errhdl0; }

void sys_int_hdl_(void inthdl0(int sig))
{ inthdl=inthdl0; }

void sys_try_(void proc())
{ int sig, status;
  void (*sgh[32]) (int);

  assert(!called); /* nested calls not allowed */
  called=1;
  sgh[SIGFPE] =signal(SIGFPE, sighdl);
  sgh[SIGINT] =signal(SIGINT, *inthdl);
  status=setjmp(env);
  if (status==0) /* first return of setjmp */
  { proc(); }
  else
  { (*errhdl)(); };
  signal(SIGFPE, sgh[SIGFPE]);
  signal(SIGINT, intcatch);
  called=0;
}

void sys_abort_()
{ if (called) longjmp(env,-2);
}


void sys_exit_hdl_(void hdl())
{ int res;
  res=atexit(hdl);
}

struct termios atts;

void sys_get_raw_key_(char *key, int *tmo, int k_len)
{
  struct termios attr;
  int ires, ntmo, chr;

  ires=tcgetattr(STDIN_FILENO,&attr);
  atts=attr; /* save term. attr. */
  if (ires!=0) {perror("***\n");}
  attr.c_lflag &= ~(ICANON) & ~(ECHO); /* canonical mode off, echo off */
  attr.c_cc[VMIN]=0;
  ires= tcsetattr(STDIN_FILENO,TCSANOW,&attr);
  if (ires!=0) {perror("***\n");}

/*
  ires=fflush(stdin);
  ires=fflush(stderr);
*/

  ntmo=*tmo*100;
  chr=fgetc(stdin);
  if (chr==EOF) {
    while ((chr==EOF) & (ntmo>0)) {
      usleep(10000); /* wait 10 ms */
      chr=fgetc(stdin);
      ntmo--;
    }
  }
  if (chr==EOF) chr=0;

  *key=chr;

  ires=tcsetattr(STDIN_FILENO,TCSANOW,&atts); /* restore term. attributes */
  if (ires!=0) {perror("***\n");};
}


void usleep_(int *usec) { usleep(*usec); }
int getppid_(void) { return getppid(); }

int sys_setenv_(ename,evalue,ilen1,ilen2)
char *ename, *evalue;
int  ilen1, ilen2;
{
  int setenv(char *p1, char *p2, int ow), i1, i2, ow, rc;
  char *p1, *p2;

  i1 = lnblnk_(ename,ilen1);
  i2 = lnblnk_(evalue,ilen2);

  p1 = malloc((unsigned) i1+1); if( p1 == NULL ) return (-1);
  p2 = malloc((unsigned) i2+1); if( p2 == NULL ) { free(p1); return (-1); }

  strncpy(p1,ename,i1); p1[i1] = '\0';
  strncpy(p2,evalue,i2); p2[i2] = '\0';

  ow = 1;

  rc = setenv(p1, p2, ow);
  free(p1); free(p2);
  return(rc);
}


void sys_rd_tmo_(char *prompt, char *result, int *reslen, int p_len, int r_len)
{
  struct termios atts;
  struct termios attr;
  int ires, i, ntmo, chr;

  ires=tcgetattr(STDIN_FILENO,&attr);
  atts=attr; /* save term. attr. */
  if (ires!=0) {perror("***\n");}
  attr.c_lflag &= ~(ICANON) & ~(ECHO); /* canonical mode off, echo off */
  attr.c_cc[VMIN]=0;
  ires= tcsetattr(STDIN_FILENO,TCSANOW,&attr);
  if (ires!=0) {perror("***\n");}

  do { chr=fgetc(stdin); } while (chr!=EOF);

  for (i=0; i<p_len; i++)
  { fputc(prompt[i], stderr);
  };

  ires=fflush(stdin);
  ires=fflush(stderr);

  *reslen=0;
  if (prompt[0]=='\0') { ntmo=10; }
  else { ntmo=200; }; /* wait 2 sec. for the first char */
  while (*reslen<r_len)
  { chr=fgetc(stdin);
    if (chr==EOF)
    { while ((chr==EOF) & (ntmo>0))
      { usleep(10000); /* wait 10 ms */
        chr=fgetc(stdin);
        ntmo--;
      };
      if (chr==EOF) break;
      if (chr==10) {ntmo=10;} else {ntmo=100;}; /* wait 0.1 sec after LF, 1 sec else */
    };
    result[(*reslen)++]=(char)chr;
    if (chr==24) {(*reslen)=0;}; /* ctrl-X purges buffer */
  };
  if (result[(*reslen)-1]=10) {(*reslen)--;}; /* strip trailing LF */

  ires=tcsetattr(STDIN_FILENO,TCSANOW,&atts); /* restore term. attributes */
  if (ires!=0) {perror("***\n");};
}
