/********************************************************************
* $Author: jgoerzen $
* $Revision: 1.5 $
* $Date: 2002/01/08 17:36:14 $
* $Source: /home/jgoerzen/tmp/gopher-umn/gopher/head/gopher/CURcurses.c,v $
* $State: Exp $
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: CURcurses.c
* Abstraction of all Curses Functions
*********************************************************************
* Revision History:
* $Log: CURcurses.c,v $
* Revision 1.5  2002/01/08 17:36:14  jgoerzen
* Finally builds!
*
* Changes:
*
*   * config.h.in: rebuilt by autoheader
*
*   * configure: rebuilt by autoconf
*
*   * configure.in:
*     * Added REGEXLIBS test for NetBSD -- look for re_comp in -lcompat
*     * Added checkes for term.h, re_comp.h, regex.h
*
*   * gopher/ourutils.c, gopher/CURcurses.c: Use term.h check
*
*   * gopher/Makefile.in, gopherd/Makefile.in, gophfilt/Makefile.in:
*     * Use REGEXLIBS
*
*   * gopher/globals.h, gopher/gopher.c: Remove sys_errlist
*
*   * object/GSgopherobj.c: Removed <regex.h> #include, now use
*     "Regex.h" that has proper regex determining logic
*
*   * object/Regex.h: Moved regex.h include to here.  Make it conditional
*     based on configure test.  Add conditional re_comp.h include.
*
* Revision 1.4  2001/01/17 21:48:05  jgoerzen
* Many fixes and tune-ups.  Now compiles cleanly with -Wall -Werror!
*
* Revision 1.3  2001/01/03 22:25:57  s2mdalle
* Code cleanups, fixes for copious compiler warnings.
*
* Revision 1.2  2000/12/27 21:25:01  s2mdalle
* Added #include "gopher.h"
*
* Revision 1.1.1.1  2000/08/19 00:28:56  jgoerzen
* Import from UMN Gopher 2.3.1 after GPLization
*
* Revision 3.61  1996/01/04  18:28:51  lindner
* Updates for autoconf
*
* Revision 3.60  1995/11/03  21:18:15  lindner
* ANSIfication
*
* Revision 3.59  1995/11/03  20:50:47  lindner
* Coen: fixes..
*
* Revision 3.58  1995/05/02  06:06:57  lindner
* Fix for message str..
*
* Revision 3.57  1995/04/15  07:09:46  lindner
* New funky C-x stuff
*
* Revision 3.56  1995/03/09  05:24:39  lindner
* F.Macrides 01-Mar-1995 Removed the CURsetScreen(cur, initscr()) that
* someone #ifdef'ed for VMS in CURresize().
*
* Revision 3.55  1995/02/27  17:44:51  lindner
* New pacbell fcn CURrightline
*
* Revision 3.54  1995/02/07  19:34:41  lindner
* Fix for unix systems without SIGTSTP
*
* Revision 3.53  1995/01/25  23:09:24  lindner
* Word wrapping in gopher forms, provided by Pacific Bell Knowledge Net
*
* Revision 3.52  1994/12/03  01:51:39  lindner
* Fix prototype for DCLspawn_exception
*
* Revision 3.51  1994/10/21  04:41:07  lindner
* Add ANSI attached printer code..
*
* Revision 3.50  1994/06/29  07:06:59  lindner
* moved gcatd to Locale.[ch] (Coopersmith)
*
* Revision 3.49  1994/06/29  05:11:07  lindner
* None..
*
* Revision 3.48  1994/06/12  05:28:17  lindner
* Fix for locales
*
* Revision 3.47  1994/06/09  22:13:35  lindner
* More language conversions
*
* Revision 3.46  1994/05/24  05:49:54  lindner
* Fix for bad free() in CURrequest()
*
* Revision 3.45  1994/04/25  20:43:29  lindner
* Remove ugly pointer debug statement
*
* Revision 3.44  1994/04/25  03:49:41  lindner
* Fixed ^_ mislabel of the ^- help command.
*
* Added prompt for RETURN in VMSExit(), to enable reading of ACCVIO tracebacks.
*
* Changed return values of DCLSystem() to be compatible with the
* for-Unix code.
*
* Fixed spawns to work with OpenVMS/AXP v6.1 (still doesn't have the
* TRUSTED flag).
*
* Revision 3.43  1994/04/25  03:37:35  lindner
* Modifications for Debug() and mismatched NULL arguments, added Debugmsg
*
* Revision 3.42  1994/04/13  19:13:20  lindner
* AskL modifications
*
* Revision 3.41  1994/04/01  02:24:41  lindner
* Remove debugging stuff
*
* Revision 3.40  1994/03/31  21:26:43  lindner
* Mitra's debug routine
*
* Revision 3.39  1994/03/08  15:55:02  lindner
* gcc -Wall fixes
*
* Revision 3.38  1994/03/04  23:39:45  lindner
* Fix for log entries
*
* Revision 3.37  1994/03/04  23:36:28  lindner
* Many, many fixes and enhancements for Beckett and Co.
*
* Revision 3.36  1994/02/21  15:47:34  lindner
* Remove conditional compilation for DEC ALPHAS trusted flag on VMS
*
* Revision 3.35  1994/02/20  21:45:18  lindner
* Better sanity checks for box drawing characters
*
* Revision 3.34  1994/01/20  06:46:15  lindner
* Fix boundary condition
*
* Revision 3.33  1994/01/14  16:40:04  lindner
* Added VMSVersion() for getting the VMS version number, and modified
* spawn_DCLprocess() so that it includes the TRUSTED flag for VAX v6+
* and AXP v2+.  If DEC changes AXP v2.0 to v6.1 (I've heard rumors to
* that affect), remove the __ALPHA conditional compilation in
* spawn_DCLprocess() so it just checks for v6+.
*
* Revision 3.32  1993/12/28  17:29:09  lindner
* Better method of drawing box characters
*
* Revision 3.31  1993/11/04  02:06:50  lindner
* Add Ben's AIS hack
*
* Revision 3.30  1993/10/26  18:24:31  lindner
* Make sure we cleanup before exiting
*
* Revision 3.29  1993/10/26  18:12:56  lindner
* Fixes for Input routines, screen redrawing, ASK fixes
*
* Revision 3.28  1993/10/22  20:22:12  lindner
* Range checks for lots of things
*
* Revision 3.27  1993/10/07  05:10:12  lindner
* Make TrimmedTitle[] (twice) a larger array (256 elements).  Makes
* 132-column terminals and huge xterm windows happier.
*
* In CURwgetch(), fix vt100 "Help" escape sequence.  Add vt100 "Enter",
* vt200 "Help", and vt200 "Do" escape sequences.
*
* Revision 3.26  1993/09/30  22:41:57  lindner
* Add option for bolding of searched words
*
* Revision 3.25  1993/09/29  22:47:17  lindner
* Fix for AskBlock memory cruft
*
* Revision 3.24  1993/09/29  20:52:47  lindner
* Add [space to cycle] prompt earlier
*
* Revision 3.23  1993/09/26  09:19:19  lindner
* Add bold #defines
*
* Revision 3.22  1993/09/22  19:58:15  lindner
* Fix for extra cruft on screen from Select/Choose
*
* Revision 3.21  1993/09/22  04:13:44  lindner
* none
*
* Revision 3.20  1993/09/22  01:15:44  lindner
* Add support for DEC HELP key/KEY_HELP
*
* Revision 3.19  1993/09/21  02:42:18  lindner
* Exit on error reading from terminal (kills spinning processes)
*
* Revision 3.18  1993/09/21  01:46:06  lindner
* Implement all remaining ASK block items..
*
* Revision 3.17  1993/09/11  06:31:33  lindner
* spelling correction
*
* Revision 3.16  1993/09/11  04:51:00  lindner
* Fix to skip labels in CURrequest()
*
* Revision 3.15  1993/09/03  03:28:59  lindner
* Modified CURGetOneOption() to have a Title field.  Serveral other
* routines modified for improved line editing and to show all available
* commands while answering prompts.  If dialog box has only one prompt,
* as for search query entries, start user entry on new line so more of
* the screen is available for the entry (useful for search terms with
* booleans).
*
* Revision 3.14  1993/08/19  20:22:45  lindner
* Mitra's Debug patch
*
* Revision 3.13  1993/08/16  18:17:01  lindner
* Fixes from F.Macrides:
*
* Added temporary code to work around DECC/AXP's problems with screen
* clearing and cursor homing (we'll get rid of that code if the problem
* goes away in the next version of DECC/AXP).  It's bolding via
* standout() or wstandout(win) still doesn't work, but that's not a
* serious functional problem for gopher users on Alphas.
*
* Added exit block to ensure that the terminal characteristics are
* retored and cleanups are done on VMS.
*
* Added code for getting terminal characteristics
* from the terminal table on VMS.
*
* Replaced/modified Cruft for VMS with routines which handle both
* Control C and Control Y, enable use of ReallyQuit(), and restore all
* original terminal characteristics for spawns and intentional or
* unintentional exits.  Did it in a way that should stay transparent to
* the otherwise "for-Unix" code, and should hold up with future mods or
* enhancements of that code.  Standout() doesn't work at all and
* endwin() and delwin() are unreliable on Alphas (due to bugs in the
* Alpha's Curses library).  Andrew Heyler is looking into workarounds,
* should DEC not fix the bugs soon.  Code compiles with DECC on Alphas
* without warnings or error messages, but still get lots of
* "informational" messages due to incomplete prototyping (no problems or
* compiler messages with VAXC).
*
* Revision 3.12  1993/08/12  06:32:06  lindner
* Add needed variable
*
* Revision 3.11  1993/08/09  20:44:48  lindner
* Fix for really long strings
*
* Revision 3.10  1993/08/09  20:29:50  lindner
* Get rid of the beep during a ^G in CURwgetstr().
*
* Make CURChoice() delete its window when it exits.
*
* Revision 3.9  1993/07/30  17:31:34  lindner
* Mods to support AskP:
*
* Revision 3.8  1993/07/27  02:02:47  lindner
* More comments
*
* Revision 3.7  1993/07/23  04:33:48  lindner
* Mods to curchoice for default
*
* Revision 3.6  1993/07/20  23:10:17  lindner
* none
*
* Revision 3.5  1993/04/15  21:23:36  lindner
* Removed extraneous wattron/wattroff
*
* Revision 3.4  1993/03/26  19:42:32  lindner
* Fix for skip by two problem in CURrequest
*
* Revision 3.3  1993/03/18  23:15:24  lindner
* Mods to support titles inside of a CURrequest.
*
* Revision 3.2  1993/02/16  23:26:02  lindner
* Fixes for SIG_ERR (for Crays)
*
* Revision 3.1.1.1  1993/02/11  18:02:56  lindner
* Gopher+1.2beta release
*
* Revision 1.8  1993/01/11  20:25:31  lindner
* Fixed weird wprintw error on EP/IX.
*
* Revision 1.8  1993/01/11  20:25:31  lindner
* Fixed weird wprintw error on EP/IX.
*
* Revision 1.7  1993/01/09  02:16:21  lindner
* Changed (void*)-1 constructs to SIG_ERR
*
* Revision 1.6  1993/01/09  01:28:11  lindner
* Replaced hosed Log messages (Ooops!)
*
* Revision 1.5  1993/01/09  01:24:42  lindner
* Added CURchoice(), let's you choose one option from [2-9] items.
*
* Revision 1.4  1993/01/09  00:49:16  lindner
* More mods for VMS from jqj.  Looks like better ctrl-y and ctrl-c
* processing.
*
* Revision 1.3  1993/01/06  17:05:46  lindner
* Added nl() to CURexit() for EP/IX machines.
*
* Revision 1.2  1992/12/31  05:55:44  lindner
* Mods for VMS
*
* Revision 1.1  1992/12/10  06:16:51  lindner
* Initial revision
*
*
*********************************************************************/

#include "CURcurses.h"
#include "gopher.h"
#include "Malloc.h"

#include <signal.h>
#ifndef SIG_ERR
#define SIG_ERR ((void *) -1)
#endif
#include "Stdlib.h"
#include "compatible.h"
#include "Debug.h"
#include "String.h"
#include "util.h"

#ifdef VMS
static int w_getch();
#undef wgetch
#define wgetch w_getch
void setterm_pas();
void resetterm();
void VMSCURinit(/* CursesObj* */);
void VMSinit();
int VMSVersion(/* char *, int */);
void VMSexit();
int spawn_DCLprocess();
unsigned int DCLspawn_exception();
#endif /* VMS */

#include <curses.h>
#ifdef HAVE_TERM_H
#include <term.h>
#endif

#ifdef CTRLCPROMPTS
#  define DIALOGCANCELSTR "Cancel - ^C"
#  define HELPCANCEL      "^C      : Cancel"
#  define HELP2CANCEL     "^C        : Cancel"
#  define FORMCANCEL      "[Help: ^-]  [Cancel: ^C] "
#else
#  define DIALOGCANCELSTR "Cancel - ^G"
#  define HELPCANCEL      "^G      : Cancel"
#  define HELP2CANCEL     "^G        : Cancel"
#  define FORMCANCEL      "[Help: ^-]  [Cancel: ^G] "
#endif /*NEWCTRLC*/


#ifdef CONTROLX
#  define CXPROMPT1    "^X      : Accept"
#else
#  define CXPROMPT1    "Enter   : Accept"
#endif

/*
* Do we use wordwrap?
*/

boolean wordwrap = 0;


/*
* Initialize data space for various screen information
*/

CursesObj *
CURnew(void)
{
    CursesObj *cur;

    cur = (CursesObj *) malloc(sizeof(CursesObj));

    cur->Screen       = NULL;
    cur->Termtype     = STRnew();
    cur->Clearscreen  = STRnew();
    cur->AudibleBell  = STRnew();
    cur->Highlighton  = STRnew();
    cur->Highlightoff = STRnew();

    cur->canUseACS    = FALSE;
    cur->inCurses     = FALSE;
    cur->sigtstp      = SIG_ERR;
    cur->sigwinch     = SIG_ERR;

    CURinit(cur);

    return(cur);

}


/*
* Initialize various strings and such.
*/

void
CURinit(CursesObj *cur)
{
#ifdef VMS
    VMSCURinit(cur);
#else
    int err;
    static char terminal[1024];
    static char capabilities[1024];   /* String for cursor motion */
    static char *ptr = capabilities;  /* for buffering         */
    char *cp;


    /*** Set the terminal type ***/
    if (getenv("TERM") != NULL)
         CURsetTerm(cur, getenv("TERM"));
    else
         CURsetTerm(cur, "unknown");

    err = tgetent(terminal, CURgetTerm(cur));

    if (err !=1)
         CURsetTerm(cur, "unknown");

    /*** Get the clearscreen code ***/
    if ((cp = (char *)tgetstr("cl", &ptr)) != NULL)
         CURsetCLS(cur, cp);
    else
         CURsetCLS(cur, "");

    /*** Set the bell ***/
    if ((cp = (char *) tgetstr("bl", &ptr)) != NULL)
         CURsetBell(cur, cp);
    else
         CURsetBell(cur, "\007");

    /*** Set the highlight codes ***/
    if ((cp = (char *) tgetstr("so", &ptr)) != NULL) {
         CURsetHighon(cur, cp);
         if ((cp = (char *) tgetstr("se", &ptr)) != NULL)
              CURsetHighoff(cur, cp);
    } else {
         CURsetHighon(cur, "");
         CURsetHighoff(cur, "");
    }

    /*** Does terminal support alternate character set? ***/
    if ((char *) tgetstr("as", &ptr) != NULL)
         cur->canUseACS = TRUE;

    CURsetScreen(cur,initscr());
#endif
    cur->inCurses = FALSE;

    CURsetGraphicsChars(cur, CURgetTerm(cur));
}

/*
* Set the characters to output depending on the terminal type...
*
* Most systems don't do it right..
*/

static char *CURgraphicChars[] = {
    "vtxxx",  "qxlkmjwvtun",
    "xterm",  "qxlkmjwvtun",
    "z29",    "aqfcedsuvtb",
    "wyse50", "zvrsqupwtyx",
    "isc",    "D3Z?@Y+++++",
    "sun-cmd","-|+++++++++",
    NULL,     NULL
};

void
CURsetGraphicsChars(CursesObj *cur, char *termtype)
{
    cur->Box_hline  = '-';
    cur->Box_vline  = '|';
    cur->Box_ul     = '/';
    cur->Box_ur     = '\\';
    cur->Box_ll     = '\\';
    cur->Box_lr     = '/';
    cur->Box_tt     = '+';
    cur->Box_bt     = '+';
    cur->Box_lt     = '+';
    cur->Box_rt     = '+';
    cur->Box_ct     = '+';

/*
    cur->Box_hline  = '-';
    cur->Box_vline  = '|';
    cur->Box_ul     = '+';
    cur->Box_ur     = '+';
    cur->Box_ll     = '+';
    cur->Box_lr     = '+';
    cur->Box_tt     = '+';
    cur->Box_bt     = '+';
    cur->Box_lt     = '+';
    cur->Box_rt     = '+';
    cur->Box_ct     = '+';
*/

#if defined(A_ALTCHARSET) && !defined(ultrix) && !defined(__osf__)
    /** use ALTCHARSET if termcap or terminfo knows how **/
    if (cur->canUseACS) {

         char *term, *chars;
         char termname[20];
         int count=0;

         strcpy(termname, termtype);

         /** VT100 terminals and above support alternate character set **/
         if (strncasecmp(termname, "vt1", 3) == 0 ||
             strncasecmp(termname, "vt2", 3) == 0 ||
             strncasecmp(termname, "vt3", 3) == 0)
              strcpy(termname, "vtxxx");  /* name in CURgraphicChars[] */

         while ((term = CURgraphicChars[count++]) != NULL) {
              chars = CURgraphicChars[count++];
              if (strcmp(termname, term) == 0) {
#ifdef ACS_BSSB
                   cur->Box_hline  = ACS_HLINE;
                   cur->Box_vline  = ACS_VLINE;
                   cur->Box_ul     = ACS_BSSB;
                   cur->Box_ur     = ACS_BBSS;
                   cur->Box_ll     = ACS_SSBB;
                   cur->Box_lr     = ACS_SBBS;
                   cur->Box_tt     = ACS_TTEE;
                   cur->Box_bt     = ACS_BTEE;
                   cur->Box_lt     = ACS_LTEE;
                   cur->Box_rt     = ACS_RTEE;
                   cur->Box_ct     = ACS_PLUS;
#else /* ACS_BSSB */
                   cur->Box_hline  = A_ALTCHARSET|chars[0];
                   cur->Box_vline  = A_ALTCHARSET|chars[1];
                   cur->Box_ul     = A_ALTCHARSET|chars[2];
                   cur->Box_ur     = A_ALTCHARSET|chars[3];
                   cur->Box_ll     = A_ALTCHARSET|chars[4];
                   cur->Box_lr     = A_ALTCHARSET|chars[5];
                   cur->Box_tt     = A_ALTCHARSET|chars[6];
                   cur->Box_bt     = A_ALTCHARSET|chars[7];
                   cur->Box_lt     = A_ALTCHARSET|chars[8];
                   cur->Box_rt     = A_ALTCHARSET|chars[9];
                   cur->Box_ct     = A_ALTCHARSET|chars[10];
#endif /* ACS_BSSB */

              }
         }
    }
#endif
}



/*
* Given a properly "CURnew" cursesobj, initialize the screen..
*/

void
CURenter(CursesObj *cur)
{
    /* for safety */
    if (cur->inCurses == TRUE)
         return;

#ifdef VMS
    (void)setterm_pas();
    CURsetScreen(cur,initscr());
#else
    tputs(CURgetCLS(cur),1,CURoutchar);
    fflush(stdout);
#endif

    cur->inCurses = TRUE;

    CURwenter(cur,stdscr);

#ifdef SIGWINCH
    if (cur->sigwinch != SIG_ERR)
         signal(SIGWINCH, cur->sigwinch);
#endif
#ifdef SIGTSTP
    if (cur->sigtstp != SIG_ERR)
         signal(SIGTSTP, cur->sigtstp);
#endif
}

/*
* Set up processing for the window (especially for system V curses!
*/

void
CURwenter(CursesObj *cur, WINDOW *win)
{
    cbreak();
    noecho();
    nonl();
#ifdef SYSVCURSES
    intrflush(win, FALSE);
    nodelay(win, FALSE);
#ifndef ultrix                  /** Causes wgetch to dump core in ultrix **/
#ifndef _SEQUENT_
    keypad(win, TRUE);
#endif
#endif
#endif
}
/*
* Exit curses system.
*/

void
CURexit(CursesObj *cur)
{

    if (!cur->inCurses)
       return;
    cur->inCurses = FALSE;
    echo();
    nl();
    endwin();

#ifdef SYSVCURSES
#ifndef  _SEQUENT_
    keypad(stdscr, FALSE);
#endif
#endif

#ifdef VMS
    (void)resetterm();
#endif

#ifdef SIGTSTP
    tputs(CURgetCLS(cur),1,CURoutchar);
    fflush(stdout);

    cur->sigtstp = signal(SIGTSTP, SIG_DFL);
#endif

#ifdef SIGWINCH
    cur->sigwinch = signal(SIGWINCH, SIG_DFL);
#endif

}



/*
* send a character to stdout, not really curses, but we do use it...
*/

int
CURoutchar(int c)
{
    /** output the given character.  From tputs... **/
    /** Note: this CANNOT be a macro!              **/

    putc(c, stdout);
    return(c);
}

/*
* Rightline, uses curses routines to center a line.
*/
void CURrightline(CursesObj *cur, char *theline, int yval)
{
    mvaddstr(yval, (COLS - strlen(theline) -1), theline);
}




/*
* Centerline, uses curses routines to center a line.
*/
void CURcenterline(CursesObj *cur, WINDOW *win, char *theline, int yval, int windowidth, boolean bright)
{
    char TrimmedLine[256];

    strcpy(TrimmedLine, theline);

    /** Make sure theline fits in the window **/
    if (strlen(theline) > windowidth) {
         TrimmedLine[windowidth-3] = '.';
         TrimmedLine[windowidth-2] = '.';
         TrimmedLine[windowidth-1] = '.';
         TrimmedLine[windowidth] = '\0';
    }

    wmove(win, yval, (windowidth - strlen(TrimmedLine))/2);

    if (bright)
         wstandout(win);

    waddstr(win, TrimmedLine);

    if (bright)
         wstandend(win);
}


/*
* CURwgetstr is a replacement of getstr that allows editing of the string
* if the user types control codes we don't recognize, it's returned instead
* We assume that the incoming string is shorter than the max..
*/


int
CURwgetstr(CursesObj *cur, WINDOW *win, char *inputline, int maxlength, boolean hidden)
{
    int pointer = 0;
    int curpointer = 0;
    int ch;
    int y,x;

    wstandout(win);

    /*** Check to see if there's something in the inputline already ***/
    while (inputline[pointer] != '\0') {
         if (hidden)
              waddch(win, '*');
         else
              waddch(win, inputline[pointer]);
         pointer ++;
         curpointer ++;
    }

    wrefresh(win);

    for (;;) {
         ch = CURwgetch(cur,win);

         switch (ch) {

         case '\n':
#ifdef CONTROLX
         case '\030':
#endif
              inputline[pointer] = '\0';
              wstandend(win);
              return(ch);
              break;

         case '\b':  /*** backspace and delete ***/
              if (curpointer > 0) {
                   char *cp;

                   getyx(win, y, x);
                   wmove(win, y, x-1);

                   /* Update the string */

                   for (cp = inputline + curpointer - 1; *cp != '\0'; cp++) {
                        *cp = *(cp + sizeof(char));
                        if (*cp != '\0') {
                             if (hidden)
                                  waddch(win, '*');
                             else
                                  waddch(win, *cp);
                        }
                   }
                   *cp = '\0';
                   waddch(win, ' ');

                   pointer--;
                   curpointer--;

                   wmove(win, y, x-1);
                   wrefresh(win);
              }
              break;

         case '\001':  /*** ^A go to beginning of line ***/
              getyx(win, y, x);
              wmove(win, y, x-curpointer);
              curpointer = 0;
              wrefresh(win);
              break;

         case '\005':  /*** ^E go to end of line ***/
              getyx(win, y, x);
              wmove(win, y, x+pointer-curpointer);
              curpointer = pointer;
              wrefresh(win);
              break;

         case '\007':  /*** ^G cancel... ***/
              wstandend(win);
              return(-1);
              break;

         case '\013':  /*** ^K delete to end of line ***/
              getyx(win, y, x);

              while (curpointer < pointer) {
                   waddch(win, ' ');
                   pointer--;
              }
              inputline[pointer] = '\0';
              wmove(win, y, x);
              wrefresh(win);
              break;

         case '\025':  /*** ^U erase whole line ***/
              getyx(win, y, x);
              wmove(win, y, x-curpointer);

              while (pointer > 0) {
                   waddch(win, ' ');
                   pointer--;
              }
              inputline[pointer] = '\0';
              wmove(win, y, x-curpointer);
              curpointer = 0;
              wrefresh(win);
              break;

         case KEY_LEFT:
              if (curpointer > 0) {
                   curpointer--;
                   getyx(win, y, x);
                   wmove(win, y, x-1);
                   wrefresh(win);
              }
              break;

         case KEY_RIGHT:
              if (curpointer < pointer) {
                   curpointer++;
                   getyx(win, y, x);
                   wmove(win, y, x+1);
                   wrefresh(win);
              }
              break;

         default:
              if (ch < 256 && isprint(ch) && curpointer >= maxlength) {
                   /* Return at the end for word wrapping */
                   if (wordwrap) {
                        /*inputline[curpointer] = ch;*/
                        inputline[curpointer] = '\0';
                        wstandend(win);
                        wrefresh(win);
                        return(ch);
                   } else
                        CURBeep(cur);
              }

              else if (ch < 256 && isprint(ch)) {

                   inputline[curpointer++] = ch;

                   if (curpointer > pointer) {
                        pointer = curpointer;
                        inputline[curpointer+1] = '\0';
                   }
                   if (hidden)
                        waddch(win, '*');
                   else
                        waddch(win, ch);
                   wrefresh(win);
              }
              else {
                   wstandend(win);
                   return(ch);
              }
         } /* switch */
    } /* for */
}


/*
* This stuff is stolen and modified from hytelnet  Thanks Earl!
*/

int
CURwgetch(CursesObj *cur, WINDOW *window)
{
    int a, b, c;

    if (ControlCpressed) {
         return ('\007');  /** Control-C cancels us **/
    }

    if (signal(SIGINT, controlcJmp) == SIG_ERR)
         perror("signal died:\n"), exit(-1);

    if (setjmp(Jmpenv)) {
         /* Note controlcJmp will reprime controlc and set flag*/
         Debug("interruptable_wgetch triggered\r\n",NULL);
         ControlCpressed = FALSE;      /* Deal with return code, not flag*/
         return('\007');
    }


    while (1) {

         c = wgetch(window);
         reprimeControlc();

         switch (c) {

         case -1:  /* error usually means connection was closed.. */
              CleanupandExit(-1);
              break;

         case 12:  /* ^L */
         case 18:  /* ^R */
         case 23:  /* ^W */
              /* redraw the screen */
#ifdef VMS
              clearok(curscr, TRUE);
              wrefresh(window);
#else
              wrefresh(curscr);
#endif
              /* don't return yet, get another keystroke */
              break;

         case 27:  /* handle escape sequence */
              b = wgetch(window);
              if (b == '[' || b == 'O')
                   a = wgetch(window);
              else
                   a = b;

              switch (a) {
              case 'A': c = KEY_UP; break;
              case 'B': c = KEY_DOWN; break;
              case 'C': c = KEY_RIGHT; break;
              case 'D': c = KEY_LEFT; break;

              case 'M':                   /* vt100 enter */
                   if (b == 'O')
                       c = KEY_ENTER;
                   break;

              case 'Q':                   /* vt100 Help */
                   if (b == 'O')
                       c = KEY_HELP;
                   break;

              case '2':
                   if (b == '[')
                       b = wgetch(window);
                   switch(b) {
                   case '8':             /* vt200 Help */
                       if (wgetch(window) == '~')
                           c = KEY_HELP;
                       break;
                   case '9':             /* vt200 Do */
                       if (wgetch(window) == '~')
                           c = KEY_ENTER;
                       break;
                   }

              case '5':                       /* vt 200 prev. screen */
                   if (b == '[' && wgetch(window) == '~')
                       c = KEY_PPAGE;
                   break;
              case '6':                       /* vt 200 next screen */
                   if (b == '[' && wgetch(window) == '~')
                       c = KEY_NPAGE;
                   break;
              }
              /* fall through to default to reassign certain keys */

         default:

         /* The many forms of the return key... */
         if ((c == KEY_ENTER)|| (c=='\r'))
              c = '\n'; /** SYSV curses Gack! **/

         /* The many forms of backspace */
         if (c == '\010' || c == '\177' || c == KEY_BACKSPACE)
              return('\b');

         return(c);
         break;

         }  /* switch(c) */
    }  /* while(1) */
}

int
CURgetch(CursesObj *cur)
{
  return(CURwgetch(cur, stdscr));
}

/*
* Resets the screen when a size change has happened
*/

void
CURresize(CursesObj *cur)
{
    if (cur->inCurses) {
         CURexit(cur);
         CURenter(cur);
    }
}

/*
* Get one option displays a message, and gets a response
*
* If the Response has something in it, it is displayed and editable
*
* If the user wants to abort, GetOneOption returns a -1, otherwise it
* returns a 0
*/

int
CURGetOneOption(CursesObj *cur, char *Title, char *OptionName, char *Response)
{
    int i;
    Requestitem *items[2], item;

    items[0] = &item;
    items[1] = NULL;

    item.prompt = OptionName;
    item.stowage = Response;
    item.thing = CUR_PROMPT;

    i = CURrequester(cur, Title, items);

    refresh();
    return(i);
}

/*
* This is the old version of GetOneOption, for those times when the
* garsh darn terminal is just too gadblam slow :-)
*/
int
CUROldGetOneOption(CursesObj *cur, char *OptionName, char *Response)
{
    int i;

    mvaddstr(LINES-1, 0, OptionName);
    standout();
    addstr("    ");
    standend();
    clrtoeol();
    move(LINES-1, strlen(OptionName));

    refresh();
    i = CURwgetstr(cur, stdscr, Response, 4, FALSE);

    return(i);
}



/*
* Fills in the Response with either a lowercase 'y' or 'n'
*/

void
CURgetYesorNo(CursesObj *cur, char *OptionName, char *Response)
{
    int c;
    int posx, posy;

    mvaddstr(LINES-1, 0, OptionName);
    clrtoeol();
    getyx(cur->Screen, posy, posx);
    addch(' ');

    if (*Response == 'y')
         mvaddstr(posy, posx+1, "y");
    else {
         *Response = 'n';
         mvaddstr(posy, posx+1, "n ");
    }
    move(posy, posx+1);

    refresh();

    while (1) {
         c = CURgetch(cur);

         if (c == 'y') {
              mvaddstr(posy, posx+1, Gtxt("Yes",180));
              move(posy, posx+1);
              refresh();
              *Response = 'y';
              *(Response +1) = '\0';
              return;
         }
         else if (c == 'n') {
              mvaddstr(posy, posx+1, Gtxt("No ",111));
              move(posy, posx+1);
              refresh();
              *Response = 'n';
              *(Response +1) = '\0';
              return;
         }

         else if ((c == '\n')||(c=='\r')) {
              return;
         }
#ifdef VMS
         else if ( c == '\032' ) {     /* control-Z */
               return;
         }
#endif
         else {
              Debugmsg("CURgetYesorNo beep\r\n")
              CURBeep(cur);
         }
    }
}

void
CURBeep(CursesObj *cur)
{
       Debugmsg("CURBeep\r\n")
#ifdef SYSVCURSES
    beep();
#else
/*   CURcenterline(cur, stdscr, CURgetBell(cur), 1, COLS, FALSE); */
    tputs(CURgetBell(cur), 1, CURoutchar);
    fflush(stdout);
#endif
}


void
CURbox(CursesObj *cur, WINDOW *win, int height, int width)
{
    int i;

    wmove(win, 0, 0);
    waddch(win, CURgetBox_ul(cur));
    for (i=0; i<width-2; i++)
         waddch(win, CURgetBox_hline(cur));
    waddch(win, CURgetBox_ur(cur));

    for (i=1; i<height-1; i++) {
         wmove(win, i, 0);
         waddch(win, CURgetBox_vline(cur));
         wmove(win, i, width-1);
         waddch(win, CURgetBox_vline(cur));
    }

    wmove(win, height-1, 0);
    waddch(win, CURgetBox_ll(cur));
    for (i=0; i<width-2; i++)
         waddch(win, CURgetBox_hline(cur));
    waddch(win, CURgetBox_lr(cur));
}


void
CURbutton(CursesObj *cur, WINDOW *win, char *Label, boolean bright)
{
/*     wboldout(win);*/

    if (bright)
         wstandout(win);

    waddstr(win, "[");
    waddstr(win, Label);
    waddstr(win, "]");

    if (bright)
         wstandend(win);

/*     wboldend(win);*/
}



int
CURDialog(CursesObj *cur, char *Wintitle, char **Message)
{
    int i;
    int messheight=0;
    int messlength=0;
    WINDOW *tempwin;
    int winwidth;

    while (Message[messheight] != NULL) {
         if (strlen(Message[messheight]) > messlength)
             messlength = strlen(Message[messheight]);
         messheight++;
    }

    winwidth = max(31, messlength+6);
    winwidth = max(winwidth, strlen(Wintitle)+6);
    winwidth = min(winwidth, COLS-2);

    tempwin = newwin(5+messheight, winwidth, (LINES-(5+messheight))/2,
                       (COLS-winwidth)/2);

    if (tempwin == (WINDOW *)0)
         return(-1);

    CURwenter(cur,tempwin);
    CURbox(cur, tempwin, 5+messheight, winwidth);

    /** Add the message **/
    for (i=0; i<messheight; i++) {
         int len = strlen(Message[i]), j;

         wmove(tempwin, 2+i,(winwidth - messlength)/2);

         if (len > messlength) {
              for (j=0; j < messlength; j++)
                   waddch(tempwin, Message[i][j]);
         }
         else
              waddstr(tempwin, Message[i]);

    }



    /** Add the window title, centered **/
    if (Wintitle != NULL)
         CURcenterline(cur, tempwin, Wintitle, 0, winwidth, TRUE);

    /** Add the keyboard labels **/
    wmove(tempwin, 3+messheight, winwidth - 28);
    CURbutton(cur, tempwin, Gtxt(DIALOGCANCELSTR,211), FALSE);
    waddch(tempwin, ' ');
    CURbutton(cur, tempwin, Gtxt("OK: Enter",191), FALSE);

    wrefresh(tempwin);

    switch(CURwgetch(cur, tempwin)) {
    case -1:
    case '\007':
         delwin(tempwin);
         return(-1);
    default:
         delwin(tempwin);
         return(0);
    }
}

int
CURRequest(CursesObj *cur, char *Wintitle, char **Prompts, char **Stowages)
{
    Requestitem **items;
    int i, choice, numprompts = 0;

    while (Prompts[numprompts] != NULL)
         numprompts++;

    items = (Requestitem **) malloc(sizeof(Requestitem *) * (numprompts+1));

    for (i = 0; i < numprompts; i++) {
         items[i] = (Requestitem*) malloc(sizeof(Requestitem));
         items[i]->prompt  = Prompts[i];
         items[i]->stowage = Stowages[i];

         if (Stowages[i] == NULL)
              items[i]->thing = CUR_LABEL;
         else
              items[i]->thing = CUR_PROMPT;
    }
    items[i] = NULL;

    choice = CURrequester(cur, Wintitle, items);

    /** Free memory **/
    for (i=0; i <= numprompts; i++)
         if (items[i])
              free(items[i]);
    free(items);

    return(choice);
}

/*
* CURrequester takes an array of requestitems and does the right
* things with it..
*/

int
CURrequester(CursesObj *cur, char *Wintitle, Requestitem **items)
{
    int         BottomField;
    int         ch;
    Requestitem *curitem;
    int         currentfield = 0;
    int         FieldsPerPage;
    boolean     Found;
    int         i,j,k;
    int         maxlength;
    int         maxpromptwidth = 0;
    int         numprompts = 0;
    WINDOW      *tempwin;
    int         TopField;
    int         WinLines;
    int         WinWidth = COLS - 1;
    static char **RequestHelp = NULL;


    if (RequestHelp == NULL) {
         RequestHelp = (char**) malloc(sizeof(char*) * 8);
         RequestHelp[0] = Gtxt(HELPCANCEL,190);
         RequestHelp[1] = Gtxt("Tab, ^N : Move to next field",192);
         RequestHelp[2] = Gtxt("^P      : Move to previous field",193);
         RequestHelp[3] = Gtxt("^F      : Display next page",194);
         RequestHelp[4] = Gtxt("^B      : Display previous page",195);
         RequestHelp[5] = Gtxt("^-      : Help (^/ or ^7 may work)",196);
         RequestHelp[6] = Gtxt(CXPROMPT1,197);
         RequestHelp[7] = NULL;
    }

    /*** Find the number of prompts... and the max width ***/
    while ((curitem = items[numprompts++]) != NULL) {

         /*** Skip non editable prompts ***/
         if (curitem->thing != CUR_LABEL) {
              if (curitem->prompt && strlen(curitem->prompt) > maxpromptwidth)
                   maxpromptwidth = strlen(curitem->prompt);
         } else {
              if (currentfield == numprompts-1)
                   currentfield++;
         }
    }

    numprompts--;
    if (numprompts == 0)
         return(-1);

    TopField = 0;
    FieldsPerPage = min(numprompts, LINES - 5);
    BottomField = min(TopField + FieldsPerPage, numprompts) - 1;

    /** If only one prompt, offer an entire line for input **/
    if (numprompts == 1 && items[0]->thing == CUR_PROMPT) {
         WinLines = 8;
         maxlength = WinWidth - 4;
    }
    else {
         WinLines = FieldsPerPage + 5;
         maxlength = WinWidth - maxpromptwidth - 6;
    }

    /*** Create the new window ***/
    tempwin = newwin(WinLines, WinWidth, (LINES - WinLines) / 2, 0);
    CURwenter(cur, tempwin);
    CURbox(cur, tempwin, WinLines, WinWidth);

    /*** Add the window title, centered ***/
    if (Wintitle != NULL)
         CURcenterline(cur, tempwin, Wintitle, 0, WinWidth, TRUE);

    /*** Display the form and allow the user to update it ***/
    while (1) {
         /*** Add the labels ***/
         wmove(tempwin, WinLines - 2, 2);
         waddstr(tempwin, Gtxt(FORMCANCEL,198));
         if (items[currentfield]->thing == CUR_CHOICE)
              waddstr(tempwin, Gtxt("  [Cycle Values: Space]  [List Values: l]",199));
         else {
#ifdef CONTROLX
              waddstr(tempwin, " [Accept: ^X] ");
#else
              waddstr(tempwin, " [Accept: Enter] ");
#endif
              waddstr(tempwin, " [Next field: TAB] ");
              waddstr(tempwin, "           ");
         }


         /** Add the prompts and typing area **/
         if (numprompts == 1 && items[0]->thing == CUR_PROMPT) {
              wmove(tempwin, 2, 2);
              waddstr(tempwin, items[0]->prompt);

              /** Trim the stowage if necessary **/
              if ((j = strlen(items[0]->stowage)) > maxlength)
                   while (j > maxlength)
                        (items[0])->stowage[--j] = '\0';

              /** Add the stowage if it exists and its black space **/
              wmove(tempwin, 4, 2);
              wstandout(tempwin);
              waddstr(tempwin, items[0]->stowage);
              for (j=strlen(items[0]->stowage)+2; j < WinWidth-2; j++)
                   waddch(tempwin, ' ');
              wstandend(tempwin);
         }
         else {
              i = 0;
              for (k = TopField; k <= BottomField ; k++) {
                   wmove(tempwin, 2 + i, 2);
                   if (items[k]->prompt) {

                        waddstr(tempwin, items[k]->prompt);

                        for (j = WinWidth-strlen(items[k]->prompt)-4; j>0;j--)
                             waddch(tempwin, ' ');
                   }

                   switch (items[k]->thing) {

                   case CUR_LABEL:
                        break;

                   case CUR_CHOICE:
                        /** Add the default **/
                        wmove(tempwin, 2 + i, maxpromptwidth + 4);
                        waddstr(tempwin,
                             items[k]->choices[items[k]->chooseitem]);
                        break;

                   case CUR_ASKL:
                        wordwrap = TRUE;
                        wmove(tempwin, 2 + i, 2);
                        wstandout(tempwin);
                        waddstr(tempwin, items[k]->stowage);
                        for (j = WinWidth - strlen(items[k]->stowage) - 4
                             ; j > 0; j--)
                             waddch(tempwin, ' ');

                        wstandend(tempwin);
                        break;

                   default:
                        /** Add the black space for the stowage, **/
                        /** and the stowage, if it exists        **/
                        wordwrap = FALSE;
                        wmove(tempwin, 2 + i, maxpromptwidth + 4);
                        wstandout(tempwin);

                        if (items[k]->thing == CUR_PASSWD)
                             for (j = strlen(items[k]->stowage); j > 0; j--)
                                  waddch(tempwin, '*');
                        else if (items[k]->stowage)
                             waddstr(tempwin, items[k]->stowage);



                        for (j = WinWidth -
                             (items[k]->stowage ? strlen(items[k]->stowage):0)
                             - maxpromptwidth - 6; j > 0; j--)
                             waddch(tempwin, ' ');

                        wstandend(tempwin);
                   }
                   i++;
              }
         }

         if (numprompts == 1)
              wmove(tempwin, 4, 2);
         else
              wmove(tempwin, 2 + currentfield - TopField, maxpromptwidth + 4);

         if (items[currentfield]->thing == CUR_ASKL)
              wmove(tempwin, 2 + currentfield - TopField, 2);

         wrefresh(tempwin);

         if (items[currentfield]->thing == CUR_CHOICE) {
              int choice = items[currentfield]->chooseitem;
              boolean done = FALSE;

              wmove(tempwin, 2 + currentfield - TopField, maxpromptwidth +
                   strlen(items[currentfield]->choices[choice]) + 4);

              wrefresh(tempwin);

              while (!done) {
                   ch = CURwgetch(cur, tempwin);

                   wmove(tempwin, 2 + currentfield - TopField,
                        maxpromptwidth + 4);
                   for (i=strlen(items[currentfield]->choices[choice]);
                        i>0; i--)
                        waddch(tempwin, ' ');

                   if (ch == ' ') {

                        choice++;

                   }
                   else if (ch == 'l') {
                        int tempx;
                        tempx = CURChoice(cur, items[currentfield]->prompt,
                                          items[currentfield]->choices,
                                          Gtxt("Select an item",200),
                                          items[currentfield]->chooseitem);
                        touchwin(tempwin);
                        if (tempx != -1)
                             choice = tempx;
                   }

                   else
                        done = TRUE;

                   if (items[currentfield]->choices[choice] == NULL)
                        choice = 0;

                   wmove(tempwin, 2 + currentfield - TopField,
                        maxpromptwidth + 4);

                   waddstr(tempwin, items[currentfield]->choices[choice]);
                   items[currentfield]->chooseitem = choice;
                   wrefresh(tempwin);
              }
         }
         else {
              int zemax = maxlength;

              if (items[currentfield]->thing == CUR_ASKL)
                   zemax = COLS - 6;

              ch = CURwgetstr(cur,tempwin,items[currentfield]->stowage,
                   zemax, items[currentfield]->thing == CUR_PASSWD);
         }

#ifdef CONTROLX
         if (ch == '\n') {
              if (numprompts > 1) {
                   ch = '\t';     /* Treat as tab, next field */
              } else {
                   ch = '\030';      /* Treat as control-X, accept */
              }
         }
#endif


         switch (ch) {
         case '\016':
         case '\t':
              /*** Move to next non-label field (tab) ***/
              do {
                   currentfield++;
                   if (currentfield > numprompts - 1) {
                        TopField = 0;
                        BottomField = min(TopField + FieldsPerPage,
                                          numprompts) - 1;
                        currentfield = TopField;
                   }
                   else if (currentfield > BottomField) {
                        TopField++;
                        BottomField++;
                   }
              } while (items[currentfield]->thing == CUR_LABEL);
              break;

         case '\020':
              /*** Move to the previous non-label field (back tab) ***/
              do {
                   currentfield--;
                   if (currentfield < 0) {
                        currentfield = numprompts - 1;
                        TopField = max(0, numprompts - FieldsPerPage);
                        BottomField = currentfield;
                   }
                   else if (currentfield < TopField) {
                        TopField--;
                        BottomField--;
                   }
              } while (items[currentfield]->thing == CUR_LABEL);
              break;

         case KEY_DOWN:
              /*
               * Move to the next non-label field on the current
               * screen.  If there is no such field, scroll down
               * one line.  If a non-label field is found, move
               * to it, otherwise don't move. (Line down)
               */
              if (currentfield == BottomField) {
                   TopField++;
                   BottomField++;
                   if (BottomField > numprompts - 1) {
                       TopField = 0;
                       BottomField = min(TopField + FieldsPerPage,
                                         numprompts) - 1;
                       currentfield = TopField;
                       while (items[currentfield]->thing == CUR_LABEL)
                            currentfield++;
                   }
                   else if (items[BottomField]->thing != CUR_LABEL)
                       currentfield = BottomField;
              }
              else {
                   Found = FALSE;
                   for (i = currentfield + 1; i <= BottomField; i++)
                        if (items[i]->thing != CUR_LABEL) {
                             currentfield = i;
                             Found = TRUE;
                             break;
                        }
                   if (!Found) {
                        TopField++;
                        BottomField++;
                        if (BottomField > numprompts - 1) {
                             TopField = 0;
                             BottomField = min(TopField + FieldsPerPage,
                                               numprompts) - 1;
                             currentfield = TopField;
                             while (items[currentfield]->thing == CUR_LABEL)
                                  currentfield++;
                        }
                        else if (items[BottomField]->thing != CUR_LABEL)
                             currentfield = BottomField;
                   }
              }
              break;

         case KEY_UP:
              /*
               * Move to the previous non-label field on the current
               * screen.  If there is no such field, scroll up
               * one line.  If a non-label field is found, move
               * to it, otherwise don't move. (Line up)
               */
              if (currentfield == TopField) {
                   TopField--;
                   BottomField--;
                   if (TopField < 0) {
                       TopField = max(0, numprompts - FieldsPerPage);
                       BottomField = numprompts - 1;
                       currentfield = BottomField;
                       while (items[currentfield]->thing == CUR_LABEL)
                            currentfield--;
                   }
                   else if (items[TopField]->thing != CUR_LABEL)
                       currentfield = TopField;
              }
              else {
                   Found = FALSE;
                   for (i = currentfield - 1; i >= TopField; i--)
                        if (items[i]->thing != CUR_LABEL) {
                             currentfield = i;
                             Found = TRUE;
                             break;
                        }
                   if (!Found) {
                        TopField--;
                        BottomField--;
                        if (TopField < 0) {
                             TopField = max(0, numprompts - FieldsPerPage);
                             BottomField = numprompts - 1;
                             currentfield = BottomField;
                             while (items[currentfield]->thing == CUR_LABEL)
                                  currentfield--;
                        }
                        else if (items[TopField]->thing != CUR_LABEL)
                             currentfield = TopField;
                   }
              }
              break;

         case '\006':
         case KEY_NPAGE:
              /*** Move down a page ***/
              if (BottomField == numprompts - 1) {
                   currentfield = BottomField;
                   while (items[currentfield]->thing == CUR_LABEL)
                        currentfield--;
              }
              else {
                   BottomField = min(BottomField + FieldsPerPage,
                                     numprompts) - 1;
                   TopField = max(0, BottomField - FieldsPerPage + 1);
                   if (currentfield < TopField) {
                        currentfield = TopField;
                        while (items[currentfield]->thing == CUR_LABEL)
                             currentfield++;
                   }
              }
              break;

         case '\002':
         case KEY_PPAGE:
              /*** Move up a page ***/
              if (TopField == 0) {
                   currentfield = 0;
                   while (items[currentfield]->thing == CUR_LABEL)
                        currentfield++;
              }
              else {
                   TopField = max(0, TopField - FieldsPerPage + 1);
                   BottomField = min(TopField + FieldsPerPage, numprompts) - 1;
                   if (currentfield > BottomField) {
                        currentfield = BottomField;
                        while (items[currentfield]->thing == CUR_LABEL)
                             currentfield--;
                   }
              }
              break;

         case '\037':
         case KEY_HELP:
              CURDialog(cur, "Form Help", RequestHelp);
              touchwin(tempwin);
              break;

         case '\007':
         case -1:
              /*** Cancel ***/
              delwin(tempwin);
              return(-1);

#ifdef CONTROLX
         case '\030':
#else
         case '\n':
#endif
              delwin(tempwin);
              return(0);

         default:
              if (wordwrap) {
                   char *p;
                   if ( currentfield == numprompts-1){
                        CURBeep(cur);
                        continue;
                   }
                   if (items[currentfield+1]->thing != CUR_ASKL ) {
                        CURBeep(cur);
                        continue;
                   }

                   /* we must go and find the last space in stowage and move
                      to next line */
                   p=strrchr(items[currentfield]->stowage,' ');

                   /* copy rest of string to next line */
                   if ( p != NULL ) {
                        char addit[2];
                        *addit = ch;
                        *(addit+1) = '\0';
                        strcat(p+1, addit);

                        strcpy(items[currentfield+1]->stowage,p+1);

                        /* now blank out those characters */
                        *p='\0';
                        wmove(tempwin, 2 + currentfield - TopField, 2);
                        wstandout(tempwin);
                        waddstr(tempwin, items[currentfield]->stowage);
                        for (j=strlen(items[currentfield]->stowage)+
                             maxpromptwidth+4; j < COLS-6; j++)
                             waddch(tempwin, ' ');
                        wstandend(tempwin);
                   }
                   currentfield++;     /* go to next ASKL field */

                   if (currentfield > BottomField) {
                        TopField++;
                        BottomField++;

                   }

              }
         }
    }
}


/*
* CURChoice takes a bunch of titles, throws them on the screen,
* and asks the user to choose one.
*
* Returns the number chosen, or -1 if the user cancels.
*
* Limitation: The number of choices must be less than 100.
*/


int
CURChoice(CursesObj *cur, char *WinTitle, char **Choices, char *Prompt, int DefaultChoice)
{
    int    BottomChoice;
    int    ch;
    int    ChoicesPerPage;
    int    CurrentChoice = 0;
    int    CurrentLine;
    int    CurrentPage;
    int    i;
    char   InBuf[3];
    int    j;
    int    k;
    int    MaxChoiceWidth = 0;
    int    NumChoices = 0;
    int    NumPages;
    WINDOW *TempWin;
    int    TopChoice;
    int    WinLines;
    int    WinWidth;
    static char ** ChoiceHelp = NULL;

    if (ChoiceHelp == NULL) {
         ChoiceHelp = (char**) malloc(sizeof(char*) * 10);
         ChoiceHelp[0] = Gtxt(HELP2CANCEL,202);
         ChoiceHelp[1] = Gtxt("Down, ^N  : Move to next choice",203);
         ChoiceHelp[2] = Gtxt("Up, ^P    : Move to previous choice",204);
         ChoiceHelp[3] = Gtxt("Space, ^F : Display next page",205);
         ChoiceHelp[4] = Gtxt("b, ^B     : Display previous page",206);
         ChoiceHelp[5] = Gtxt("^         : Display first page",207);
         ChoiceHelp[6] = Gtxt("$         : Display last page",208);
         ChoiceHelp[7] = Gtxt("0-9       : Select a specific choice",209);
         ChoiceHelp[8] = Gtxt("Enter     : Select current choice",210);
         ChoiceHelp[9] = NULL;
    }


    /*** Determine number of choices and verify it's within limits ***/
    while (Choices[NumChoices] != NULL)
         NumChoices++;
    if (NumChoices == 0)
         return(-1);
    if (NumChoices > 99) {
         CursesErrorMsg("More than 99 choices!  Please gripe.");
         return(-1);
    }

    ChoicesPerPage = min(NumChoices, LINES - 6);
    NumPages = NumChoices / ChoicesPerPage + 1;
    WinLines = ChoicesPerPage + 6;

    CurrentChoice = (DefaultChoice > -1) ? DefaultChoice : 0;
    CurrentPage = CurrentChoice / ChoicesPerPage;
    BottomChoice = min(CurrentPage * ChoicesPerPage + ChoicesPerPage,
                       NumChoices) - 1;
    TopChoice = max(0, BottomChoice - ChoicesPerPage + 1);
    CurrentLine = CurrentChoice - TopChoice + 2;

    /*** Determine dialog box width ***/
    for (i = 0; i < NumChoices; i++)
         if (i == DefaultChoice)
              MaxChoiceWidth = max(strlen(Choices[i]) + 10, MaxChoiceWidth);
         else
              MaxChoiceWidth = max(strlen(Choices[i]), MaxChoiceWidth);

    WinWidth = max(strlen(Prompt) + 17, MaxChoiceWidth + 15);
    WinWidth = max(strlen(WinTitle) + 8, WinWidth);
    WinWidth = max(29, WinWidth);
    WinWidth = min(COLS - 2, WinWidth);

    /*** Create the new window ***/
    TempWin  = newwin(WinLines, WinWidth,
                      (LINES - WinLines) / 2, (COLS - WinWidth) / 2);
    CURwenter(cur, TempWin);
    CURbox(cur, TempWin, WinLines, WinWidth);

    /*** Add the window title, centered ***/
    if (WinTitle != NULL)
         CURcenterline(cur, TempWin, WinTitle, 0, WinWidth, TRUE);

    /*** Add the prompt ***/
    wmove(TempWin, ChoicesPerPage + 3, 3);
    wprintw(TempWin, "%s (1-%d): ", Prompt, NumChoices);

    /*** Add the labels ***/
    wmove(TempWin, ChoicesPerPage + 4, 3);
    CURbutton(cur, TempWin, Gtxt("Help: ?",96), FALSE);
    waddstr(TempWin, "  ");
    CURbutton(cur, TempWin, Gtxt(DIALOGCANCELSTR,211), FALSE);

    /*** Display the dialog box and allow the user to make a choice ***/
    while (1) {
         /*** Add the current page of choices ***/
         i = 0;
         for (j = TopChoice; j <= BottomChoice; j++) {
              wmove(TempWin, 2 + i, 8);
              for (k = 0; k < WinWidth - 10; k++)
                   waddch(TempWin, ' ');
              wmove(TempWin, 2 + i, 8);
              wprintw(TempWin, "%2d.", j + 1);
              wmove(TempWin, 2 + i, 12);
              wprintw(TempWin, "%s", Choices[j]);
              if (j == DefaultChoice)
                   waddstr(TempWin, Gtxt(" (default)",212));
              i++;
         }

         /*** Add the cursor ***/
         wmove(TempWin, CurrentLine, 3);
         waddstr(TempWin, "-->");

         /*** Update the screen ***/
         wrefresh(TempWin);

         /*** Let the user choose ***/
         ch = CURwgetch(cur, TempWin);
         switch(ch) {

         case '\016':
         case KEY_DOWN:
              /*** Move down a line ***/
              if (CurrentChoice == BottomChoice) {
                   TopChoice++;
                   BottomChoice++;
                   if (BottomChoice > NumChoices - 1) {
                        CurrentChoice = 0;
                        TopChoice = 0;
                        BottomChoice = min(TopChoice + ChoicesPerPage,
                                           NumChoices) - 1;
                   }
                   else
                        CurrentChoice++;
              }
              else
                   CurrentChoice++;
              break;

         case '\020':
         case KEY_UP:
              /*** Move up a line ***/
              if (CurrentChoice == TopChoice) {
                   TopChoice--;
                   BottomChoice--;
                   if (TopChoice < 0) {
                        CurrentChoice = NumChoices - 1;
                        TopChoice = max(0, NumChoices - ChoicesPerPage);
                        BottomChoice = CurrentChoice;
                   }
                   else
                        CurrentChoice--;
              }
              else
                   CurrentChoice--;
              break;

         case '\006':
         case ' ':
         case '+':
         case KEY_NPAGE:
              /*** Move down a page ***/
              if (BottomChoice == NumChoices - 1)
                   CurrentChoice = BottomChoice;
              else {
                   BottomChoice = min(BottomChoice + ChoicesPerPage,
                                      NumChoices) - 1;
                   TopChoice = max(0, BottomChoice - ChoicesPerPage + 1);
                   CurrentChoice = max(TopChoice, CurrentChoice);
              }
              break;

         case '\002':
         case 'b':
         case '-':
         case KEY_PPAGE:
              /*** Move up a page ***/
              if (TopChoice == 0)
                   CurrentChoice = TopChoice;
              else {
                   TopChoice = max(0, TopChoice - ChoicesPerPage + 1);
                   BottomChoice = min(TopChoice + ChoicesPerPage,
                                      NumChoices) - 1;
                   CurrentChoice = min(CurrentChoice, BottomChoice);
              }
              break;

         case '^':
              /*** Move to top ***/
              TopChoice = 0;
              BottomChoice = min(TopChoice + ChoicesPerPage,
                                 NumChoices) - 1;
              CurrentChoice = TopChoice;
              break;

         case '$':
              /*** Move to bottom ***/
              BottomChoice = NumChoices - 1;
              TopChoice = max(0, BottomChoice - ChoicesPerPage + 1);
              CurrentChoice = BottomChoice;
              break;

         case '\037':
         case KEY_HELP:
         case 'h':
         case '?':
              /*** Help ***/
              CURDialog(cur, Gtxt("Choice Dialog Help",213), ChoiceHelp);
              touchwin(TempWin);
              break;

         case '1': case '2': case '3': case '4': case '5':
         case '6': case '7': case '8': case '9': case '0':
              /*** Literal ***/
              InBuf[0] = ch;
              InBuf[1] = '\0';
              InBuf[2] = '\0';

              i = strlen(Prompt) + (NumChoices < 10 ? 11 : 12);
              j = ChoicesPerPage + 3;
              wmove(TempWin, j, i);

              ch = CURwgetstr(cur, TempWin, InBuf, (NumChoices < 10 ? 1 : 2), FALSE);
              k = atoi(InBuf);

              if ((ch == '\n') && (0 < k) && (k <= NumChoices)) {
                   delwin(TempWin);
                   return (k - 1);
              }
              else {
                   wmove(TempWin, j, i);
                   waddstr(TempWin, "  ");
                   if (ch != -1)
                       CURBeep(cur);
                   break;
              }

         case '\007':
              /*** Cancel ***/
              delwin(TempWin);
              return (-1);

         case '\n':
         case '\030':  /* Control X */
              /*** Accept ***/
              delwin(TempWin);
              return (CurrentChoice);

         default:
              CURBeep(cur);
              break;
         }
         wmove(TempWin, CurrentLine, 3);
         waddstr(TempWin, "   ");
         CurrentPage = CurrentChoice / ChoicesPerPage;
         CurrentLine = CurrentChoice - TopChoice + 2;
    }
}

/********************** Cruft for VMS follows ****************************/

#ifdef VMS
#include <descrip.h>
#include <iodef.h>
#include <ssdef.h>
#include <ttdef.h>
#include <tt2def.h>
#include <libclidef.h>
#include <smg$routines.h>
#include <smgdef.h>
#include <smgtrmptr.h>
#include <clidef.h>
#include <syidef.h>
#ifdef signal
#undef signal
#endif
#include <signal.h>
#ifdef system
#undef system
#endif
#include <processes.h>

#ifndef CLI$M_TRUSTED
#define CLI$M_TRUSTED 64 /* May not be in the compiler's clidef.h       */
#endif
#define EFN     0                               /* Event flag           */
static  int     mask = LIB$M_CLI_CTRLY|LIB$M_CLI_CTRLT; /* ^Y and ^T    */
static  int     old_msk;                        /* Saved control mask   */
static  short   trap_flag = FALSE;              /* TRUE if AST is set   */
static $DESCRIPTOR (term_name, "SYS$INPUT:");   /* For channel assigns  */
short term_chan;                                /* The channel          */
static short first = TRUE;              /* Terminal initialization flag */
struct char_buffer_type {               /* Terminal characteristics     */
                         char           class;
                         char           type;
                         unsigned short size;
                         unsigned long  tchars;
                         unsigned long  tchars2;
} oldbuf;
static int in_pos, in_len;                      /* For escape sequence  */
static unsigned char buffer[20];                /*  handling in w_getch */
boolean DidCleanup = FALSE;                     /* Exit handler flag    */
static char VersionVMS[20];                     /* Version of VMS       */


/*
* Define local replacement for wgetch that returns the characters without
* having to set the terminal /pasthru, which screws up control-Y processing.
*/
static int w_getch(win)
    int win;
{
    int status;
    unsigned short iosb[4];

    if (in_pos < in_len)
         return(buffer[in_pos++]);

    status = sys$qiow (0, term_chan, IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
                       &iosb, 0, 0, &buffer, 1, 0, 0, 0, 0);
    if ((status&1) == 1)
         status = iosb[0];
    if (status == SS$_PARTESCAPE) {
         /* escape sequence in progress, fake a successful read */
         status = 1;
    }
    if ((status&1) != 1)
         exit(status);
    in_pos = 1;
    in_len = iosb[1] + iosb[3];
    return (buffer[0]);
}

void
setterm_pas()
{
    int status;
    short iosb[4];

    if(first)
         VMSinit();
    else
         status = lib$disable_ctrl(&mask);

    in_pos = 0; in_len = 0;
}

void
resetterm()
{
    int status;

    status = sys$qiow(0,term_chan,IO$_SETMODE,0,0,0,
                      &oldbuf,12,0,0,0,0);
    status = lib$enable_ctrl(&old_msk);
}


/* VMS doesn't have termcap.  Unfortunately, the code in this */
/* module uses termcap just a little bit (it really shouldn't) */
/* rather than doing everything through curses */

/* The following simulates tputs, but does not support padding */
int
tputs(register char *cp, int affcnt, int (*outc)())
{
    while (*cp)
         outc(*(cp++));
    return(0);
}


/*
*  VMSsignal -- F.Macrides 31-Jul-1993 (modification of my LYNX routine)
*      Sets up AST for both Ctrl-C and Ctrl-Y, with system response to Ctrl-T
*       disabled.  If called with a sig other than SIGINT, it will use the C
*       library's signal(sig, func).
*      VMSsignal(SIGINT, SIG_DFL) is treated as a call to resetterm().
*      Call VMSsignal(SIGINT, SIG_IGN) before system() calls to enable Ctrl-C
*       and Ctrl-Y in the subprocess, and then call VMSsignal(SIG_INT, func)
*       on return from the subprocess.
*      For func's which do not invoke an exit, the func should reassert itself.
*      The VMS C signal() calls do not fully emulate the Unix calls, and
*       VMSsignal() is just a "helper", also not a full emulation.
*/

void
(*VMSsignal (sig, func)) (int)
int sig;
void (*func)();
{
    int status;
    short iosb[4];
    static int SIG_IGN_flag;

    /* pass all signals other than SIGINT to signal() */
    if (sig != SIGINT) {
         return(signal(sig, func));
    }

    /* if func is SIG_DFL, treat it as resetterm() */
    if (func == SIG_DFL) {
         resetterm();
         return(SIG_DFL);
    }

    /* Clear any previous AST */
    if (trap_flag) {
         status = sys$dassgn (term_chan);
         status = lib$enable_ctrl(&old_msk);
         trap_flag = FALSE;
    }

    /* if func is SIG_IGN, leave the TT channel closed and the  */
    /* system response to interrupts enabled for system() calls */
    if (func == SIG_IGN)
         return(SIG_IGN);

    /* if we get to here, we have a SIGINT func, so set the AST */
    if(first)
         VMSinit();
    else {
        status = sys$assign (&term_name, &term_chan, 0, 0);
        status = lib$disable_ctrl(&mask);
    }
    status = sys$qiow (EFN, term_chan,
                       IO$_SETMODE|IO$M_CTRLCAST|IO$M_CTRLYAST,
                       &iosb, 0, 0,
                       func, SIGINT, 0, 0, 0, 0);
    trap_flag = TRUE;
    return(func);
}


/*
*  VMSCURinit, VMSinit, VMSexit -- F.Macrides 12-Aug-1993
*      Save termial characteristics at the time when gopher.c's Initialize()
*       is called.  Make sure they're retored and that cleanup is done if we
*       exit via exit(-1)'s or ACCVIO's.
*/

void
VMSCURinit(cur)
 CursesObj *cur;
{
    int status, i;
    short Type = 0;
    long buf_siz = 20, len;
    unsigned long Addr;
    char ch, Name[20], cl[20], bl[20], so[20], se[20];

    /** Keep DECC from complaining **/
    struct dsc$descriptor_s Name_desc;
    Name_desc.dsc$w_length  = 20;
    Name_desc.dsc$b_class   = DSC$K_CLASS_S;
    Name_desc.dsc$b_dtype   = DSC$K_DTYPE_T;
    Name_desc.dsc$a_pointer = Name;

    /*** Initialize the terminal, if we haven't already ***/
    if(first)
         VMSinit();

    /*** Can't handle "unknown" terminal type ***/
    if (oldbuf.type == 0) {
         CURsetTerm(cur, "unknown");
         return;
    }

    /*** Get the terminal table ready ***/
    Type = (short) oldbuf.type;
    status = smg$init_term_table_by_type (&Type, &Addr, &Name_desc);
    if (!(status&1))
    {
         CURsetTerm(cur, "unknown");
         return;
    }

    /*** Set the terminal name ***/
    i = 0;
    while ((Name[i++] != ' ') && i <= 20) ;
    Name[--i] = '\0';
    if (strlen(Name))
         CURsetTerm(cur, Name);
    else
         CURsetTerm(cur, "VMS");

    /*** Get the clearscreen code ***/
    status = smg$get_term_data(&Addr, &SMG$K_ERASE_WHOLE_DISPLAY,
                               &buf_siz, &len, cl);
    if (status&1) {
         cl[len] = '\0';
         CURsetCLS(cur, cl);
    }
    else
         /*** Assume 7-bit ***/
         CURsetCLS(cur, "\033[2J");

    /*** Set the bell ***/
    CURsetBell(cur, "\007");

    /*** Set the highlight codes ***/
    status = smg$get_term_data (&Addr, &SMG$K_BEGIN_BOLD,
                                &buf_siz, &len, so);
    if (status&1) {
         so[len] = '\0';
         CURsetHighon(cur, so);
    }
    else
         CURsetHighon(cur, "\033[1m");

    status = smg$get_term_data (&Addr, &SMG$K_BEGIN_NORMAL_RENDITION,
                                &buf_siz, &len, se);
    if (status&1) {
         se[len] = '\0';
         CURsetHighoff(cur, se);
    }
    else
         CURsetHighoff(cur, "\033[m");

}

void
VMSinit()
{
    int status;
    static unsigned long condition;
    static struct _exit_block {
         unsigned long forward;
         unsigned long address;
         unsigned long zero;
         unsigned long condition;
    } exit_handler_block;

    if(first) {
         /* get a channel and save terminal parms if we haven't already */
         status = sys$assign (&term_name, &term_chan, 0, 0 );
         status = sys$qiow(0,term_chan,IO$_SENSEMODE,0,0,0,
                           &oldbuf,12,0,0,0,0);
         first = FALSE;
         status = lib$disable_ctrl(&mask, &old_msk);

         exit_handler_block.forward   = 0;
         exit_handler_block.address   = (unsigned long) &VMSexit;
         exit_handler_block.zero      = 0;
         exit_handler_block.condition = (unsigned long) &condition;

         /* declare the exit handler block */
         status = sys$dclexh(&exit_handler_block);
         if (status != SS$_NORMAL) {
              printf("exit_status: %d", status);
         }

         /* Get the version of VMS */
         if (VMSVersion(VersionVMS, 20) < 3)
               /* Load zeros on error */
               strcpy(VersionVMS, "V0.0-0");
    }
}

void
VMSexit()
{
   /*
    * If we didn't get here via CleanupandExit(), it was via an
    *  exit(-1) or ACCVIO, so make *sure* we reset the terminal,
    *  then attempt a cleanup.
    */
    if (!DidCleanup) {
         fprintf(stderr,"\nPress RETURN to clean up and exit: ");
         (void) getchar();
         resetterm();
         CleanupandExit(-1);
    }
}


/*
*  VMSVersion -- F.Macrides 09-Jan-1994
*      Modification of UNZIP routine for getting VMS Version.
 */

int
VMSVersion(VerString, VerLen)
char *VerString;
int VerLen;
{
    int status, i = SYI$_VERSION, verlen = 0;
    struct dsc$descriptor version;
    char *m;

    version.dsc$a_pointer = VerString;
    version.dsc$w_length = VerLen - 1;
    version.dsc$b_dtype = DSC$K_DTYPE_B;
    version.dsc$b_class = DSC$K_CLASS_S;

    status = lib$getsyi(&i, 0, &version, &verlen, 0, 0);
    if (!(status&1) || verlen == 0)
         return 0;

    /* Cut out trailing spaces */
    for (m=VerString+verlen, i=verlen-1; i > 0 && VerString[i] == ' '; --i)
         *(--m) = '\0';

    return strlen(VerString)+1;        /* Transmit ending 0 too */
}


/*
*  DCLsystem, spawn_DCLprocess, DCLspawn_exception -- F.Macrides 31-Jul-1993
*      Exception-handler routines for enabling interrupts and Control-T
*      during spawns when setjmp() has been established, and the parent
*      passes a DCL CLI.
*  F.Macrides 09-Jan-1994 -- spawn_DCLprocess includes TRUSTED flag for
*      spawns with a command (but not spawns to DCL) if the version of
*      OpenVMS/VAX requires it in captive accounts.
*/

int
DCLsystem(command)
char *command;
{
    int status;
    extern void controlc();

    VMSsignal(SIGINT, SIG_IGN);
    status = spawn_DCLprocess(command);
    VMSsignal(SIGINT, controlc);
    if ((status&1) != 1)
        return(-1);
    else
        return(0);
}

int
spawn_DCLprocess(command)
char *command;
{
    int status;
    /** Keep DECC from complaining **/
    struct dsc$descriptor_s  command_desc;
    command_desc.dsc$w_length  = strlen(command);
    command_desc.dsc$b_class   = DSC$K_CLASS_S;
    command_desc.dsc$b_dtype   = DSC$K_DTYPE_T;
    command_desc.dsc$a_pointer = command;

    VAXC$ESTABLISH(DCLspawn_exception);

    if (command == "")
         status = lib$spawn(0);
    else {
#ifndef __ALPHA /* OpenVMS/AXP v6.1 still doesn't have the TRUSTED flag ;( */
         if(VersionVMS[1] >= '6')
              /** Include TRUSTED flag **/
              status = lib$spawn(&command_desc,0,0,&CLI$M_TRUSTED);
         else
#endif
              status = lib$spawn(&command_desc);
    }

    return(status);
}

unsigned int
DCLspawn_exception(sigarr, mecharr)
void *sigarr, *mecharr;
{
    int status;

    status = lib$sig_to_ret(sigarr, mecharr);
    return(SS$_UNWIND);
}
#endif /* VMS */