/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by Mike Parker.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#if defined(sgi)
#include <termio.h>
#define NO_RESTART_READ
#endif

#if defined(linux)
#include <termios.h>
#define NO_RESTART_READ
#endif

#include <signal.h>
#include <sys/time.h>

#ifndef VXWORKS_CFG
#include <sysexits.h>
#ifndef sgi
#ifdef _AIX
#include <sys/tty.h>
#endif

#include <sgtty.h>
#endif
#include <sys/wait.h>
#include <sys/types.h>
#else
#include <vxWorks.h>
#include <stdioLib.h>
#include <ioLib.h>
#include <types.h>
#define SIG_DFL	0
#define getpid() taskIdSelf()
#endif


#include <ctype.h>
#include <varargs.h>
#include "charsdef.h"
#include "ctree.h"

#include "fsm.h"
#include "defs.h"

#ifdef vax
#define vsprintf tree_match_vsprintf
#endif

#if !defined(sgi)
#define HAS_VFORK
#endif

#ifndef VXWORKS_CFG
char *getenv();
#endif
char *index();

#ifdef VXWORKS_CFG
static int didinit = 0;
#endif
/*static*/ IND_CHAIN *tree_match__input = 0;
#define input tree_match__input
static char *whole_line = 0;
#ifndef VXWORKS_CFG
static char *shell;
#endif
static TREE *tree;
static char *prompt;
static char *buf;
static int len;
#ifndef VXWORKS_CFG
#if defined(sgi) || defined(linux)
static struct termio old_state;
static struct termio new_state;
static int ungotten;
#else
#ifdef _AIX
char old_ldisp[TTNAMEMAX+1];
#endif
static struct sgttyb old_state;
static struct sgttyb new_state;
static char ungotten;
#endif
#else
static int old_state;
static int new_state;
static char ungotten;
#endif
static I_F num;
static I_F min;
static I_F max;
static double factor;
static int sign;
static int exp_sign;
static int exponent;
static int do_return;
static int in_dquote;
static int dquoteplace;
static int printcol;
static struct sigvec oldsigs[32];
static char sigscaught[32];
static unsigned int catchsigs = ~0;
static int (*get_char_fxn)() = 0;
static int (*put_char_fxn)() = 0;
static int flags = 0;
#define FLAG(name) 0
static int is_setup = 0;
static int want_input_reset = 1;
static int nb_inline = 0;
static int is_nb = 0;
static int debugging = 0;
static int caughtasignal;

extern FILE *C_match_fp;
FILE *C_match_fp;

static int fixup_input();
static int uncatch_signals();
static SIG_RETURN catch_signals();
static int init_input();
static int put_char();
static int do_redraw();
static int print_keys();
static int print();
static char *unctrl();
static get_min_max();
static int printlen();
static char *unctrl();
static get_min_max();
static int printlen();
static print_tree();
static int print_key();
static done();
static push_back();
static char get_char();
static int get_char_nb();
static do_delete();
static do_control_r();
static do_control_u();
static do_control_g();
static int is_int();
static int is_hex();
static int is_oct();
static int is_real();
static TREE *traverse();
static int common_part();
static do_comment_char();
static do_dquote_char();
static do_normal_char();
static do_command_char();
static do_indir_char();
static traverse_load();
static do_begin();
static do_end();

int *tree_match_flags()
{
 return(&flags);
}

tree_match_catch_sig(do_catch,dont_catch)
int do_catch;
int dont_catch;
{
 catchsigs = (catchsigs & ~ dont_catch) | do_catch;
}

static char *unctrl(c)
char c;
{
 static char rvb[8];
 char *cp;

 cp = &rvb[0];
 if (c & 0x80)
  { *cp++ = '|';
    c &= ~0x80;
  }
 if (c == '\177')
  { *cp++ = '^';
    *cp++ = '?';
  }
 else if (c < ' ')
  { *cp++ = '^';
    *cp++ = c ^ 64;
  }
 else
  { *cp++ = c;
  }
 *cp = '\0';
 return(rvb);
}
#ifdef LINUX_CFG
static SIG_RETURN signal_catch(sig)
int sig;
#else
static SIG_RETURN signal_catch(sig,code,scp)
int sig;
int code;
struct sigcontext *scp;
#endif
{
 int mask;
 int redraw;

 redraw = 0;
 caughtasignal ++;
	if (debugging)
	 { fprintf(stderr,"[signal_catch %d, old %x]\n",sig,(int)oldsigs[sig-1].sv_handler);
	 }
#ifndef VXWORKS_CFG
 if (oldsigs[sig-1].sv_handler == SIG_IGN)
  { return;
  }
#endif
#ifdef NO_RESTART_READ
 if (catchsigs & sigmask(sig))
  { mask = sigblock(~0);
    fixup_input();
    uncatch_signals();
    sigsetmask(mask);
    if (oldsigs[sig-1].sv_handler == SIG_DFL)
     { mask = sigblock(0);
       kill(getpid(),sig);
       sigsetmask(mask&~sigmask(sig));
       sigsetmask(mask);
       if (sig == SIGTSTP)
	{ redraw = 1;
	}
     }
    else
     { 
#ifdef LINUX_CFG
       (*oldsigs[sig-1].sv_handler)(sig);
#else
       (*oldsigs[sig-1].sv_handler)(sig,code,scp);
#endif
     }
    mask = sigblock(~0);
    catch_signals();
    init_input();
    sigsetmask(mask);
  }
 else
  { if (oldsigs[sig-1].sv_handler == SIG_DFL)
     { struct sigvec sv;
       sigvec(sig,&oldsigs[sig-1],&sv);
       mask = sigblock(0);
       kill(getpid(),sig);
       sigsetmask(mask&~sigmask(sig));
       sigsetmask(mask);
       if (sig == SIGTSTP)
	{ redraw = 1;
	}
       sigvec(sig,&sv,(struct sigvec *)0);
     }
    else
     { 
#ifdef LINUX_CFG
       (*oldsigs[sig-1].sv_handler)(sig);
#else
       (*oldsigs[sig-1].sv_handler)(sig,code,scp);
#endif

     }
  }
#else /* ifdef NO_RESTART_READ */
 mask = sigblock(~0);
 fixup_input();
 uncatch_signals();
 sigsetmask(mask);
 if (oldsigs[sig-1].sv_handler == SIG_DFL)
  { mask = sigblock(0);
    kill(getpid(),sig);
    sigsetmask(mask&~sigmask(sig));
    sigsetmask(mask);
    if (sig == SIGTSTP)
     { redraw = 1;
     }
  }
 else
  { 
#ifdef LINUX_CFG
       (*oldsigs[sig-1].sv_handler)(sig);
#else
       (*oldsigs[sig-1].sv_handler)(sig,code,scp);
#endif
  }
 mask = sigblock(~0);
 catch_signals();
 init_input();
 sigsetmask(mask);
#endif /* ifdef/ifndef NO_RESTART_READ */
 if (redraw)
  { put_char('\n');
    do_redraw();
  }
}

static SIG_RETURN catch_signals()
{
 int i;

	if (debugging)
	 { fprintf(stderr,"[catch_signals, catchsig %08x]\n",catchsigs);
	 }
 for (i=0;i<32;i++)
#ifdef NO_RESTART_READ
  { struct sigvec new;
    struct sigvec old;
    sigscaught[i] = 0;
    if (sigvec(i+1,(struct sigvec *)0,&old) == 0)
     { oldsigs[i] = old;
       new = old;
       sigscaught[i] = 1;
       new.sv_handler = signal_catch;
       sigvec(i+1,&new,(struct sigvec *)0);
     }
  }
#else /* ifndef NO_RESTART_READ */
  { sigscaught[i] = 0;
    if (catchsigs&(1<<i))
     { struct sigvec new;
       struct sigvec old;
       if (sigvec(i+1,(struct sigvec *)0,&old) == 0)
	{ oldsigs[i] = old;
	  new = old;
	  sigscaught[i] = 1;
	  new.sv_handler = signal_catch;
	  sigvec(i+1,&new,(struct sigvec *)0);
	}
     }
  }
#endif /* ifdef/ifndef NO_RESTART_READ */
}

static uncatch_signals()
{
 int i;

	if (debugging)
	 { fprintf(stderr,"[uncatch_signals]\n");
	 }
 for (i=0;i<32;i++)
#ifdef NO_RESTART_READ
  { sigvec(i+1,&oldsigs[i],(struct sigvec *)0);
  }
#else
  { if (sigscaught[i])
     { sigvec(i+1,&oldsigs[i],(struct sigvec *)0);
     }
  }
#endif
}

static get_min_max(k)
KEY *k;
{
 switch (k->type)
  { case KEY_I: case KEY_X: case KEY_O:
       min.i = k->flags.s.min_addr ? *k->min.ip :
				     k->min.i;
       max.i = k->flags.s.max_addr ? *k->max.ip :
				     k->max.i;
       break;
    case KEY_R: case KEY_D:
       min.f = k->flags.s.min_addr ? *k->min.fp :
				     k->min.f;
       max.f = k->flags.s.max_addr ? *k->max.fp :
				     k->max.f;
       break;
  }
}

static int printlen(n)
register int n;
{
 register int i;

 if (i = n < 0)
  { n = - n;
  }
 i ++;
 n /= 10;
 do
  { i ++;
    n /= 10;
  } while (n);
 return(i);
}

static print_tree(t,indent)
TREE *t;
int indent;
{
#ifdef lint
 indent = indent;
#endif
 printcol = 0;
 print_keys(t,0);
}

static int print_key(k)
register KEY *k;
{
 register int nc;

 switch (k->type)
  { case KEY_NORMAL:
       print("%s",k->name);
       nc = strlen(k->name);
       break;
    case KEY_I:
       print("[integer");
       nc = 8;
       get_min_max(k);
       if (min.i != MIN_INT)
	{ if (max.i != MAX_INT)
	   { print(" %d to %d]",min.i,max.i);
	     nc += 6 + printlen(min.i) + printlen(max.i);
	   }
	  else
	   { print(" >= %d]",min.i);
	     nc += 5 + printlen(min.i);
	   }
	}
       else
	{ if (max.i != MAX_INT)
	   { print(" <= %d]",max.i);
	     nc += 5 + printlen(max.i);
	   }
	  else
	   { put_char(']');
	     nc ++;
	   }
	}
       break;
    case KEY_X:
       print("[hex integer");
       nc = 12;
       get_min_max(k);
       if (min.i != MIN_INT)
	{ if (max.i != MAX_INT)
	   { print(" %x to %x]",min.i,max.i);
	     nc += 6 + printlen(min.i) + printlen(max.i);
	   }
	  else
	   { print(" >= %x]",min.i);
	     nc += 5 + printlen(min.i);
	   }
	}
       else
	{ if (max.i != MAX_INT)
	   { print(" <= %x]",max.i);
	     nc += 5 + printlen(max.i);
	   }
	  else
	   { put_char(']');
	     nc ++;
	   }
	}
       break;
    case KEY_O:
       print("[octal integer");
       nc = 14;
       get_min_max(k);
       if (min.i != MIN_INT)
	{ if (max.i != MAX_INT)
	   { print(" %o to %o]",min.i,max.i);
	     nc += 6 + printlen(min.i) + printlen(max.i);
	   }
	  else
	   { print(" >= %o]",min.i);
	     nc += 5 + printlen(min.i);
	   }
	}
       else
	{ if (max.i != MAX_INT)
	   { print(" <= %o]",max.i);
	     nc += 5 + printlen(max.i);
	   }
	  else
	   { put_char(']');
	     nc ++;
	   }
	}
       break;
    case KEY_R:
    case KEY_D:
       print("[real");
       nc = 5;
       get_min_max(k);
	{ char tempspace[64];
	  if (FtoI(min.f) != MIN_FLOAT)
	   { if (FtoI(max.f) != MAX_FLOAT)
	      { sprintf(tempspace," %g to %g]",min.f,max.f);
	      }
	     else
	      { sprintf(tempspace," >= %g]",min.f);
	      }
	   }
	  else
	   { if (FtoI(max.f) != MAX_FLOAT)
	      { sprintf(tempspace," <= %g]",max.f);
	      }
	     else
	      { strcpy(tempspace,"]");
	      }
	   }
	  nc += strlen(tempspace);
	  print("%s",tempspace);
	}
       break;
    case KEY_S:
       print("[string]");
       nc = 8;
       break;
    case KEY_V:
       print("%s",(char *)k->number);
       nc = strlen((char *)k->number);
       break;
  }
 return(nc);
}

static print_keys(t,indent)
TREE *t;
int indent;
{
 register KEY *k;

 for (k=t->keys;k;k=k->link)
  { while (printcol < indent)
     { put_char(' ');
       printcol ++;
     }
    printcol += print_key(k);
    if (k->subtree)
     { print_keys(k->subtree,printcol+1);
     }
    else
     { put_char('\n');
       printcol = 0;
     }
  }
}

static init_input()
{
 static int already = 0;

 if (! already)
  { already = 1;
#ifdef VXWORKS_CFG
    requestFlagInit(&already);
#endif
    input = NEW(IND_CHAIN);
    input->link = 0;
    input->file = stdin;
#ifdef VXWORKS_CFG
    input->noclose = 1;
#endif
    C_match_fp = stdin;
    if (is_nb) setbuf(stdin,(char *)0);
#ifndef VXWORKS_CFG
    shell = getenv("SHELL");
    if (shell == 0)
     { shell = "/bin/csh";
     }
#endif
  }
#ifndef VXWORKS_CFG
#ifdef sgi
 if (ioctl(fileno(stdin),TCGETA,&old_state) == -1)
  { return;
  }
 new_state = old_state;
 new_state.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
 new_state.c_cc[VMIN] = 1;
 new_state.c_cc[VTIME] = 0;
 ioctl(fileno(stdin),TCSETA,&new_state);
#else
#if defined(_AIX)
 if (ioctl(fileno(stdin),TXGETLD,old_ldisp) == -1)
  { printf ("couldn't get old ldisp\n");
    return;
  }
 if (ioctl(fileno(stdin),TXSETLD,"bsd") == -1)
  { printf ("couldn't set new ldisp\n");
    return;
  }
#endif

#ifdef LINUX_CFG
 if (ioctl(fileno(stdin),TCGETS,&old_state) == -1)
  { return;
  }
 new_state = old_state;
 new_state.c_iflag &= ~ECHO;
 ioctl(fileno(stdin),TCSETS ,&new_state);
#else
 if (ioctl(fileno(stdin),TIOCGETP,&old_state) == -1)
  { return;
  }
 new_state = old_state;
 new_state.sg_flags |= CBREAK;
 new_state.sg_flags &= ~ECHO;
 ioctl(fileno(stdin),TIOCSETN,&new_state);
#endif


#endif
#else
 if ((old_state = ioctl(fileno(stdin),FIOGETOPTIONS)) == -1)
  { return;
  }
 new_state = old_state;
 new_state &= ~(OPT_ECHO|OPT_TANDEM|OPT_LINE);
 ioctl(fileno(stdin),FIOSETOPTIONS,new_state);
#endif
}

static fixup_input()
{
#ifndef VXWORKS_CFG
#ifdef sgi
 ioctl(fileno(stdin),TCSETA,&old_state);
#else
#ifdef LINUX_CFG
  ioctl(fileno(stdin),TCSETS,&old_state);
#else
  ioctl(fileno(stdin),TIOCSETN,&old_state);
#endif
#ifdef _AIX
  ioctl(fileno(stdin),TXSETLD,old_ldisp); 
#endif
#endif
#else
 ioctl(fileno(stdin),FIOSETOPTIONS,old_state);
#endif
}

static done()
{
 int mask;
 
 mask = sigblock(~0);
 fixup_input();
 sigsetmask(mask);
 exit(0);
}

static push_back(c)
int c;
{
 ungotten = c;
}

tree_match_set_get_routine(fxn)
int (*fxn)();
{
 get_char_fxn = fxn;
}

tree_match_set_put_routine(fxn)
int (*fxn)();
{
 put_char_fxn = fxn;
}

FILE *tree_match_input_stream()
{
 return(input?input->file:0);
}

static put_char(c)
char c;
{
 if (put_char_fxn)
  { (*put_char_fxn)(c);
  }
 else
  { putchar(c);
  }
}

#ifdef vax
/* to make this standalone on systems that aren't up to date yet */
static char *vsprintf(str,fmt,rest)
char *str;
char *fmt;
va_list rest;
{
 FILE _strbuf;

 _strbuf._flag = _IOWRT+_IOSTRG;
 _strbuf._ptr = str;
 _strbuf._cnt = 32767;
 _doprnt(fmt,(int *)rest,&_strbuf);
 putc('\0',&_strbuf);
 return(str);
}
#endif

#ifdef lint
/* VARARGS1 */
static print(fmt,va_alist)char*fmt;
#else
static print(va_alist)
#endif
va_dcl
{
#ifndef lint
 char *fmt;
#endif
 va_list ap;
 static char tempbuf[1024];
 register char *cp;

 va_start(ap);
 fmt = va_arg(ap,char *);
#ifndef VXWORKS_CFG
 vsprintf(tempbuf,fmt,ap);
#else
 vsnprintf(tempbuf,1024,fmt,ap);
#endif
 va_end(ap);
 for (cp=tempbuf;*cp;cp++)
  { put_char(*cp);
  }
}

static char get_char()
{
 int c;
 IND_CHAIN *icp;

 if (ungotten >= 0)
  { c = ungotten;
    ungotten = -1;
    return(c);
  }
 if (input == 0)
  { done();
  }
 if (! put_char_fxn)
  { fflush(stdout);
  }
 if (get_char_fxn)
  { c = (*get_char_fxn)(input->file);
  }
 else
  {
again:;
    c = getc(input->file);
    if ((c == EOF) && caughtasignal)
     { caughtasignal = 0;
       goto again;
     }
  }
 while (input && (c == EOF))
  { if (input->file != stdin)
     { fclose(input->file);
     }
    icp = input->link;
    OLD(input);
    input = icp;
    if (input)
     { C_match_fp = input->file;
       if (get_char_fxn)
	{ c = (*get_char_fxn)(input->file);
	}
       else
	{ c = getc(input->file);
	}
     }
  }
 if (input == 0)
  { return(-1);
  }
 return(c);
}

static int get_char_nb(cp)
char *cp;
{
 int c;
 IND_CHAIN *icp;

 if (ungotten >= 0)
  { *cp = ungotten;
    ungotten = -1;
    return(0);
  }
 if (input == 0)
  { done();
  }
 if (! put_char_fxn)
  { fflush(stdout);
  }
 while (input)
  { fd_set fds;
    struct timeval tv;
    C_match_fp = input->file;
    FD_ZERO(&fds);
    FD_SET(fileno(C_match_fp),&fds);
    tv.tv_sec = 0;
    tv.tv_usec = 0;
    c = select(fileno(C_match_fp)+1,&fds,(fd_set *)0,(fd_set *)0,&tv);
    if ((c < 1) || !FD_ISSET(fileno(C_match_fp),&fds)) return(1);
    c = read(fileno(C_match_fp),cp,1);
    if (c < 1)
     { if (input->file != stdin)
	{ fclose(input->file);
	}
       icp = input->link;
       OLD(input);
       input = icp;
     }
    else
     { return(0);
     }
  }
 if (input == 0)
  { *cp = EOF;
    return(0);
  }
}

static do_delete()
{
 if (len > 0)
  { len --;
    if (in_dquote && (len < dquoteplace))
     { in_dquote = 0;
     }
    print("\b \b");
  }
}

static do_control_r()
{
 print("^R\n");
 do_redraw();
}

static do_redraw()
{
 print("%s%.*s",prompt,len,buf);
}

static do_control_u()
{
 while (len > 0)
  { len --;
    print("\b \b");
  }
 in_dquote = 0;
}

static do_control_g()
{
 put_char(ctrl_g);
}

static int is_int(s)
char *s;
{
 return(_TMPIFSM_(s));
}

static int is_hex(s)
register char *s;
{
 num.i = 0;
 for (;isxdigit(*s);s++)
  { num.i <<= 4;
    if (isdigit(*s))
     { num.i += *s - '0';
     }
    else if (isupper(*s))
     { num.i += *s - 'A' + 10;
     }
    else
     { num.i += *s - 'a' + 10;
     }
  }
 return(!*s);
}

static int is_oct(s)
register char *s;
{
 num.i = 0;
 for (;isdigit(*s)&&(*s<='7');s++)
  { num.i <<= 3;
    num.i += *s - '0';
  }
 return(!*s);
}

_TMPI_init(ch)
int ch;
{
#ifdef lint
 ch = ch;
#endif
 sign = 1;
}

_TMPI_negative(ch)
int ch;
{
#ifdef lint
 ch = ch;
#endif
 sign = -1;
}

/* VARARGS */
_TMPI_value(v)
int v;
{
 num.i = v * sign;
}

static int is_real(s)
char *s;
{
 return(_TMPRFSM_(s));
}

_TMPR_init(ch)
int ch;
{
#ifdef lint
 ch = ch;
#endif
 factor = 1;
 num.f = 0;
 sign = 1;
 exp_sign = 1;
 exponent = 0;
}

_TMPR_negative(ch)
int ch;
{
#ifdef lint
 ch = ch;
#endif
 sign = -1;
}

_TMPR_negate_exp(ch)
int ch;
{
#ifdef lint
 ch = ch;
#endif
 exp_sign = -1;
}

_TMPR_set_exp(n)
int n;
{
 exponent = n;
}

_TMPR_int_digit(d)
char d;
{
 num.f *= 10;
 num.f += d - '0';
}

_TMPR_frac_digit(d)
char d;
{
 num.f += (d-'0') * (factor /= 10);
}

_TMPR_finish_real(ch)
int ch;
{
#ifdef lint
 ch = ch;
#endif
 num.f *= sign;
 exponent *= exp_sign;
 while (exponent < 0)
  { num.f /= 10;
    exponent ++;
  }
 while (exponent > 0)
  { num.f *= 10;
    exponent --;
  }
}

static TREE *traverse(T,Cp)
TREE **T;
#define t (*T)
char **Cp;
#define cp (*Cp)
{
 char *sp;
 KEY *k;
 int cond;

 while (1)
  { if (*cp == '"')
     { sp = index(cp+1,'"');
       if (sp != 0)
	{ sp ++;
	}
     }
    else
     { sp = index(cp,' ');
     }
    if (sp == 0)
     { return(t);
     }
    *sp = '\0';
    for (k=t->keys;k;k=k->link)
     { switch (k->type)
	{ case KEY_NORMAL:
	     cond = (strcmp(k->name,cp) == 0);
	     break;
	  case KEY_I:
	     cond = is_int(cp);
	     break;
	  case KEY_X:
	     cond = is_hex(cp);
	     break;
	  case KEY_O:
	     cond = is_oct(cp);
	     break;
	  case KEY_R:
	  case KEY_D:
	     cond = is_real(cp);
	     break;
	  case KEY_S:
	     cond = 1;
	     break;
	  case KEY_V:
	     cond = (strcmp((char *)k->number,cp) == 0);
	     break;
	}
       if (cond)
	{ break;
	}
     }
    if (! cond)
     { return(0);
     }
    *sp = ' ';
    cp = sp + 1;
    t = k->subtree;
  }
}
#undef t
#undef cp

static int common_part(s1,s2)
register char *s1;
register char *s2;
{
 register int ri = 0;

 while (s1[ri] == s2[ri])
  { if (s1[ri] == '\0')
     { break;
     }
    ri ++;
  }
 return(ri);
}

static do_comment_char(c)
char c;
{
 switch (c)
  { case '\b':
    case del:
       do_delete();
       break;
    case ctrl_u:
    case ctrl_x:
       do_control_u();
       break;
    case ctrl_r:
       do_control_r();
       break;
    case '\n':
       put_char('\n');
       len = 0;
       do_redraw();
       break;
    default:
       if ((c >= ' ') && (c <= '~'))
	{ buf[len++] = c;
	  put_char(c);
	}
       else
	{ do_control_g();
	}
       break;
  }
}

static do_dquote_char(c)
char c;
{
 switch (c)
  { case '\b':
    case del:
       do_delete();
       break;
    case ctrl_u:
    case ctrl_x:
       do_control_u();
       break;
    case ctrl_r:
       do_control_r();
       break;
    case '\n':
    case esc:
    case '"':
	{ TREE *t;
	  char *cp;
	  KEY *k;
	  t = tree;
	  cp = buf;
	  traverse(&t,&cp);
	  for (k=t->keys;k;k=k->link)
	   { if ( (k->type == KEY_S) &&
		  ( !k->match_routine ||
		    (*k->match_routine)(cp) ) )
	      { break;
	      }
	   }
	  if (k)
	   { print("\" ");
	     if (! k->subtree)
	      { do_return = 1;
	      }
	     buf[len++] = '"';
	     buf[len++] = ' ';
	     in_dquote = 0;
	   }
	  else
	   { do_control_g();
	     while (in_dquote)
	      { do_delete();
	      }
	   }
	}
       break;
    default:
       if ((c >= ' ') && (c <= '~'))
	{ buf[len++] = c;
	  put_char(c);
	}
       else
	{ do_control_g();
	}
       break;
  }
}

static do_normal_char(c)
char c;
{
 switch (c)
  { case '\b':
    case del:
       do_delete();
       break;
    case '\n':
    case ' ':
    case esc:
	{ TREE *t;
	  char *cp;
	  KEY *k;
	  KEY *exact;
	  KEY *match;
	  char *partstr;
	  char *str;
	  int count;
	  int cond;
	  int COND;
	  int l;
	  int max_match;
	  int min_match;
	  int match_part;
	  t = tree;
	  cp = buf;
	  traverse(&t,&cp);
	  l = strlen(cp);
	  if (FLAG(DEBUG))
	   { print("\nafter traverse, l=%d c=%s cp=\"%s\"\n",l,unctrl(c),cp);
	   }
	  if ((l == 0) && (c == '\n'))
	   { put_char('\n');
	     do_redraw();
	     break;
	   }
	  exact = 0;
	  count = 0;
	  partstr = 0;
	  max_match = -1;
	  for (k=t->keys;k;k=k->link)
	   { FSMarg *_TMPIFSM_getarg();
	     FSMarg *_TMPRFSM_getarg();
	     str = 0;
	     if (FLAG(DEBUG))
	      { print("Key = ");
		print_key(k);
	      }
	     switch (k->type)
	      { case KEY_NORMAL:
		   str = k->name;
		   cond = (strncmp(str,cp,l) == 0);
		   COND = (strcmp(str,cp) == 0);
		   match_part = common_part(str,cp);
		   if ( cond &&
			k->match_routine &&
			!(*k->match_routine)(cp) )
		    { cond = COND = 0;
		      match_part = 0;
		    }
		   break;
		case KEY_I:
		   cond = 0;
		   COND = is_int(cp);
		   match_part = _TMPIFSM_getarg()->string - cp;
		   if ( COND &&
			k->match_routine &&
			!(*k->match_routine)(num.i) )
		    { COND = 0;
		      match_part = 0;
		    }
		   if (COND)
		    { get_min_max(k);
		      if ((num.i < min.i) || (num.i > max.i))
		       { COND = 0;
			 match_part = 0;
		       }
		    }
		   break;
		case KEY_X:
		   cond = 0;
		   COND = is_hex(cp);
		   match_part = _TMPIFSM_getarg()->string - cp;
		   if ( COND &&
			k->match_routine &&
			!(*k->match_routine)(num.i) )
		    { COND = 0;
		      match_part = 0;
		    }
		   if (COND)
		    { get_min_max(k);
		      if ((num.i < min.i) || (num.i > max.i))
		       { COND = 0;
			 match_part = 0;
		       }
		    }
		   break;
		case KEY_O:
		   cond = 0;
		   COND = is_oct(cp);
		   match_part = _TMPIFSM_getarg()->string - cp;
		   if ( COND &&
			k->match_routine &&
			!(*k->match_routine)(num.i) )
		    { COND = 0;
		      match_part = 0;
		    }
		   if (COND)
		    { get_min_max(k);
		      if ((num.i < min.i) || (num.i > max.i))
		       { COND = 0;
			 match_part = 0;
		       }
		    }
		   break;
		case KEY_R:
		case KEY_D:
		   cond = 0;
		   COND = is_real(cp);
		   match_part = _TMPRFSM_getarg()->string - cp;
		   if ( COND &&
			k->match_routine &&
			!(*k->match_routine)(num.f) )
		    { COND = 0;
		      match_part = 0;
		    }
		   if (COND)
		    { get_min_max(k);
		      if ((num.f < min.f) || (num.f > max.f))
		       { COND = 0;
			 match_part = 0;
		       }
		    }
		   break;
		case KEY_S:
		   cond = 0;
		   COND = (l != 0);
		   match_part = l;
		   if ( COND &&
			k->match_routine &&
			!(*k->match_routine)(cp))
		    { COND = 0;
		      match_part = 0;
		    }
		   break;
		case KEY_V:
		   str = (char *) k->number;
		   cond = (strncmp(str,cp,l) == 0);
		   COND = (strcmp(str,cp) == 0);
		   match_part = common_part(str,cp);
		   if ( cond &&
			k->match_routine &&
			!(*k->match_routine)(cp) )
		    { cond = 0;
		      COND = 0;
		      match_part = 0;
		    }
		   break;
	      }
	     if (FLAG(DEBUG))
	      { print(" --> cond = %d, COND = %d, match_part = %d\n",
			cond,COND,match_part);
	      }
	     if (match_part > max_match)
	      { max_match = match_part;
	      }
	     if (COND)
	      { exact = k;
	      }
	     if (cond || COND)
	      { int i;
		if (str)
		 { if (partstr)
		    { i = common_part(str,partstr);
#ifdef lint
		      min_match = 0;
#endif
		      if (i < min_match)
		       { min_match = i;
		       }
		    }
		   else
		    { min_match = strlen(partstr=str);
		    }
		 }
		count ++;
		match = k;
	      }
	   }
	  if (exact != 0)
	   { put_char(' ');
	     buf[len++] = ' ';
	     if (exact->subtree == 0)
	      { do_return = 1;
	      }
	   }
	  else
	   { if (count == 0)
	      { put_char(ctrl_g);
		while (l > max_match)
		 { print("\b \b");
		   l --;
		   len --;
		 }
	      }
	     else if (count == 1)
	      { register char *dp;
		dp = match->name + l;
		print("%s ",dp);
		while (buf[len++] = *dp++) ;
		buf[len-1] = ' ';
		if (match->subtree == 0)
		 { do_return = 1;
		 }
	      }
	     else if (l < min_match)
	      { register char *dp;
		register int i;
		dp = partstr + l;
		i = min_match - l;
		print("%.*s",i,dp);
		for (;i;i--)
		 { buf[len++] = *dp++;
		 }
	      }
	     else
	      { put_char(ctrl_g);
	      }
	   }
	}
       break;
    case ctrl_u:
    case ctrl_x:
       do_control_u();
       break;
    case ctrl_r:
       do_control_r();
       break;
    case '?':
	{ TREE *t = tree;
	  char *cp = buf;
	  char *str;
	  KEY *k;
	  int cond;
	  int l;
	  int col;
	  int n_cols;
	  int width;
	  put_char('\n');
	  traverse(&t,&cp);
	  l = strlen(cp);
	  col = 1;
	  width = 1;
	  for (k=t->keys;k;k=k->link)
	   { switch (k->type)
	      { case KEY_NORMAL:
		   str = k->name;
		   cond = (strncmp(str,cp,l) == 0);
		   break;
		case KEY_I:
		   cond = 1;
		   break;
		case KEY_X:
		   cond = 1;
		   break;
		case KEY_O:
		   cond = 1;
		   break;
		case KEY_R:
		case KEY_D:
		   cond = 1;
		   break;
		case KEY_S:
		   cond = 1;
		   break;
		case KEY_V:
		   str = (char *) k->number;
		   cond = (strncmp(str,cp,l) == 0);
		   break;
	      }
	     if ( cond &&
		  k->match_routine &&
		  !(*k->match_routine)(cp) )
	      { cond = 0;
	      }
	     if (cond)
	      { register int ll;
		k->flags.s.swept = 1;
		switch (k->type)
		 { case KEY_NORMAL:
		      ll = strlen(k->name);
		      if (width < ll)
		       { width = ll;
		       }
		      break;
		   case KEY_I:
		      col = 0;
		      break;
		   case KEY_X:
		      col = 0;
		      break;
		   case KEY_O:
		      col = 0;
		      break;
		   case KEY_R:
		   case KEY_D:
		      col = 0;
		      break;
		   case KEY_S:
		      col = 0;
		      break;
		   case KEY_V:
		      ll = strlen((char *)k->number);
		      if (width < ll)
		       { width = ll;
		       }
		      break;
		 }
	      }
	     else
	      { k->flags.s.swept = 0;
	      }
	   }
	  width += 2;
	  if (col)
	   { n_cols = (width > 79) ? 1 : 79/width;
	     width = 79 / n_cols;
	   }
	  else
	   { n_cols = 1;
	   }
	  col = 0;
	  l = 0;
	  for (k=t->keys;k;k=k->link)
	   { FSMarg *_TMPIFSM_getarg();
	     FSMarg *_TMPRFSM_getarg();
	     if (k->flags.s.swept)
	      { for (;l<(col*width);l++)
		 { put_char(' ');
		 }
		switch (k->type)
		 { case KEY_NORMAL:
		      print("%s",k->name);
		      l += strlen(k->name);
		      break;
		   case KEY_I:
		      if (k->help)
		       { print("%s\n",k->help);
		       }
		      else
		       { print("An integer");
			 get_min_max(k);
			 if (min.i != MIN_INT)
			  { if (max.i != MAX_INT)
			     { print(" %d to %d",min.i,max.i);
			     }
			    else
			     { print(" not less than %d",min.i);
			     }
			  }
			 else
			  { if (max.i != MAX_INT)
			     { print(" not more than %d",max.i);
			     }
			  }
		       }
		      break;
		   case KEY_X:
		      if (k->help)
		       { print("%s\n",k->help);
		       }
		      else
		       { print("A hex integer");
			 get_min_max(k);
			 if (min.i != MIN_INT)
			  { if (max.i != MAX_INT)
			     { print(" %x to %x",min.i,max.i);
			     }
			    else
			     { print(" not less than %x",min.i);
			     }
			  }
			 else
			  { if (max.i != MAX_INT)
			     { print(" not more than %x",max.i);
			     }
			  }
		       }
		      break;
		   case KEY_O:
		      if (k->help)
		       { print("%s\n",k->help);
		       }
		      else
		       { print("An octal integer");
			 get_min_max(k);
			 if (min.i != MIN_INT)
			  { if (max.i != MAX_INT)
			     { print(" %o to %o",min.i,max.i);
			     }
			    else
			     { print(" not less than %o",min.i);
			     }
			  }
			 else
			  { if (max.i != MAX_INT)
			     { print(" not more than %o",max.i);
			     }
			  }
		       }
		      break;
		   case KEY_R:
		   case KEY_D:
		      if (k->help)
		       { print("%s\n",k->help);
		       }
		      else
		       { print("A real number");
			 get_min_max(k);
			 if (FtoI(min.f) != MIN_FLOAT)
			  { if (FtoI(max.f) != MAX_FLOAT)
			     { print(" %g to %g",min.f,max.f);
			     }
			    else
			     { print(" not less than %g",min.f);
			     }
			  }
			 else
			  { if (FtoI(max.f) != MAX_FLOAT)
			     { print(" not more than %g",max.f);
			     }
			  }
		       }
		      break;
		   case KEY_S:
		      if (k->help)
		       { print("%s\n",k->help);
		       }
		      else
		       { print("A string");
		       }
		      break;
		   case KEY_V:
		      print("%s",(char *)k->number);
		      l += strlen((char *)k->number);
		      break;
		 }
		if (++col >= n_cols)
		 { put_char('\n');
		   l = 0;
		   col = 0;
		 }
	      }
	   }
	  if (col > 0)
	   { put_char('\n');
	   }
	}
       do_redraw();
       break;
    case '*':
	{ TREE *t = tree;
	  char *cp = buf;
	  traverse(&t,&cp);
	  put_char('\n');
	  print_tree(t,0);
	}
       do_redraw();
       break;
    case '"':
	{ TREE *t;
	  char *cp;
	  int l;
	  t = tree;
	  cp = buf;
	  traverse(&t,&cp);
	  l = strlen(cp);
	  if (l != 0)
	   { buf[len++] = '"';
	     put_char('"');
	     break;
	   }
	  dquoteplace = len;
	  buf[len++] = '"';
	  in_dquote = 1;
	  put_char('"');
	}
       break;
    default:
       if ((c >= ' ') && (c <= '~'))
	{ buf[len++] = c;
	  put_char(c);
	}
       else
	{ do_control_g();
	}
       break;
  }
}

static do_command_char(c)
char c;
{
 int mask;

 switch (c)
  { case del:
    case '\b':
       do_delete();
       break;
    case '\n':
#ifndef VXWORKS_CFG
       mask = sigblock(~0);
       fixup_input();
       sigsetmask(mask);
	{ 
#ifdef HAS_VFORK
	  int pid = vfork();
#else
	  int pid = fork();
#endif
	  if (pid < 0)
	   { extern int errno;
	     extern int sys_nerr;
#ifdef LINUX_CFG
	     extern __const char *__const _sys_errlist[];
#else
	     extern char *sys_errlist[];
#endif
	     print(" --- Cannot fork");
	     if ((errno >= 0) && (errno < sys_nerr))
	      { print("%s\n",sys_errlist[errno]);
	      }
	     else
	      { print("Unknown error (%d)\n",errno);
	      }
	   }
	  else if (pid != 0)
	   { union wait stat;
	     put_char('\n');
	     while (wait(&stat) != pid) ;
	     if (WIFEXITED(stat) && (stat.w_retcode == EX_UNAVAILABLE))
	      { print("No shell\n");
	      }
	   }
	  else /* if (pid == 0) */
	   { buf[len] = '\0';
	     setuid(getuid());
	     setgid(getgid());
	     if (len == 1)
	      { execl(shell,shell,0);
	      }
	     else
	      { execl(shell,shell,"-c",buf+1,0);
	      }
	     exit(EX_UNAVAILABLE);
	   }
	  len = 0;
	  printf("%s",prompt);
	}
       mask = sigblock(~0);
       init_input();
       sigsetmask(mask);
       break;
#else
       printf ("shell escape unavailable in vxWorks\n");
#endif
    case ctrl_u:
    case ctrl_x:
       do_control_u();
       break;
    case ctrl_r:
       do_control_r();
       break;
    default:
       if ((c >= ' ') && (c <= '~'))
	{ buf[len++] = c;
	  put_char(c);
	}
       else
	{ do_control_g();
	}
  }
}

static do_indir_char(c)
char c;
{
 switch (c)
  { case '\b':
    case del:
       do_delete();
       break;
    case ' ':
    case '\n':
    case esc:
       put_char('\n');
	{ IND_CHAIN *icp;
	  FILE *f;
	  buf[len] = '\0';
	  f = fopen(buf+1,"r");
	  if (f == NULL)
	   { print("Cannot open `%s'\n",buf+1);
	   }
	  else
	   { icp = NEW(IND_CHAIN);
	     icp->file = f;
#ifdef VXWORKS_CFG
	     icp->noclose = 0;
#endif
	     icp->link = input;
	     input = icp;
	     C_match_fp = input->file;
	     if (is_nb) setbuf(C_match_fp,(char *)0);
	   }
	  print("%s",prompt);
	  len = 0;
	}
       break;
    case ctrl_u:
    case ctrl_x:
       do_control_u();
       break;
    case ctrl_r:
       do_control_r();
       break;
    default:
       if ((c >= ' ') && (c <= '~'))
	{ buf[len++] = c;
	  put_char(c);
	}
       else
	{ do_control_g();
	}
  }
}

static traverse_load()
{
 TREE *t;
 char *cp;
 char *sp;
 char *to;
 KEY *k;
 int cond;

 t = tree;
 cp = to = buf;
 while (1)
  { if (*cp == '"')
     { sp = index(cp+1,'"');
       if (sp != 0)
	{ sp ++;
	}
     }
    else
     { sp = index(cp,' ');
     }
    if (sp == 0)
     { to[-1] = '\0';
       return;
     }
    *sp = '\0';
    for (k=t->keys;k;k=k->link)
     { switch (k->type)
	{ case KEY_NORMAL:
	     cond = (strcmp(k->name,cp) == 0);
	     break;
	  case KEY_I:
	     cond = is_int(cp);
	     break;
	  case KEY_X:
	     cond = is_hex(cp);
	     break;
	  case KEY_O:
	     cond = is_oct(cp);
	     break;
	  case KEY_R:
	  case KEY_D:
	     cond = is_real(cp);
	     break;
	  case KEY_S:
	     cond = 1;
	     break;
	  case KEY_V:
	     cond = (strcmp((char *)k->number,cp) == 0);
	     break;
	}
       if (cond)
	{ break;
	}
     }
    switch (k->type)
     { case KEY_I:
       case KEY_X:
       case KEY_O:
	  *(int *)k->number = num.i;
	  break;
       case KEY_R:
	  *(float *)k->number = num.f;
	  break;
       case KEY_D:
	  *(double *)k->number = (double) num.f;
	  break;
       case KEY_S:
	   { int isquoted;
	     isquoted = (*cp == '"');
	     if (isquoted)
	      { cp ++;
		sp[-1] = '\0';
	      }
	     strcpy((char *)k->number,cp);
	     if (isquoted)
	      { cp --;
		sp[-1] = '"';
	      }
	   }
	  break;
       default:
	  while (*to++ = *cp++) ;
	  to[-1] = ' ';
	  break;
     }
    *sp = ' ';
    cp = sp + 1;
    t = k->subtree;
  }
}

tree_match_save_whole_line(where)
char *where;
{
 whole_line = where;
}

static do_begin()
{
 int mask;

 if (is_setup) return;
 mask = sigblock(~0);
 catch_signals();
 init_input();
 sigsetmask(mask);
 is_setup = 1;
}

static do_end()
{
 int mask;

 if (! is_setup) return;
 mask = sigblock(~0);
 fixup_input();
 uncatch_signals();
 sigsetmask(mask);
 is_setup = 0;
}

tree_match_setup()
{
 want_input_reset = 0;
}

tree_match_reset()
{
 do_end();
 want_input_reset = 1;
}

tree_match(Tree,Prompt,Buf)
char *Tree;
char *Prompt;
char *Buf;
{
 char c;
 int mask;

#ifndef VXWORKS_CFG
	if (getenv("CTREE_DEBUG"))
	 { debugging = atoi(getenv("CTREE_DEBUG"));
	 }
#endif
 is_nb = 0;
 tree_match__maybe_init();
 tree = (TREE *) Tree;
 prompt = Prompt;
 buf = Buf;
 len = 0;
 do_return = 0;
 _TMP_current_tree = tree;
 push_back(-1);
 do_begin();
 print("%s",prompt);
 in_dquote = 0;
 while (1)
  { c = get_char();
    buf[len] = '\0';
    switch (buf[0])
     { case '!':
	  do_command_char(c);
	  break;
       case '@':
	  do_indir_char(c);
	  break;
       case '#':
	  do_comment_char(c);
	  break;
       default:
	  if (in_dquote)
	   { do_dquote_char(c);
	   }
	  else
	   { do_normal_char(c);
	   }
	  break;
     }
    if (do_return)
     { buf[len] = '\0';
       if (whole_line)
	{ strcpy(whole_line,buf);
	}
       whole_line = 0;
       traverse_load();
       put_char('\n');
       if (want_input_reset) do_end();
       return;
     }
  }
}

int tree_match_nb(Tree,Prompt,Buf,fdp)
char *Tree;
char *Prompt;
char *Buf;
int *fdp;
{
 char c;
 int mask;

 is_nb = 1;
 tree_match__maybe_init();
 tree = (TREE *) Tree;
 prompt = Prompt;
 buf = Buf;
 do_return = 0;
 _TMP_current_tree = tree;
 do_begin();
 if (! nb_inline)
  { push_back(-1);
    len = 0;
    in_dquote = 0;
    print("%s",prompt);
    nb_inline = 1;
  }
 while (1)
  { if (get_char_nb(&c))
     { if (want_input_reset) do_end();
       if (fdp) *fdp = fileno(C_match_fp);
       return(0);
     }
    buf[len] = '\0';
    switch (buf[0])
     { case '!':
	  do_command_char(c);
	  break;
       case '@':
	  do_indir_char(c);
	  break;
       case '#':
	  do_comment_char(c);
	  break;
       default:
	  if (in_dquote)
	   { do_dquote_char(c);
	   }
	  else
	   { do_normal_char(c);
	   }
	  break;
     }
    if (do_return)
     { buf[len] = '\0';
       if (whole_line)
	{ strcpy(whole_line,buf);
	}
       whole_line = 0;
       traverse_load();
       put_char('\n');
       nb_inline = 0;
       if (want_input_reset) do_end();
       if (fdp) *fdp = fileno(C_match_fp);
       return(1);
     }
  }
}

tree_match__maybe_init()
{
#ifdef VXWORKS_CFG
/* Really should close files and free stuff.
   This version loses the resources instead of freeing them. */
 if (! didinit)
  { input = 0;
    whole_line = 0;
    catchsigs = ~0;
    get_char_fxn = 0;
    put_char_fxn = 0;
    is_setup = 0;
    want_input_reset = 1;
    nb_inline = 0;
    flags = 0;
    didinit = 1;
    requestFlagInit(&didinit);
  }
#endif
}
