The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * term.c - deal with termcap, and unix terminal mode settings
 *
 * Pace Willisson, 1983
 *
 * Copyright 1987, 1988, 1989, 1992, 1993, Geoff Kuenning, Granada Hills, CA
 * All rights reserved.
 *
 * see COPYRIGHT file for more information.
 */



/* new */
#ifndef TCSETAW
#define TCSETAW               0x5407
#endif

/* new */
#ifndef TCGETA
#define TCGETA                0x5405
#endif

#include <string.h>
#include <stdlib.h>
#include <unistd.h>


#include "jsconfig.h"
#include "jspell.h"
#include "proto.h"
#include "msgs.h"
#include "myterm.h"

#ifndef NOCURSES
#include <termios.h>
#include <curses.h>
#include <sys/ioctl.h>
#endif

#include <signal.h>


static int          putch(int c);
SIGNAL_TYPE        done(int signo);
#ifdef SIGTSTP
static SIGNAL_TYPE onstop(int signo);
#endif /* SIGTSTP */
int shellescape(char * buf);


/*---------------------------------------------------------------------------*/

void inverse(void)
{
#ifndef NOCURSES
    tputs(so, 10, putch);
#endif
}

/*---------------------------------------------------------------------------*/

void normal(void)
{
#ifndef NOCURSES
    tputs(se, 10, putch);
#endif
}

/*---------------------------------------------------------------------------*/

void erase_EOL(void)
{
#ifndef NOCURSES
    tputs(ce, 10, putch);
#endif
}

/*---------------------------------------------------------------------------*/

void curs_left(void)
{
#ifndef NOCURSES
    tputs(le, 10, putch);
#endif
}

/*---------------------------------------------------------------------------*/

void curs_right(void)
{
#ifndef NOCURSES
    tputs(nd, 10, putch);
#endif
}

/*---------------------------------------------------------------------------*/

void backup(void)
{
#ifndef NOCURSES
    if (BC)
	tputs(BC, 1, putch);
    else
	putchar('\b');
#endif
}

/*---------------------------------------------------------------------------*/

static int putch(int c)
{
#ifndef NOCURSES
   return putchar(c);
#endif
}

/*---------------------------------------------------------------------------*/

#ifndef NOCURSES
static struct termios      sbuf;
static struct termios      osbuf;
#endif

static int                termchanged = 0;
static SIGNAL_TYPE        (*oldint) ();
static SIGNAL_TYPE        (*oldterm) ();
#ifdef SIGTSTP
static SIGNAL_TYPE        (*oldttin) ();
static SIGNAL_TYPE        (*oldttou) ();
static SIGNAL_TYPE        (*oldtstp) ();
#endif

/*---------------------------------------------------------------------------*/

void terminit(void)
{
#ifndef NOCURSES
#ifdef TIOCPGRP
    int tpgrp;
#else
#ifdef TIOCGPGRP
    int tpgrp;
#endif
#endif
#ifdef TIOCGWINSZ
    struct winsize      wsize;
#endif /* TIOCGWINSZ */

    tgetent(termcap, getenv("TERM"));
    termptr = termstr;
    BC = tgetstr("bc", &termptr);
    cd = tgetstr("cd", &termptr);        /* clear to end of screen */
    ce = tgetstr("ce", &termptr);        /* clear to end of line */
    cl = tgetstr("cl", &termptr);        /* clear screen and cursor home */
    cm = tgetstr("cm", &termptr);        /* cursor move */
    ho = tgetstr("ho", &termptr);        /* cursor home */
    le = tgetstr("nd", &termptr);        /* cursor left one character */
    nd = tgetstr("nd", &termptr);        /* cursor right one character */
    so = tgetstr("so", &termptr);        /* inverse video on */
    se = tgetstr("se", &termptr);        /* inverse video off */

    kl = tgetstr("kl", &termptr);        /* cursor left key */
    kr = tgetstr("kr", &termptr);        /* cursor right key */
    ku = tgetstr("ku", &termptr);        /* cursor up key */
    kd = tgetstr("kd", &termptr);        /* cursor down key */
    kP = tgetstr("kP", &termptr);        /* cursor pgup key */
    kN = tgetstr("kN", &termptr);        /* cursor pgdown key */
    kh = tgetstr("kh", &termptr);        /* cursor home key */
    kH = tgetstr("kH", &termptr);        /* cursor end key */
    kI = tgetstr("kI", &termptr);        /* insert key */  /* NOT STANDARD */
    kD = tgetstr("kD", &termptr);        /* delete key */

    if ((sg = tgetnum("sg")) < 0)        /* space taken by so/se */
       sg = 0;
    ti = tgetstr("ti", &termptr);        /* terminal initialization */
    te = tgetstr("te", &termptr);        /* terminal termination */
    co = tgetnum("co");                  /* Number of columns */
    li = tgetnum("li");                  /* Number of lines */
#ifdef TIOCGWINSZ
    if (ioctl(0, TIOCGWINSZ, (char *) &wsize) >= 0) {
       if (wsize.ws_col != 0)
          co = wsize.ws_col;
       if (wsize.ws_row != 0)
          li = wsize.ws_row;
    }
#endif /* TIOCGWINSZ */
    /*
     * Let the variables "LINES" and "COLUMNS" override the termcap
     * entry.  Technically, this is a terminfo-ism, but I think the
     * vast majority of users will find it pretty handy.
     */
    if (getenv("COLUMNS") != NULL)
       co = atoi (getenv ("COLUMNS"));
    if (getenv("LINES") != NULL)
       li = atoi(getenv("LINES"));
#if MAX_SCREEN_SIZE > 0
    if (li > MAX_SCREEN_SIZE)
       li = MAX_SCREEN_SIZE;
#endif /* MAX_SCREEN_SIZE > 0 */
#if MAXCONTEXT == MINCONTEXT
    contextsize = MINCONTEXT;
#else /* MAXCONTEXT == MINCONTEXT */
    if (contextsize == 0)
#ifdef CONTEXTROUNDUP
       contextsize = (li * CONTEXTPCT + 99) / 100;
#else /* CONTEXTROUNDUP */
       contextsize = (li * CONTEXTPCT) / 100;
#endif /* CONTEXTROUNDUP */
    if (contextsize > MAXCONTEXT)
       contextsize = MAXCONTEXT;
    else if (contextsize < MINCONTEXT)
        contextsize = MINCONTEXT;
#endif /* MAX_CONTEXT == MIN_CONTEXT */
    /*
     * Insist on 2 lines for the screen header, 2 for blank lines
     * separating areas of the screen, 2 for word choices, and 2 for
     * the minimenu, plus however many are needed for context.  If
     * possible, make the context smaller to fit on the screen.
     */
    if (li < contextsize + 8  &&  contextsize > MINCONTEXT) {
        contextsize = li - 8;
        if (contextsize < MINCONTEXT)
            contextsize = MINCONTEXT;
    }
    if (li < MINCONTEXT + 8)
       fprintf(stderr, TERM_C_SMALL_SCREEN, MINCONTEXT + 8);

#ifdef SIGTSTP
#ifdef TIOCPGRP
retry:
#endif /* SIGTSTP */
#endif /* TIOCPGRP */

    if (!isatty(0)) {
       fprintf(stderr, TERM_C_NO_BATCH);
       exit(1);
    }
    ioctl(0, TCGETA, (char *) &osbuf);
    termchanged = 1;

    sbuf = osbuf;
    sbuf.c_lflag &= ~(ECHO | ECHOK | ECHONL | ICANON);
    sbuf.c_oflag &= ~(OPOST);
    sbuf.c_iflag &= ~(INLCR | IGNCR | ICRNL);
    sbuf.c_cc[VMIN] = 1;
    sbuf.c_cc[VTIME] = 1;
    ioctl(0, TCSETAW, (char *) &sbuf);

    jerasechar = osbuf.c_cc[VERASE];
/*
    killchar = osbuf.c_cc[VKILL];
*/


#ifdef SIGTSTP
    sigsetmask (1<<(SIGTSTP-1) | 1<<(SIGTTIN-1) | 1<<(SIGTTOU-1));
#endif

#ifdef TIOCGPGRP
    if (ioctl(0, TIOCGPGRP, (char *) &tpgrp) != 0) {
       fprintf(stderr, TERM_C_NO_BATCH);
       exit(1);
    }
#endif
#ifdef SIGTSTP
#ifdef TIOCPGRP
    if (tpgrp != getpgrp(0)) {  /* not in foreground */
        signal(SIGTTOU, SIG_DFL);
        kill(0, SIGTTOU);
        /* job stops here waiting for SIGCONT */
        goto retry;
    }
#endif
#endif

    if ((oldint = signal (SIGINT, SIG_IGN)) != SIG_IGN)
        signal (SIGINT, done);
    if ((oldterm = signal (SIGTERM, SIG_IGN)) != SIG_IGN)
        signal (SIGTERM, done);

#ifdef SIGTSTP
    if ((oldttin = signal(SIGTTIN, SIG_IGN)) != SIG_IGN)
        signal(SIGTTIN, onstop);
    if ((oldttou = signal(SIGTTOU, SIG_IGN)) != SIG_IGN)
        signal(SIGTTOU, onstop);
    if ((oldtstp = signal(SIGTSTP, SIG_IGN)) != SIG_IGN)
        signal(SIGTSTP, onstop);
#endif
    if (ti)
        tputs(ti, 1, putch);
    init_filt();
#endif
}

/*---------------------------------------------------------------------------*/

/* ARGSUSED */
SIGNAL_TYPE done(int signo)
{
#ifndef NOCURSES
   if (tempfile[0] != '\0')
      unlink(tempfile);
   if (termchanged) {
      if (te)
         tputs(te, 1, putch);
      ioctl(0, TCSETAW, (char *) &osbuf);
    }

   /* WAS: ((nothing)) */
   endwin();
#endif
   exit(0);

}

/*---------------------------------------------------------------------------*/

#ifdef SIGTSTP
static SIGNAL_TYPE onstop(int signo)
{
#ifndef NOCURSES
    ioctl(0, TCSETAW, (char *) &osbuf);
    signal(signo, SIG_DFL);
    kill(0, signo);
    /* stop here until continued */
    signal(signo, onstop);
    ioctl(0, TCSETAW, (char *) &sbuf);
#endif
}
#endif

/*---------------------------------------------------------------------------*/

void stop(void)
{
#ifndef NOCURSES
#ifdef SIGTSTP
    onstop(SIGTSTP);
#else
    /* for System V */
    move(li - 1, 0);
    fflush(stdout);
    if (getenv("SHELL"))
        shellescape(getenv("SHELL"));
    else
        shellescape("sh");
#endif
#endif
}

/*---------------------------------------------------------------------------*/
/* Fork and exec a process.  Returns NZ if command found, regardless of
** command's return status.  Returns zero if command was not found.
** Doesn't use a shell.
*/

#define NEED_SHELLESCAPE

#ifndef REGEX_LOOKUP
#define NEED_SHELLESCAPE
#endif /* REGEX_LOOKUP */
#ifdef NEED_SHELLESCAPE
int shellescape(char *buf)
{
#ifndef NOCURSES
    char *argv[100];
    char *cp = buf;
    int i = 0;
    int termstat;

    /* parse buf to args (destroying it in the process) */
    while (*cp != '\0') {
        while (*cp == ' '  ||  *cp == '\t')
            ++cp;
        if (*cp == '\0')
            break;
        argv[i++] = cp;
        while (*cp != ' '  &&  *cp != '\t'  &&  *cp != '\0')
            ++cp;
        if (*cp != '\0')
            *cp++ = '\0';
    }
    argv[i] = NULL;

    ioctl(0, TCSETAW, (char *) &osbuf);
    signal(SIGINT, oldint);
    signal(SIGTERM, oldterm);
#ifdef SIGTSTP
    signal(SIGTTIN, oldttin);
    signal(SIGTTOU, oldttou);
    signal(SIGTSTP, oldtstp);
#endif
    if ((i = fork()) == 0) {
        execvp(argv[0], (char **) argv);
        _exit(123);                /* Command not found */
    }
    else if (i > 0) {
        while (wait (&termstat) != i)
            ;
        termstat = (termstat == (123 << 8)) ? 0 : -1;
        }
    else {
       printf(TERM_C_CANT_FORK);
       termstat = -1;                /* Couldn't fork */
    }

    if (oldint != SIG_IGN)
       signal(SIGINT, done);
    if (oldterm != SIG_IGN)
       signal(SIGTERM, done);

#ifdef SIGTSTP
    if (oldttin != SIG_IGN)
       signal(SIGTTIN, onstop);
    if (oldttou != SIG_IGN)
       signal(SIGTTOU, onstop);
    if (oldtstp != SIG_IGN)
       signal(SIGTSTP, onstop);
#endif

    ioctl(0, TCSETAW, (char *) &sbuf);
    if (termstat) {
       printf(TERM_C_TYPE_SPACE);
       fflush(stdout);
#ifdef COMMANDFORSPACE
       i = GETKEYSTROKE();
       if (i != ' ' && i != '\n' && i != '\r')
          ungetc(i, stdin);
#else
       while (GETKEYSTROKE() != ' ')
          ;
#endif
    }
    return termstat;
#endif
}
#endif /* NEED_SHELLESCAPE */

/*---------------------------------------------------------------------------*/

#define NFILTS 12

struct sfilter {
   char st[20];
   int cont;
   int key;
} filt[NFILTS];


void put_key(int pos, char *st, int key)
{
   if (st)
      strcpy(filt[pos].st, st);
   else
      strcpy(filt[pos].st, "////");  /* dummy value */

   filt[pos].key = key;


}

void init_filt(void)
{
#ifndef NOCURSES
   put_key(0, kl, CLEFT);
   put_key(1, kr, CRIGHT);
   put_key(2, kh, HOME);
   put_key(3, kH, END_);
   put_key(4, kI, INS);
   put_key(5, kD, DEL);
   /* put also ANSI definitions */
   strcpy(filt[6].st, "\E[D");  filt[6].key = CLEFT;
   strcpy(filt[7].st, "\E[C");  filt[7].key = CRIGHT;
   strcpy(filt[8].st, "\E[1~"); filt[8].key = HOME;
   strcpy(filt[9].st, "\E[4~"); filt[9].key = END_;
   strcpy(filt[10].st, "\E[2~"); filt[10].key = INS;
   strcpy(filt[11].st, "\E[B");  filt[11].key = DEL;
#endif
}

int verify_cond(char ch, int i)
{
   int j, suc;

   j = 0;
   suc = 0;
   while (j < NFILTS && !suc)
      if (filt[j].st[i+1] != '\0' && ch == filt[j].st[i] && filt[j].cont == i)
         suc = 1;
       else
          j++;
   return suc;
}

int jfilter(int ch)
{
#ifndef NOCURSES
   int i, j, suc;
   char old_ch;

   if (ch == jerasechar) return BACKSPACE;
   if (ch == 8) return BACKSPACE;          /* CTRL-H used in ANSI and XTERM */
   i = 0;

   for (j = 0; j < NFILTS; j++)
      filt[j].cont = 0;
/*   printf("ch=%c, kh[0]=%c ", ch, kh[0]); fflush(stdout); */
   while (verify_cond(ch, i)) {

      /* increment counter */
      for (j = 0; j < NFILTS; j++)
         if (ch == filt[j].st[i]) filt[j].cont++;

      old_ch = ch;
      ch = (GETKEYSTROKE() & NOPARITY);
      i++;
/*      printf("ch=%c,kh[%d]=%c; ", ch, i, kh[i]); fflush(stdout); */
   }
   j = 0;
   suc = 0;
   while (j < NFILTS && !suc) {
      if (filt[j].st[i+1] == '\0' && ch == filt[j].st[i] && filt[j].cont == i) /* conseguiu ler toda a string kl */
         suc = 1;
      else
         j++;
   }
   if (suc)
      return filt[j].key;
   else
      return ch;
#endif
}