/* PIE.C version 25 Sept 2006 */
/* No makefile is required; a simple command line may be used.  */
/* To compile this source, edit PIESETUP.H, setting exactly one */
/* variable equal to 1. This chooses the OS and the compiler.   */
/* */
/* Some compiler command lines:                                 */
/* LCC=/c/lcc/bin/lcc.exe; LCCLINK=/c/lcc/bin/lcclnk.exe        */
/* LCCWIN32:  LCC -O pie.c                                      */
/*            LCCLINK -s pie.obj tcconio.lib                    */
/* GCC=gcc -Os -I. -fno-builtin -fno-strength-reduce            */
/*         -fomit-frame-pointer -finline-functions              */
/* CYGWIN:  GCC pie.c                                           */
/* LINUX:   GCC pie.c                                           */
/* macOSX:  gcc -I. -fno-builtin pie.c                          */
/* SOLARIS: GCC pie.c                                           */
/* WIN3:    See makefile MakeTC3; uses tcpp 1.0. Swapper        */
/*          and wildargs OBJ appear in this makefile. WIN3 is   */
/*          for win 95, win 98, win 2000, win xp DOS compile.   */
/* */
/* Source files included below are VG.C, V0.C --> V15.C, VEE.C  */
/* These were historically separate files, compiled separately. */
/* Compilers are more capable and faster, hence reasons for the */
/* separate files no longer exist. For TURBOC, a compile-time   */
/* split is made using symbols PIE1_C_SRC and PIE2_C_SRC. This  */
/* is done to give small ASM output so that tasm can compile it */
/* without running out of memory. Older machines with less      */
/* physical memory may require additional splits.               */
/* The split details are hidden inside makefile MakeTC3, where  */
/* compiler command lines contain the necessary DEFINES.        */
/* */
/* Some supported OS/compiler combinations:
 * LINUX           Linux, gcc
 * CYGWIN          WIN32 console, cygwin/Xfree86 xterm, gcc
 * LCCWIN32        WIN32 console, lcc-win32
 * WIN3            Windows 3.1 DOSbox, TurboC /w ASM code
 * TURBOC          DOS or MSWINDOWS DOSbox, Turbo C /w ASM code
 * DOS386I         SUN386I, dos -w, TurboC /w ASM code
 * SUN350          SUN 3/50 or SUN 386/I unix
 * SOLARIS         SUN solaris unix
 * macOSX          Mac OS X unix-like
 * IBMAIX          IBM AIX unix
 * ARDENT          Ardent Titan unix
 * DEC3100         DEC 3100 and similar DEC unix
 * ALPHA           DEC ALPHA unix
 * INDIGO          Silicon Graphics unix
 */
/* */
/*
 * VG.C    Editor global variables - Editor default keys, ANSI terminal
 * WARNING: All globals must be initialized. See V0.C.
 *
 *=========================================================================
 *                             GLOBAL VARIABLES
 *=========================================================================
 */
#define KEYDEFS 1       /* include key definitions from v.h */
#include "pie.h"        /* Set options here for OS and compiler */
#include "pieproto.h"   /* Prototypes for all functions in pie.c */
#ifndef PIE2_C_SRC              /* Extract pie1.c source */
#if MSDOS
#if MONO
#define TITLEATTR       7+8     /* boldface */
#define WINDOWATTR      7       /* white background, black letters */
#define BORDERATTR      7+8     /* boldface */
#define REVERSEVIDEO    0x70    /* reverse video */
#define BOLDFACEATTR    7+8     /* boldface */
#define OTHERATTR       7       /* white background, black letters */
#endif
/* IBMPC: Use 0F, 07, 0F for mono 1F, 13, 1F for color (blue & white) */
#if !MONO
#define TITLEATTR       31       /* boldface */
#define WINDOWATTR      19       /* dark blue background, white letters */
#define BORDERATTR      31       /* boldface */
#define REVERSEVIDEO    0x70     /* reverse video */
#define BOLDFACEATTR    31       /* boldface */
#define OTHERATTR       48       /* light blue background, black letters */
#endif
/*extern unsigned _heaplen=(unsigned)0;*/
#endif
#if !MSDOS
#define TITLEATTR       0x80    /* boldface */
#define WINDOWATTR      0x07    /* normal text background/foreground */
#define BORDERATTR      0x70    /* reverse video */
#define REVERSEVIDEO    0x70    /* reverse video */
#define BOLDFACEATTR    0x80    /* doesn't actually exist */
#define OTHERATTR       7       /* white background, black letters */
#endif

char __fff[2]={'P','I'};        /* marks start of data area */
int _egavga = MONOEGAVGA;
int abortkey = CTRLU;
int markset=0;
int markcol=0;
LLONG markline=0;
int SYSATT=OTHERATTR;
int PRTleft=5,PRTtop=5,PRTbot=6,PRTlpp=55,PRTff=1;
int TABINSERT=0;
int repeats=1;
int repkey=0;
unsigned lastkey=0;
int MAXKILLER=400;
struct bigarray far *killerlist=0;
int killerlen=0;
int mouseswitch=0;      /* X-windows mouse. 1=on, 0=off */
int hsens=8;            /* 8=default mickeys/8 pixels. 0=off MSDOS only. */
int vsens=16;           /* 16=default mickeys/8 pixels. 0=off MSDOS only.*/
int ANNOYING=1;         /* annoying.startup.message=1; None=0 */
int KEYMODE=1;          /* 0=none, 1=emacs, 2=wdstar, 3=wdperf */
int cursorCH=1;         /* VGA:  ch=1, cl=13 for cursor shape */
int cursorCL=13;
int swapswitch=1;       /* 1=on, 0=off swap to disk on system() */
int zero = '\200';      /* Search string char to match begin/end line */
int zeroleft=0;         /* TRUE if srcharg[0] is a zero token */
int zeroright=0;        /* TRUE if tailchar(srcharg) is a zero token */
char *MATHFMT=0;        /* Math format in printf style syntax "%.08g";*/
int blockflag=0;        /* Prompt for block read parameters */
int regdef=0;           /* 0=Region by lines, 1=by columns (toggle) */
int ACTIONSTART=1;      /* 0=start UnTitled, 1=start with Menu */
#if MSDOS  /* Mapping of GREY keys on the keypad to other keycodes */
           /* The code appears in routine ccBIOS (v8.c) */
int GREYplus=0x8300;    /* Grey plus==0x4e2b mapped to 0x8300=="Alt= " */
int GREYast=0x0c1f;     /* Grey asterisk==0x372a mapped to 0x0c1f=="Ctrl _" */
int GREYent=0x8300;     /* Grey enter==0xe00d mapped to 0x8300=="Alt =" */
int STASHmin=6;         /* Memory blocks reserved for F9 string copies */
int SUSPENDexit=0;      /*Suspend on ctrl-KX "Save then exit" FILE menu. */
#endif
#if !MSDOS
int SUSPENDexit=1;      /* Suspend on ctrl-KX "Save then exit" FILE menu. */
#if LCCWIN32
int ansimode=2;         /* Flag for ansi mode (0=off,1=unix,2=lccwin32) */
int textfgcolor=0;      /* Foreground text color 0-15 (default=BLACK) */
int textbgcolor=11;     /* Background text color 0-15 (default=LIGHTCYAN) */
int menufgcolor=1;     /* Foreground menu color 0-15 (default=WHITE) */
int menubgcolor=15;      /* Background menu color 0-15 (default=BLUE) */
#else
int ansimode=1;         /* Flag for ansi mode (0=off) */
int textfgcolor=0;      /* Foreground text color 30-37 ANSI mode (0=default) */
int textbgcolor=0;      /* Background text color 40-47 ANSI mode (0=default) */
int menufgcolor=37;     /* Foreground menu color 30-37 ANSI mode (37=white) */
int menubgcolor=44;     /* Background menu color 40-47 ANSI mode (44=blue) */
#endif
char *UNIXtextfgbg=0;   /* Unix text foreground+background ANSI */
char *UNIXmenufgbg=0;   /* Unix menu foreground+background ANSI */
char *UNIXdefaultfgbg=0;/* Unix default foreground+background ANSI */
#endif

int
  insmode=0,            /* insert mode toggle                   */
  tabstop=0,            /* tabstop variable                     */
  row = 0,              /* row position 0...23                  */
  col = 0,              /* column position 0...79               */
  macmax = 0,           /* maximum index in macbuf[]            */
  macrep = 1,           /* number of times to repeat macro      */
  macind = 0,           /* current index into macbuf[]          */
  macrun = 0,           /* macro running flag                   */
  sysarg = 0,           /* ENTER key numerical argument         */
  record = 0,           /* record keystrokes flag               */
  popcount = 1,         /* number of lines in push buffer       */
  nodisp = 0,           /* display flag, 0=on, 1=off            */
  statblank = 1,        /* status line has been erased          */
  nonzeroScreenLines=0; /* Count the non-null lines on screen   */
#if MSDOS
int
  STOPS=8,              /* tab stop increment                   */
  ROWMAX = 23,          /* rows numbered 0..22                  */
  COLMAX = 79,          /* columns numbered 0..79               */
  MAXLN = 160,          /* longest line length                  */
  MAXROWS = 24,         /* most rows/screen, >= 1+ROWMAX        */
  DOSTATUS = 1;         /* Print status line during edit        */
#endif
#if !MSDOS
int
  STOPS=8,              /* tab stop increment                   */
  ROWMAX = 22,          /* rows numbered 0..22                  */
  COLMAX = 79,          /* columns numbered 0..79               */
  MAXLN = 160,          /* longest line length                  */
  MAXROWS = 40,         /* most rows/screen, >= 1+ROWMAX        */
  DOSTATUS = 1,         /* Print status line during edit        */
  PIPEopen=0;           /* Status of open pipe, unix sys5/bsd   */
#endif
int
  INDENAUTO = 1,        /* indent flag                          */
  HangingIndent=1,      /* Allow hanging indents                */
  WRDWRP = 0,           /* word wrap toggle                     */
  margin = 72,          /* initial right margin                 */
  BITSTRIP = 0,         /* strip high bits, 1=on, 0=off         */
  DETABBER = 1,         /* process tabs to spaces (1=on,0=off)  */
  BLANKSTRIP=0,         /* Strip blanks off end of line (1=on)  */
  TSTOPS = 8,           /* screen tab stop                      */
  FOLDCASE=1,           /* fold case on search                  */
  MAXMB=256,            /* size of macro recording buffer       */
  INSMD=1,              /* State of initial insert mode         */
  drawstyle=1,          /* Style of box/line-drawing (1-5)      */
#if CRLFBOX             /* input/output file types              */
  intexttype = CRLFtexttype,    /* 1=cr/lf, 2=cr, 3=lf          */
  outtexttype = CRLFtexttype,
#endif
#if CRBOX
  intexttype = CRtexttype,
  outtexttype = CRtexttype,
#endif
#if LFBOX
  intexttype = LFtexttype,
  outtexttype = LFtexttype,
#endif
  titlattr = TITLEATTR, /* Window title color attribute         */
  windattr = WINDOWATTR,/* Window color attribute               */
  bordattr=BORDERATTR,  /* Window border color attribute        */
  boxcharset=BOXCHAR,   /* Box characterset 0=text,1=latin1,2=latin2 */
  BACKUPS=1,            /* 0=FALSE,1=$HOME/PIbackups,2=./PIbackups */
  EMACSMODE=0;          /* cr inserts cr/lf by default          */
#if MSDOS || LCCWIN32
char *
  BROWSER="iexplore";  /* Choose a default browser */
#else
char *
  BROWSER="mozilla";   /* Choose a default browser */
#endif
int
  drawline=0;           /* box/line drawing switch, 0=off,1=on  */
LLONG
  lncount = 0;          /* line number variable                 */
char
  PiVersion[] =         /* Version string for PI editor compile */
   {VERSIONofPI};       /* Puts version info into the executable */
char *
  progname=0;           /* Program name from argv[0] */
char                    /* Directory name for backups */
  *PIBACKUPS="PIbackups";
                        /* name of notepad file                 */
char NOTEPAD[]={"NotePad"};
                        /* name of untitled file                */
char UNTITLED[] ={"UnTitled"};
char HELPSTR[] = {" [Menu=four ESC]"};
char QUIT_PROMPT[]={"Quit Editor"};
char INSERTMODE[]={" [NoInsert]"};
char LINEDRAWMODE[]={"[Line Drawing]"};
char AUTHOR[]={"G.B.Gustafson, 155 So 1400 East #233, SLC, UT 84112-0090"};
char ADDRESSofAUTHOR[]={"http://www.math.utah.edu/~gustafso/, (801) 581-6879"};

char *cd;               /* connected dir pointer                */
char
  *SHELL=SHELLDEFAULT;  /* Name of command shell                */
char
  *srcharg,             /* pointer to search argument string    */
  *lastarg,             /* pointer to last getstr arg string    */
  *replarg,             /* pointer to replace argument string   */
  *sysfile,             /* command line filename                */
  *sysv,                /* pointer to system scratch buffer     */
  *sysx,                /* pointer to system scratch buffer     */
  *sysvv,               /* pointer to system scratch buffer     */
  *bufbase=(char *)0,   /* edit buffer base (absolute)          */
  *lbuf,                /* lower buffer end (movable)           */
  *hbuf,                /* high buffer start (movable)          */
  *ebuf,                /* end of edit buffer + 1 (movable)     */
  *scrbuf,              /* screen buffer pointer (fixed)        */
  *macbuf,              /* memorization buffer pointer (fixed)  */
  *optbuf;              /* pointer to optional line buffer      */
char
  **line;               /* pointers to 24 screen lines          */
int
  taberr=0,             /* error count variable for tabs        */
  HEADROOM=0,           /* must be > 1+MAXLN+SIZEBLOCK          */
  LINESIZE=0,           /* usually (1+ROWMAX)*sizeof(char *)    */
  SCRSIZE=0,            /* usually (1+MAXLN)*(1+ROWMAX)         */
                        /* but also SCRSIZE >= HEADROOM         */
  FARSCROLL=0,          /* must be > ROWMAX                     */
  halfdone=0;           /* measure dirty screen half-displayed  */

char  *termtype;        /* pointer to terminal type string      */
#define HelpSheetROWS 3
#define HelpSheetCOLS 5
struct windows helpsheet =
{"",
2,1,(2+1+HelpSheetROWS),(1+HelpSheetCOLS),
0,0,0,0,
""};
int ishelpsheetopen=0;

#if !MSDOS && !LCCWIN32
char   *rs_termcap    = "";             /* reset terminal */
char   *is_termcap    = "";             /* init terminal */
char   *ks_termcap    = "";             /* start keypad shifted mode */
char   *ke_termcap    = "";             /* exit keypad shifted mode */
char   *al_termcap    = "";             /* insert line */
char   *dl_termcap    = "";             /* delete line */
char   *ce_termcap    = "";             /* erase to end of line */
char   *cd_termcap    = "";             /* clear to end of display */
char   *cm_termcap    = "";             /* cursor motion */
char   *cs_termcap    = "";             /* change scrolling region */
char   *sf_termcap    = "";             /* scroll forward */
char   *sr_termcap    = "";             /* scroll reverse */
char   *bl_termcap    = "\7";           /* audible bell */
char   *pc_termcap    = "";             /* pad char (null) */
char   *REVVIDEO      = "";             /* enter inverse video (so) */
char   *NORMVIDEO     = "";             /* exit inverse/bold video (se) */
char   *BOLDF         = "";             /* enter boldface chars (md) */
char   *ERACHR        = "\b\40\b";      /* erase char left */
char   *INIFILE       ="";              /* keyboard file name */
int    HASSCROLL      = 0;              /* cs && sf && sr */
int    HASINSERTLINE  = 0;              /* HASSCROLL || *al_termcap */
int    HASDELETELINE  = 0;              /* HASSCROLL || *dl_termcap */
int    ospeed         = 0;              /* output speed of terminal */
#endif

#if MSDOS || LCCWIN32
#if ANSISYS
char   *rs_termcap    = "";             /* reset terminal */
char   *is_termcap    = "";             /* init terminal */
char   *ks_termcap    = "";             /* start keypad shifted mode */
char   *ke_termcap    = "";             /* exit keypad shifted mode */
char   *al_termcap    = "\033[L";       /* insert line */
char   *dl_termcap    = "\033[M";       /* delete line */
char   *ce_termcap    = "\033[K";       /* erase to end of line */
char   *cd_termcap    = "\033[J";       /* clear to end of display */
char   *cm_termcap    = "\033[%d;%dH";  /* cursor motion */
char   *cs_termcap    = "";             /* change scrolling region */
char   *sf_termcap    = "";             /* scroll forward */
char   *sr_termcap    = "";             /* scroll reverse */
char   *bl_termcap    = "\7";           /* audible bell */
char   *pc_termcap    = "\200";         /* pad char (null) */
char   *REVVIDEO      = "\033[7m";      /* enter inverse video (so) */
char   *NORMVIDEO     = "\033[0m";      /* exit inverse/bold video (se) */
char   *BOLDF         = "\033[1m"  ;    /* enter boldface chars (md) */
#endif

char   *ERACHR        = "\b\40\b";      /* erase char left */
char   *INIFILE       ="";              /* keyboard file name */
int    HASSCROLL      = 0;              /* cs && sf && sr */
int    HASINSERTLINE  = 1;              /* HASSCROLL || *al_termcap */
int    HASDELETELINE  = 1;              /* HASSCROLL || *dl_termcap */
int    ospeed         = 0;              /* output speed of terminal */
#endif

#if MSDOS
#define MAXFNAME 128    /* length of largest file name */
#define MAXFILES 50     /* allow fifty files at once */
#endif

#if !MSDOS
#define MAXFNAME 256    /* length of largest file name */
#define MAXFILES 100    /* allow 100 files at once */
#endif

#define MKLEN 20
#ifndef SIZBLK
#define SIZBLK   4096   /* depends on disk controller & OS */
#endif
#ifndef MAXBL
#define MAXBL    640    /* depends on OS and installed memory */
#endif
#ifndef MAXBLxms
#define MAXBLxms  1024   /* Relevant for MSDOS with HIMEM.SYS */
#endif
#define NUMMACBUF 100    /* number of macro buffers              */

int MACNUM=NUMMACBUF;   /* Global counter for macro buffers     */
int MAXFN=MAXFNAME;     /* max length of a file name */
int SIZEBLOCK=SIZBLK;
int SIZEKBLOCK=SIZBLK/1024; /* Size in kilobytes */
int USEDBLOCK=0;
int MAXBLOCK=MAXBL;     /* Gets changed in v0.c to reflect actual memory */
int MAXXMS=MAXBLxms;
int maxinfo=MAXFILES;  /* possible file handles */

char **macrobuf;
int *macromax;

#if 0  /* See v.h */
struct bucket {
 int record;    /* record in memblock[] array */
 int numlines;  /* number of lines in bucket */
 int numbytes;  /* number of bytes in bucket */
 int flag;      /* 0 if converted, 1 if not */
};
#endif

struct bucket far *  /* bucket info array */
meminfo;

char far *
memmark;
unsigned far *
handles;                /* handles for TURBOC xms routines */

FARSTR far *            /* bucket addresses */
memblock;
#if 0
struct fileinfo {
int active;     /* 0=not used, 1=all external, 2=uses main buffer */
char *fname;    /* file name */
int nbLOW;      /* number of low blocks used by file */
int nbTOT;      /* total number of blocks used by file */
int rowc;       /* cursor row, not used if active=2 */
int colc;       /* cursor col, not used if active=2 */
LLONG linec;    /* cursor line, not used if active=2 */
int dirty;      /* 0 if never touched, nonzero otherwise */
int curoffset;  /* screen offset, see drawall() */
int setmark;    /* text marker flag, 1=on,0=off */
int setmarkcol; /* absolute column marker where marker was set */
LLONG                   
 setmarkline;   /* line number where marker was set. */
int ReadFtype;  /* type on read 0=unix, 1=MSDOS, 2=MAC */
int WriteFtype; /* type on write 0=unix, 1=MSDOS, 2=MAC */
};                      
#endif                  
                        
int curfile = 1;        /* currently active file 0...(maxinfo-1) */
int curoff = 0;         /* current screen offset, see drawall() */
                        
struct fileinfo         
*finfo;                 
/*finfo[MAXFILES];*/          
#if 0
struct fileinfo {
int active;     /* 0=not used, 1=all external, 2=uses main buffer */
char *fname;    /* file name */
int nbLOW;      /* number of low blocks used by file */
int nbTOT;      /* total number of blocks used by file */
int rowc;       /* cursor row, not used if active=2 */
int colc;       /* cursor col, not used if active=2 */
LLONG linec;    /* cursor line, not used if active=2 */
int dirty;      /* 0 if never touched, nonzero otherwise */
int curoffset;  /* screen offset, see drawall() */
int setmark;    /* text marker flag, 1=on,0=off */
int setmarkcol; /* absolute column marker where marker was set */
LLONG                   
 setmarkline;   /* line number where marker was set. */
int ReadFtype;  /* type on read 0=unix, 1=MSDOS, 2=MAC */
int WriteFtype; /* type on write 0=unix, 1=MSDOS, 2=MAC */
};                      
#endif                  
                        
#if 0                   
struct keybrd {               
  unsigned value;            
  unsigned char *seq;   
};                      
#endif                  
                        
unsigned char *LeadinSTR=0;
                        
struct keybrd ourkeys[] = {     
/* #if MSDOS || LCCWIN32 */
#if MSDOS               
        {ENTERKEY     , "\33\33"        },      /*      Escape             */
        {ENTERKEY     , "\200\213"      },      /*      F12                */
        {ENTERKEY     , "\340\206"      },      /*      F12                */
        {TABKEY       , "\t"            },      /*      Ctrl-I (tab)       */
        {WORDAHEAD    , "\6",           },      /*      Ctrl-F word ahead  */
        {RETURNKEY    , "\r"            },      /*      Ctrl-M (return)    */
        {EMACSRETURNKEY , "\12"         },      /*      Ctrl-J (linefeed)  */
        {WORDBACK     , "\1",           },      /*      Ctrl-A word back   */
        {UPARROW      , "\5"            },      /*      Ctrl-E             */
        {DOWNARROW    , "\30"           },      /*      Ctrl-X             */
        {FILEBOTTOM   , "\21\3"         },      /*      Ctrl-Q,Ctrl-C      */
        {LEFTARROW    , "\23"           },      /*      Ctrl-S             */
        {EXTLEFTARROW , "\21\23"        },      /*      Ctrl-Q,Ctrl-S      */
        {RIGHTARROW   , "\4"            },      /*      Ctrl-D             */
        {EXTRIGHTARROW, "\21\4"         },      /*      Ctrl-Q,Ctrl-D      */
        {HOMEKEY      , "\21\5"         },      /*      ctrl-Q,ctrl-E      */
        {REVHOMEKEY   , "\21\30"        },      /*      Ctrl-Q,Ctrl-X      */
        {INSERTLINE   , "\16"           },      /*      Ctrl-N             */
        {DELLINE      , "\31"           },      /*      Ctrl-Y             */
        {DELCHAR      , "\7"            },      /*      Ctrl-G             */
        {INSMODE      , "\26"           },      /*      Ctrl-V             */
        {QUERYREPLACE , "\21\1"         },      /*      Ctrl-Q,Ctrl-A      */
        {NEWSEARCH    , "\21\6"         },      /* ctrl-QF new search str  */
        {FWDSEARCH    , "\17f"          },      /* ctrl-O,f                */
        {REVSEARCH    , "\17b"          },      /* ctrl-O,b                */
        {PAGEFORWARD  , "\3"            },      /*      Ctrl-C             */
        {LINEFORWARD  , "\32"           },      /*      Ctrl-Z             */
        {FILETOP      , "\21\22"        },      /*      Ctrl-Q,Ctrl-R      */
        {LINEBACK     , "\27"           },      /*      Ctrl-W             */
        {PAGEBACK     , "\22"           },      /*      Ctrl-R             */
        {ERASEKEY     , "\21\24"        },      /*      ctrl-Q,Ctrl-T      */
        {QPUSHKEY     , "\17\03"        },      /*      Ctrl-O,Ctrl-C      */
        {PUSHKEY      , "\13\03"        },      /*      Ctrl-K,Ctrl-C      */
        {QPOPKEY      , "\17\26"        },      /*      Ctrl-O,Ctrl-V      */
        {QZAPKEY      , "\17\32"        },      /*      Ctrl-O,Ctrl-Z      */
        {POPKEY       , "\13\26"        },      /*      Ctrl-K,Ctrl-V      */
        {SAVE_EXIT    , "\13\30"        },      /*      Ctrl-K,Ctrl-X      */
        {DSKFILES     , "\13\6"         },      /*      Ctrl-K,Ctrl-F      */
        {COLPOSITION  , "\13C"          },      /*      Ctrl-K,C           */
        {ROWPOSITION  , "\13R"          },      /*      Ctrl-K,R           */
        {CREATEFOLDER , "\13\7"         },      /*      Ctrl-K,Ctrl-G      */
        {LOAD         , "\13\14"        },      /*      Ctrl-K,Ctrl-L      */
        {INJECT       , "\13\22"        },      /*      Ctrl-K,Ctrl-R      */
        {SPLITLINE    , "\17\16"        },      /*      Ctrl-O,Ctrl-N      */
        {BACKUP       , "\13\23"        },      /*      Ctrl-K,Ctrl-S      */
        {SAVEASKEY    , "\13\27"        },      /*      Ctrl-K,Ctrl-W      */
        {CONTROLENTRY , "\20"           },      /*      Ctrl-P             */
        {BAILOUT      , "\25\25"        },      /*      Ctrl-U,Ctrl-U      */
        {GRABTEXT     , "\177"          },      /*      DELETE             */
        {ADDBUFFER    , "\17\13"        },      /*      Ctrl-O,Ctrl-K      */
        {DELMANYLINES , "\17\31"        },      /*      Ctrl-O,Ctrl-Y      */
        {OPTPUSHKEY ,   "\17\20"        },      /*      Ctrl-O,Ctrl-P      */
        {ERASELINE    , "\21\31"        },      /*      Ctrl-Q,Ctrl-Y      */
        {SCRAPBUFFER  , "\13\21"        },      /*      Ctrl-K,Ctrl-Q      */
        {SETTABS      , "\17\24"        },      /* ctrl-O,ctrl-T           */
        {SETSTATUS    , "\17\23"        },      /* ctrl-O,ctrl-S           */
        {SETAUTO      , "\17\11"        },      /* ctrl-O,ctrl-I           */
        {SETHANGING   , "\17\25"        },      /* ctrl-O,ctrl-U           */
        {OPTMATH      , "\17i"          },      /* ctrl-O,i optbuf math    */
        {OPTHEX       , "\17H"          },      /* ctrl-O,H hextobase10    */
        {OPTOCT       , "\17O"          },      /* ctrl-O,H octtobase10    */
        {SWAPSW,        "\17!"          },      /* ctrl-O,! swap switch    */
        {REFORMKEY    , "\2"            },      /* ctrl-B is reform        */
        {REVTABKEY     ,"\21\t"         },      /* ctrl-Q,ctrl-I           */
        {COMPLETEKEY  , "\17h"          },      /* ctrl-O,h keymap editor  */
        {SHOWKEYPRESS,  "\21k"          },      /* ctrl-Q,k show keypress  */
        {MACROLIBRARY,  "\17e"          },      /* ctrl-O,e macro lib edit */
        {RECORDING    , "\17m"          },      /* ctrl-O,m memorize steps */
        {RUNMEMORIZE  , "\17r"          },      /* ctrl-O,r run macro      */
        {RUNPROGRAM   , "\17\22"        },      /* ctrl-O,ctrl-R           */
        {RUNBACKGROUND, "\17\21"        },      /* ctrl-O,ctrl-Q           */
        {RUNMYPROGRAM , "\17R"          },      /* ctrl-O,R                */
        {RUNHELP      , "\17\10"        },      /* ctrl-O,ctrl-H           */
        {WORDWRAP     , "\17\27"        },      /* ctrl-O,ctrl-W           */
        {NEXTEDIT     , "\f"            },      /* ctrl-L (next buffer)    */
        {PREVEDIT     , "\21\f"         },      /* ctrl-QL (prev buffer)   */
        {SAVE_NEXT    , "\13\4"         },      /* ctrl-KD, save+continue  */
        {EXTENDEDCHAR , "\17\5"         },      /* ctrl-OE, prev |= 128    */
        {SETDETAB     , "\17\4"         },      /* ctrl-OD, detab toggle   */
        {BACKTOGGLE   , "\17\2"         },      /* ctrl-OB, toggle backups */
        {ASCIITABLE   , "\17a"          },      /* ctrl-O,a show ascii code*/
        {OPTIONSET    , "\17\17"        },      /* ctrl-OO, option settings*/
        {FOLDSRCH     , "\17\6"         },      /* ctrl-OF, togg fold case */
        {MACROKEY     , "\17s"          },      /* ctrl-O,s, save macro    */
        {DISPLAYMACRO,  "\17\61"        },      /* reveal macro codes      */
        {TSRKEY       , "\13\32"        },      /* ctrl-KZ, kill process   */
        {CASELOWER    , "\17l"          },      /* ctrl-O,l case char lower*/
        {CASELLOWER   , "\17L"          },      /* ctrl-O,L case line lower*/
        {CASEUPPER    , "\17u"          },      /* ctrl-O,u case char upper*/
        {CASELUPPER   , "\17U"          },      /* ctrl-O,U case line upper*/
        {CASECAPITAL  , "\17C"          },      /* ctrl-O,C word capitalize*/
        {GOTOKEY      , "\17\7"         },      /* ctrl-OG, goto line #    */
        {MENUKEY      , "\037"          },      /* ctrl-underline, menu    */
        {CENTERTEXT   , "\17c"          },      /* ctrl-O,c center text    */
        {APPENDLINE   , "\17\1"         },      /* ctrl-OA, app line to NP */
        {KILLNOTEPAD  , "\17\30"        },      /* ctrl-OX, Clear NotePad  */
        {NEWLOAD      , "\13\25"        },      /* ctrl-KU, new untitled   */
        {RELOAD       , "\013\05"       },      /* ctrl-KE, reload file    */
        {COPYBLOCK    , "\13c"          },      /* ctrl-K,c copy block     */
        {MOVEBLOCK    , "\13m"          },      /* ctrl-K,m move block     */
        {PASTEBLOCK   , "\13p"          },      /* ctrl-K,p paste block    */
        {MANYRUNMEM   , "\17\12"        },      /* ctrl-OJ Run macro many  */
        {LINEDRAWKEY  , "\17\14"        },      /* ctrl-OL Setup line draw */
        {MENUBOXSET   , "\21b"          },      /* ctrl-Q,b Menu box chars */
        {SETTEXTTYPE  , "\13\24"        },      /* ctrl-KT Set In/Out type */
        {NAMECHANGE   , "\13\16"        },      /* ctrl-KN Change filename */
        {FOREIGNKEY   , "\34"           },      /* ctrl-\ swap foreignkey  */
        {SETMARK      , "\13\2"         },      /* ctrl-KB set block mark  */
        {SWAPMARK     , "\13\13"        },      /* ctrl-KK swap mark       */
        {REVEALMARK   , "\13s"          },      /* ctrl-K,s show block     */
        {CHARTRANSPOSE, "\17t"          },      /* ctrl-O,t transpose chars*/
        {LINETRANSPOSE, "\17T"          },      /* ctrl-O,T Transpose lines*/
        {WORDERASE    , "\24"           },      /* ctrl-T Erase word       */
        {PRINTFILE    , "\13\20"        },      /* ctrl-KP Format & print  */
        {SETPRINTER   , "\17P"          },      /* ctrl-O,p printer setup  */
        {REPEATKEY    , "\21\21"        },      /* ctrl-QQ repeat count    */
        {POPLIST,       "\17&"          },      /* Recover kill list       */
        {PURGELIST,     "\17X"          },      /*ctrl-O,X Purge kill list */
        {MOUSEPRESS,    "\33[M"         },      /* Mouse entry as escapes  */
        {SETBUFFER,     "\13\01"        },      /* ctrl-KA Change buffer   */
        {LFWDSEARCH,    "\17F"          },      /*  Line Search forward    */
        {LREVSEARCH,    "\17B"          },      /*  Line Search reverse    */
        {ZEROTOKEN,     "\17z"          },      /*  Insert zero token      */
        {CENTERREFRESH, "\17p"          },      /* Ctrl-O,p Center Refresh */
        {OPTMATHFLOAT , "\17j"          },      /* ctrl-O,j optbuf cv2 flo */
        {OPTEVALEXPR,   "\17k"          },      /* ctrl-O,k apply eval expr*/
        {MATHFORMAT ,   "\17\15"        },      /* ctrl-OM set math format */
        {VIDEOMODE    , "\17v"          },      /* ctrl-O,v Set video mode */
        {ENTERKEY     , "\200\203"      },      /*      ALT =              */
        {FWDSEARCH    , "\200?"         },      /* function F5             */
        {FWDSEARCH    , "\340?"         },      /* function F5             */
        {REVSEARCH    , "\200@"         },      /* function F6             */
        {REVSEARCH    , "\340@"         },      /* function F6             */
        {QPUSHKEY     , "\200f"         },      /*      Ctrl-F9            */
        {QPUSHKEY     , "\340f"         },      /*      Ctrl-F9            */
        {QPOPKEY      , "\200g"         },      /*      Ctrl-F10           */
        {QPOPKEY      , "\340g"         },      /*      Ctrl-F10           */
        {REVTABKEY     ,"\200\17"       },      /* shift tab               */
        {UPARROW      , "\200H"         },      /* up arrow                */
        {UPARROW      , "\340H"         },      /* up arrow                */
        {DOWNARROW    , "\200P"         },      /* down arrow              */
        {DOWNARROW    , "\340P"         },      /* down arrow              */
        {LEFTARROW    , "\200K"         },      /* left arrow              */
        {LEFTARROW    , "\340K"         },      /* left arrow              */
        {RIGHTARROW   , "\200M"         },      /* right arrow             */
        {RIGHTARROW   , "\340M"         },      /* right arrow             */
        {HOMEKEY      , "\200w"         },      /* ctrl-HOME               */
        {INSERTLINE   , "\200="         },      /* function F3             */
        {DELLINE      , "\200>"         },      /* function F4             */
        {PAGEFORWARD  , "\200Q"         },      /* PgDn                    */
        {PAGEFORWARD  , "\340Q"         },      /* PgDn                    */
        {PAGEBACK     , "\200I"         },      /* PgUp                    */
        {PAGEBACK     , "\340I"         },      /* PgUp                    */
        {LINEFORWARD  , "\200v"         },      /* ctrl-PgDn               */
        {LINEBACK     , "\200\204"      },      /* ctrl-PgUp               */
        {RUNMEMORIZE  , "\200A"         },      /* function F7             */
        {PUSHKEY      , "\200C"         },      /* function F9             */
        {POPKEY       , "\200D"         },      /* function F10            */
        {ADDBUFFER    , "\200W"         },      /* shift Function F4       */
        {RECORDING    , "\200B"         },      /* function F8             */
        {REVHOMEKEY   ,"\200u"          },      /* ctrl-END key            */
        {EXTLEFTARROW, "\200G"          },      /* HOME KEY                */
        {EXTLEFTARROW, "\340G"          },      /* HOME KEY                */
        {EXTLEFTARROW, "\200s"          },      /* ctrl-leftarrow          */
        {EXTRIGHTARROW,"\200O"          },      /* END KEY                 */
        {EXTRIGHTARROW,"\340O"          },      /* END KEY                 */
        {EXTRIGHTARROW,"\200t"          },      /* ctrl-rightarrow         */
        {DELCHAR      , "\200S"         },      /* DEL key                 */
        {INSMODE      , "\200R"         },      /* INS key                 */
        {INSMODE      , "\340R"         },      /* INS key                 */
        {RUNPROGRAM   , "\200<"         },      /* function F2             */
        {RUNMYPROGRAM , "\200U"         },      /* function shift-F2       */
        {RUNBHELP     , "\200h"         },      /* Alt F1 Brief help       */
        {RUNHELP      , "\200T"         },      /* Shift F1 Full help      */
        {MACROLIBRARY,  "\200^"         },      /* ctrl-F1 macro lib edit  */
        {COMPLETEKEY  , "\200_"         },      /* ctrl-F2 keymap editor   */
        {CENTERREFRESH, "\200;"         },      /* F1 Center Refresh       */
        {MACROKEY     , "\200\201"      },      /* alt-0, select macro 1-9 */
        {DISPLAYMACRO,  "\200\200"      },      /* reveal macro codes      */
        {TSRKEY       , "\200-"         },      /* ALT-X,   kill process   */
        {CENTERTEXT   , "\200."         },      /* ALT-C, center text line */
        {APPENDLINE   , "\200V"         },      /* shift-F3 app line to NP */
        {MANYRUNMEM   , "\200Z"         },      /* shift-F7 Run macro many */
        {FOREIGNKEY   , "\200\205"      },      /* F11=(0,5) swap foreign  */
        {CHARTRANSPOSE, "\200\24"       },      /* Alt-T transpose chars   */
        {LINETRANSPOSE, "\200&"         },      /* Alt-L Transpose lines   */
        {ZEROTOKEN,     "\200\3"        },      /*  Insert zero token      */
        {SHRINKWINDOW,  "\17-"          },      /* Resize window rows+cols */
        {OPTEVALEXPR,   "\200\206"      },      /* F12 apply eval expr*/
        {SETBOOKMARK,   "\013\060"      },      /* Set bookmark 0          */
        {GOBOOKMARK,    "\021\060"      },      /* GOTO bookmark 0         */
        {NULLKEY,       "\377"          },      /* map biggest key         */
#endif                  
#if PIINILOAD           
/* #if !MSDOS && !LCCWIN32 */
#if !MSDOS                    
{MENUKEY, "\037"},       
{MENUKEY, "\033Oj"},    
{MENUKEY, "\033[209z"},       
{MENUKEY, "\033[4z"},
{MENUKEY, "\033[P"},
{MENUKEY, "\033[29~"},
{ENTERKEY, "\033\E"},
{ENTERKEY, "\033Ok"},
{ENTERKEY, "\033OM"},
{ENTERKEY, "\033[208z"},
{ENTERKEY, "\033[24~"},
{TABKEY, "\011"},
{RETURNKEY, "\015"},
{EMACSRETURNKEY, "\012"},
{REVTABKEY, "\021\011"},
{WORDAHEAD, "\006"},
{WORDBACK, "\001"},
{UPARROW, "\005"},
{UPARROW, "\033OA"},          
{UPARROW, "\033Ox"}, 
{UPARROW, "\033[215z"},
{UPARROW, "\033[A"},          
{DOWNARROW, "\030"},
{DOWNARROW, "\033OB"},
{DOWNARROW, "\033Or"},
{DOWNARROW, "\033[221z"},
{DOWNARROW, "\033[B"},
{LEFTARROW, "\023"},
{LEFTARROW, "\033OD"},
{LEFTARROW, "\033Ot"},
{LEFTARROW, "\033[217z"},
{LEFTARROW, "\033[D"},
{RIGHTARROW, "\004"},
{RIGHTARROW, "\033OC"},
{RIGHTARROW, "\033Ov"},
{RIGHTARROW, "\033[219z"},
{RIGHTARROW, "\033[C"},
{EXTRIGHTARROW, "\021\004"},  
{EXTRIGHTARROW, "\033[212z"},
{EXTRIGHTARROW, "\033[220z"},
{EXTRIGHTARROW, "\033Oq"},    
{EXTRIGHTARROW, "\033Oe"},
{EXTRIGHTARROW, "\033[4~"},
{EXTRIGHTARROW, "\033[8~"},
{EXTRIGHTARROW, "\033O\220"},
{EXTRIGHTARROW, "\033OF"},
{EXTLEFTARROW, "\021\023"},
{EXTLEFTARROW, "\033[211z"},
{EXTLEFTARROW, "\033[214z"},
{EXTLEFTARROW, "\033Ow"},
{EXTLEFTARROW, "\033O\200"},
{EXTLEFTARROW, "\033[1~"},
{EXTLEFTARROW, "\033[7~"},
{EXTLEFTARROW, "\033OH"},
{HOMEKEY, "\021\005"},
{HOMEKEY, "\033Ou"},
{HOMEKEY, "\033[218z"},       
{HOMEKEY, "\033[G"}, 
{HOMEKEY, "\033OE"},
{REVHOMEKEY, "\021\030"},     
{FILETOP, "\021\022"},
{FILETOP, "\033[210z"},
{FILETOP, "\033Om"}, 
{FILEBOTTOM, "\021\003"},
{FILEBOTTOM, "\033[213z"},
{FILEBOTTOM, "\033Ol"},
{LINEFORWARD, "\032"},
{LINEBACK, "\027"},
{PAGEFORWARD, "\003"},
{PAGEFORWARD, "\033[222z"},
{PAGEFORWARD, "\033[6z"},
{PAGEFORWARD, "\033Os"},
{PAGEFORWARD, "\033[6~"},
{PAGEBACK, "\022"},
{PAGEBACK, "\033[216z"},
{PAGEBACK, "\033[5z"},        
{PAGEBACK, "\033Oy"},
{PAGEBACK, "\033[5~"},
{GOTOKEY, "\017\007"},        
{CENTERREFRESH, "\017p"},
{CENTERREFRESH, "\033[[A"},
{CENTERREFRESH, "\033[11~"},
{CENTERREFRESH, "\033[224z"},
{CENTERREFRESH, "\033OP"},
{INSERTLINE, "\033[13~"},
{INSERTLINE, "\016"},
{INSERTLINE, "\033[194z"},
{INSERTLINE, "\033[25~"},
{INSERTLINE, "\033[[C"},
{INSERTLINE, "\033[226z"},
{INSERTLINE, "\033OR"},
{DELLINE, "\033[14~"},
{DELLINE, "\031"},
{DELLINE, "\033[195z"},
{DELLINE, "\033[26~"},        
{DELLINE, "\033[[D"},
{DELLINE, "\033[227z"},
{DELMANYLINES, "\017\031"},   
{DELCHAR, "\007"},
{DELCHAR, "\033[3z"},
{DELCHAR, "\033[3~"},
{DELCHAR, "\033On"},
{DELCHAR, "\033OS"},
{INSMODE, "\026"},
{INSMODE, "\033Op"},
{INSMODE, "\033[2z"},
{INSMODE, "\033[2~"},
{ERASEKEY, "\021\024"},
{ERASELINE, "\021\031"},
{SPLITLINE, "\017\016"},
{CONTROLENTRY, "\020"},
{PUSHKEY, "\033[20~"},
{PUSHKEY, "\013\003"},
{PUSHKEY, "\033[200z"},       
{PUSHKEY, "\033[33~"},
{PUSHKEY, "\033[232z"},
{POPKEY, "\033[21~"},         
{POPKEY, "\013\026"},
{POPKEY, "\033[201z"},
{POPKEY, "\033[34~"},
{POPKEY, "\033[233z"},
{POPLIST, "\017&"},
{PURGELIST, "\017X"},
{FOREIGNKEY, "\034"},
{SETBOOKMARK, "\013\060"},
{GOBOOKMARK, "\021\060"},
{CENTERTEXT, "\017c"},
{CHARTRANSPOSE, "\017t"},
{LINETRANSPOSE, "\017T"},
{WORDERASE, "\024"},
{NEWSEARCH, "\021\006"},
{FWDSEARCH, "\033[15~"},
{FWDSEARCH, "\017f"},         
{FWDSEARCH, "\033[196z"},
{FWDSEARCH, "\033[28~"},
{FWDSEARCH, "\033[[E"},       
{FWDSEARCH, "\033[228z"},
{REVSEARCH, "\017b"},
{REVSEARCH, "\033[197z"},
{REVSEARCH, "\033[17~"},
{REVSEARCH, "\033[229z"},
{QUERYREPLACE, "\021\001"},
{QUERYREPLACE, "\033[1z"},
{RECORDING, "\017m"},
{RECORDING, "\033[199z"},
{RECORDING, "\033[32~"},
{RECORDING, "\033[19~"},
{RECORDING, "\033[231z"},
{RUNMEMORIZE, "\033[18~"},
{RUNMEMORIZE, "\017r"},
{RUNMEMORIZE, "\033[198z"},
{RUNMEMORIZE, "\033[31~"},    
{RUNMEMORIZE, "\033[230z"},
{MANYRUNMEM, "\017\012"},
{DISPLAYMACRO, "\017\060"},   
{DUMPKEYS, "\017+"},
{KEYSLOADUP, "\017@"},
{REPEATKEY, "\021\021"},
{SETEMACSMODE, "\013em"},
{SETWDSTARMODE, "\013ws"},
{SETWDPERFMODE, "\013wp"},
{SETMARK, "\013\002"},
{SWAPMARK, "\013\013"},
{REVEALMARK, "\013s"},
{COPYBLOCK, "\013c"},
{MOVEBLOCK, "\013m"},
{PASTEBLOCK, "\013p"},
{KILLNOTEPAD, "\017\030"},
{APPENDLINE, "\017\001"},
{ADDBUFFER, "\017\013"},
{QPUSHKEY, "\017\003"},       
{QPUSHKEY, "\033[23~"}, /* F11 */
{QPOPKEY, "\017\026"},
{QZAPKEY, "\017\032"},        
{OPTPUSHKEY, "\017\020"},
{OPTMATH, "\017i"},
{OPTHEX       , "\17H"},
{OPTOCT       , "\17O"},
{SETTABS, "\017\024"},
{SETDETAB, "\017\004"},
{SETTEXTTYPE, "\013\024"},
{REFORMKEY, "\002"},
{RUNHELP, "\017\010"},  /* ctrl-OH */
{RUNBHELP, "\021\010"}, /* ctrl-QH*/
{COMPLETEKEY  , "\17h"},
{COMPLETEKEY, "\033[192z"},
{SHOWKEYPRESS, "\21k"},
{MACROLIBRARY,  "\17e"},
{SETSTATUS, "\017\023"},
{SETAUTO, "\017\011"},
{SETHANGING, "\017\025"},
{WORDWRAP, "\017\027"},
{EXTENDEDCHAR, "\017\005"},   
{FOLDSRCH, "\017\006"},
{BACKTOGGLE, "\017\002"},
{ASCIITABLE, "\017a"},
{LOAD, "\013\014"},
{TSRKEY, "\013\032"},
{TSRKEY, "\033x"},
{TSRKEY, "\370"},  
{TSRKEY, "\303\270"},   /* Linux */
{SAVE_EXIT, "\013\030"},
{SAVE_NEXT, "\013\004"},
{BACKUP, "\013\023"},
{SAVEASKEY, "\013\027"},
{NAMECHANGE, "\013\016"},
{NEWLOAD, "\013\025"},
{RELOAD, "\013\05"},
{INJECT, "\013\022"},
{DSKFILES, "\013\006"},
{COLPOSITION  , "\13C"},
{ROWPOSITION  , "\13R"},
{CREATEFOLDER , "\13\7"},
{SCRAPBUFFER, "\013\021"},
{NEXTEDIT, "\014"},
{PREVEDIT, "\021\014"},
{SETBUFFER, "\013\001"},
{LFWDSEARCH, "\017F"},
{LREVSEARCH, "\017B"},
{ZEROTOKEN, "\200"},
{ZEROTOKEN, "\17z"},
{OPTMATHFLOAT, "\017j"},
{MATHFORMAT, "\017M"},
{OPTEVALEXPR, "\017k"},
{BAILOUT, "\025\025"},
{PRINTFILE, "\013\020"},
{SETPRINTER, "\017P"},
{CASELOWER, "\017l"},
{CASELLOWER, "\017L"},
{CASEUPPER, "\017u"},
{CASELUPPER, "\017U"},
{CASECAPITAL, "\017C"},
{MOUSEPRESS, "\033[M"},
{MOUSERELEASEVALID, "\033[t"},
{MOUSERELEASEINVALID, "\033[T"},
{RUNPROGRAM, "\017\022"},
{RUNPROGRAM, "\033[[B"},
{RUNPROGRAM, "\033[12~"},
{RUNPROGRAM, "\033[225z"},
{RUNPROGRAM, "\033OQ"},
{RUNMYPROGRAM, "\017R"},
{READPIPE, "\021\020"},
{RUNBACKGROUND, "\017\021"},
{OPTIONSET, "\017\017"},
{LINEDRAWKEY, "\017\014"},
{MENUBOXSET, "\21b"},
{SHRINKWINDOW, "\017-"},
{VIDEOMODE, "\017v"},
{NEWWINSIZE, "\017W"},
{MACROKEY, "\017s"},
{NULLKEY, "\377"},
#endif
/* Following is space for extra keys, which are entered in the editor's */
/* TOOLS menu. New definitions are saved in .pirc or PI.SET and invoked */
/* at runtime, when .pirc is loaded. You can also just edit .pirc. Special */
/* key assignments are possible by copying .pirc to a subdirectory, then */
/* edit that copy - launch PI from that directory to invoke just those keys. */
/* */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
#if 0
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
        {-1           , ""              },      /* NULL pointer is the end */
#endif
#endif
        {-1           , ""              }       /* NULL pointer is the end */
};
int MAXKEYS= (sizeof(ourkeys)/sizeof(ourkeys[0]))-1;
/*
 *
 * V0.C         Main program for the editor.
 *
 */

#include "pie.h"                /* Program-wide definitions             */

void adjustNonzeroScreenLines(){
char *p;
  for(p=bufbase;p<=lbuf;p += 1+strlen(p)) if(p[0]) ++nonzeroScreenLines;
/*  if(finfo[curfile].nbTOT) ++nonzeroScreenLines; */
}

#if MSDOS
void newuserAnnoyingMessage() {
char *msg[]={
 "==   PI Text Editor   ==",
 "ESC,ESC,ESC,ESC     Menu",
 "Ctrl-K,Ctrl-Z       Quit",
 "Q  [on menu]        Quit",
 "[Msg enabled in PI.SET]",
  0};
 if(ANNOYING==0) return;
 closehelpsheet();
 if(nonzeroScreenLines) return;
 WarningMessage(msg); /* memory released; erase screen later */
}       
#else
void newuserAnnoyingMessage() {
char *msg[]={
 "==   PI Text Editor   ==",
 "ESC,ESC,ESC,ESC     Menu",
 "Ctrl-K,Ctrl-Z       Quit",
 "Q  [on menu]        Quit",
 "[Msg enabled in ~/.pirc]",
  0};
 if(ANNOYING==0) return;
 closehelpsheet();
 if(nonzeroScreenLines) return;
 WarningMessage(msg); /* memory released; erase screen later */
}       
#endif

int main(argc,argv) int argc; char **argv; {
int i,ii,iii,NN; int n;
LLONG lx,ly;
int x,mainMACRO,maclevel;
int flg,fiddle,wasENTERKEY;
int killcurfile;
int repeatflag;
int cmd,freeze;
char *doGOTO;
unsigned c=0;
unsigned xc;
char **q;
char *p;
extern int regdef;
int bookcol;
LLONG bookline;
struct bigarray X;
struct bigarray far *PXbigarray= &X;
extern char *SRCDIR;
    
top:
  setup();                      /* TURBOC setup() sets screen black. */
  if(bufbase == NULLP) {        /* Only do this loop on first entry */
    progname=argv[0];           /* Program name on startup */
#if PIINILOAD                   /* These functions use core() or malloc */
    loadinifile(argc,argv);     /* All dimensions set here. */
#if TERMCAP
    loadtcap(argc,argv);        /* load termcap info. may quit(1). */
#endif
#endif
    /* Most variables below are set in PI.SET, ~/.pirc or ./pirc */
    insmode = INSMD;
    if(COLMAX>=MAXLN) MAXLN=COLMAX+10;
    if(ROWMAX>=MAXROWS) MAXROWS=ROWMAX+1;
    NN=1+MAXLN;
    iii=MYmax(NN,MAXFN);
    HEADROOM=NN+NN+(SIZEBLOCK=1024*SIZEKBLOCK);
    LINESIZE=MAXROWS*sizeof(char *);
    SCRSIZE=MYmax(NN*MAXROWS,HEADROOM);
    FARSCROLL=1+ROWMAX;                         /* must be > ROWMAX */
    bookline=bookcol=
    repeatflag=maclevel=flg=fiddle=tabstop=macmax=mainMACRO=0;
    XMSinit();  /* MSDOS: Changes MAXBLOCK, sets up handles[] */
    getcorememblock();
    line = (char **)core(LINESIZE); /* screen line pointers */
    scrbuf = core(SCRSIZE);             /* screen buffer SCRSIZE */
    optbuf = core(NN);                  /* phrase buffer */
    getkillercore();                    /* Killed line buffer alloc */
    macrobuf=(char **)core((MACNUM+1)*sizeof(char *));
    macromax=(int *)core((MACNUM+1)*sizeof(int));
    for(i=0;i<MAXROWS;++i) {line[i]= (char *)&scrbuf[i*NN]; }
    macbuf = macrobuf[0]                /* macro recording buffer */
           = core(MAXMB);
    sysv   = core(iii);                 /* system scratch */
    sysx   = core(iii);                 /* system scratch */
    sysvv  = core(iii);                 /* system scratch */
    srcharg = core(NN);                 /* last search argument */
    lastarg = core(NN);                 /* last getstr argument */
    replarg = core(NN);                 /* last replace argument */
    sysfile = core(MAXFN);              /* system file name, 128 */
    cd = core(CDSIZE);                  /* conn dir name, 128-1024 */
    i =3*HEADROOM;                      /* size of work buffer */
    bufbase = core(i);                  /* work buffer base */
    hbuf = ebuf = bufbase-1+i;
    *bufbase++ = EOS;                   /* guard byte for nonzero searches */
    *cd = *bufbase = *(lbuf=bufbase-1) = EOS;
    initfinfo();                        /* Initialize finfo[] array */
    corefinfo(0,NOTEPAD);
    finfo[0].ReadFtype=intexttype;      /* NotePad contains default */
    finfo[0].WriteFtype=outtexttype;    /* Import-Export text types */
    setfile(0); finfo[0].active=1;      /* Default file is NotePad */
#if MATHFLOAT
    safecore(&MATHFMT,"%.08g");
#endif
#if PIINILOAD
    loadkeys(argc,argv);                /* Load key definitions and sort! */
#endif
#if !(MSDOS || LCCWIN32)
    setdrawbuff();                      /* Set up buffer for drawing screen */
#endif
    sorttable();                        /* sort the key table! */
    getSHELL();                         /* Determine shell variable */
  }                                     /* end of setup routine */
 /* process command line options */
#if PIINILOAD
  initterminal();               /* set application pad (loadini data) */
                                /* Clear the screen (loadini data) */
  clrcon(); macabort();         /* clear type-aheads & buffer */
#endif
  stylechange(drawstyle);
#if BSDunix || SYS5
  freeze=0;
#endif
  if(argc>1) drawall();
  for(doGOTO=0,cmd=1;cmd<argc;cmd++){
    strcpy(sysv,argv[cmd]);
#if BSDunix || SYS5
    if(sysv[0]=='-'){                   /* command line goto line # */
      if(sysv[1]=='x') freeze=1;        /* freeze exits for xterm window */
      if(sysv[1]=='m')                  /* Use unix mouse with xterm */
        {mouseswitch=1; setmouse(1);}
    }
#endif
    flg = (cmd != argc-1);
    if(flg && *(p=argv[cmd+1])=='-' && p[1]=='g') doGOTO=p+2;
    if(sysv[0]!='-' && isinfo(sysv)==0) goto doload;
cmdline: ;
  }
  copydefaultfile();
  flg=0;
  if(usedfiles()==1) {
    if(ACTIONSTART) {
      drawall();  
      c=MENUKEY; lcount(); 
      goto popupmenu;
    }
    goto loadnewfile;
  }
  drawall();
  while(TRUE) {
loop:
    
#if BSDunix || SYS5          /* Window size change detected */
      if(doWinUpdate()) goto winupdate;
#endif
    lastkey=c;
    i = macrun;
    wasENTERKEY=0;
#if BSDunix || SYS5          /* Window size change detected */
    if(PIPEopen) c=getpipe();
    else
#endif
    if((c = getkey()) == ENTERKEY) {wasENTERKEY=1; c=entkey();}
popupmenu:
    /*newuserAnnoyingMessage();*/
    if(c==MENUKEY) {c=menu(); wasENTERKEY=0; curses();}
    if(repeatflag){ repeats=repeatflag; repeatflag=0; repkey=c;}
    if(c == COMPLETEKEY){ c=NULLKEY; HelpKeys((int *)&c);
                          goto popupmenu;}
    if(c == SHOWKEYPRESS){
         {char *msg[]={
           "====== Show keypress characters ======",
           "",
           " RETurn      Quit",
           " BACKspace   Erase char left",
           "",
           "Press ctrl, Alt, Shift, function keys",
           "to see the emitted Ascii chars.",
           0};
           WarningMessage(msg); /* memory released; erase screen later */
         }
      sysvv[0]=0;
      DisplayKeySeq(" RETURN quits: ",sysvv);
      closehelpsheet(); 
      goto loop;
    }
    testdraw();
    if(!macrun && i) lcount();
/* Regular characters change buffer text */
    if((0xFF00 & c) == 0) goto editor;
    /* this changes buffer text, then goes on to do cursor motion */
    if(drawline){
      iii=insmode;
      insmode=0;
      i=row; ii=col;
      if((x=boxdraw(c)) != 0){
        editln(x); fiddle=1; row=i; col=ii;
        if(col==COLMAX) --col;
      }
      insmode=iii;
    } /* now go do the cursor motion */

/* Is it a macro key? */
    i=c-MACROKEY; if(1<=i && i<= MACNUM) goto macroexec;

/* First try keys that don't change the buffer text */
/* These all accept a repeat count but may not optimize. */
    switch(c) {
#if BSDunix || SYS5
    case SETMOUSE:  /* Enable unix mouse */
               mouseswitch=1;
               setmouse(1); goto newcursor;
    case UNSETMOUSE: /* Disable unix mouse */
               mouseswitch=0;
               setmouse(0); goto newcursor;
    case MOUSEPRESS: /* Mouse event */
               pressmouse();
               goto newcursor;
    case MOUSERELEASEVALID: /* Valid mouse release */
               releasemouse('t'); goto newcursor;
    case MOUSERELEASEINVALID: /* Invalid mouse release */
               releasemouse('T'); goto newcursor;
#endif
#if MSDOS && DOS386I
    case MOUSEPRESS: /* Mouse event */
               pressmouse();
               goto newcursor;
#endif
    case OPTMATH:            /* Replace optbuf[] by math answer */
         convert2int(optbuf);
         goto newcursor;
      case OPTHEX:
        hexstrToBase10str(optbuf);
        goto newcursor;
      case OPTOCT:
        octstrToBase10str(optbuf);
        goto newcursor;
#if MATHFLOAT
    case OPTMATHFLOAT:  /* optbuf[] replaced by float math answer */
         convert2double(optbuf);
         goto newcursor;
    case OPTEVALEXPR:  /* Convert via expression eval */
         calculator(optbuf);
         goto newcursor;
    case MATHFORMAT:    /* Set sprintf format for float math answer */
         setmathformat();
         goto newcursor;
#endif
    case RECORDING:
recording:
         macabort();
         maclevel=macromax[mainMACRO=0]=nodisp=macmax=0;
         macbuf=macrobuf[mainMACRO];          /* set new macro */
         recordbanner();                /* set record=1, print message*/
         goto justcurses;
    case RUNMEMORIZE:                   /* Run macro */
         if(macrep>1) goto exitmacro;   /* ignore key in running macro */
         if(macrun || !wasENTERKEY)     /* poplevel or stop */
           goto runmem;
    case MANYRUNMEM:                    /* start recording or mult macro */
         if(!wasENTERKEY){
           sysarg=0;
           getnumquery("Repeat how many? ",&sysarg,0,-1);
           if(sysarg) repeats=sysarg; else goto newcursor;
         }
         if(!sysarg) goto recording;
runmem:
         if(maclevel) {                 /* deep recursion */
           setlevel(--maclevel,0);      /* pop level */
           goto exitmacro;
         }
         if(record) goto offrecording;  /* signal to quit recording */
         if(macrun) goto exitmacro;     /* ignore key in running macro */
         i=mainMACRO;
setmacro:                               /* do next macro on macrobuf[i] */
         macrun = 1;                    /* macro running */
         macind = 0;                    /* set start of buffer */
         macbuf=macrobuf[i];            /* set new buffer address */
         macmax=macromax[i];            /* set new buffer count */
         macrep=repeats;
         if(wasENTERKEY) {              /* Repeat count entered */
           if(sysarg<0) nodisp = 1;     /* Negative? Display off */
           macrep=MYabs(sysarg);          /* Set macro repeat count */
         }
         if(macrep<=0) macrep=1;        /* don't use bad repeat count */
         repeats=1;                     /* No one else gets repeats */
exitmacro:
         goto justcurses;
offrecording:
         /* Recording off by some key sequence but we don't know */
         /* the length of the key sequence. So we have to include */
         /* in the macro the termination sequence. */
         /* User macros can remove the extra characters. */
         macromax[mainMACRO=0]=macmax;
         macabort();
         maclevel=nodisp=0;             /* Display on, stack restored */
         banner("Stopped Recording");
         lnumcount();
         goto justcurses;
    /* Select and run a given macro */
macroexec:
         if(macromax[i]==0) break;      /* nothing to do */
         if((record && mainMACRO==i) || /* simple recursion detected */
            (maclevel>=10)){            /* deep recursion detected */
             macmax=0;
             goto offrecording;
         }
         if(macrun || record)           /* if key from a macro, then */
           setlevel(maclevel++,1);      /* push parameters */
         macabort();
         goto setmacro;
    case BACKTOGGLE:
         /* 0=FALSE,1=$HOME/PIbackups,2=./PIbackups */
         setbackups();
         goto justcurses;
    default: goto secondswitch;           /* key not found in list */
    }                                   /* end switch(c) */
    continue;

secondswitch:

    if(wasENTERKEY) c += WASENTER;      /* Mark enter key sequences */

    switch(c){
    case NEWSEARCH+WASENTER:            /* setup search string */
    case NEWSEARCH:                     /* setup search string */
         {char *msg[]={
           "Enter a search string.",
           "Then press:",
           "",
           " RET  Save string",
           " F5   Search forward",
           " F6   Search backward",
           "",
           "Recall string history",
           "with UpArrow, DnArrow.",
           "",
           "Ctrl-O,z matches begin",
           "line or end line. Two of",
           "them match a blank line.",
           0};
           WarningMessage(msg); /* memory released; erase screen later */
           /* closehelpsheet(); */  /* Clean up the screen */
         }
         c=getquery("Search for: ");
         closehelpsheet();
         switch(c){
         case RETURNKEY: if(sysv[0]) banner("Search string saved");
         case FWDSEARCH:
         case REVSEARCH: if(sysv[0]) copysrcharg(sysv);
                         if(c!=RETURNKEY) break;
         default: goto justcurses;
         }
    }

/* Character c wasn't harmless. Do next set which might change text. */
    switch(c){
    case TABKEY:                        /* TAB key ctrl-I */
           i = TSTOPS - ((col+curoff) % TSTOPS);
           if(TABINSERT){
             if(!DETABBER){editln('\t'); goto newcursor;}
             if(strlen(line[row])+i>=MAXLN-1){bell(); goto newcursor;}
             fillchr(sysx,' ',i); sysx[i]=0;
             addstring(sysx,line[row],1,1);
           }
           col = MYmin(COLMAX,i+col);
           goto newcursor;
    case TABKEY+WASENTER:
    case REVTABKEY:                     /* reverse tab */
         if((i = ((col+curoff) % TSTOPS)) == 0) i = TSTOPS;
         if(TABINSERT){                 /* reverse hacking tab */
           while(i && curchar()[-1]==' ') { editln(127); fiddle=1; --i; }
         }
         col = MYmax(0,col-i);
         goto newcursor;
    case WORDAHEAD:                    /* cursor to next word */
         nextword(0); goto newcursor;
    case WORDBACK:                     /* cursor to previous word */
         prevword(); goto newcursor;
    case UPARROW:                       /* cursor up */
         edup();
         goto newcursor;
    case DOWNARROW:                     /* cursor down */
         eddn();
         goto newcursor;
    case RIGHTARROW:                    /* cursor forward */
         edright(); goto newcursor;
    case LEFTARROW:                     /* cursor backward */
         edleft(); goto newcursor;
    case PAGEFORWARD:                   /* Page forward */
         setSYSone();                   /* copy "1" to sysv[] array */
    case PAGEFORWARD+WASENTER:          /* Enter+Page forward */
         if(sysv[0]==0) goto endoffile;
         Prefreshh(1);                  /* refresh, same cursor */
         goto newcursor;
    case CENTERREFRESH:                 /* Cursor at center, refresh */
         sysv[0]=0;
    case LINEFORWARD+WASENTER:          /* Enter+Line forward */
         Crefreshh(getatoiLL());        /* refresh, cursor center */
         goto newcursor;
    case LINEFORWARD:                   /* Line forward */
         linedn();
         if(row) decrROW();
         goto newcursor;
    case PAGEBACK:                      /* Page backward */
         setSYSone();
    case PAGEBACK+WASENTER:             /* Enter+Page backward */
         if(sysv[0]==0){ topfile(); goto newcursor;}
         Prefreshh(-1);                 /* refresh, same cursor */
         goto newcursor;
    case LINEBACK+WASENTER:             /* Enter+Line backward */
         Crefreshh(-getatoiLL());       /* refresh, cursor center */
         goto newcursor;
    case LINEBACK:                      /* Line backward */
         lineup();
         if(row<ROWMAX) incrROW();
         goto newcursor;
    case EMACSRETURNKEY:
emacsReturnkey:
         i=divide(); homeit(); col=i; eddn(); goto markchanged;
    case RETURNKEY:                     /* carriage return */
         if(insmode && EMACSMODE) goto emacsReturnkey;
         homeit(); eddn();
         /* Maybe indent to previous line if current line is blank. */
         /* Arrive here with row>0 and col==0. */
         if(HangingIndent && INDENAUTO && !line[row][0]){
           while(line[row-1][col] == ' ') ++col;
         }
         goto newcursor;
    case SPLITLINE:                     /* split line at cursor */
         divide(); goto markchanged;
    case INSERTLINE:                    /* insert line */
         addln("",TRUE); goto markchanged;
    case DELLINE:                       /* delete line */
         if(addtolist(line[row],secondkill(),1)){delln(sysv); goto markchanged;}
         goto justcurses;
    case DELLINE+WASENTER:              /* Enter+delete line */
massdel2:
         n=1;
massdel:
         lx=getatoiLL();
massdel1:
         if(massdelete(n,lx)) goto markchanged;
         goto newcursor;
    case ERASELINE:                     /* Erase but don't join lines */
         if(*curchar()==0) goto newcursor;
    case ERASEKEY+WASENTER:             /* Enter+erase to end of line */
         col = 0; curses();             /* erase whole line */
    case ERASEKEY:                      /* erase to end of line */
         killline();
         goto markchanged;
    case ADDBUFFER:
         setSYSone();
    case ADDBUFFER+WASENTER:
         goto massdel2;
    case DELCHAR:                       /* delete char */
         editln(0); goto textchanged;
    case WORDERASE:     /* erase word at cursor */
         if(nextword(1)) goto markchanged;
         goto newcursor;
    case PUSHKEY:                       /* Copy line to killerlist */
         if(addtolist(line[row],secondpush(),1)){
           eddn(); goto newcursor;
         }
         goto justcurses;
    case SETBUFFER:
         sysv[0]=0;
         if(FindFile(sysv,sysv,1)==0) goto newcursor;
         i=sysarg;
         goto setnextbuffer;
    case PREVEDIT:
         sysarg=1;
    case PREVEDIT+WASENTER:
         sysarg= -MYabs(sysarg);
         goto nextbuffer;
    case NEXTEDIT+WASENTER:             /* Edit next file in finfo[] chain */
         if(sysarg==0) sysarg = -1;
         goto nextbuffer;
    case NEXTEDIT:                      /* Edit next file in finfo[] chain */
         if(flg) goto cmdline;
         sysarg=1;
         goto nextbuffer;
    case REFORMKEY:                     /* reform line */
         reform(); goto markchanged;
    case CENTERTEXT:
         textcenter(); goto textchanged;/* center text on line */
    case CASELOWER:                     /* cursor char to lower case */
    case CASELLOWER:                    /* convert word to lower case */
    case CASEUPPER:                     /* cursor char to upper case */
    case CASELUPPER:                    /* convert word to upper case */
    case CASECAPITAL:                   /* capitalize word */
changecase:
         if(casechange(c)) goto textchanged;
         goto newcursor;
    case ZEROTOKEN:     /* Insert ZERO token for begin/end line */
        editln(zero); goto textchanged;
    case LFWDSEARCH+WASENTER:           /* Line Search forward */
    case LFWDSEARCH:                    /* Line Search forward */
         ii=3; goto searching;
    case LREVSEARCH+WASENTER:           /* Line Reverse search */
    case LREVSEARCH:                    /* Line Reverse search */
         ii=4; goto searching;
    case FWDSEARCH+WASENTER:            /* Search forward */
    case FWDSEARCH:                     /* Search forward */
         ii=1; goto searching;
    case REVSEARCH+WASENTER:            /* Reverse search */
    case REVSEARCH:                     /* Reverse search */
         ii=2; goto searching;
searching:
         if(wasENTERKEY) copysrcharg(sysv);
         if(!search(ii)) { macabort(); testdraw(); }
         /* Trick to load next key and erase highlighting */
         else {repkey=getkey(); ++repeats; unshowmatch();}
         goto justcurses;
    case QUERYREPLACE:
         if(queryreplace(insmode)) goto textchanged;
         goto newcursor;
    }/* end switch */


thirdswitch:


    if(!macrun) repeats=1;              /* Other commands can't use */
    switch(c){                          /* a repeat count at all */
    case GRABTEXT:                      /* copy screen text to status read */
         editln(127);                   /* or delete char left */
         goto textchanged;
    case SETBOOKMARK:                   /* Set bookmark line no. */
       bookline=lncount; bookcol=col;
       banner("Bookmark set");
       goto newcursor;
    case GOBOOKMARK:                    /* Goto bookmark line no. */
/* lncount problems */
       lx=bookline; col=bookcol; goto gotokey1;
    case GOTOKEY:                       /* ctrl-OG goto line # */
       getquery("GOTO line number: ");
    case GOTOKEY+WASENTER:              /* Enter+ctrl-OG goto line # */
gotokey:
        lx=getatoiLL();
gotokey1:
         ly=lx-lncount;
         if(lx>0 && ly) Crefreshh(ly);
         goto newcursor;
    case LINEDRAWKEY:                   /* ctrl-OL Toggle box/line draw*/
         stylechange(0);                /* ctrl-OL Set box/line style */
         goto newcursor;
    case HOMEKEY:                       /* cursor home */
         lncount -= row;
         row = col = 0; goto newcursor;
    case REVHOMEKEY:                   /* Cursor to screen bottom */
         lncount += (ROWMAX-row);
         col = 0; row = ROWMAX; goto newcursor;
    case FILETOP:                       /* Top of file */
         topfile(); break;
    case FILETOP+WASENTER:              /* Enter+Top of file */
    case FILEBOTTOM:                    /* keypad enter + function key f3 */
endoffile:
         eofile();
         endit();
         goto newcursor;
    case INSMODE:                       /* Insert mode toggle */
         settoggle("Insert",&insmode);
         goto justcurses;
    case QPUSHKEY:                      /* copy sysv to phrase buffer */
         getquery("Fill phrase: ");
         strcpy(optbuf,sysv);
         goto newcursor;
    case EXTRIGHTARROW:                 /* Cursor to right edge */
         endit();
         goto newcursor;
    case EXTLEFTARROW:                  /* Cursor to left edge */
         homeit();
         goto newcursor;
    case NAMECHANGE:                    /* change name of current file */
         if(curfile==0) break;          /* can't change notepad */
         if(getfname("New name: ",sysv) && !exists(sysv)) {
           if(corefinfo(curfile,sysv)) strcpy(sysfile,sysv);
         }
         goto newcursor;
    case SETTEXTTYPE:                   /* set In/Out Text Type */
         setinputoutput();
         goto newcursor;
    case SETTABS:
         settabs();
         goto newcursor;
    case ASCIITABLE:
         DOSTATUS =((DOSTATUS==3) ? 0 : 2);  /* fall through */
    case SETSTATUS:                     /* toggle status line on/off */
         /* 0=off, 1=full, 2=file only, 3=ascii table */
         if(++DOSTATUS > 3) DOSTATUS=0;
         { char *statusString[]={"OFF","NORMAL","FILEONLY","ASCII TABLE"};
           banner("Status line "); etype(statusString[DOSTATUS]); 
         }
         goto justcurses;
    case SETAUTO:                       /* toggle auto-indent mode */
         settoggle("Indent",&INDENAUTO);
         goto justcurses;
    case SETHANGING:                    /* toggle hanging-indent mode */
         settoggle("Hanging indent",&HangingIndent);
         goto justcurses;
    case WORDWRAP:                      /* toggle word wrap mode */
         settoggle("Word wrap",&WRDWRP);
         goto justcurses;
    case ANNOYINGMSG:                      /* toggle word wrap mode */
         closehelpsheet();
         settoggle("Annoying startup msg",&ANNOYING);
         goto justcurses;
    case SETDETAB:                      /* toggle auto-detab forward scroll */
         setdetab();
         goto newcursor;
    case SETBLANKSTRIP:                 /* Toggle strip of trailing spaces */
         settoggle("Stripping", &BLANKSTRIP);
         goto justcurses;
    case SETSTRIP:                      /* toggle strip parity bits */
         settoggle("High strip",&BITSTRIP);
         goto justcurses;
    case OPTIONSET:
         options(); goto redraw;
    case FOLDSRCH:
         settoggle("Fold case",&FOLDCASE);
         goto justcurses;
    case MACROKEY:
         macabort(); mainMACRO=maclevel=0;
         macmax=macromax[0]; macbuf=macrobuf[0];
         copymacro();
         goto newcursor;
    case SWAPMARK:
         if(markset) {
           swapmarker();
           if(lastkey!=SWAPMARK) goto justcurses;
           if(col+curoff>=markcol && lncount>=markline) goto reveal;
           goto revealmark;
         }
         goto reveal;
    case SETMARK:                       /* Set mark for block operations. */
         markblock(col+curoff);
         banner("Block start. ");
         strinvvideo("Move cursor to end of block.");
         goto justcurses;
    case REVEALMARK:
revealmark:
         if(markset) setcorner();
reveal:
         if(markset){
           i=regionbanner("RETURN to exit","");
           goto newcursor;
         }
reveal1:
         badregion(); goto justcurses;
    case MENUBOXSET:
         getnumquery("Menu box character set (0=Text,1=vga1,2=vga2,3=iso1,4=iso2) ",&boxcharset,0,4);
         goto newcursor;
    case COLORSET:
         setupcolors();
         goto redraw;
#if MSDOS
    case SWAPSW:                        /* Toggle MSDOS disk swap mode */
         swapswitch=1-swapswitch;
         banner( swapswitch ? "Ralf Brown SPAWN" : "TurboC");
         goto justcurses;
    case VIDEOMODE:
         i=x= getvideomode();
         getnumquery("Mode (0-3,7,80-90)",&x,0,100);
         if(x!=i) setvideomode(x);
         goto redraw;
    case CURSORSTYLESET:
         getnumquery("High (0-15) ",&cursorCH,0,15);
         getnumquery("Low (0-15) ",&cursorCL,0,15);
         cursorstyleset(cursorCH,cursorCL);
         goto redraw;
#endif
    case REPEATKEY:             /* Set repeat count for next key */
         repeats=repeatflag=1;
         i=getnumquery("Repeat count",&repeatflag,1,0);
         if(repeatflag<=1 || i==ENTERKEY || i==MENUKEY) repeatflag=0;
         else
         if(i!=RETURNKEY){
           repeats=repeatflag+1; repeatflag=0; repkey=i;
         }
         goto newcursor;
    default: goto fourthswitch;
    }/* end switch */
    continue;

fourthswitch:
/* Character c wasn't harmless. Do next set which changes text. */
    switch(c){
    case CONTROLENTRY:                  /* Control code input */
         /* ^@ comes out as 128 below */
         c=getbyte();
#if MSDOS                   /* Handle IBM-PC function keys */
         if(c==128) {editln(c); c=getbyte();}
#endif
         c |= 256;                      /* transparent to ^H and DEL */
editor:
         editln(c);
         goto textchanged;
    case DELMANYLINES:
         getquery("Delete lines. How many? ");
         n=0;
         goto massdel;
    case QZAPKEY:                       /* del phrase match at cursor */
         if(zap(optbuf)) goto textchanged;
         goto newcursor;
    case QPOPKEY:
         if(addstring(optbuf,line[row],TRUE,insmode)>=1) fiddle=1;
         goto newcursor;
    case CHARTRANSPOSE:   /* transpose characters at cursor */
         if(chartranspose()) goto textchanged;
         goto newcursor;
    case LINETRANSPOSE:  /* transpose lines at cursor */
         linetranspose();
         goto textchanged;
    case COPYBLOCK:                     /* dup onto notepad */
         if(!markset) goto reveal1;     /* Bad region */
         setcorner();
         i=regionbanner("Copy to NotePad [N]","YN");
         if(i != 'Y') goto newcursor;
         lx = 1+getoffset();
         if(regdef==0) goto pushmanylines1;
         i=MYmin(1+markcol-col,MAXLN-curoff-col); /* copy matrix */
         if(i<=0) goto newcursor;
         X.str=0;
         pushlines(curoff+col,i,PXbigarray,absLL(lx));
         goto newcursor;
    case APPENDLINE:            /* append line to NotePad */
    case OPTPUSHKEY:            /* Copy phrase to end of NotePad */
         setSYSone();           /* goto pushmanylines; */
    case APPENDLINE+WASENTER:   /* append line to NotePad */
    case PUSHKEY+WASENTER:      /* Copy line to killerlist */
pushmanylines:
          lx=getatoiLL();
pushmanylines1:
          ii = (absLL(lx)==1);
          if(ii)  strcpy(sysvv,(c==OPTPUSHKEY ? optbuf : line[row]));
          X.str=(ii ? (FARSTR)sysvv : (FARSTR)0);
          pushlines(0,0,PXbigarray,lx);
         if(c==APPENDLINE) eddn();
         goto newcursor;
    case MOVEBLOCK:                     /* Cut block to notepad */
         if(!markset){badregion(); goto justcurses;}
         setcorner();                   /* cursor to upper left corner */
         i=regionbanner("Move to NotePad [N]","YN");
         if(i!='Y') goto newcursor;
         lx = 1+getoffset();
movesetup:
         if(regdef==0){n=1; goto massdel1;}
movematrix:
         i=MYmin(1+markcol-col,MAXLN-curoff-col);
         if(i<=0) goto newcursor;
         X.str=0;
         x=(int)pushlines(curoff+col,i,PXbigarray,lx);
         if(!x) goto newcursor;
         dozap(x,i);                    /* delete matrix from screen */
         goto markchanged;
    case PURGELIST:
         purgelist2notepad(0); break;
    case POPLIST:
          if(yanklist()) goto markchanged; goto justcurses;
    case POPKEY:                        /* print lines from killerlist */
         if(poplist(0)) goto markchanged; goto justcurses;
    case POPKEY+WASENTER:               /* Enter+print killerlist */
         sysarg=MYmax(sysarg,1);
         goto insertlines;
    case PASTEBLOCK:                    /* print lines from NotePad */
         curses(); strinvvideo(" ");
         { char *msg[]={
           "1=Paste lines.",
           "  Inserts new rows above the",
           "  cursor line.",
           "2=Paste matrix.",
           "  Inserts new columns left of the",
           "  cursor column. Text moves right.",
           "3=Paint the last NotePad record",
           "  over text right of the cursor.",
           "",
           "The record separator is ctrl-A.",
           "Methods 1,2 cycle through records.",
           0};
           WarningMessage(msg);
         }
         banner("Pasting method [123]: ");
         switch(getupper()){
         case '1': /* Insert lines */
insertlines:
            i=0; ii=1; break;
         case '2': /* Insert matrix */
            i=1; ii=1; break;
         case '3': /* Writeover old text */
            bell();
            if(!getyesno("Confirm paste-over")) i=2;
            else {i=1; ii=0;}
            break;
         default: i=2;
         }
         closehelpsheet();
         sysarg=1;
pastelines:
         xPrint(curchar(),row,col);
         if(i!=2){
           if((lx=poplines(i,ii,sysarg,&iii,&x))>0) {
             if(ii==1){
              regdef=i;
              if(x) xc = 'P';     /* Matrix error */
                 else
              xc=regionbanner("P=Previous. Save it [N]","YNP");
              if(xc=='Y') fiddle=1;
              else {
                if(!i) massdelete(0,lx); else
                dozap((unsigned int)lx,iii);   /* delete matrix from screen */
                markset=0;
                if(xc=='P'){++sysarg; goto pastelines;}
              }
             } else fiddle=1;
           }
         }
         goto newcursor;
    case KILLNOTEPAD:
         scrapfile(0);  /* does not kill off finfo[0] */
         if(curfile==0){setfile(0); goto editnext;}
         goto newcursor;
    case SCRAPBUFFER:                   /* scrap current file, edit next */
         if(!fiddle && usedfiles()==1) goto exiteditor;
         goto scrap;
    case SAVE_NEXT:                     /* Save and edit next ctrl-KD */
         if(!fiddle){
           if(usedfiles()==1) goto exiteditor;
           goto junkfile;
         }
    case BACKUP:                        /* Save and continue ctrl-KS */
         if( (x=closef(sysfile)) == -1) goto redraw;
         if(x && curfile) { /* Don't change NotePad name */
           if(MYstrcmp(sysfile,finfo[curfile].fname))
             corefinfo(curfile,sysfile);
           finfo[curfile].dirty=fiddle=0;
         }
         if(!x || c==BACKUP) break;
         goto junkfile;
    case SAVEASKEY:                     /* ctrl-KW save file as .... */
         if(!getfname("Enter new file name: ",sysv)) goto newcursor;
         goto backup;
    case SAVE_EXIT:                     /* Save and quit, ctrl-KX */
         if(!fiddle) goto leaveeditor;  /* Nothing to save */
         if( (x=closef(sysfile)) == -1) goto redraw;
         if(!x) break;
         finfo[curfile].dirty=fiddle=0;
         if(x==2) c=TSRKEY;             /* For forked child exit */
leaveeditor:
#if MSDOS || LCCWIN32
         if(SUSPENDexit){ /* MSDOS simulation of unix suspend */
           savefinfo(fiddle);
#if LCCWIN32
           putenv("prompt=Editor suspended. EXIT returns.$_$p$g");
#endif
           runprogram(SHELL,0);         /* SHELL defined in v.h */
           goto redraw;
         }
#endif
    case BAILOUT:                       /* Quit editor ctrl-U */
         macabort();
         clrcon();
    case TSRKEY:                        /* kill process ctrl-KZ */
exiteditor: sysv[0]=EOS;
backup:
    case SAVE_NEXT+WASENTER:            /* Enter+Save and edit next ctrl-KD */
    case BACKUP+WASENTER:               /* Save and continue ctrl-KS */
    case SAVEASKEY+WASENTER:            /* ctrl-KW save file as .... */
         savefinfo(fiddle);
#if MSDOS
         if(sysv[0] == EOS) {           /* or quit without save */
           if(c==TSRKEY || c==BAILOUT || c==SAVE_EXIT){
             if(showdirty()) goto redraw; quit(1);}
           quit(2);
         }
#endif
#if BSDunix || SYS5                     /* Suspend and continue */
         if(sysv[0] == EOS) {           /* or quit without save */
           if(c==TSRKEY ) { if(showdirty()) goto redraw; quit(1); }
           if(!freeze) quit(2);
           goto redraw;
         }
#endif
         if(exists(sysv) == 0) {        /* query for over-write */
           x=closef(sysv);              /* save the file */
           if(x == -1) goto redraw;
         }
         break;
    case RELOAD:                        /* reload current file */
         if(curfile==0) goto newcursor; /* can't reload NotePad */
         if(fiddle){
           bell();
           if(!getyesno("Discard edit")) goto newcursor;
         }
         strcpy(sysv,sysfile);
         *(finfo[curfile].fname)=255;   /* stop load twice complaint */
         killcurfile=curfile;
         goto doload;
    case NEWLOAD:                       /* load new Untitled ctrl-KU */
loadnewfile:
         if((NN=getslot()) != -1) {
           savefinfo(fiddle);
           sysfile[0]=0;
           goto doNEWfile;
         }
         break;
    case LOAD:                          /* load new file */
         if(!getfname("Enter file to load: ",sysv)) 
           goto newcursor;
    case LOAD+WASENTER:                 /* Enter+load new file */
doload:
         savefinfo(fiddle);
         if(sysv[0] != EOS){
           strcpy(sysfile,sysv);
           NN=getfile(sysfile);         /* might change sysfile */
           /* Beware: this is a trick */
           if(c==RELOAD) strcpy(finfo[killcurfile].fname,sysv);
           if(NN<0){
             if(NN == -1){
               char *msg[]={"Too many files, can't open","",0};
               msg[1]=sysfile;
               WarningMessage(msg);
             }
             else if(NN == -2) {
               char *msg[]={"Can't open","",0};
               msg[1]=sysfile;
               WarningMessage(msg);
               if(getyesno("New File")
                  && sysfile[0] && (NN=getslot()) != -1) goto doNEWfile;
             } else if(NN == -3) {
               if(newedit(nextfile(sysarg)) != -1) goto editnext;
             }
             copydefaultfile();
             goto newcursor;
           }
           else {
doNEWfile:   if(!corefinfo(NN,sysfile[0] ? sysfile : UNTITLED) ||
                newedit(NN) != -1) {
                  getfinfo(&fiddle); startup();
                  convertall(NN);
                  reportfilekind();
                  if(c==RELOAD){
                     lx=finfo[killcurfile].linec;
                     col=finfo[killcurfile].colc;
                     scrapfile(killcurfile); /* don't kill notepad! */
                     goto gotokey1;
                  }
                  if(doGOTO){
                    strcpy(sysv,doGOTO);
                    doGOTO=0; goto gotokey;
                  }
                }
             else goto copyname;
           }
           goto newcursor;
copyname:
           copydefaultfile();
           goto redraw;
         }
scrap:
         if(fiddle){
           bell();
           if(!getyesno("Discard edit")) goto newcursor;
         }
junkfile:
         scrapfile(curfile);            /* won't kill notepad */
         i=nextfile(curfile+1);         /* usually don't edit NotePad */
         setfile(i ? i : nextfile(curfile-1));
editnext:
         lbuf=bufbase-1; hbuf=ebuf;
         UnPackScreenBuffer(row);
         getfinfo(&fiddle);
         goto redraw;
nextbuffer:
         i=nextfile(curfile+sysarg);
setnextbuffer:
         savefinfo(fiddle);
         if(i!=curfile && newedit(i) != -1) goto editnext;
         goto newcursor;
    case INJECT:                        /* Inject file at cursor */
         if(!getfname("Enter file to inject: ",sysvv)) goto newcursor;
    case INJECT+WASENTER:               /* Enter+Inject file at cursor*/
         if(inject(sysvv)<0) {
           bellpressany("Inject Failed");
           goto newcursor;
         }
         reportfilekind();
         goto markchanged;
    case EXTENDEDCHAR:                  /* Change prev to extended char */
         if(col>0){
           xc=curchar()[-1];
           xc ^= 128;                   /* Add or subtract 128-bit */
           goto changeforeign;
         }
         goto newcursor;
    case FOREIGNKEY:
         if(col>0 && (xc = foreign(curchar()[-1]))!=0){
changeforeign:
           i=insmode; insmode=0;        /* insert off */
           edleft(); curses();
           editln(xc);  /* replace with foreign char extended char set */
           insmode=i;
           fiddle=1;
         }
         goto newcursor;
    case RUNPROGRAM:                    /* fork to system command processor */
         runprogram(SHELL,0);           /* SHELL defined in v.h */
         goto redraw;
    case READPIPE:
    case RUNBACKGROUND:                 /* Run background, no re-entry prompt */
    case RUNMYPROGRAM:                  /* fork system program or batch file */
         getquery("Enter system command: ");
         if(!sysv[0]) goto newcursor;
#if SYS5 || BSDunix
         if(c==READPIPE){
           readpipe(sysv);
           goto redraw;
         }
#endif
    case RUNBACKGROUND+WASENTER:
    case RUNMYPROGRAM+WASENTER:
    case RUNPROGRAM+WASENTER:
         /* clearscreen(); */
         runprogram(sysv,c==RUNMYPROGRAM ? 1 : 0);
         goto redraw;
    case DSKFILES:                      /* inject list of directory files */
         getquery("Enter filespec: ");
         curses();
         if(sysv[0] && freespace()){
           banner(sysv); fiddle=1; markset=0; goto justcurses;
         }

         goto newcursor;
      case CREATEFOLDER: /* Make folder or directory */
         if(getfname("Enter folder pathname: ",sysv)){
           int x;
           x=mymkdir(sysv);
           {
             char *msg[]={0,0,0};
             msg[1]=sysv;
             msg[0] = ((x == -1) ? "Can't create folder" : "Created folder");
             WarningMessage(msg);
           }
         }
         goto justcurses;
    case -1:
    case NULLKEY:
    case 0: break;
    default: goto extras;
    }
    continue;
markchanged: markset=0;                 /* mark changed */
textchanged: fiddle=1;                  /* text changed */
newcursor:   /* newuserAnnoyingMessage();*/
             lcount(); continue;        /* update status line */
justcurses:  curses(); continue;        /* just put cursor back*/
redraw:      killstatus(); drawall();   /* redraw whole screen */
             continue;                  

extras:
#if PIINILOAD || PIHELPDOC || PIHELPEXE
    switch(c){
#if PIHELPDOC || PIHELPEXE
    case HELPBROWSER:
               { char *msg[]={
                 "Set the browser default in",
#if MSDOS
                 "in PI.SET, e.g.,",
                 " browser=iexplore",
                 " browser=firefox",
#endif
#if LCCWIN32
                 "in .pirc, e.g.,",
                 " browser=iexplore",
                 " browser=firefox",
#endif
#if !(MSDOS || LCCWIN32)
                 "in .pirc, e.g.,",
                 " browser=lynx",
                 " browser=mozilla &",
#endif
                 0};
                 WarningMessage(msg);
               }
           bell();
           strcpy(sysv,BROWSER);
           if(ggetstr("Program name:",sysv)==RETURNKEY){
             if(MYstrcmp(sysv,BROWSER)!=0)
               safecore(&BROWSER,sysv);
           }
           goto redraw;
#if MSDOS || LCCWIN32
    case HELPHTML:
           ssprintf(sysvv,"start %s %s",BROWSER,SRCDIR);
           addslash(sysvv);
           strcat(sysvv,"pihelp.htm");
           backslashes(sysvv);
runp:
           runprogram(sysvv,0); goto redraw;
#if LCCWIN32
    case RUNBHELP:
           ssprintf(sysvv,"start %s",SRCDIR);
           addslash(sysvv);
           strcat(sysvv,"pihelpbu.exe");
           backslashes(sysvv);
           goto runp;
#endif
#endif
#if !(MSDOS || LCCWIN32)
#if CYGWIN
    case HELPHTML:
           ssprintf(sysvv,"start %s %s",BROWSER,SRCDIR);
           if((p=MYstrchr(sysvv,'&'))!=0) p[0]=' ';
           addslash(sysvv);
           strcat(sysvv,"pihelp.htm");
           p=MYstrstr(sysvv,SRCDIR);
           if(p!=(char *)0){
             cygwin_conv_to_full_win32_path(p,sysx);
             ssprintf(p,"\"%s\"",sysx);
           }   
runp:          
           runprogram(sysvv,0); goto redraw;
    case RUNBHELP:
           p=isXwindows() ? "xterm -geometry 80x25 -e " : "start ";
           ssprintf(sysvv,"%s %s",p,SRCDIR); addslash(sysvv);
           strcat(sysvv,"pihelpbu.exe");
           if(isXwindows()) strcat(sysvv,"&");
           else {
             p=MYstrstr(sysvv,SRCDIR); 
             if(p!=(char *)0){
               cygwin_conv_to_full_win32_path(p,sysx);
               ssprintf(p,"\"%s\"",sysx);
             }
           };
           goto runp;
#endif
#if !CYGWIN
    case HELPHTML:
           ssprintf(sysvv,"%s %s",BROWSER,SRCDIR);
           if((p=MYstrchr(sysvv,'&'))!=0) p[0]=' ';
           addslash(sysvv);
           strcat(sysvv,"pihelp.htm");
           if(p) strcat(sysvv," &");
runp:
           runprogram(sysvv,0); goto redraw;
    case RUNBHELP:
           if(isXwindows()) strcpy(sysvv,"xterm -geometry 80x25 -e ");
           else sysvv[0]=0;
           strcat(sysvv,SRCDIR); addslash(sysvv);
           strcat(sysvv,"pihelpbu.exe");
           if(isXwindows()) strcat(sysvv,"&");
           goto runp;
#endif
#endif
#endif
#if PIINILOAD
    case MACROLIBRARY:
         HelpKeys((int *)&c);
         if(c==MACROLIBRARY) c=NULLKEY;
         goto popupmenu;
    case KEYSLOADUP:
         readkeymap();
         goto newcursor;
    case DISPLAYMACRO:
         pimacrodump();
         goto newcursor;
    case DUMPKEYS:
         pidumpkey();                         
         goto newcursor;
    case SHRINKWINDOW:
         shrinkwindow();
         goto newcursor;
#if BSDunix || SYS5
    case NEWWINSIZE:
winupdate:
#if !LCCWIN32
         resetWinSize(&ii,&iii);
         setwindowsize(ii,iii);
#endif
         goto newcursor;
    case SETEMACSMODE:  setEmacs(); goto newcursor;
    case SETWDSTARMODE: setWdstar(); goto newcursor;
    case SETWDPERFMODE: setWdperf(); goto newcursor;
#endif

#endif
#if PIHELPEXE
    case RUNHELP:                       /* run help program */
         p="pihelph"; goto rundosbox;
    case RUNBHELP:
         p="pihelpb";
rundosbox:
         ssprintf(sysx,"%s%s",(isWinEnhanced()? "start ":""),SRCDIR);
         addslash(sysx); strcat(sysx,p);
         runprogram(sysx,0); goto redraw;
    case SETPRINTER:
         printsetup(); goto newcursor;
    case PRINTFILE:

         if(getyesno("Format for printer")){
           if(fiddle && curfile){
             pressany("Save the file first.");
             goto newcursor;
           }                            /* can't change notepad */
           if(curfile && MYstrcmp(sysfile,UNTITLED)){ 
             if(corefinfo(curfile,UNTITLED)) strcpy(sysfile,UNTITLED);
           }
           printformat();
           fiddle=1;
         }
         strcpy(sysv,"prn");
         ssprintf(sysvv,"Send to printer %s",sysv);
         if(getyesno(sysvv)) goto backup;
         goto justcurses;
#endif
#if PIHELPDOC
    case RUNHELP:                       /* run help program */
         clearscreen();
         help(/* c-RUNBHELP */);
         goto redraw;
    case SETPRINTER:
         printsetup(); goto newcursor;
    case PRINTFILE:
{

         if(getyesno("Format for printer")){
           if(fiddle && curfile){
             pressany("Save the file first.");
             goto newcursor;
           }                            /* can't change notepad */
           if(curfile && MYstrcmp(sysfile,UNTITLED)){ 
             if(corefinfo(curfile,UNTITLED)) strcpy(sysfile,UNTITLED);
           }
           printformat();
           fiddle=1;
         }
         strcpy(sysv,"Send to default printer");
         if(!getyesno(sysv)) goto justcurses;
         ssprintf(sysvv,"/tmp/%s",(char *)ffname(sysfile));
         maketempfilename(sysvv);
         i=closef(sysvv);               /* save the file */
         { char *p; extern char *SRCDIR; extern char *getenv();
           if(i != -1){   /* it worked */
             p=getenv("HOME");
             if(p!=(char *)0) {strcpy(sysv,p); addslash(sysv);} else sysv[0]=0;
             strcat(sysv,".printfilter");
             if(classify(sysv)!=1){
               ssprintf(sysv,"%sprintfilter",SRCDIR);
             }
             strcat(sysv," "); strcat(sysv,sysvv);
             system(sysv);
             unlink(sysvv);
           }
         }
         goto justcurses;
}
#endif

    } /* end switch(c) on reform, iniload, help */
#endif

  } /* end while(TRUE) */
  return 0;
} /* end main() */

#if SUN350 || ARDENT || DEC3100 || IBMAIX /* sun 3/50, sun 386/i, BSD4.x*/

#if macOSX
#include <sys/stat.h>                      
#else
#include <unistd.h>         
#include <sys/types.h>                     
#include <sys/stat.h>
#endif

static struct stat stattmp;
                
void modeSetup(filename) char *filename;{
  stat(filename,(struct stat *)&stattmp);
#if macOSX      /* bug in stat() 2006 */
  if(stattmp.st_mode==0){
    char s[MAXFNAME]; char *t; int x;
    strcpy(s,filename);
    t=MYstrrchr(s,'/');
    if(t != (char *)0) t[0]=0; else strcpy(s,"./");
    stat(s,(struct stat *)&stattmp);
    x= stattmp.st_mode & 
       (S_IRUSR| S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
    stattmp.st_mode=x;
  }
#endif
}

void modeFix(filename) char *filename;{
  if(stattmp.st_mode!=0)  chmod(filename,stattmp.st_mode);
}

#endif


int mymkdir(s) char *s;{
int x;
#if MSDOS || LCCWIN32
      x=mkdir(s);              /* make folder */
#else
#include <sys/stat.h>
      x=mkdir((const char *)s,(mode_t)0777);         /* make directory [folder] */
#endif
return x;
}


/* This code can be used to perform simple timing tests */
/* Usage: timer(0) sets timer, timer(1) displays elapsed time. */
#if 0
#if MSDOS
timer(n)
int n;
{
static long m; int k1,k2;
char tmp[128]; long clock();
char *endof();
if(n==0){
m = clock();
} else {
m = clock() - m;
 k1=m/100; k2=m%100;
 ssprintf(tmp,"Elapsed time = %u and %u/100 seconds",k1,k2);
 pressany(tmp);
}
}

GETtime(time) unsigned time[2];
{
void __int__();
_AX=0x2c00; __int__(0x21); time[1]= _CX; time[0]= _DX;
}

long clock(){ long x; unsigned time[2];
  GETtime(time);
  x=time[0]/256+60*(time[1]&255) + (time[1]/256)*3600L;
  x=100*x+(time[0]&255);
  return x;
}
#endif

#if BSDunix || SYS5
#include <time.h>
timer(n)
int n;
{
static long m;
char tmp[128];

if(n==0){
m = clock();
} else {
m = clock() - m;
#define FACTOR 1000000L
ssprintf(tmp,"Elapsed time = %ld.%ld sec",
m/FACTOR,m%FACTOR);
#undef FACTOR
pressany(tmp);
}
}
#endif

#endif


#if 0
showmemarray(){
 int i,j;
 char far *x;           
 char s[128];
#define FP_OFF(fp)      ((unsigned)(fp))
#define FP_SEG(fp)      ((unsigned)((unsigned long)(fp) >> 16))
  for(i=0;i<MAXBLOCK;++i) {
    j=memmark[i];
    if(j){
       x=(char far *)getmemblock(i);
       ssprintf(s,"Block %u: %u.%u",i,FP_SEG(x),FP_OFF(x));
       getyesno(s);
    }
  }
}
#endif
/*
 * V1.C
 *
 */

#include "pie.h"
/*
 * GETKEY
 *
 * Purpose:
 *              Accept regular or function keys from the console.
 * Returns:
 *              Coded 16-bit integer for each single keystroke.
 *              Each distinct keystroke must be encoded to
 *              a unique integer in the range 0...65535.
 * Notes:
 *              o Ideas for most termcap terminals.
 *              o One character is returned if it is not a leadin
 *                character from one of the strings in ourkeys[].
 *              o No timer event flags are implemented.
 *              o MYstrncmp(), MYstrcmp() must be specially written
 *                to handle unsigned chars.
 */
static char safekeybuf[MKLEN+2];
char *getkeybuf=safekeybuf;
int getkeynchars;       
                        
unsigned getkey() {          
int lo,hi,i,j,flg; unsigned c;  
                        
  /* Repetition count on last key */
  if(repeats>1){--repeats; if(!ccBIOS(2)) return repkey; repeats=1;}
  c = getbyte();        
  if(MYstrchr(LeadinSTR,c)==0) return c; /* not a leadin chr, return c*/
  getkeynchars = 0;     
  while(TRUE) {         
    getkeybuf[getkeynchars] = c;
    getkeybuf[++getkeynchars] = EOS;
    if(getkeynchars < MKLEN){
      flg = FALSE;      
      hi = MAXKEYS-1; lo = 0;
      while(lo <= hi){    /* requires the keys to be sorted */
        i = (lo+hi)/2;  
        if((j=MYstrncmp(ourkeys[i].seq,getkeybuf,getkeynchars)) == 0){
          if(strlen(ourkeys[i].seq)==getkeynchars) return (ourkeys[i].value);
          flg=TRUE; break;
        }               
        if(j < 0) lo = i+1; else hi = i-1;
      }                 
      if(flg) {c=getbyte(); continue;}
      if(getkeynchars == 1) return c;
    }                   
    return NULLKEY;     
  }                     
  return NULLKEY;       
}                       
                                
#if !MSDOS             /* MSDOS is in assembler elsewhere */
int MYstrncmp(s,t,n) unsigned char *s,*t; int n; {
  while(n-- && s[0]==t[0]){ if(!s[0] || !n) return 0; ++s; ++t;}
  return ((unsigned char)s[0]-(unsigned char)t[0]);
}                       
                        
int MYstrcmp(s,t) unsigned char *s,*t; {
  while(s[0] && s[0]==t[0]){ ++s; ++t;}
  return ((unsigned char)s[0]-(unsigned char)t[0]);
}                       
#endif                  
                        
void makeLeadinCharString(){
int i,j; unsigned c;
  if(LeadinSTR==0) LeadinSTR=(unsigned char *)core(1+MAXKEYS);
  LeadinSTR[0]=0;
  for(i=j=0;i<MAXKEYS;++i){
    c = *(ourkeys[i].seq);
    if(c!=0 && MYstrchr(LeadinSTR,c)==0){
      LeadinSTR[j++]=c;    
      LeadinSTR[j]=0;      
    }                      
  }                        
}                          
                           
int sorttable(){           
int n,gap,i,ig,j,t; char *tmp;
  n=MAXKEYS;               
  for (gap = n/2 ; gap > 0 ; gap /= 2)
    for (j = gap ; j < n; j++)
      for (i = j-gap ; i >= 0 ; i -= gap) {
              ig = i + gap;
              if (MYstrcmp(ourkeys[i].seq,ourkeys[ig].seq) <= 0)
                      break;
              tmp=ourkeys[i].seq; ourkeys[i].seq=ourkeys[ig].seq;
              ourkeys[ig].seq=tmp;
              t=ourkeys[i].value; ourkeys[i].value=ourkeys[ig].value;
              ourkeys[ig].value=t; 
      }
  makeLeadinCharString();
  return 0;
}

/*
 * GGETSTR
 *
 * Purpose:
 *              Read in a line of text, terminate on RETURN
 *              or a special key. Delete key edits
 *              the input. TAB completes filename.
 *              Search keys recall the last search string.
 *              The string is NULL-terminated.
 * Returns:
 *              The integer value of the break character.
 */
int ggetstr(msg,p)
char *msg;  /* expect msg to be null terminated */
char *p;    /* expect p to be null terminated */
{
char buf[MAXFNAME];
int c,n;
int j;
char *q,*s,*r;

  q = (char *)curchar();
top:
  bbanner(msg,p);
  n = strlen(p);
  while(n<MAXLN) {
    if((c = getkey()) == CONTROLENTRY) c = getbyte();
    else {
      switch(c){
#if MSDOS && DOS386I
      case MOUSEPRESS: /* Mouse event */
               pressmouse(); goto killchars;
#endif
#if BSDunix || SYS5
      case MOUSEPRESS: /* Mouse click event. Kill trailing bytes. */
           getbyte(); getbyte(); getbyte(); goto killchars;
#endif
      case POPKEY:   /* Recopy last argument */
                     strcpy(p,lastarg);
                     goto killchars;
      case TABKEY:
        if((s=MYstrrchr(p,' ')) != (char *)0) ++s; else s=p;
        strcpy(buf,s);
        if((j=dir(buf,-1,0,(char *)0,(char **)0)) >= 1) { /* complete file name */
          stripstars(buf);      /* Remove extra * on end */
          strcpy(s,buf);
          goto killchars;
        } else bell();
        goto loop;
      case EXTRIGHTARROW:
      case GRABTEXT:            /* Grab token from cursor position */
                     j=fnb(sob(q))-q;
                     if(j+n<MAXLN){
                       strnzcpy(p+n,q,j); q += j;
                       goto printend;
                     }
                     goto loop;
      case QPOPKEY:
        j=MYmin(strlen(optbuf),MAXLN-n-1);
        strnzcpy(p+n,optbuf,j); goto killchars;
printend:
        ePrint(p+n); n=strlen(p); goto loop;
#if MATHFLOAT
      case OPTMATHFLOAT:
        convert2double(p);
        goto killchars;
      case OPTEVALEXPR:
        calculator(p);
        goto killchars;
#endif
      case OPTHEX:
        hexstrToBase10str(p);
        goto killchars;
      case OPTOCT:
        octstrToBase10str(p);
        goto killchars;
      case OPTMATH:
        convert2int(p);
        goto killchars;
      case RIGHTARROW: c = (q[0]? *q++ : ' '); break;
      case FWDSEARCH: /*  Search forward */
      case REVSEARCH: /*  Search reverse */
      case LFWDSEARCH: /*  Line Search forward */
      case LREVSEARCH: /*  Line Search reverse */
                     if(n==0){strcpy(p,srcharg); goto killchars;}
                     break;
      case DOWNARROW: getsrcharg(p,-1); goto killchars;
      case UPARROW:   getsrcharg(p,1); goto killchars;
      case REVTABKEY: /* Reverse tab key mungs prev file name to unix std */
                     if(n){
                       if((r=MYstrrchr(p+n-1,' '))==(char *)0) r=p;
                       while((r=MYstrchr(r,'\\'))!=(char *)0) r[0]='/';
                       goto killchars;
                     }
                     break;
      case DSKFILES: /* Insert file name */
                     if(1+n+strlen(sysfile)<MAXLN){
                       strcpy(p+n,sysfile);
                       goto printend;
                     }
                     goto loop;
      case COLPOSITION: /* Insert column number */
                     (void)lltoa((LLONG)(col+1), buf);
                     if(1+n+strlen(buf)<MAXLN){
                       strcpy(p+n,buf);
                       goto printend;
                     }
                     goto loop;
      case ROWPOSITION: /* Insert column number */
                     (void)lltoa((LLONG)(row+1), buf);
                     if(1+n+strlen(buf)<MAXLN){
                       strcpy(p+n,buf);
                       goto printend;
                     }
                     goto loop;
      case REPEATKEY: /* Multiply argument by 4*/
                     if((j=(int)atoiLL(p))==0) j=1;
                     lltoa((LLONG)(4*j),p);
killchars:
                     goto top;
      case 8:case 127:case LEFTARROW:
                     if(n) { erachar(); p[--n]=EOS; } else bell();
                     goto loop;
      case ZEROTOKEN:   /* Insert begin or end of line token */
                     c=zero; break;
      }/* end switch */
      if(c > 1000) {p[n] = EOS; break;}
    }
    p[n++] = c;
    p[n] = EOS;
    ePrint(&p[n-1]);
loop: ;
  }/* End while() */
end:
  p[n] = EOS;
  if(p[0]) strcpy(lastarg,p);    /* Save whatever was typed in */
  return c;
}

/*
 * ENTKEY
 *
 * Purpose:
 *              Process keys after enter key.
 * Returns:
 *              Coded break key and system variables.
 * Notes:
 *              Independent of key definitions.
 */
int entkey() {
int c;

  c=getquery("ENTER: ");
  sysarg=(int)getatoiLL();    /* Collect numeric argument, if any */
  lcount();
  if(c==ENTERKEY && sysv[0]==0) return MENUKEY;
  if(c==RETURNKEY) return NULLKEY;
  return c;
}

/*
 * GETBYT
 *
 * Purpose:
 *              1. Get raw character from keyboard, no echo.
 *              2. If macrun <> 0, then get the char from macbuf[].
 *              3. If record <> 0, then add the char to macbuf[].
 * Returns:
 *              Integer c in the range 0...127, usually. The
 *              keyboard is expected to transmit 0...255.
 * Notes:
 *              o ccBIOS(3) is a library direct-bios call
 *                that reads the console, no-echo; ccBIOS(2) checks
 *                console status.
 *              o Any keyboard hit stops a running macro.
 *
 */
int getbyte() {
int c;

  while(TRUE) {
    if(macrun) {
      if(macind>=macmax) {
        if(macind==0) statblank=TRUE;
        macind = 0;
        if(ccBIOS(2)) goto quit;
        if(macrep>1) --macrep;
        else goto quit;
      }
      else
      return (macbuf[macind++]);
      continue;
    }
    c = ccBIOS(3);
    if(c==0) c=128;     /* normally can't use null in a string */
    if(record) {
      if(macmax>=MAXMB) { bell(); record = 0;}
      else {
        macbuf[macmax++] = c;
        macbuf[macmax]=0;  /* Terminate the string */
      }
    }
    return c;
  }
quit: macabort(); return -1;
}

/*
 * MACABO
 *
 * Purpose:
 *              Abort a running macro sequence.
 */
void macabort() {
  record = macind = macrun = 0; macrep = 1;
}

/*
 * TESTDR
 *
 * Purpose:
 *              Test to see if we need to redraw the screen
 *              after a silent macro.
 * Notes:
 *
 */
void testdraw() {
extern int nodisp,halfdone;
  if(halfdone || nodisp) {
    if(!macrun) {
      nodisp = 0;
      drawall();
    }
  }
}

int getquery(s) char *s; {
  sysv[0]=0; return ggetstr(s,sysv);
}

int getnumquery(s,x,low,high) char *s; int *x; int low,high;{
int y; char v[128]; int c; char *endof();
  ssprintf(v,"%s [%u]: ",s,x[0]);
  c=getquery(v);
  if(sysv[0]){
    y=(int)getatoiLL();
    if(high<low) high=y;
    if(low<=y && y<=high) x[0]=y;
  }
  return c;
}

int getupper()
{
int MYtoupper();
  return (MYtoupper(getbyte()));
}

int getyesno(s) char *s; {
  bbanner(s," [y/n]? [N]: ");
  return (getupper() == 'Y');
}
    
int secondkill(){
extern unsigned lastkey;
  switch(lastkey){
  case WORDERASE:
  case ERASEKEY:
  case ERASELINE:
  case DELLINE:
  return 1;
  }
  return 0;
}

int secondpush(){
extern unsigned lastkey;
 return (lastkey==PUSHKEY);
}

#if 0
struct optinfo {char *descrip; int *val;};
#endif
/* negative cursor index means display in col 30 */
/* line, string, data */
struct optinfo opt[]={
{PiVersion,      0},
{AUTHOR,         0},
{ADDRESSofAUTHOR,0},
{"",             0},
{"Max files    ",&maxinfo},
{"Active Files ",&sysarg},
{"Max file name",&MAXFN},
{"Blocks in use",&USEDBLOCK},
{"Blocks total ",&MAXBLOCK},
{"Block size   ",&SIZEBLOCK},
{"Max columns  ",&MAXLN},
{"Max rows     ",&MAXROWS},
{"Columns 0 to ",&COLMAX},
{"Rows 0 to    ",&ROWMAX},
{"",0},
};
#define SIZEOPT sizeof(opt)/sizeof(opt[0])

/*
 * OPTIONS
 *
 * Prints option toggles
 */
void options(){
register int i;
char s[128];
struct optinfo *p;
extern char *SRCDIR,*USERFILE;

clearscreen();
sysarg=usedfiles();
for(i=0;i<SIZEOPT;++i){
  strcpy(s,(p= &opt[i])->descrip);
  if(p->val) ssprintf(endof(s),": %u",*(p->val));
  xPrint(s,i,1);
}
crlf();
         displaymemoryleft();

#if PIINILOAD
#if TERMCAP
ePrint("TERM="); ePrint(termtype); crlf();
#endif
ePrint("Keymap="); ePrint(INIFILE); crlf();
ePrint("SrcDir="); ePrint(SRCDIR); crlf();
ePrint("SetUp="); ePrint(USERFILE); crlf();
#endif

         pressany("");
}

#if 0
struct rusage
   {
        struct timeval ru_utime; /* user time used */
        struct timeval ru_stime; /* system time used */
        long ru_maxrss;          /* maximum resident set size */
        long ru_ixrss;      /* integral shared memory size */
        long ru_idrss;      /* integral unshared data size */
        long ru_isrss;      /* integral unshared stack size */
        long ru_minflt;          /* page reclaims */
        long ru_majflt;          /* page faults */
        long ru_nswap;      /* swaps */
        long ru_inblock;         /* block input operations */
        long ru_oublock;         /* block output operations */
        long ru_msgsnd;          /* messages sent */
        long ru_msgrcv;          /* messages received */
        long ru_nsignals;        /* signals received */
        long ru_nvcsw;      /* voluntary context switches */
        long ru_nivcsw;          /* involuntary context switches */
   };
#endif

#if BSDunix || LINUX || CYGWIN

#if macOSX || SOLARIS
#include <sys/time.h>
#include <sys/resource.h>
int my_getrusage(n,r)
int n; struct rusage *r;
{
  getrusage(n, r);
}
#else
#include <sys/time.h>
#include <sys/resource.h>
int my_getrusage(n,r)
int n; struct rusage *r;
{
#if 0
/proc/pid/status has these lines:
VmSize:     1376 kB
VmLck:         0 kB
VmRSS:       588 kB
VmData:      144 kB
VmStk:         8 kB
VmExe:        84 kB
VmLib:      1100 kB
structure names:
long ru_ixrss;      /* integral shared memory size */
long ru_idrss;      /* integral unshared data size */
long ru_isrss;      /* integral unshared stack size */
#endif
char s[512];
char *p;
FILEP fi;
  ssprintf(s,"/proc/%d/status",getpid());
  fi=fopenbread(s);
  if(fi==0) goto err;
  while(fgetLN(s,fi)>0){
    if((p=MYstrstr(s,"VmExe"))!=0)
        r->ru_ixrss=atol(sob(fnb(p)));  /* See usage below */
    if((p=MYstrstr(s,"VmData"))!=0)
        r->ru_idrss=atol(sob(fnb(p)));
    if((p=MYstrstr(s,"VmStk"))!=0)
        r->ru_isrss=atol(sob(fnb(p)));
  }
  closedfile(fi);
  return 0;
err:
  return -1;  /* Error */
}
#endif

int getusageBSD(s) char *s; {
struct rusage r;
  if(my_getrusage(RUSAGE_SELF,(struct rusage *)&r) == -1)
  s[0]=0; else
#if macOSX 
  ssprintf(s,"Shared Text=%lu, Shared Memory=%lu",
              r.ru_idrss,r.ru_ixrss);
#else
  ssprintf(s,"Shared Text=%lu, Private Data=%lu, Stack=%lu",
              r.ru_ixrss, r.ru_idrss, r.ru_isrss);
#endif     
}

long farcoreleft(){
  return MAXBLOCK*(long)SIZEBLOCK;
}
#endif

#if SYS5 && !LINUX && !CYGWIN && !LCCWIN32
long farcoreleft(){
long ulimit();
long x,y;
  x=ulimit(3,0L);       /* hard limit */
  y=MAXBLOCK*SIZEBLOCK;
  return (x<y ? x : y);
}
#endif
#if SYS5 && LCCWIN32
long farcoreleft(){
  return MAXBLOCK*(long)SIZEBLOCK;
}
#endif

/*
 * GETWDIR
 *
 * Get working directory string
 */

char * cwd(){
extern char *cd;
extern char *getwdir(),*getenv();

#if (BSDunix || SYS5) && !LCCWIN32
   char *p;
   if(cd[0]==0){
     if((p=getenv("PWD")) != (char *)0) strcpy(cd,p);
     else getwdir(cd);
     if(cd[0]!=0 && tailchar(cd) != '/') strcat(cd,"/");
   }
#endif
#if MSDOS || LCCWIN32
   if(cd[0]==0)
   if(getwdir(cd) != (char *)0 && tailchar(cd) != '\\') strcat(cd,"\\");
#endif
  return cd;
}

/*
 * GETFNAME
 *
 * Get a file name from user.
 */
int getfname(msg,s) char *msg,*s;{
  s[0]=0;
  if(cwd()) strcpy(s,cwd());
  if(isadir(s)) addslash(s);
#if POPUPS
  if(ggetstr(msg,s)!=RETURNKEY) s[0]=0;
  else
  if(isadir(s) || iswild(s)){
      strcpy(sysx,s);
      if(!FindFile(sysx,s,0)) s[0]=0;
  }         
#else
  ggetstr(msg,s);
  if(cwd()) {
    if(MYstrcmp(cwd(),s)==0) s[0]=0;
  }
#endif
  return s[0];
}

int queryreplace(insmode) int insmode;{
int fiddle,c,i;
int gotreplarg;
  fiddle=0;
top:
  strcpy(sysv,srcharg);
  if(ggetstr("Search for: ",sysv)!=RETURNKEY || !sysv[0])
    goto quit;
  copysrcharg(sysv);
  if(search(1)){ /* Search forward */
    c='X';
    gotreplarg=0;
    while(1){
      nodisp=0;
     if(c=='A' && ccBIOS(2)) break;
     if(c!='A'){
        banner("REPLACE: Y=Yes, A=All, Q=Quit, P=Prev, N=Next [N]: ");
        if(MYstrchr("YAQPN\003\025\r\n\t\040",c=getupper())==(char *)0)
          continue;
        if(MYstrchr("Q\003\025",c)) goto erase;
        if(c=='P'){
          unshowmatch(); if(!rfind()) goto top; continue;
        }
        if(c<=' ') c='N';
      }
      if(c!='N'){
        if(!gotreplarg){
          if(ggetstr("Replace with: ",replarg) != RETURNKEY) goto erase;
          gotreplarg=1;
        }
        /* del match at cursor, and replace */
        if(!zap(sysx))break; /* see showmatch() */
        fiddle=1;
        if((i=addstring(replarg,line[row],TRUE,insmode))== -1) break;
        if(i) curfix(col+curoff+i,0); else {edleft(); curses();}
      } else
      unshowmatch();       /* Erase inverse video of showmatch() */
      if(!ffind()) {bell(); goto quit;}
    }
  } else goto quit;
erase:
  unshowmatch();       /* Erase the match */
quit:
  lcount();
  clrcon();            /*  clear type-aheads */
  return fiddle;
}

/* stack for macros so they can do recursion to 10 levels */
void setlevel(maclevel,poppush)
int maclevel;           /* level 0-9 to pop or push */
int poppush;            /* 1=push, 0=pop */
{
struct keep {int MACrep,MACind,MACmax,MACrun,RECord; char *MACbuf;};
static struct keep level[10];
  if(poppush){  /* push values  */
    level[maclevel].MACrep=macrep;
    level[maclevel].MACind=macind;
    level[maclevel].MACmax=macmax;
    level[maclevel].MACrun=macrun;
    level[maclevel].RECord=record;
    level[maclevel].MACbuf=macbuf;
  } else {      /* pop values */
    macrep=level[maclevel].MACrep;
    macind=level[maclevel].MACind;
    macmax=level[maclevel].MACmax;
    macrun=level[maclevel].MACrun;
    record=level[maclevel].RECord;
    macbuf=level[maclevel].MACbuf;
  }
}

#if 0  /* For LINUX */
#if 0
Example:
 int main(){
  ash_system ("date >/tmp/theDate");
  exit(0);
 }
#endif

#include <errno.h>

 int ash_system (command) char *command; {
           int pid, status;
           extern int errno;
           extern char **environ;

           pid = fork();
           if (pid == -1)
               return -1;
           if (pid == 0) {
               char *argv[4];
               argv[0] = "ash";
               argv[1] = "-c";
               argv[2] = command;
               argv[3] = 0;
               execve("/bin/ash", argv, environ);
               exit(127);
           }
#if 0
           do {
               if (waitpid(pid, &status, 0) == -1) {
                   if (errno != EINTR)
                       return -1;
               } else
                   return status;
           } while(1);
#else
           return 0;
#endif
       }

#endif

#if BSDunix || SYS5
int isXwindows(){
extern char *getenv();
  if(getenv("DISPLAY")) return 1;
  return 0;
}
#endif

#if BSDunix || SYS5
/* Inject system command pipe [READPIPE] */

static FILEP PIPEinput=0;

#include <signal.h>
void pipehandler(n) int n;{
extern int PIPEopen;
 signal(SIGINT,SIG_DFL);
    PIPEopen=0;
    termraw();          /* Interrupt chars off */
    pclose(PIPEinput);
}

int readpipe(s) char *s;{
extern int PIPEopen;
int i;
  PIPEopen=0;
  PIPEinput = (FILEP)popen(s,"r");
  if(!PIPEinput){
    ssprintf(sysvv,"Can't pipe %s",s);  bellpressany(sysvv);
    return 1;
  }
  PIPEopen=1;
  termcanon();          /* Interrupt chars on */
  signal(SIGINT,pipehandler);
  return 0;
}

int getpipe(){
int i;
    if((i=getc(PIPEinput)) != EOF){
      if(i=='\r') return EXTLEFTARROW;
      if(i=='\n') return RETURNKEY;
      return i;
    }
quit:
    if(PIPEopen){
      PIPEopen=0;
      termraw();          /* Interrupt chars off */
      pclose(PIPEinput);
    }
    return i;
}
#endif

/* Get the shell variable */
void getSHELL(){
extern char *SHELL;
extern char *getenv();
char *p;
   if((p=getenv("SHELL"))!=0) newcore(&SHELL,p);
}

/* Run a system program under unix, msdos */
void runprogram(s,flag) char *s; int flag; {
  if(s[0]) {
    exitterminal();     /* Clear screen, set colors */
    termDoExit();
    system(s);          /* MSDOS: Could be swapped if swapswitch==1 */
    setup();
    if(flag){
      etype("\r\nPress any key to return to PI:");
      ccBIOS(0);
    }
  }
  initterminal();       /* reset terminal in case we lost keypad */
  clrcon();             /* clear out type-aheads */
}

/* set STOPS to power of two */
void setdetab() {
char s[100]; int x; extern int STOPS;
  ssprintf(s,"Detab %s. New 2,4,8,16,32",
  ((DETABBER=1-DETABBER)!=0 ? " ON" : " OFF"));
  x=STOPS; getnumquery(s,&x,2,32);
  if(MYstrchr("\2\4\10\20\40",x)!=(char *)0) STOPS=x;
}

void settabs(){
extern int margin,curoff,col,TSTOPS,MAXLN;
extern char *sysv;
  getnumquery("Tab key indent",&TSTOPS,0,MAXLN-2);
  if(sysv[0]=='.') TSTOPS=col;
  getnumquery("Right margin",&margin,0,MAXLN-2);
  if(sysv[0]=='.') margin=curoff+col;
}

void settoggle(msg,x) char *msg; int *x; {
  bbanner(msg,((x[0]= (x[0]? 0:1))!=0 ? " ON" : " OFF"));
}
                     
void shrinkwindow() {
extern int ROWMAX,COLMAX,MAXROWS,MAXLN;
int rrow,ccol;
  rrow=ROWMAX+1; getnumquery("New ROWS",&rrow,1,MAXROWS);
  ccol=COLMAX+1; getnumquery("New COLUMNS",&ccol,1,MAXLN-1);
  setwindowsize(rrow-1,ccol-1);
}

void setwindowsize(rrow,ccol) int rrow,ccol; {
#if TURBOC
extern int curoff,row,col,ROWMAX,COLMAX,MAXROWS,FARSCROLL;
  if(PackScreenBuffer(0)) return;
  equipment();                  /* Have to erase old colors */
  FARSCROLL=1+(ROWMAX=rrow);
  COLMAX=ccol;
  UnPackScreenBuffer(0);
  row=MYmin(row,ROWMAX); col=MYmin(col,COLMAX);
  drawall(); killstatus();
#else
extern int curoff,row,col,ROWMAX,COLMAX,MAXROWS,HASSCROLL,FARSCROLL;
int NN,f,fiddle;
  if(PackScreenBuffer(0)) return;
  FARSCROLL=1+(ROWMAX=rrow);
  COLMAX=ccol;
  setdrawbuff();                /* unix network screen buffer */
  UnPackScreenBuffer(0);
#if !LCCWIN32
if(HASSCROLL) setscroll(0);
#endif
  row=MYmin(row,ROWMAX); col=MYmin(col,COLMAX);
  drawall(); killstatus();
#endif                          /* for fast Xterm refresh */
}

#if 0
Foreign.
Unix iso8859-1 and DOS5.0 Translation table for Czech characters
Not all characters are correct. However, there is a character editor,
called fed, which may be able to alter a font for the purpose.
Newer versions of X already have the latin-2 fonts (eg, X11R6).
See www.biz.net.pl in Warsaw for free fonts 8859-2.
;==========================================================
;Unix iso8859-1 and DOS5.0 swap table for Czech characters
;==========================================================
;Chr  Oct  Dec      DOS5.0 Decimal
;==========================================================
;' ', 264   180     243     /* Hook on blank space */
;'a', 341   225     160     /* long vowel, lower case */
;'e', 351   233     130
;'i', 355   237     161
;'o', 363   243     162
;'u', 372   250     163
;'y', 375   253     236
;'c', 342   226     159     /* hook consonant, lower case */
;'d', 313   203     212
;'n', 361   241     229
;'r', 374   252     253
;'s', 344   228     231
;'t', 345   229     156
;'z', 366   246     167
;'A', 301   193     181     /* long vowel, upper case */
;'E', 311   201     144
;'I', 315   205     214
;'O', 323   211     224
;'U', 332   218     233
;'Y', 335   221     237
;'C', 302   194     172     /* hook consonant, upper case */
;'D', 310   200     210
;'N', 321   209     213
;'R', 334   220     252
;'S', 304   196     230
;'T', 333   219     155
;'Z', 326   214     166
;372, 371   249     163, 133     /* long u to u-circle */
;371, 'u'           133, 'u'     /* u-circle to u */
;351, 352   234     130, 216     /* long e to e-hook */
;352, 'e'           216, 'e'     /* e-hook to e */
;311, 312   202     144, 183     /* long E to E-hook */
;312, 'E'           183, 'E'     /* E-hook to E */
;
#endif

#if 0

8859-2 Codes from unix

base    hex     dec     oct     description
===========================================
a       e1      225     341     a-long
A       c1      193     301     A-long
c       e8      232     350     c-hook
C       c8      200     310     C-hook
d       ef      239     357     d-soft
D       cf      207     317     D-hook
e       e9      233     351     e-long
e       ec      236     354     e-hook
E       c9      201     311     E-long
E       cc      204     314     E-hook
i       ed      237     355     i-long
I       cd      205     315     I-long
n       f2      242     362     n-hook
N       d2      210     322     N-hook
o       f3      243     363     o-long
O       d3      211     323     O-long
r       f8      248     370     r-hook
R       d8      216     330     R-hook
s       b9      185     271     s-hook
S       a9      169     251     S-hook
t       bb      187     273     t-soft
T       ab      171     253     T-hook
u       fa      250     372     u-long
u       f9      249     371     u-circle
U       da      218     332     U-long
U       d9      217     331     U-circle
y       fd      253     375     y-long
Y       dd      221     335     Y-long
z       be      190     276     z-hook
Z       ae      174     256     Z-hook

Cycle table constructed by hand from the above info:
a\341      /*   a ==>  a-long */
A\301      /*   A ==>  A-long */
c\350      /*   c ==>  c-hook */
C\310      /*   C ==>  C-hook */
d\357      /*   d ==>  d-soft */
D\317      /*   D ==>  D-hook */
i\355      /*   i ==>  i-long */
I\315      /*   I ==>  I-long */
n\362      /*   n ==>  n-hook */
N\322      /*   N ==>  N-hook */
o\363      /*   o ==>  o-long */
O\323      /*   O ==>  O-long */
r\370      /*   r ==>  r-hook */
R\330      /*   R ==>  R-hook */
s\271      /*   s ==>  s-hook */
S\251      /*   S ==>  S-hook */
t\273      /*   t ==>  t-soft */
T\253      /*   T ==>  T-hook */
y\375      /*   y ==>  y-long */
Y\335      /*   Y ==>  Y-long */
z\276      /*   z ==>  z-hook */
Z\256      /*   Z ==>  Z-hook */
/* Special cases of three chars on one base */
e\351      /*   e ==>  e-long */
\351\354   /*   e-long ==> e-hook */
E\311      /*   E ==>  E-long */
\311\314   /*   E-long ==> E-hook */
u\372      /*   u ==>  u-long */
\372\371   /*   -long ==> u-circle */
U\332      /*   U ==>  U-long */
\332\331   /*   U-long ==> U-circle */

Cycle table in input format:
a\341A\301c\350C\310d\357D\317i\355I\315n\362N\322o\363O\323r\370
R\330s\271S\251t\273T\253y\375Y\335z\276Z\256e\351
\351\354E\311\311\314u\372\372\371U\332\332\331

#endif

#if 0
struct swapchar {unsigned char old,new;};
#endif
#if 0
static
struct swapchar czechchars[]={
{' ', 243},     /* Hook on blank space */
{'a', 160},     /* long vowel, lower case */
{'e', 130},
{'i', 161},
{'o', 162},
{'u', 163},
{'y', 236},
{'c', 159},     /* hook consonant, lower case */
{'d', 212},
{'n', 229},
{'r', 253},
{'s', 231},
{'t', 156},
{'z', 167},
{'A', 181},     /* long vowel, upper case */
{'E', 144},
{'I', 214},
{'O', 224},
{'U', 233},
{'Y', 237},
{'C', 172},     /* hook consonant, upper case */
{'D', 210},
{'N', 213},
{'R', 252},
{'S', 230},
{'T', 155},
{'Z', 166},
{163, 133},     /* long u to u-circle */
{133, 'u'},     /* u-circle to u */
{130, 216},     /* long e to e-hook */
{216, 'e'},     /* e-hook to e */
{144, 183},     /* long E to E-hook */
{183, 'E'}      /* E-hook to E */
};
#define NUMSWAP (sizeof(czechchars)/sizeof(czechchars[0]))
int NUMACCENT=NUMSWAP;
struct swapchar *accentchars= (struct swapchar *)&czechchars[0];
#endif

int NUMACCENT=0;
struct swapchar *accentchars= (struct swapchar *)0;

unsigned foreign(c)
unsigned char c;
{
extern int NUMACCENT; extern struct swapchar *accentchars;
static struct swapchar *p;
int i;
  for(p=accentchars,i=0;i<NUMACCENT;++i,++p){
    if(c == p->old) return (unsigned)p->new;
  }
  for(p=accentchars,i=0;i<NUMACCENT;++i,++p){
    if(c == p->new) return (unsigned)p->old;
  }
  return 0;
}

#if 0
void dumpforeign()
{
extern int NUMACCENT; extern struct swapchar *accentchars;
unsigned char *p;
char s[256];
p = (unsigned char *)&accentchars[0];
strncpy(s,p,NUMACCENT); s[NUMACCENT]=0;
ePrint(s);
crlf(); fprintf(stderr,"NUMACCENT=%d\r\n",NUMACCENT);
}
#endif

unsigned char *curchar() {
int i; char *p;
  i=strlen(p=line[row]);
  while(i<curoff+col) p[i++]=' '; p[i]=0;
  return ((unsigned char *)&line[row][curoff+col]);
}

int makeupper(p) char *p; {
  if(MYislower(p[0])) {p[0] = MYtoupper(p[0]); return 1;}
  return 0;
}

int makelower(p) char *p; {
  if(MYisupper(p[0])) {p[0] = MYtolower(p[0]); return 1;}
  return 0;
}

/*
 * CASECHANGE
 *
 * Purpose: Change character(s) at cursor to lower or upper case.
 */
int casechange(n)
int n;                  /* n=0 is lower, 1=upper */
{
int tag;
char *p,*q,*r; extern char *sobl();
  tag=0;
  if(strlen(r=line[row])<=col+curoff)  /* nothing to map */
    goto nextline;
  q=p= r+col+curoff;
  switch(n){                            /* Single character map */
  case CASELOWER: /* change to lower case */
          tag=makelower(p++);
          goto print;
  case CASEUPPER: /* change to upper case */
          tag=makeupper(p++);
          goto print;
  }/* end switch */

  if(*(p=sobl(p))==0) goto nextline;

  switch(n){                            /* map whole words */
  case CASECAPITAL: /* Capitalize: first char upper, rest lower */
          tag=makeupper(p++);
          if(p != sobl(p)) break;
  case CASELLOWER: /* change word to lower case */
          while(*p){
            tag |= makelower(p++);
            if(p != sobl(p)) break;
          }
          break;
  case CASELUPPER: /* change word to upper case */
          while(*p){
            tag |= makeupper(p++);
            if(p != sobl(p)) break;
          }
          break;
  }/* end switch */
print:
  n=(int)(p-q);
  if(tag) {
    strnzcpy(sysvv,q,n);
    xPrint(sysvv,row,col);
  }
  if(*sobl(p)!=0) {curfix(1+(int)(p-r),0); goto quit;}
nextline:
  homeit(); eddn();
quit:
  return tag;
}

void copydefaultfile(){
  strcpy(sysfile,finfo[curfile].fname);
}

#if 0 /* For Hilite mouse tracking and region information */
      /* This did not work very well with the current version of xterm */
      /* but someday it might, if they fix it, so I leave it here. */
void pressmouse(){
 char s[128];
 int a,b,c;
 a=getbyte()&3; b=getbyte()-' '-1; c=getbyte()-' '-2;
 if(a == 1 || a == 2) { /* Press MB2 or MB3? Ignore hilite */
   etype("\033[0T");
 }
 if(a == 0) {           /* Press MB 1 */
   if(c>=0 && b>=0 && c<=ROWMAX && b<=COLMAX) { /* Enable Hilite */
     while(row >c) decrROW();
     while(row <c) incrROW();
     row=c; col=b;
     ssprintf(s,"\033[1;%u;%u;2;%u;T", b+1, c+2, ROWMAX+1);
     etype(s);
   }                                            /* Ignore hilite */
   else {
     etype("\033[0;0;0;0;0T");
   }
 }
/*
 banner("Mouse Press:");
 fprintf(stderr,"MB%d: b=%d, c=%d",a+1,b,c);
*/                        
}                         
                          
void releasemouse(c) char c; {
 /* Release MB1, MB2, MB3 */
 /* Valid: ESC [ t Cx Cy */
 /* Invalid: ESC [ T Cx Cy Cx Cy Cx Cy */
 int startx, starty, endx, endy, mousex, mousey;
 /* startx, starty, endx, and endy give the starting and ending
    character positions of the region.  mousex and mousey give the
    location of the mouse at button up, which may not be over a
    character.            
  */                      
 if(c == 't'){            
  endx=getbyte(); endy=getbyte();
  /* banner("Mouse t:"); fprintf(stderr,"%d %d",endx,endy); */
 }
 if(c == 'T'){
  startx=getbyte(); starty=getbyte();
  endx=getbyte(); endy=getbyte();
  mousex=getbyte(); mousey=getbyte();
  /* banner("Mouse T:"); */
  /* fprintf(stderr,"%d %d %d %d %d %d",
                     startx,starty,endx,endy,mousex,mousey); */
 }
}

void setmouse(n){
char s[128];
static int wasdone=0;

 if(n==0 && wasdone==1){
   etype("\033[?1001l");
   wasdone=0;
   return;
 }
 identifykey(MOUSEPRESS,s);
 if(n==1 && MYstrcmp(s,"Keymap") != 0 && mouseswitch !=0) {
   etype("\033[?1001h");
   wasdone=1;
 }
}

#endif

#if BSDunix || SYS5 || (MSDOS && DOS386I)
void pressmouse(){
 char s[128];
 int a,b,c;
 a=getbyte()&3; b=getbyte()-' '-1; c=getbyte()-' '-2;
 /* Press MB2 (a==1) or MB3 (a==2)? Ignore */
 if(a == 0) {           /* Press MB 1 (a==1)? */
   if(c>=0 && b>=0 && c<=ROWMAX && b<=COLMAX) { /* Valid position */
     while(row >c) decrROW();
     while(row <c) incrROW();
     row=c; col=b;
   }
 }
}
#endif

#if BSDunix || SYS5
void releasemouse(c) char c;{
/* Release taken care of by pressmouse() above */
}

void setmouse(n){
 etype(n==0 ? "\033[?9l" :"\033[?9h");
 banner(n==0 ? "OFF" : "ON");
}

int mouseexists() {
extern int mouseswitch;
   return mouseswitch;
}

int mousekeys() {
char s[128];
   identifykey(MOUSEPRESS,s);
   if(MYstrcmp("Keymap",s)==0) return 0;  /* Must be a key defined */
   return 1;
}
#endif

/* Replace dest by its integer computation */
void convert2int(dest) char *dest; {
LLONG x,y; char *p; int i;
char *sob(); LLONG atoiLL(); extern char *lltoa();

   x=atoiLL(p=sob(dest));
   while(p[0]){
     if((p[0]=='-' || p[0]=='+') && MYisdigit(p[1])) ++p;
     while(MYisdigit(p[0])) ++p; p=sob(p);
     i= *p++; y=atoiLL(p);
     switch(i){
     case '+': x += y; goto addone;
     case '-': x -= y; goto addone;
     case '/': x /= y; goto addone;
     case '*': x *= y;
     addone: p=sob(p); break;
     case '=': if(p[0]=='='){
                 if(x == atoiLL(p+1)) x=1; else x=0;
               }
     default: goto quit;
     }
   }
quit:
   lltoa(x,dest);
}

/* Replace hexadecimal string dest by its base 10 equivalent */
void hexstrToBase10str(dest) char *dest; {
LLONG x,y; char *p;
extern char *sob(); extern char *lltoa();
extern void cvupper();

   cvupper(dest);
   p=sob(dest);
   if(p[0]=='0' && p[1]=='X') p += 2;   /* 0x23fe is a hex number format */
   x=0;
   while((y=hexdigit(p[0]))!= -1){
     x=x*16+y; ++p;
   }
   (void)lltoa(x,dest);
}

/* Replace octal string dest by its base 10 equivalent */
void octstrToBase10str(dest) char *dest; {
LLONG x,y; char *p;
extern char *sob(); extern char *lltoa();
extern void cvupper();

   cvupper(dest);
   p=sob(dest);
   if(p[0]=='\\') ++p;  /* \033 is an octal number format */
   x=0;
   while((y=octdigit(p[0]))!= -1){
     x=x*8+y; ++p;
   }
   (void)lltoa(x,dest);
}


/* Warning: This MYstrchr() must not match the null string terminator! */
/* See v13.c for the source to MYstrchr() */
int MYisdigit(c) char c; {
extern char *MYstrchr();
 return (MYstrchr("0123456789",c) != (char *)0);
}

int hexdigit(c) char c; {
  if('0'<= c && c<= '9') return c-'0';
  if('A' <= c && c <= 'F') return c-'A'+10;
  return -1;
}

int octdigit(c) char c; {
  if('0'<= c && c<= '7') return c-'0';
  return -1;
}

void setSYSone(){
 sysv[0]= '1'; sysv[1]=0;
}

static int tmp;
void PUSHnodisp(){
 tmp=nodisp;
}
void POPnodisp(){
 nodisp=tmp;
}

LLONG absLL(lx) LLONG lx; {
 return (lx<0) ? -lx: lx;
}

LLONG getatoiLL() {
extern char *sysv;
 return atoiLL(sysv);
}

void getbackupfname(s) char *s; {
/* variable BACKUPS: 0=FALSE,1=$HOME/PIbackups,2=./PIbackups */
char *p;
#if 0
extern int BACKUPS;
extern char *PIBACKUPS;
extern char *getenv();
#endif
  if(!BACKUPS) {s[0]=0; return;}   /* Nov 2002 edit, bug fix */
  p=".";
  if(BACKUPS==1){ p=getenv("HOME"); if(!p) p=".";}
  strcpy(s,p);
  addslash(s);
  strcat(s,PIBACKUPS);
}

/* variable BACKUPS: 0=FALSE,1=$HOME/PIbackups,2=./PIbackups */
void setbackups(){
/* extern int BACKUPS; */
char buf[MAXFNAME];
  if((++BACKUPS)==3) BACKUPS=0;
  getbackupfname(buf);
  ssprintf(sysv,"Backups->%s",(!BACKUPS ? "OFF" : buf) );
  banner(sysv);
}

void setupcolors(){
#if MSDOS
int x,i;
  x=(SYSATT >> 4);
  getnumquery("Background color (0-15) ",&x,0,15);
  i=(SYSATT & 15);
  getnumquery("Foreground color (0-15) ",&i,0,15);
  SYSATT = ((i & 15) + (x << 4));
  getnumquery("Window title attribute (19=blue+yell,15=mono) ",&titlattr,0,128);
  getnumquery("Window border attribute (31=bold,15=mono) ",&bordattr,0,128);
#endif
#if (BSDunix || SYS5) && !LCCWIN32
  getnumquery("Screen attribute (0=std,7=mono,128=bold,112=rev video) ",&SYSATT,0,128);
  getnumquery("Window title attribute (0,7,128,112) ",&titlattr,0,128);
  getnumquery("Window border attribute (0,7,128,112) ",&bordattr,0,128);
  getnumquery("Text foreground color (0,30-37) ",&textfgcolor,0,37);
  getnumquery("Text background color (0,40-47) ",&textbgcolor,0,47);
  getnumquery("Menu foreground color (0,30-37) ",&menufgcolor,0,37);
  getnumquery("Menu background color (0,40-47) ",&menubgcolor,0,47);
  setansicolors();
  clearscreen();
#endif
#if LCCWIN32
         {char *msg[]={
          "ansimode: 0=1=off, 2=on",
          "colors: 0=defaults (1,12,16,2)",
          "1  BLACK      9  DARKGRAY",
          "2  BLUE       10 LIGHTBLUE",
          "3  GREEN      11 LIGHTGREEN",
          "4  CYAN       12 LIGHTCYAN",
          "5  RED        13 LIGHTRED",
          "6  MAGENTA    14 LIGHTMAGENTA",
          "7  BROWN      15 YELLOW",
          "8  LIGHTGRAY  16 WHITE",
           0};
           WarningMessage(msg); /* memory released; erase screen later */
         }/* closehelpsheet(); */  /* Clean up the screen */

  getnumquery("Screen attribute (0=std,7=mono,128=bold,112=rev video) ",&SYSATT,0,128);
  getnumquery("Window title attribute (0,7,128,112) ",&titlattr,0,128);
  getnumquery("Window border attribute (0,7,128,112) ",&bordattr,0,128);
  getnumquery("Ansi mode (0=off,2=win32) ",&ansimode,0,2);
  getnumquery("Text foreground color (1-16) ",&textfgcolor,0,16);
  getnumquery("Text background color (1-16) ",&textbgcolor,0,16);
  getnumquery("Menu foreground color (1-16) ",&menufgcolor,0,16);
  getnumquery("Menu background color (1-16) ",&menubgcolor,0,16);
  setansicolors();
  clearscreen();
#endif
}

/* kind of instr() for C */
char *MYstrstr(a,b)
unsigned char *a,*b;
{
int n;
  n=strlen(b);
  while(a[0]){
   if(MYstrncmp(a,b,n)==0) return  (char *)a;
   ++a;
  }
  return (char *)0;
}
/*
 * V2.C
 *
 */

#include "pie.h"

#if POPUPS
/* popup data and routines; see end of file for basics */
/*  popup menu without overlaps, looks like this, essentially: */
#if 0
Keymap:^K^L
ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ Pi Editor ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
º  File  Keys  Tools  Search  Edit  Word  Block  Values  Other  Help  º
ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼
  ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
  ºOpen            º
  ºNew             º
  ºReload          º
  ºRename          º
  ºSave to disk    º
  ºSave as ...     º
  ºSave then exit  º
  ºSave then close º
  ºClose           º
  ºInject file     º
  ºPaste directory º
  ºPrint file      º
  ºFind window     º
  ºExport-import   º
  ºQuit editor     º
  ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼
#endif
/* The Tools display can be 57 characters wide, leaving 54 for data */
/* Tools items after those compiled in come from PI.SET macros. */

#ifndef _TCCONIO_H_
enum COLORS {
        BLACK,                  /* dark colors, background, 0-7 */
        BLUE,
        GREEN,
        CYAN,
        RED,
        MAGENTA,
        BROWN,
        LIGHTGRAY,
        DARKGRAY,               /* light colors, 8-15 */
        LIGHTBLUE,              /* background cannot be 8-15 */
        LIGHTGREEN,             /* foreground can be 0-15 */
        LIGHTCYAN,
        LIGHTRED,
        LIGHTMAGENTA,
        YELLOW,
        WHITE
};
#endif /* These are already defined in tcconio.h, lccwin32 */

#define REVERSEVIDEO    0x70    /* reverse video attribute */

/*
ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ Pi Editor ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
º  File  Keys  Tools  Search  Edit  Word  Block  Values  Other  Help  º
ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼
*/

#if 0
struct menu { char *name; unsigned keycode; };
#endif

static struct menu File[]={
{"Open",            /*  "^KL", */          LOAD},
{"New file",        /*  "^KU", */          NEWLOAD},
{"New folder",      /*  "^KG", */          CREATEFOLDER},
{"Reload",          /*  "^KE", */          RELOAD},
{"Rename",          /*  "^KN", */          NAMECHANGE},
{"Save to disk",    /*  "^KS", */          BACKUP},
{"Save as ...",     /*  "^KW", */          SAVEASKEY},
{"Save then exit",  /*  "^KX", */          SAVE_EXIT},
{"Save then close", /*  "^KD", */          SAVE_NEXT},
{"Close",           /*  "^KQ", */          SCRAPBUFFER},
{"Inject file",     /*  "^KR", */          INJECT},
{"Paste directory", /*  "^KF", */          DSKFILES},
{"Print file",      /*  "^KP", */          PRINTFILE},
{"Find window",     /*  "^K^A", */         SETBUFFER},
{"Export-import",   /*  "^KT", */          SETTEXTTYPE},
{"Quit editor",     /*  "Alt-X", */        TSRKEY},
{0,0}
};


static struct menu Edit[]={
{"Goto line # ",      /*  "^OG", */          GOTOKEY},
{"Focus at cursor",   /*  "^O p", */         CENTERREFRESH},
{"Goto bookmark",     /*  "^Q0", */          GOBOOKMARK},
{"Set Bookmark",      /*  "^K0", */          SETBOOKMARK},
{"Center line",       /*  "ALT-C", */        CENTERTEXT},
{"Justify line",      /*  "^B", */           REFORMKEY},
{"Split line",        /*  "^ON", */          SPLITLINE},
{"Insert line",       /*  "^N  or F3", */    INSERTLINE},
{"Delete line",       /*  "^Y  or F4", */    DELLINE},
{"Kill to end",       /*  "^QY", */          ERASEKEY},
{"Erase # lines",     /*  "^OY", */          DELMANYLINES},
{"Copy line",         /*  "^KC or F9", */    PUSHKEY},
{"Paste line",        /*  "^KV or F10", */   POPKEY},
{"Scroll ahead",      /*  "^Z or ^PgDn", */  LINEFORWARD},
{"Scroll back",       /*  "^W or ^PgUp", */  LINEBACK},
{"Recover lines",     /*  "Shift-F10", */    POPLIST},
{"Memory purge",      /*  "Shift-F10", */    PURGELIST},
{0,0}
};

static struct menu Word[]={
{"Next word",                 /*  "^F", */        WORDAHEAD},
{"Previous word",             /*  "^A", */        WORDBACK},
{"Delete word",               /*  "alt-G", */     WORDERASE},
{"Capitalize word",           /*  "^O,C", */      CASECAPITAL},
{"Lowercase word",            /*  "^O,L", */      CASELLOWER},
{"Uppercase word",            /*  "^O,U", */      CASELUPPER},
{"Fill phrase string",        /*  "^OC", */       QPUSHKEY},
{"Paste phrase string",       /*  "^OV", */       QPOPKEY},
{"Kill phrase match",         /*  "^OZ", */       QZAPKEY},
{"Copy phrase to notepad",    /* "^OP", */        OPTPUSHKEY},
{"IntMath on phrase",         /* "^O,i", */       OPTMATH},
{"HexToBase10 on phrase",     /* "^O,H", */       OPTHEX},
{"OctToBase10 on phrase",     /* "^O,H", */       OPTOCT},
#if MATHFLOAT
{"FloatMath on phrase",       /* "^O,j", */       OPTMATHFLOAT},
{"SciCalc on phrase",         /* "^O,k", */       OPTEVALEXPR},
{"FloatMath format",          /* "^OM", */        MATHFORMAT},
#endif
{0,0}
};


static struct menu Block[]={
{"Begin block set mark",      /* "^KB", */        SETMARK},
{"Swap mark with cursor",     /* "^KK", */        SWAPMARK},
{"Reveal block setup",        /* "^K,s", */       REVEALMARK},
{"Copy block to notepad",     /* "^K,c", */       COPYBLOCK},
{"Move block to notepad",     /* "^K,m", */       MOVEBLOCK},
{"Paste block from notepad",  /* "^K,p", */       PASTEBLOCK},
{"Erase the notepad",         /* "^OX", */        KILLNOTEPAD},
{"Dup line onto notepad",     /* "^OA", */        APPENDLINE},
{"Kill line, add on notepad", /* "^OK", */        ADDBUFFER},
{0,0}
};


static struct menu Search[]={
{"Set search string",   /* "^QN", */                 NEWSEARCH},
{"Forward search",      /* "^Of or F5", */           FWDSEARCH},
{" ... within Line",    /* "^OF or   ", */           LFWDSEARCH},
{"Reverse search",      /* "^Ob or F6", */           REVSEARCH},
{" ... within Line",    /* "^OB or   ", */           LREVSEARCH},
{"Query-replace",       /* "^QQ or Alt-S", */        QUERYREPLACE},
{"Fold case on-off",    /* "^OF", */                 FOLDSRCH},
{0,0}
};

static struct menu Tools[]={
{"Execute program",    /* "shift-F2", */      RUNMYPROGRAM},
{"Execute script",     /* "ctrl-OQ",  */      RUNBACKGROUND},
{"Command shell",      /* "F2", */            RUNPROGRAM},
#if !(MSDOS || LCCWIN32)
{"Inject command pipe",/*  "alt-p", */        READPIPE},
{"Suspend job",        /*  "^U", */           BAILOUT},
#endif
{0,0}
};

static struct menu Keys[]={
{"Memorize keys",                 /* "F8", */           RECORDING},
{"Replay keys",                   /* "F7", */           RUNMEMORIZE},
{"Repeat replay #",               /* "Shift-F7", */     MANYRUNMEM},
{"Save current macro",            /* "ALT-0", */        MACROKEY},
#if PIINILOAD
{"Keymap editor",                 /* "^O,h", */         COMPLETEKEY},
{"Macro editor",                  /* "ctrl-O,e", */     MACROLIBRARY},
{"Show keypress",                 /* "ctrl-Q,k", */     SHOWKEYPRESS},
#endif
{0,0}
};

#if 0  /* Removed March 2000 because of single interface COMPLETEKEY */
   /* Removed March 2000 because of single interface COMPLETKEY */
  {"Complete key list",             /* "^O,h", */         COMPLETEKEY},
  {"Define key",                    /* "^O,=", */         DEFINEKEY},
  {"Undefine key",                  /* "^O,=", */         KILLKEY},
#endif

static struct menu Values[]={
{"Word wrap",       /* "^OW", */          WORDWRAP},
{"Auto-indent",     /* "^OI", */          SETAUTO},
{"Hanging indent",  /* "^OU", */          SETHANGING},
{"Detab mode",      /* "^OD", */          SETDETAB},
{"Trailing space",  /* "   ", */          SETBLANKSTRIP},
{"Insert mode",     /* "Ins", */          INSMODE},
{"Highbit strip",   /* "   ", */          SETSTRIP},
{"Tab and margin",  /* "^OT", */          SETTABS},
{"Backup modes",    /* "^OB", */          BACKTOGGLE},
{"Status line",     /* "^OS", */          SETSTATUS},
{"Show ascii code", /* none   */          ASCIITABLE},
{"Printer",         /* "^OP", */          SETPRINTER},
{"startup msg",     /* "   ", */          ANNOYINGMSG},
#if SYS5 || BSDunix
{"Mouse enable",    /* none  */           SETMOUSE},
{"Mouse disable",   /* none  */           UNSETMOUSE},
#endif
{0,0}
};

static struct menu Other[]={
{"Window resize",        /* "^O,-", */        SHRINKWINDOW},
{"Menu box setup",                            MENUBOXSET},
{"Color & menu setup",   /* none  */          COLORSET},
{"Box and line draw",    /* "^OL", */         LINEDRAWKEY},
#if MSDOS
{"Video mode",           /* none  */          VIDEOMODE},
{"Set cursorstyle",      /* none  */          CURSORSTYLESET},
{"Disk swap mode",       /* "^O,!",*/         SWAPSW},
#endif
#if PIINILOAD
{"Write pi-macro.key",   /* "ALT-9", */       DISPLAYMACRO},
{"Write pi-dump.key",    /* "^O,+", */        DUMPKEYS},
{"Read a keymap file",   /* "^O,@", */        KEYSLOADUP},
#if BSDunix || SYS5                      
{"Set Emacs keys",       /* "^O,@", */        SETEMACSMODE},
{"Set Wordstar keys",    /* "^O,@", */        SETWDSTARMODE},
{"Set WordPerfect keys", /* "^O,@", */        SETWDPERFMODE},
#endif
#endif
{0,0}
};

static struct menu Help[]={
{"Internal info",      /*  "^O^O", */         OPTIONSET},
{"Brief manual",       /* "^Oh", */           RUNBHELP},
{"HTML manual",           /*  */              HELPHTML},
{"Set browser",           /*  */              HELPBROWSER},
{"Text manual",           /* "^O^H", */       RUNHELP},
{"Quit editor",           /*  "Alt-X", */     TSRKEY},
{0,0}
};

enum {
HFile=   1+       sizeof("  ")-1,
HKeys=   HFile+   sizeof("File  ")-1,
HTools=  HKeys+   sizeof("Keys  ")-1,
HSearch= HTools+  sizeof("Tools  ")-1,
HEdit=   HSearch+ sizeof("Search  ")-1,
HWord=   HEdit+   sizeof("Edit  ")-1,
HBlock=  HWord+   sizeof("Word  ")-1,
HValues= HBlock+  sizeof("Block  ")-1,
HOther=  HValues+ sizeof("Values  ")-1,
HHelp=   HOther+  sizeof("Other  ")-1,
Htotal=  HHelp+   sizeof("Exit  ")-1
};

/*
   File  Edit  Word  Block  Search  Tools  Keys  Values  Other  Quit  |
*/

#define MaininfoTITLE " Pi Editor "
#define SELECTROW 1
#define MAINSTARTROW 2
#define MaininfoCOLS Htotal
#define MaininfoROWS 1
#define DROP (1+2+MaininfoROWS+1) /* how much to drop for secondary menu */

#if 0
struct mainmenu {
char *name; unsigned offset; struct menu *pm;
char *title; unsigned items; unsigned hitrow;};
#endif

struct mainmenu Maininfo[]={
{"File",   HFile,   (struct menu *)&File[0],   "File Menu",        0, 1},
{"Keys",   HKeys,   (struct menu *)&Keys[0],   "Keyboard",         0, 1},
{"Tools",  HTools,  (struct menu *)&Tools[0],  "Custom Tools",     0, 1},
{"Search", HSearch, (struct menu *)&Search[0], "Search",           0, 1},
{"Edit",   HEdit,   (struct menu *)&Edit[0],   "Edit Line",        0, 1},
{"Word",   HWord,   (struct menu *)&Word[0],   "Word Operations",  0, 1},
{"Block",  HBlock,  (struct menu *)&Block[0],  "Block Operations", 0, 1},
{"Values", HValues, (struct menu *)&Values[0], "Set Values",       0, 1},
{"Other",  HOther,  (struct menu *)&Other[0],  "Other",            0, 1},
{"Help",   HHelp,   (struct menu *)&Help[0],   "Help",             0, 1}
};
#define MaininfoITEMS sizeof(Maininfo)/sizeof(Maininfo[0])

#if 0
struct textinfo {
        int topcol;
        int toprow;
        int botcol;
        int botrow;
};

struct windows {
          char *title_center;                /* centered title */
          int top_row,top_col,               /* window coordinates*/
              bot_row,bot_col;
          struct textinfo oldw;              /* old window coordinates */
          char *info_center;                 /* centered information */
};
#endif

struct windows mainwind =
{MaininfoTITLE,
MAINSTARTROW,1,(MAINSTARTROW+1+MaininfoROWS),(1+MaininfoCOLS),
0,0,0,0,
""};

struct windows tmpwin =
{
"",               /* title */
DROP,             /* top_row */
0,                /* top_col, depends on screen width, start col */
(DROP+1+0),       /* bot_row, depends on # of items */
(0+0+1),          /* bot_col, depends on width of items */
0,0,0,0,          /* prev window coordinates */
""};              /* information line info_center */

/* Custom tools is a special case, where we add entries on the fly */
struct menu *UserTools= 0;  /* Pointer to Tools menu */
int UserToolsSize=0;        /* Menu entries in Tools menu */

void killUserTools(){
  if(UserTools) releaseMenu(UserTools,UserToolsSize);
  UserToolsSize=0; UserTools=0;
}

#if 0
struct windows {
          char *title_center;                /* centered title */
          int top_row,top_col,               /* window coordinates*/
              bot_row,bot_col;
          struct textinfo oldw;              /* old window coordinates */
          char *info_center;                 /* centered information */
};
#endif

void setupwindow(n) int n; {
int k;
int numlines,width;
struct menu *q;
extern void getUserTools();

  if(--n == 2){
    getUserTools(); /* Changes Maininfo */
    Maininfo[2].pm=UserTools;
  }
  /* Fix entries of tmpwin to match current data */
  width=strlen(tmpwin.title_center=Maininfo[n].title);
  numlines=0; q=Maininfo[n].pm;
  while(q->name) {++numlines; width=MYmax(width,strlen(q->name)); ++q;}
  Maininfo[n].items=numlines;
  tmpwin.top_col=Maininfo[n].offset;          /* Might not work */
  tmpwin.bot_col=tmpwin.top_col+1 + 1+width;
  if(tmpwin.bot_col > Htotal){  /* window is too wide */
    tmpwin.top_col=Htotal+1-width-2;
    tmpwin.bot_col=Htotal+1;
  }
  /* The largest allowed ROWS size is ROWMAX-DROP because of screen size */
  tmpwin.top_row=DROP;
  k=MYmin(numlines,ROWMAX-DROP);     /* Must fit on the screen */
  tmpwin.bot_row=DROP+1+k;
  tmpwin.info_center= (k == numlines) ?  "" : " + ";
}

static
int last=0;

unsigned menu()
{
unsigned retval;
int i,j,n,k;

  sysarg=sysv[0]=0;
  menumouse(1);
  retval=NULLKEY;
  setcursortype(0);
  setwindow(1,1,COLMAX+1,ROWMAX+1); /* using whole screen */
  if(winopen(&mainwind)){
     opnmain(); n=last+1;
     goto top1;
top:
     while(1){
       hilite(last,1);
       i=getkey();
       retval=NULLKEY;
       k=last;
       n=translate(i,&last,MaininfoITEMS);
       hilite(k,0);
       if(n==0) continue;
       if(n == -2) goto quit;
       if(n != -1) break;
     }
top1:
  while(1){
    if(n<1) n=MaininfoITEMS;
    if(n>MaininfoITEMS) n=1;
    last=n-1;
    hilite(last,1);
    setupwindow(n);
    if(winopen(&tmpwin)){
       j=opnselection(n,(int *)&retval);
       winclose(&tmpwin);
       killUserTools();
    } else {j = -1; bell();}
    if(j == 2) goto quit;
    hilite(last,0);
    if(j == -1) goto top;
    if(j == 3) --n;
    if(j == 4) ++n;
  }
quit:
  winclose(&mainwind);
  }
  setcursortype(1);
  lcount();
  menumouse(0);
  return retval;        /* NULLKEY if nothing happened */
}

void opnmain()
{
int i; extern char *endof();
  setattribute(windattr);
  strcpy(sysx,"  ");
  for(i=0;i<MaininfoITEMS;++i){
    strcat(sysx,Maininfo[i].name);
    strcat(sysx,"  ");
  }
    mycputs(1,SELECTROW,sysx);
}

/* Requires setupwindow to initialize variables in tmpwin */
int opnselection(n,retval) int n; int *retval; {
int j,k,hit,width,NUML;
/* struct menu { char *name; unsigned keycode; }; */
struct menu *p;
char *endof();

  p=Maininfo[--n].pm;
  k=Maininfo[n].items;     /* number of items */
  width=tmpwin.bot_col - tmpwin.top_col-2; /* number of columns*/
  NUML=tmpwin.bot_row - tmpwin.top_row - 1;     /* number of window rows */
  NUML=MYmin(NUML,ROWMAX-DROP);     /* Must fit on the screen */
    hit =Maininfo[n].hitrow-1;
    j=scrollingwindow((char **)0,p,k,width,NUML,&hit,sysvv,1,0);
    Maininfo[n].hitrow=hit+1;
    if(j==2){ p += hit; retval[0]=p->keycode;}
    return j;
}

void hilite(n,flag) int n,flag; {
  setattribute(windattr);
  if(flag) setattribute(REVERSEVIDEO);
  mycputs(Maininfo[n].offset,SELECTROW,Maininfo[n].name);
}

unsigned tounsignedupper(i) unsigned i; {
  if('a' <= i && i <= 'z') i -= ' ';
  return i;
}

int translate(i,last,maxnum)
unsigned i; int *last,maxnum; {
int n;
  i=tounsignedupper(i);
  switch(i){
  case ' ':case BAILOUT:case ENTERKEY:case MENUKEY: n= -2;break;
  case LEFTARROW:case 8:case 127:
    --last[0]; if(last[0]<0) last[0]=maxnum-1; n=0; break;
  case RIGHTARROW:case TABKEY:
    ++last[0]; if(last[0]>=maxnum) last[0]=0; n=0; break;
  case RETURNKEY: case DOWNARROW: case UPARROW:
    n=last[0]+1; break;
#if BSDunix || SYS5
  case MOUSEPRESS: getbyte(); getbyte(); getbyte(); break;
#endif
  default:
    n=menusearch(i,last[0],maxnum); /* returns -1 on failure */
    break;
  } /* end switch */
  return n;
}

int menusearch(i,last,maxnum)
int i,last,maxnum;
{
int j;
int flg=0;
char *MYstrchr();
twice:
  for(j=last;j<maxnum;++j){
    if(MYstrchr(Maininfo[j].name,i) != (char *)0) return j+1;
  }
  if(!flg){flg=1; maxnum=last; last=0; goto twice;}
  return -1;
}

/* Show a key sequence definition */
/* buf[] contains keys already hit */
void DisplayKeySeq(prompt,buf) char *prompt,*buf; {
char d[256];
char *p;
int x;
extern char *endof();
  p=endof(buf);
  while(1){
    printseq(prompt,buf,d);
    banner(d);
    if((x = getbyte()) == '\r') break;
    if((x==127 || x==8) && p>buf) --p;
        else
    *p++ =x;
    p[0]=0;
  }
}

int statflag=0;
int statwidth=0;
int verbMode=0;
#define STATBYTECHR 'D'         /* for delete a file */

int scrollingwindow(s,qq,len,width,NUML,index,sysvv,keys,statmark)
char **s; struct menu *qq;
int len,width,NUML; int *index; char *sysvv; int keys; int statmark;
{
int row,page,oldpage,last,n,i,j,k,flg,tmp,keycode;
char *MYstrchr(), *statbyte(); char *p,*r; struct menu *q; char *pp;
char *endof();
char bline[4];
extern int savcount;
extern char *getkeybuf;
             
      bline[0]=bline[2]=' '; bline[3]=bline[1]='\0';
      statflag=statmark;
      statwidth=width;
      last = index[0];
top:
      setcursortype(0);
      n=0;
      row=(last%NUML)+1;
      page=last/NUML; oldpage=page-1;
      while(n==0){
        if(page != oldpage){
          setattribute(windattr);
          for(oldpage=page, j=NUML*page,i=1;i<=NUML;++i,++j) {
            if(keys){q=qq+j; p = q->name; ++q;} else p = s[j];
            if(j>=len) p=bline;
            scrollcputs(i,p);
          }
        }
        setattribute(0);

        if(keys){  /* What to do with keymaps */
          q=qq+last; p = q->name; keycode=q->keycode;
          identifykey(keycode,sysvv);
          killduplicates(sysvv);
          if(MYstrcmp(sysvv,"Keymap")==0) strcpy(sysvv,"No key mapping");
          if(keys==2){
            /* p is 2 characters longer than its string length */
            ssprintf(sysv,"%s %s",(verbMode ? endof(p)+1:""),sysvv);
            strcpy(sysvv,sysv);
            if(keycode == 0){ /* No keymap on a header */
              strcpy(sysvv,(p[0]=='=')?"SPACEBAR quits":"");
            }
          }
          banner(sysvv);
        }
        else p = s[last];

        setattribute(REVERSEVIDEO);
        mycputs(1,row,p); /* mark target */
        setattribute(windattr);

        i=getkey();    /* Get one or more characters, return keycode */
        if(keys==2){
          /* getkey() stops on first mismatch of a key sequence */
          /* What to do with extra chars emitted by a key? */
          if(i< ' ') i=NULLKEY;
          if(i==NULLKEY) {
            bell();
            strcpy(sysvv,getkeybuf);  /* Copy what was read by getkey */
            DisplayKeySeq(" Press RETURN. Unmapped: ",sysvv);
            goto top;
          }
        }
#if BSDunix || SYS5
        if(i==MOUSEPRESS){
          int Row;
          getbyte(); getbyte(); Row=getbyte()-' '-DROP;
          if(!keys) goto top;  /* Eat mouse press */
          if(1<= Row && Row <= NUML){
             mycputs(1,row,p); /* unmark target */
             while(row>Row){ --row; --last;}
             while(row<Row){ ++row; ++last;}
          }
          goto loop;
        }
#endif

/* Working here ... */
        if(keys==2){
          setcursortype(1);
          switch(i){
/* debugging here .. */
#if 0
          case 'm':
             banner("");
             displaymemoryleft();           /* Debugging memory */
             goto loop;
#endif
          case '+': if(addkey(keycode) == -1) goto loop;
                    goto loopminus2;

          case '-': if(keycode == 0) goto loop;
                    { extern int lastidentifykey;
                      if(lastidentifykey!=-1){
                        if(killkey(ourkeys[lastidentifykey].seq) == -1)
                          goto loop;
                      }
                    }
                    goto loopminus2;

          case 'V': verbMode=1-verbMode; goto top;
          case 'S': /* S Show keypress */
                    sysvv[0]=0;
                    DisplayKeySeq(" RETURN quits: ",sysvv);
                    goto top;
          case 'M': if(!newmacrotitle(keycode)) goto top;
                    goto loopminus2;     /* "M  Macro title" */
          case 'T': if(!newtooltitle(keycode)) goto top;
loopminus2:
                    n= -2; goto loop;    /* "T  Tools menu item" */
          case '.': i=RETURNKEY;
                    /* strcpy(sysvv,p); */ /* Execute command */
                    /* n= 2; goto loop; */
          case ' ': /* Quit */
          case DOWNARROW:            /* Navigate */
          case UPARROW: goto navigate;
          default: /* match the key seq and refresh */
               for(j=0;j<len;++j){
                 if((qq+j)->keycode == i) {last=j; goto top;}
               }
          } /* End switch() */
          goto skipkeys;
        } /* end if(keys==2) */

navigate:
        switch(i){
          case INSERTLINE: tmp=' '; goto markit;
          case DELLINE:   tmp=STATBYTECHR;
markit:
                          if(statmark) *statbyte(p)=tmp;
dnarrow:
                          bline[0] = *statbyte(p);
                          mycputs(statwidth+1,row,bline);
                          bline[0]=' ';
          case DOWNARROW: if(last < len-1){
                            if(row<NUML) {scrollcputs(row,p); ++row;}
                            else {row=1; ++page;}
                            ++last;
                          }
                          break;   /* Has to goto loop */
          case UPARROW:   if(last>0) {
                            if(row>1){ scrollcputs(row,p); --row; }
                            else {row=NUML; --page;}
                            --last;
                          }
                          break;
          case PAGEFORWARD:
                          if((j=page*NUML+NUML)<len){row=1; ++page;last=j;}
                          break;
          case PAGEBACK:
                          if(page>0){
                            --page; row=NUML;
                            last = page*NUML+NUML-1;
                          }
                          break;
          case EXTLEFTARROW:
                              scrollcputs(row,p);
                              last=page*NUML;
                              row=1; break;
          case EXTRIGHTARROW:
                              scrollcputs(row,p);
                              last=page*NUML+NUML-1; row=NUML;
                              if(last>= len-1) {last=len-1; row=1+(last%NUML);}
                              break;
          case RETURNKEY: strcpy(sysvv,p);
                          n= 2; /* n= 2 is highlighted text */
                          break;
          case LEFTARROW: if(keys) n=3; break;
          case RIGHTARROW: if(keys) n=4; break;
          case ' ':
          case BAILOUT:
          case ENTERKEY:
          case MENUKEY:   n= -1; break;
          default: goto skipkeys;
        };
        goto loop;
skipkeys:

/* Search for the next text string that contains character i */

#if MSDOS      /* map to upper case */
        i=tounsignedupper(i);
#else
        if(keys) i=tounsignedupper(i);
#endif
        j=(last>=len-1) ? 0 : last+1;
        flg=0; k=len;
twice:
        for(;j<k;++j){
          if(keys){
            q=qq+j;
            if(MYstrchr(q->name,i) == (char *)0) continue;
          } else {
            if(s[j][0] != i) continue;
          }
          flg=1; break;
        }
        if(!flg){flg=2; k=last+1; j=0; goto twice;}
        if(flg==1){
          /* new page? */ page = j/NUML;
          if(page==oldpage) scrollcputs(row,p);
          last=j; row = 1+(j%NUML);
        }  else {
          goto dnarrow;  /* Which eventually does goto loop */
        }
loop: ;
      }/* end while(n==0) */

     index[0]=last;     /* return index of selection */
quit1:
     setattribute(0);
     return n;
     /* n==  3: LEFT key */
     /* n==  4: RIGHT key */
     /* n== -2: [keys=2] special exit */
     /* n== -1: abort */
     /* n==  2: sysvv[] is highlighted text, index[0]=array index */
}

void scrollcputs(row,t) int row; char *t; {
int k; extern int statflag; char *statbyte();
  strcpy(sysx,t);
  k=strlen(t); while(k<statwidth) sysx[k++]=' ';
  sysx[k++]= ((statflag!=0) ? *statbyte(t) : ' ');
  sysx[k]=0;
  mycputs(1,row,sysx);
}



/* get directory list */

#if (MSDOS || LCCWIN32)
#define DOTdotDIR "..\\"
#define BACKSL '\\'
#else
#define DOTdotDIR "../"
#define BACKSL '/'
#endif

/* ===================================================================*/
/* need scrolling window, dynamically determined, with functions to
   scroll the window and select an item. If .. is selected, then open
   the mother directory. Ditto for subdirectories. Once a file is
   selected, then copy it to sysvv[] and return with load file code.
*/
int MYstrcmpSHELL(s,t) char *s,*t; {
int c,d;
  c=tailchar(s); d=tailchar(t);
  while(s[0]){
    if(t[0]==0){
      if(c==BACKSL) return 0; /* don't swap, dir is smaller */
      return 1;               /* swap s,t */
    }
    if(s[0]==t[0]) {++s; ++t; continue;}
    /* mismatched */
    if(s[0]<t[0]){
      if(c!=BACKSL && d==BACKSL) return 1;
      return 0;
    }
    /* else s[0] > t[0] */
    if(c==BACKSL && d!=BACKSL) return 0; /* s[]==directory, t[]==file */
    return 1;                   /* swap s,t */
  }
  if(t[0]==0) return 0;         /* don't swap, matched */
  /* t is longer */
  if(d==BACKSL) return 1;       /* swap s,t because dir is smaller */
  return 0; 
}

void shellSORT(s,n) char **s; int n; {
int gap,i,ig,j; char *tmp;
  for (gap = n/2 ; gap > 0 ; gap /= 2){
    for (j = gap ; j < n; j++){
      for (i = j-gap ; i >= 0 ; i -= gap) {
              ig = i + gap;
              if (MYstrcmpSHELL(s[i],s[ig])==0) break;
              tmp=s[i]; s[i]=s[ig]; s[ig]=tmp;
      }
    }
  }
}


void removefile(ddir,fname,query) char *ddir,*fname; int *query; {
int x;
 strcpy(sysx,ddir); addslash(sysx); strcat(sysx,fname);
 x=isadir(sysx);
 if(query[0] != 'A' && query[0] != 'Q'){
   char s[256]; 
   ssprintf(s,"%s: Remove %s [y/n/a/q]? ",fname,(x?"directory":"file"));
   banner(s);
   query[0]=getupper();
 }
 if(query[0]=='Y' || query[0] == 'A'){
   if(x) {if(tailchar(sysx)==BACKSL) strcat(sysx,"."); rmdir(sysx);}
   else unlink(sysx);
 }
}

#define MAXNUML    16   /* maximum number of lines in popup */
#define DirStartRow 2   /* line where window starts */
struct windows  DirList = {
"",
DirStartRow,HFile,DirStartRow+1+MAXNUML,HFile+MAXNUML,
0,0,0,0,""};

/* FindFile addon */
/* Save context during a directory browse */

char *LastDir; char *LastFile;

/* If last filename was ".." then unlink to last entry and try to */
/* match ddir[] + filename. If found, then define last=index of filename, */
/* else set last=0. */
int LookupLastSpot(s,v,ddir) char **s; int v; char *ddir; {
int last=0;
char *t;
char dir[MAXFNAME],file[MAXFNAME];

  strcpy(dir,(LastDir==0 ? "" : LastDir));
  strcpy(file,(LastFile==0 ? "" : LastFile));

  if(MYstrcmp(file,DOTdotDIR)==0){ 
    /* unlink last directory */
    if(tailchar(dir)==BACKSL) *MYstrrchr(dir,BACKSL)=0; 
    strcpy(file,ffname(dir));
    *ffname(dir)=0;
    addslash(dir);
    addslash(file);
  }
  if(file!= 0 && dir != 0 && MYstrcmp(dir,ddir)==0){
    int i;
    for(i=0;i<v;++i) if(MYstrcmp(file,s[i])==0){last=i; break;}
  } /* end if */
  return last;
} /* end LookupLastSpot */

/* save ddir, s[last] */
void SaveLastDirSpot(ddir,filename) char *ddir,*filename; {
    freeup(LastDir);
    freeup(LastFile);
    safecore((char **)&LastDir,ddir);
    safecore((char **)&LastFile,filename);
} /* end group */

/* FindFile addon */
/* End save context during a directory browse */


int FindFile(buf,sysvv,which) char *buf,*sysvv; int which; {
/* extern char *MYstrchr(),*MYstrrchr(), *statbyte(); LLONG atoiLL(); */
char ddir[MAXFNAME];
char **s;
int v;
int i,j,n;
int query;
int width;              /* width of popup */
int NUML;               /* number of lines in popup */
int last=0;             /* offset into text array */
int status=0;           /* Exit status, 1==normal, 0==abort */

top:
    if((s=(char **)malloc(MAXDIRENT*sizeof(char *)))==(char **)0) goto quitzero;
    if(!which){
      if((v=dir(buf,1,1,ddir,s))==0) {bell(); goto quitzero;}
      shellSORT(s,v);
      last=0;
      if(which==0) last=LookupLastSpot(s,v,ddir); 
    } else {
      ddir[0]=0;
      v=getbufferlist(s,&last);
    }
    for(width=i=0;i<v;++i) width=MYmax(width,strlen(s[i]));
    /* width = max strlen of file spec */
    width = MYmax(2+strlen(ddir),width);
    NUML=MYmin(v,ROWMAX-2-4); /* popup uses 2+NUML lines */
    DirList.title_center=ddir;
    DirList.info_center=(NUML==v) ? "" : " + ";
    DirList.bot_row = DirStartRow+1+NUML;
    DirList.bot_col = HFile+width+2;
    if(winopen(&DirList)){
      n=scrollingwindow(s,(struct menu *)0,v,width,NUML,&last,sysvv,0,1);
/* debug: banner("last file="); etype(ddir); etype("+"); etype(s[last]); getbyte(); */
      if(which==0 && n != -1) SaveLastDirSpot(ddir,s[last]);
      winclose(&DirList);
    } else bell();

    for(query=j=0;j<v;++j){
      if(which==0 && *statbyte(s[j])==STATBYTECHR)
        removefile(ddir,s[j],&query);
    }

    for(j=0;j<v;++j) freeup(s[j]); freeup((char *)&s[0]);
    setcursortype(1);

    if(n== -1) goto quitzero;
    if(which==1){
      sysarg=(int)atoiLL(sysvv); goto quit;
    }
    if(n==2) n=buildspec(ddir,sysvv);
    /* buildspec: n=2 is a dir, n=3 is a filespec */
    if(n==2) {
      dirsplit(sysvv,buf);
      goto top;
    }
quit:
    status=1;
quitzero:
/*    if(which == 0) FreeLastDirSpots(); */
    lcount();
    return status;
}


#if BSDunix || SYS5 || MSDOS

#ifndef BACKSL
#if MSDOS || LCCWIN32
#define BACKSL '\\'
#else
#define BACKSL '/'
#endif
#endif

int buildspec(dir,s)
char *dir;      /* mother directory */
char *s;        /* target dir/file name, to be filled in */
{
char t[MAXFNAME];
int j; char *p; extern char *MYstrrchr();
  /* is t=dir+s a directory or a file? */
  strcpy(t,dir);
  strcat(t,s);
  if(tailchar(t)==BACKSL) *MYstrrchr(t,BACKSL)=0;
  if(isadir(t)){                /* march up dir tree? Or down tree? */
    if(s[0]=='.' && s[1]=='.') {/* up tree, remove one dir level */
      strcpy(t,dir);            /*  remove last BACKSL */
      if(tailchar(t)==BACKSL) *MYstrrchr(t,BACKSL)=0;
      if((p=MYstrrchr(t,BACKSL))!=(char *)0){   /* Is there another? */
        p[1]=0;                                 /* Then leave it, trunc. */
      } else strcpy(t,dir);                     /* Otherwise no change */
    } 
    else {      /* down tree, add new level (all done in t[]) */
      if(s[0]=='.' && (s[1]==0 || s[1]==BACKSL)){
        strcpy(t,dir);
      }
      addslash(t);      /*  add last BACKSL */
    }
    j=2;        /* it's a dir */
  } else j=3;   /* it's a file */
  strcpy(s,t);  /* Return assembled filename or directory name */
  return j;
}

#endif

/* End get directory list */

int reachedMAXDIRENT(v) int v; {
  return ((MAXDIRENT-4<v) ? 1 : 0);
}

#define HelpStartRow 2   /* line where window starts */

#if 0
struct windows {
          char *title_center;                /* centered title */
          int top_row,top_col,               /* window coordinates*/
              bot_row,bot_col;
          struct textinfo oldw;              /* old window coordinates */
          char *info_center;                 /* centered information */
};
#endif

struct windows  HelpList = {
"",
HelpStartRow,HFile,HelpStartRow+1+16,HFile+22,
0,0,0,0,
"Press SPACE to quit"};

/*#define MACROTRUNC 42*/       /* How wide before macro text is truncated */

int HelpKeys(KEYcode) int *KEYcode; { /* Sets keys=2 in scrollingwindow */
static int keeplast[2]={0,0};
extern char *sob(),*fnb(),*MYstrchr(),*MYstrrchr(),*endof();
extern char *KEYMAP;
FILEP fp;
char buff[512];
char title[128];
struct windows *wp;
struct menu *q,*qq;    /* struct menu { char *name; unsigned key; }; */
char *p,*r;
int j,k,keycode;
int n;                  /* error return code */
int v;                  /* Number of strings in menu */
int last=0;             /* offset into text array */
int width;              /* width of popup */
int NUML;               /* number of lines in popup */
int badcore;
LLONG atoiLL();
top:
    if((qq=q=(struct menu *)malloc(MAXDIRENT*sizeof(struct menu)))==(struct menu *)0){
      goto errexit;   /* "Can't malloc, low memory" */
    }
    badcore=v=0;

    switch(KEYcode[0]){
    case MACROLIBRARY: HelpList.title_center="Macro Library Editor";
                       last=keeplast[1]; goto macrokeys;
    default:           HelpList.title_center="Key Map Editor";
                       last=keeplast[0];
    }

    /* Process system-wide editor function definition file KEYMAP */

    if((fp=fopenbread(KEYMAP))==(FILEP)0){
      ssprintf(buff,"%s not found",KEYMAP);
      bellpressany(buff);
      goto macrokeys;
    }
    /* using heavily the format of keymap.dat */
    while(fgetLN(buff,fp)>=0){
      if(!MYisdigit(buff[0])) continue;  /* Skip all except digits */
      if( ( (keycode= (int)atoiLL(buff)) >= MACROKEY ) || keycode < 0) continue;
      if(!keycode) strcat(buff," []");
      else {
        if((p=MYstrstr(buff," ["))==0 || !MYstrchr(p,']')) strcat(buff," []");
        ssprintf(endof(buff),"{%u}",(unsigned)(keycode));
      }
      /* The string now ends with ']' or '}' and has length >=2 chars */
      if(!safecore(&(q->name),sob(fnb(buff)))){
         badcore=1; break;
      }
      q->keycode=keycode;
      strTruncate(q->name,'[');         /* Truncate just before bracket */
      ++q; if(reachedMAXDIRENT(++v)){badcore=1;break;};
    }
    closedfile(fp);
    if(badcore) goto badCORE;


    /* Get the macros from the memory-resident macrobuf[] array */
macrokeys:
    for(j=1;j<=MACNUM;++j){  /* Search for active macros */
      if(macromax[j]==0) continue;
      keycode=j+MACROKEY;
      getMacroTitle(keycode,title);   /* Get the title */
      ssprintf(buff,"Macro%d: %s [%u]",j,title,keycode);
      if(!safecore(&(q->name),buff)) goto badCORE;
      strTruncate(q->name,'[');         /* Truncate just before bracket */
      q->keycode=keycode;
      ++q; ++v;

      getToolTitle(keycode,title);   /* Get the title */
      if(title[0]){
        ssprintf(buff,"Tools: %s [%u]",title,keycode);
        if(!safecore(&(q->name),buff)) goto badCORE;
        strTruncate(q->name,'[');         /* Truncate just before bracket */
        q->keycode=keycode;
        ++q; ++v;
      }

      strcpy(buff," macro=");
      printseq("",macrobuf[j],endof(buff)); /* keys might not be defined */
      buff[MACROTRUNC]=0; /* truncate it; only 42 chars show on screen */
      ssprintf(endof(buff)," [%u]",keycode);
      /* The string now ends with "]" and has length >=2 chars */
      if(!safecore(&(q->name),buff)) goto badCORE;
      strTruncate(q->name,'[');         /* Truncate just before bracket */
      q->keycode=keycode;
      ++q; if(reachedMAXDIRENT(++v)) goto badCORE;
    } /* End for, search for active macros */
    if(!v){
      releaseMenu(qq,v);
      pressany("No data to process");
      return 1;
    }
    last=MYmin(last,v-1);           /* Upon reread, last could decrease */

    /* Display an information window */
    /* Don't worry about overlaps yet */
    {char *msg[]={
      "+  add a key sequence",
      "-  delete a sequence",
      ".  execute keycode",        /* Exit n= 2 */
      "M  Macro title",            /*  newmacrotitle(); */
      "T  Tools title",            /*  newtooltitle(); */
      "V  Verbose mode",
      "S  Show keypress",
      0};
      WarningMessage(msg); /* memory released; erase screen later */
    }



    /* Build the window data in which to display structure qq */
    wp= &HelpList;
    for(width=j=0;j<v;++j) width=MYmax(strlen((qq+j)->name),width);
    width = MYmax(1+sizeof(wp->title_center),width);
    width = MYmax(1+sizeof(wp->info_center),width);
    NUML=MYmin(v,ROWMAX-2-4); /* popup uses 2+NUML lines */
    wp->bot_row = HelpStartRow+1+NUML;
    wp->bot_col = HFile+width+2;
    n  = -1;
    if(winopen(wp)){
      n=scrollingwindow((char **)0,qq,v,width,NUML,&last,buff,2,0);
      winclose(wp);
    } else bell();
    if(KEYcode[0]==MACROLIBRARY) keeplast[1]=last;
    else keeplast[0]=last;
    if(n==2) KEYcode[0]=(qq+last)->keycode; /* Quit then execute keycode */
    closehelpsheet();                       /* Clean up the screen */
    releaseMenu(qq,v);                      /* free up memory */
    setcursortype(1);                       /* back to normal */
    lcount();                               /* Fix the status line */

    if(n== -2) goto top;
    return 0;
badCORE:
    releaseMenu(qq,v);
errexit:
    bellpressany("Can't malloc, low memory");
    return 1;
}


void releaseMenu(qq,v) struct menu *qq; int v; {
struct menu *q;
int j;
    for(q=qq,j=0;j<v;++j) {freeup(q->name); ++q; }
    freeup((char *)&qq[0]);
}

/* Shorten string 's' one behind last char 'c' */
void strTruncate(s,c) char *s; int c; {
char *p;
extern char *MYstrrchr();
  if((p=MYstrrchr(s,c))!=(char *)0){
         if(p != s) p[-1]=0;
  }
}

void getUserTools() /* After call, releaseMenu(UserTools,UserToolsSize) */
{
char buff[512];
FILEP fi=0;
int j,v;
int keycode;
char *p;
struct menu *qq,*q;
extern char *USERFILE;

  v=0;
  if((qq=q=(void *)malloc(MAXDIRENT*sizeof(struct menu)))==(struct menu *)0){
    bellpressany("Can't malloc, low memory");
    goto checkout;
  }
  for(j=0;(Tools[j].name != 0);++j){
    if(!safecore(&(q->name),Tools[j].name)) goto errexit;
    q->keycode=Tools[j].keycode; ++v; ++q;
    /* debug */
    /* printf("keycode=%d, title=%s\n",Tools[j].keycode,Tools[j].name); */
  }
  if((fi=fopenbread(USERFILE)) == (FILEP)0) goto checkout;
  while(fgetLN(buff,fi)>0){
     if(isacommentline(buff)) continue;
     if(isaheaderline(buff,&j)) keycode=j;
     if((p=MYstrstr(buff,"tool="))!=0){
       /* Save title and keycode as a Tools Menu item */
       if(!safecore(&(q->name),p+5)) goto errexit;
       q->keycode=keycode; ++q; if(++v >= MAXDIRENT-2) goto errexit;
       /* debug */ /* printf("keycode=%d, title=%s\n",keycode,p+5); */
     }
  }
  q->name=0; ++v;     /* Null marker for end of data */
checkout:
  if(fi) closedfile(fi);
  UserTools=qq;
  UserToolsSize=v;
  return;
errexit:
  bellpressany("Can't malloc, low memory");
  releaseMenu(qq,v);
  qq=0; v=0;
  goto checkout;
}


#endif  /* #if POPUPS */

#if POPUPS
/* window module for MSDOS/TURBOC and mainframes vt100 */
/*======================================================================*/

static
int topcol,toprow,botcol,botrow;

void setwindow(topc,topr,botc,botr) int topc,topr,botc,botr; {
  /* save window coordinates in global data set */
  topcol=topc; toprow=topr; botcol=botc; botrow=botr;
}

void mygotoxy(c,r) int c,r; {
  /* we use top row for status, row off by 1 */
  /* c,r numbered from 1, toprow,topcol numbered from 1 */
  /* putcursor row,col numbered from 0 */
  putcursor(r+(toprow-2)-1,c+(topcol-2));
}

void textreplace(topc,topr,botc,botr) int topc,topr,botc,botr; {
int c,r,i,j,width,height;  char *p; extern char *toprint();
  c = topc-1;  /* change to range 0..COLMAX */
  r = topr-1-1;  /* and range 0..ROWMAX */
  width = botc-topc+1+1;
  height = botr-topr+1;
  for(i=0;i<height;++i,++r){
    p=toprint(line[r]);
    if(strlen(p)<c) sysx[0]=0; else strcpy(sysx,p+c);
    j=strlen(sysx);
    while(j<width) sysx[j++]= ' ';
    sysx[width]=0;
    xPrint(sysx,r,c);
  }
  sysx[0]=0;
}

int winopen(w) struct windows *w;
{
int width,lastrow;

   if(w->bot_row > ROWMAX+1 || w->bot_col > COLMAX+1) return 0;
   if(w->bot_row - w->top_row - 1 <=0) return 0;
   w->oldw.topcol = topcol; w->oldw.toprow=toprow;
   w->oldw.botcol=botcol; w->oldw.botrow=botrow;
   setwindow(w->top_col,w->top_row,w->bot_col,w->bot_row+1);
   draw_box(w);
   setattribute(windattr);
   setattribute(titlattr);
   width  = w->bot_col - w->top_col +1;
   width -= 1+strlen(w->title_center);
   if(2<width) mycputs((width/2)+2,1,w->title_center);
   width  = w->bot_col - w->top_col +1;
   width -= 1+strlen(w->info_center);
   lastrow=w->bot_row-w->top_row+1;
   if(2<width) mycputs((width/2)+2,lastrow,w->info_center);
   setattribute(0);
   /* shrink window size 1 char on each edge */
   setwindow(w->top_col+1,w->top_row+1,w->bot_col-1,w->bot_row-1);
   return 1;
}

void winclose(w) struct windows *w;
{
  wintextreplace(w);
  winrestorewindow(w);
}

void wintextreplace(w) struct windows *w;
{
#if BSDunix || SYS5
  setattribute(0x00);
  textreplace(w->top_col,w->top_row,w->bot_col,w->bot_row);
#else
  textreplace(w->top_col,w->top_row,w->bot_col,w->bot_row);
  setattribute(windattr);
#endif
}

void winrestorewindow(w) struct windows *w;
{
  setwindow(w->oldw.topcol,w->oldw.toprow,w->oldw.botcol,w->oldw.botrow);
}

void draw_box(w) struct windows *w;
{
int x;
int height, width;
int horiz,vert;
int ulh,urh,llh,lrh;
char bar[2];

  if(macrun || nodisp) return;
  width     = w->bot_col - w->top_col +1;
  height    = w->bot_row - w->top_row +1;

  switch(boxcharset){
  case 1: horiz=196; vert=179;       /* single box */
          ulh=218; urh=191; llh=192; lrh=217;
          break;
  case 2: horiz=205; vert=186;       /* double box */
          ulh=201; urh=187; llh=200; lrh=188;
          break;
  case 3: horiz=232; vert=224;       /* single box iso font */
          ulh=229; urh=238; llh=226; lrh=235;
          break;
  case 4: vert=242; horiz=220;      /* double box iso font */
          llh=244; ulh=251; lrh=214; urh=163;
          break;
  default:
  case 0: horiz= '=';                /* text box */
          ulh=urh=llh=lrh=vert= ' ';
          break;
  }

  setattribute(windattr);
  setattribute(bordattr);
  /* Write the corners */
  mygotoxy(1,1);          aputc(ulh,1);
  mygotoxy(width,1);      aputc(urh,1);
  mygotoxy(1,height);     aputc(llh,1);
  mygotoxy(width,height); aputc(lrh,1);
  /* Verticals */
  for(x=2;x<height;++x) {
    mygotoxy(width,x); aputc(vert,1);
    mygotoxy(1,x);     aputc(vert,1);
  }
  /* horizontals */
  mygotoxy(2,1); aputc(horiz,width-2);
  mygotoxy(2,height); aputc(horiz,width-2);
  setattribute(0);
}

int sysattribute;

void setattribute(x) int x; {
if(x) sysattribute = x;
#if !MSDOS && !LCCWIN32
switch(x){
case 0x00: etype(NORMVIDEO); setcolorsUNIX(1); break;
case 0x07: etype(NORMVIDEO); setcolorsUNIX(3); break;
case 0x70: etype(REVVIDEO); break;
case 0x80: etype(BOLDF); break;
}
#endif
#if LCCWIN32
switch(x){
case 0x00: normvideo(); setLCCWIN32colors(); break;
case 0x07: normvideo(); setLCCWIN32menucolors(); break;
case 0x70: normvideo(); setLCCWIN32inversecolors(); break;
case 0x80: highvideo(); break;
}
#endif
}

/* output char=c with attribute=sysattribute for n times */
#if MSDOS
void aputc(c,n) int c,n; {
void __int__();
extern int sysattribute;
  _CX=n;
  _BH=0; _BL=sysattribute;
  _AH=0x9; _AL=c;
  __int__(0x10);
}
#endif

#if !MSDOS
void aputc(c,n) int c,n; {
char s[256]; int i=0;
  while(i<n) s[i++]=c; s[i]=0;
  ePrint(s);
}
#endif

#if MSDOS
void mycputs(col,row,s) int col,row; char *s; {
  if(macrun || nodisp) return;
  while(*s){
   mygotoxy(col++,row);
   aputc(*s++,1);
  }
}
#endif

#if !MSDOS
void mycputs(col,row,s) int col,row; char *s; {
  if(macrun || nodisp) return;
  mygotoxy(col,row);
  etype(s);
}
#endif

#endif /* POPUPS */

/* Line and Box drawing module, all systems */
/*======================================================================*/
/* Line and Box drawing module for IBM-PC graphics */
/* my line drawing set:   3  D    E   A   B   C   4   @   Y    Z    ?  */
/*                       179 196 197 193 194 195 180 192 217  218  191 */
enum {
VERT=1,HORIZ=2,CROSS=3,TEEup=4,TEEdn=5,TEEright=6,TEEleft=7,
BOXll=8,BOXlr=9,BOXul=10,BOXur=11
};

#if MSDOS
#define X 128
unsigned
char style[7][12] ={
/* single line */
' ','3'+X,'D'+X,'E'+X,'A'+X,'B'+X,'C'+X,'4'+X,'@'+X,'Y'+X,'Z'+X,'?'+X
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* double line */
' ',':'+X,'M'+X,'N'+X,'J'+X,'K'+X,'L'+X,'9'+X,'H'+X,'<'+X,'I'+X,';'+X
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* double horizontal, single vertical */
' ','3'+X,'M'+X,'X'+X,'O'+X,'Q'+X,'F'+X,'5'+X,'T'+X,'>'+X,'U'+X,'8'+X
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* double vertical, single horizontal */
' ',':'+X,'D'+X,'W'+X,'P'+X,'R'+X,'G'+X,'6'+X,'S'+X,'='+X,'V'+X,'7'+X
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* Ascii */
   ' ', '|', '-', '+', '-', '-', '|', '|', '*', '*', '*', '*'
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* Ascii (+|=*) */
   ' ', '|', '=', '+', '=', '=', '|', '|', '*', '*', '*', '*'
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* Eraser */
   ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
};
#define STYLEMESSAGE "1=Single,2=Dbl,3=Dbl top,4=Dbl side,5=Asc-,6=Asc=,7=Erase"
#endif

#if !MSDOS
unsigned
char style[5][12] ={
/* Ascii (+|-) */
   ' ', '|', '-', '+', '-', '-', '|', '|', '+', '+', '+', '+'
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* Ascii (+|=) */
   ' ', '|', '=', '+', '=', '=', '|', '|', '+', '+', '+', '+'
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* Ascii (+|-*) */
   ' ', '|', '-', '+', '-', '-', '|', '|', '*', '*', '*', '*'
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* Ascii (+|=*) */
   ' ', '|', '=', '+', '=', '=', '|', '|', '*', '*', '*', '*'
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
,
/* Eraser */
   ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
/*  0    1    2    3    4    5    6    7    8    9    10   11 */
};
#define STYLEMESSAGE "1=Ascii(+|-),2=Ascii(+|=),3=Ascii(+|-*),4=Ascii(+|=*),5=Erase"
#endif

int stylesize = sizeof(style)/sizeof(style[0]);

/* subscripts which determine character of connector piece */
static char NORTH[]={1,3,5,6,7,10,11}; /* connect north */
static char SOUTH[]={1,3,4,6,7,8,9};   /* etc */
static char WEST[]={2,3,4,5,6,8,10};
static char EAST[]={2,3,4,5,7,9,11};

/* table arranged as follows: */
/*
       +----+
       | u  |
  +----+----+----+
  | l  | *  | r  |      * = current row,col
  +----+----+----+       1 = connect possible
       | d  |            0 = connect impossible
       +----+            u = 8-bit, r = 4-bit, d = 2-bit, l = 1-bit
*/
/*  8*u+4*r+2*d+1*l,  Rt, Lft, Up, Dn */
#define STATE1111 8*1+4*1+2*1+1*1
#define STATE1110 8*1+4*1+2*1+1*0
#define STATE1100 8*1+4*1+2*0+1*0
#define STATE1101 8*1+4*1+2*0+1*1
#define STATE1011 8*1+4*0+2*1+1*1
#define STATE1010 8*1+4*0+2*1+1*0
#define STATE1000 8*1+4*0+2*0+1*0
#define STATE1001 8*1+4*0+2*0+1*1
#define STATE0111 8*0+4*1+2*1+1*1
#define STATE0110 8*0+4*1+2*1+1*0
#define STATE0100 8*0+4*1+2*0+1*0
#define STATE0101 8*0+4*1+2*0+1*1
#define STATE0011 8*0+4*0+2*1+1*1
#define STATE0010 8*0+4*0+2*1+1*0
#define STATE0000 8*0+4*0+2*0+1*0
#define STATE0001 8*0+4*0+2*0+1*1
static char
transtable[16][5] = {
STATE1111, CROSS, CROSS, CROSS, CROSS,
STATE1110, TEEright, CROSS, TEEright, TEEright,
STATE1100, BOXll, TEEup, BOXll, TEEright,
STATE1101, TEEup, TEEup, TEEup, CROSS,
STATE1011, CROSS, TEEleft, TEEleft, TEEleft,
STATE1010, TEEright, TEEleft, VERT, VERT,
STATE1000, BOXll, BOXlr, VERT, VERT,
STATE1001, TEEup, BOXlr, BOXlr, TEEleft,
STATE0111, TEEdn, TEEdn, CROSS, TEEdn,
STATE0110, BOXul, TEEdn, TEEright, BOXul,
STATE0100, HORIZ, HORIZ, BOXll, BOXul,
STATE0101, HORIZ, HORIZ, TEEup, TEEdn,
STATE0011, TEEdn, BOXur, TEEleft, BOXur,
STATE0010, BOXul, BOXur, VERT, VERT,
STATE0000, HORIZ, HORIZ, VERT, VERT,
STATE0001, HORIZ, HORIZ, BOXlr, BOXur
};

/* choose default line style */
static
unsigned
char *boxpiece;

void stylechange(n) int n;{
extern int drawstyle,stylesize;
  if(n==0) {
    drawline=1-drawline;
    if(drawline) getnumquery(STYLEMESSAGE,&drawstyle,1,stylesize);
    killstatus();
  }
  else
  if(1<=n && n<=stylesize) drawstyle=n;
  boxpiece= style[drawstyle-1];
}

int findnewchar(x,y) int x,y; {
 int i;
 for(i=0;i<16;++i){
   if(transtable[i][0] == x) return (0xFF & boxpiece[transtable[i][y]]);
 }
 return 0;
}

/* see if x is one of the matching pieces in boxpiece[] */
int boxmatch(x,which) unsigned int x; char *which; {
int i;
  for(i=0;i<7;++i) if((unsigned char)x == boxpiece[which[i]]) return 1;
  return 0;
}

enum {RIGHT=1,LEFT=2,UP=3,DN=4};

int boxdraw(key) int key; {
extern int row,col,ROWMAX; extern char **line;
int x,y,r,c;
#define UC unsigned char
  switch(key){
  case UPARROW:    y=UP; break;
  case DOWNARROW:  y=DN; break;
  case LEFTARROW:  y=LEFT; break;
  case RIGHTARROW: y=RIGHT; break;
  default: return 0;
  }
/* got a valid key 1-4, build 4-bit key into table */
  x=0; c=col+curoff; r=row;
  if(0<r && c<strlen(line[r-1]) && boxmatch((UC)line[r-1][c],NORTH)) x += 8;
  if(c+1<strlen(line[r]) && boxmatch((UC)line[r][c+1],EAST)) x += 4;
  if(r+1<ROWMAX && c<strlen(line[r+1]) && boxmatch((UC)line[r+1][c],SOUTH)) x += 2;
  if(0<c && c-1<strlen(line[r]) && boxmatch((UC)line[r][c-1],WEST)) ++x;
  return findnewchar(x,y);
}

#if 1
/* A one-time warning window. */
#if 0
#define HelpSheetROWS 3
#define HelpSheetCOLS 5
#endif

#if 0
struct windows {
          char *title_center;                /* centered title */
          int top_row,top_col,               /* window coordinates*/
              bot_row,bot_col;
          struct textinfo oldw;              /* old window coordinates */
          char *info_center;                 /* centered information */
};
#endif

#if 0
struct windows helpsheet =
{"",
2,1,(2+1+HelpSheetROWS),(1+HelpSheetCOLS),
0,0,0,0,
""};
int ishelpsheetopen=0;
#endif

void WarningMessage(HlpText) char **HlpText; {
int i,width,len,spaces;
struct windows *w;
extern int COLMAX;
extern int ishelpsheetopen;
  w= &helpsheet;
  if(ishelpsheetopen==1){
    bell(); clrcon(); banner("More messages. Press a key:"); getkey();
    closehelpsheet();
  }
  if(!loadInfoText(w,HlpText)) goto err;
  if(winopen(w)){
    setattribute(windattr);
    len=w->bot_row-w->top_row-1;
    for(i=1;i<=len;++i) {
      mycputs(1,i,HlpText[i-1]);
      width=1+strlen(HlpText[i-1]);
      mygotoxy(width,i);
      spaces=w->bot_col-w->top_col-width;
      aputc(' ',spaces);
    }
    winrestorewindow(w);
    ishelpsheetopen=1;
    setcursortype(1);
    curses();
  }
err:;
#if BSDunix || SYS5
  setattribute(0x00);
#endif
}

#if 0
struct windows {
          char *title_center;                /* centered title */
          int top_row,top_col,               /* window coordinates*/
              bot_row,bot_col;
          struct textinfo oldw;              /* old window coordinates */
          char *info_center;                 /* centered information */
};
#endif
int loadInfoText(w,HlpText) struct windows *w; char **HlpText; {
int len,nrows,k;

  len=2+MYmax(strlen(w->title_center),strlen(w->info_center));
  for(nrows=0;HlpText[nrows]!=0;++nrows){
    len=MYmax(len,strlen(HlpText[nrows]));
  }
  /* nrows=number of matrix rows, len=number of matrix columns */
  k=COLMAX-len;
  w->bot_col=COLMAX;              /* Justify matrix right side of screen */
  w->top_col= (k>0) ? k-1: 0;
#if 0
  w->top_row=2;                   /* And just below the status line */
  w->bot_row=2+1+nrows;
#else
#define BOXTOP (ROWMAX-nrows)
  w->top_row=BOXTOP;                   /* And just below the status line */
  w->bot_row=BOXTOP+1+nrows;
#endif
  if(2+len>COLMAX || 3+nrows>ROWMAX) return 0;
  return 1;
}



void closehelpsheet(){
extern int ishelpsheetopen;
    if(ishelpsheetopen==1){
      ishelpsheetopen=0;
      wintextreplace(&helpsheet);
      setcursortype(1);
      curses();
    }
}

#endif
/*
 * V3.C
 *
 */

#include "pie.h"

/*
 * STILLR
 *
 * Purpose:
 *              Test for room to push line onto low
 *              or high buffer. Need HEADROOM bytes free.
 * Returns:
 *              TRUE  - if room is available
 *              FALSE - out of room
 *                      Prints NO ROOM message on banner line
 * Notes:
 *              o Lines are up to 1+MAXLN long. Don't let the
 *                editor operate with less than 1+MAXLN bytes free.
 *              o This module does a lot of the dirty work for
 *                the 640k memory model. It silently purges the
 *                edit buffer when nearly full, so that attempts
 *                to add strings will succeed.
 */
int stillroom() {
  if(HEADROOM< hbuf-lbuf) return TRUE;
  /* might have to purge 2k from the buffer */
  if(bufbase+SIZEBLOCK <= lbuf) putLOWblock();
  else
  if(SIZEBLOCK<=ebuf-hbuf) putHIblock();
  return ( (HEADROOM< hbuf-lbuf) ? TRUE : noroom());
}

/*
 * LOPUSH
 *
 * Purpose:
 *              Push a line p onto the low buffer
 * Returns:
 *              p     - success
 *              NULLP - failure
 * Notes:
 *              o Strip blanks
 *              o update lbuf
 *              o check for available room
 */
char *loPush(p) char *p; {
  if(stillroom()) {
    if(curfile && BLANKSTRIP) strip(p);
    strcpy(lbuf+1,p);
    lbuf += strlen(p)+1;
    return p;
  }
  return NULLP;
}

/*
 * HIPUSH
 *
 * Purpose:
 *              Push a line onto the high buffer.
 * Returns:
 *              p     - if successful
 *              NULLP - on failure
 * Notes:
 *              o Check for room.
 *              o update hbuf, the high buffer pointer
 */
char *hiPush(p) char *p; {
  if(stillroom()) {
    if(curfile && BLANKSTRIP) strip(p);
    if(p[0]==0){
      /* don't add blank lines onto the very end of the buffer */
      if(hbuf>=ebuf && finfo[curfile].nbTOT<=finfo[curfile].nbLOW)
        return p;
    }
    hbuf -= strlen(p)+1;
    strcpy(hbuf,p);
    return p;
  }
  return NULLP;
}

/*
 * LOPULL
 *
 * Purpose:
 *              Get a line from the low buffer.
 * Returns:
 *              TRUE  - it worked
 *              FALSE - it failed
 *              lbuf  - updated
 */
int loPull(p) char *p; {
char *q,*r;
  if(lbuf<bufbase){
    if(getLOWblock()!=0) return FALSE;
    if(hbuf-lbuf <= HEADROOM) putHIblock();
  }
  if(lbuf<bufbase) return FALSE;
  *lbuf=0;
#if 0 /* Remove stripping of CR in CRLF type files May 1995 */
  if(intexttype==1 && lbuf[-1] == 13) *--lbuf = 0;
#endif
  lbuf = strstart(r=lbuf);
  if(DETABBER && curfile && dotabs(lbuf+1)) {
    q=detab(lbuf+1,p);
    if(q<r) {strcpy(lbuf+1,q); lbuf = endof(lbuf+1);}
  }
  else
  strcpy(p,lbuf+1);
  if(BITSTRIP) unsetbits(p);
  return TRUE;
}

/*
 * HIPULL
 *
 * Purpose:
 *              Pull a line off the high buffer.
 * Returns:
 *              TRUE  - it worked
 *              FALSE - it failed
 * Notes:
 *              o update hbuf
 *              o don't do it if high buffer is empty
 */
int hiPull(p) char *p; {
  if(hbuf>=ebuf) {
    if(getHIblock()==0){
      if(HEADROOM >= hbuf-lbuf) putLOWblock();
    }
  }
  if(hbuf>=ebuf) { /* add blank lines as requested */
    *p = EOS;
    return TRUE;
  }
  if(DETABBER && curfile && dotabs(hbuf)) {
    hbuf=detab(hbuf,p);
#if 0 /* Remove stripping of CR in CRLF type files May 1995 */
    if(intexttype==1 && (i=strlen(p))>0 && p[i-1]==13) p[i-1]=0;
#endif
  }
  else {
    strcpy(p,hbuf);
    hbuf += strlen(p)+1;
#if 0 /* Remove stripping of CR in CRLF type files May 1995 */
    i=strlen(p);
    if(intexttype==1 && i>0 && p[i-1]==13) p[i-1]=0;
#endif
  }
  if(hbuf>ebuf) hbuf = ebuf;
  if(BITSTRIP) unsetbits(p);
  return TRUE;
}

void decrROW(){
 --row; --lncount;
}

void incrROW(){
 ++row; ++lncount;
}

/*
 * LINEUP
 *
 * Purpose:
 *              Scroll up one line.
 * Notes:
 *              o Moves last line onto high buffer
 *              o Pulls new line from low buffer
 *              o update screen, pointers
 *              o only works if low buffer not empty
 *              o If out of room, then last line is
 *                thrown away, with warning banner.
 */
void lineup() {
int i;
char *p;

  if(bufbase > lbuf) getLOWblock();
  if(bufbase <= lbuf) {
      if(hiPush(p=line[ROWMAX]) == NULLP) {
              bellpressany("NO ROOM - Last line lost"); return;
      }
      revindex();
      loPull(p);
      for(i=ROWMAX-1;i>=0;--i) line[i+1] = line[i];
      line[0]=p;
      if(lncount) --lncount;
      xPrint(toprint(p),0,0);
  }
}

/*
 * LINEDN
 *
 * Purpose:
 *              Scroll off the first line, scroll in
 *              a new last line.
 * Notes:
 *              o Move in a blank line if the high buffer is empty
 *              o Fails if the low buffer push fails
 *              o Screen update indicates success
 */
void linedn() {
int i;
char *p;

  if((p = loPush(line[0])) != NULLP) {
    closehelpsheet();
    fwdindex();
    for(i=0;i<ROWMAX;++i) line[i] = line[i+1];
    hiPull(line[ROWMAX]=p);
    xPrint(toprint(p),ROWMAX,0);
    ++lncount;
  }
}


/*
 * REFRESHH
 *
 * Purpose:
 *              Refresh(off) computes the target line as the current
 *              line plus off and brings it to the top of the screen.
 *              Refresh is either NEAR (smooth scroll) or FAR (full
 *              screen clear plus print all lines). We define FAR as
 *              more than 2 screens away (backward or forward).
 * Returns:
 *              TRUE  - Refresh done as requested.
 *                      lncount = target line.
 *                      row & col unchanged.
 *              FALSE - Refresh failed due to no room.
 * Notes:
 *              o This was the most difficult code to write in the
 *                entire editor. Change it with caution.
 *              o lncount -  current line number (1...maxint)
 *              o hiPush(), hiPull(), loPush(), loPull(), lineup(),
 *                linedn() are previously defined editor functions.
 *              o Out-of-range requests are truncated to the nearest
 *                reasonable value.
 */
int Crefreshh(off) LLONG off; {   /* Refresh with cursor center screen */
int newrow; int r;
/* extern int row; extern LLONG lncount; */
  newrow=middle((LLONG)off);
  r=((off=refreshh(off-newrow))!=0);
  if(r) lncount += (row=newrow);
  return r;  /* 0 if off==0 and 1 otherwise */
}

/* Refresh to next page, retain cursor. */
void Prefreshh(direction) int direction; { 
int i;                      /* Argument is in array sysv[] */
  lncount -= (i = row); row=0;
  refreshh(direction*atoiLL(sysv)*(1+ROWMAX));
  lncount += (row=i);
}

int refreshh(off) LLONG off; {
LLONG target,offset,top,bot;
int tmp,flag;

  target = lncount+off;
  if(off < 0 && lncount <= -off) target = 1;
/*
 *      Compute line numbers for top and bottom of screen
 */
  top = lncount - row;
  bot = top + ROWMAX;
  flag = FALSE;
  deFRAG();  /*insure low and high buffers do not fragment */
/*
 *      Is target on current screen?
 *      Then scroll target to top line.
 */
  if(top <= target && target <= bot) {
    offset = target - top;
    while(offset-- > 0) linedn();
  }
  else
/*
 *      Is target above current screen?
 *      Then test for FAR or NEAR scroll.
 */
  if(target < top) {
    offset = top - target;
    if(offset < FARSCROLL) {            /* NEAR scroll */
      PUSHnodisp();
      if(!(HASINSERTLINE+HASSCROLL)) nodisp = flag = 1;
      while(offset-- > 0) lineup();
      POPnodisp();
    }
    else {                                  /* FAR scroll */
      if(!PackScreenBuffer(0)) {
        offset -= ROWMAX+1;
        while(offset-- > 0) {
          if(!loPull(sysx) || !hiPush(sysx)) {
            target += 1+offset;         /* correct it */
            break;
          }
        }
        UnPackScreenBuffer(ROWMAX+1);
        flag = TRUE;
      } else return FALSE;
    }
  }
  else
/*
 *      Is target below current screen?
 *      Then test for FAR or NEAR scroll.
 */
  if(target > bot) {
    offset = target - bot + ROWMAX;
    if(offset < FARSCROLL) {            /* NEAR scroll */
      while(offset-- > 0) linedn();
    }
    else {                              /* FAR scroll */
      if(!PackScreenBuffer(ROWMAX+1)) {
        offset -= 1+ROWMAX;
        while(offset-- > 0) {
          if(!hiPull(sysx) || !loPush(sysx)){
            target -= 1+offset;         /* correct it */
            break;
          }
        }
        UnPackScreenBuffer(0);
        flag = TRUE;
      } else return FALSE;
    }
  }
  lncount = target;
  if(flag) drawall();
  return TRUE;
}

/*
 * EDUP
 *
 * Purpose:
 *              Move position on screen up one line.
 * Notes:
 *              o If at top of screen, then pull in a line.
 *              o Keep track of line count.
 *              o Display line number and free space.
 */
void edup() {
  if(row) decrROW();
  else
  lineup();
}

/*
 * EDDN
 *
 * Purpose:
 *              Move cursor down one line.
 * Notes:
 *              o Scroll down as needed
 *              o Update line count
 *              o Display line count when done
 */
void eddn() {
  if(row < ROWMAX) incrROW();
  else
  linedn();
}

/*
 * HOMEIT
 *
 * Put screen back to left edge.
 *
 */
void homeit(){
  col = 0;
  if(curoff){curoff=0; drawall();}
}

/*
 * ENDIT
 *
 * Put screen back to right edge.
 *
 */
void endit(){
   col = strlen(line[row]);
   if(col>curoff+COLMAX || col<curoff) curfix(1+col,0);
   else col -= curoff;
}

/*
 * EDRIGH
 *
 * Purpose:
 *              Move cursor right one space
 */
void edright() {
#define JUMPRIGHT 5
if(col < COLMAX) ++col;
else {
  if(col+curoff<MAXLN-1){
    curoff=MYmin(curoff+JUMPRIGHT,MAXLN-1-col);
    col=MYmax(0,col+1-JUMPRIGHT);
    setoffset(curoff);
    drawall();
  }
}
}

/*
 * EDLEFT
 *
 * Purpose:
 *              Move cursor left one space.
 */
void edleft() {
#define JUMPLEFT 5
if(col) --col;
else { /* in col=0 */
  if(curoff>0) {
    curoff=MYmax(curoff-JUMPLEFT,0);
    col=MYmin(COLMAX,col+JUMPLEFT-1);
    setoffset(curoff); drawall();
  }
}
}

/*
 * ADDLN
 *
 * Purpose:
 *              Add a blank line to the screen. Push last line
 *              on screen to high buffer.
 * Returns:
 *              TRUE  - line was added
 *              FALSE - failed to add line due to no room.
 */
int addln(q,flag) char *q; int flag; {
int i;
char *p;

  if((hiPush(p=line[ROWMAX])) != NULLP) {
    if(flag) lineinsert();
    for(i=ROWMAX-1;i>=row;--i) line[i+1] = line[i];
    strcpy(line[row]=p,q);
    if(flag) xPrint(toprint(p),row,0);
    return TRUE;
  }
  return noroom(); /* noroom() returns FALSE */
}

/*
 * ADDSTRING
 *
 * Purpose:
 *              Add a string to the screen at the cursor position.
 *              The destination d[] is assumed a screen line
 *              or buffer of physical buffer size MAXLN.
 *              Position = [row,col] at offset=curoff
 * Returns:
 *              k  - string was added of length k>=0
 *             -1  - failed to add string because of no room.
 */
int addstring(s,d,flag,insmode)
char *s; char *d; int flag,insmode; /* s=source, d=destination, flag=1 or 0 */
{
int i,j,k,l;

  k=strlen(s);
  j=strlen(d);
  i=curoff+col;
  while(j<i) d[j++]= ' ';
  d[j]=0;
  l=(insmode ? (j+k): MYmax(j,i+k));
  if(l<MAXLN-1){
    d += i;
    if(insmode) strcpy(sysv,d);
    moveMEM(d,s,k);
    if(insmode) strcpy(d+k,sysv);
    d[l-i]=0;
    if(flag) xPrint(d,row,col);
    return k;
  }
  bell();
  return -1;
}

/*
 * DELLN
 *
 * Purpose:
 *              Delete a line on the screen from the screen
 *              buffer, and hence from the main buffer.
 * Returns:
 *              Deleted line copied to string pointer r.
 */
void delln(r) char *r; {
int i;
char *p;

  linedelete();
  strcpy(r,p = line[row]);
  for(i=row;i<ROWMAX;++i) line[i] = line[i+1];
  hiPull(line[ROWMAX]=p);
  xPrint(toprint(p),ROWMAX,0);
  curses();
}
/*
 * V4.C
 */

#include "pie.h"

void hiloPACK(){
LLong amt;
  if((amt = (ebuf-hbuf)) != (LLong)0) moveMEM(lbuf+1,hbuf,amt);
  *(lbuf += amt) = *(hbuf = ebuf) = EOS;
}

int HIBLtoLOW(){
int n,I,N;
  if((N=getmeminfoindex(curfile,finfo[curfile].nbLOW))== -1) return -1;
  n=meminfo[N].numbytes;
  if(lbuf+n>=ebuf) return -2;
  CPfromBLOCK(I=meminfo[N].record,lbuf+1,n);
  lbuf += n;
  rlBLOCK(I);
  --(finfo[curfile].nbTOT); 
  killinfo(N);
  return 0;
}

/*
 * HITOLO
 *
 * Purpose:
 *              Merge high buffer onto low buffer and reset
 *              lbuf and hbuf pointers.
 * Returns:
 *              Nothing.
 * Notes:
 *              o Normal use calls PackScreenBuffer() first.
 *              o The block move must be able to copy null bytes.
 *              o lbuf points to the null byte delimiter at the
 *                end of the last string in the lower buffer.
 *              o The buffer is empty if lbuf = bufbase - 1.
 *              o Always, bufbase[-1] = EOS, bufbase[-2] = 0xFF.
 *              o For the ext memory model, there is much more to do.
 *                The basic idea is to pack down the buffer to the
 *                low end, copy in a block from above, and then
 *                purge the low buffer to a block. This is repeated
 *                until all high blocks have been copied. Note:
 *                this works only because the main buffer GAP is
 *                big enough to accept a copy of a whole block (2k).
 *              o The strange code at the end is used to insure that
 *                the GAP does not collapse. The blank line purge is
 *                required after the main while() in order to keep
 *                from adding extraneous lines on the end of the file.
 */
void HiToLowBlockMove(k) int k; {
int n;
  hiloPACK();
  if(k){
    while(1){
      n = putLOWblock();
      if(n == -1) break;
      if(n == -2) goto slow;
    }
    finfo[curfile].nbLOW = finfo[curfile].nbTOT;
    goto purge;
  }
slow:
  while(1){
    if(HIBLtoLOW()) break;
    if(bufbase+SIZEBLOCK <= lbuf) putLOWblock();
  }
purge:
  while(loPull(sysx)){
    if(sysx[0]){loPush(sysx); break;}
  }
  if(HEADROOM >= hbuf-lbuf) if(putLOWblock()) noroom();
}

void lohiPACK(){
LLong amt;
  if((amt = (lbuf+1)-bufbase) != 0) moveMEM(hbuf = hbuf - amt,bufbase,amt);
  *ebuf = *(lbuf = bufbase-1) = EOS;
}

/*
 * LOWTOH
 *
 * Purpose:
 *              Moves low-packed buffer to high buffer end.
 * Notes:
 *              o Call PackScreenBuffer() before using so
 *                that the screen buffer is merged onto the
 *                low end prior to the move.
 */
void LowToHighBlockMove(k) int k; {
int n; struct fileinfo *p;
  p= &finfo[curfile];
  if(p->nbLOW>=p->nbTOT){
    hiloPACK();
    while(loPull(sysx)){
      if(sysx[0]){loPush(sysx); break;}
    }
  }
  lohiPACK();
  if(k){
    while(1){
      n = putHIblock();
      if(n == -1) break;
      if(n == -2) goto slow;
    }
    p->nbLOW = 0;
    return;
  }
slow:
  while(1){
    if(LOBLtoHI()) break;
    if(ebuf-hbuf >= SIZEBLOCK) putHIblock();
  }
  if(HEADROOM >= hbuf-lbuf) if(putHIblock()) noroom();
}

int LOBLtoHI(){
int n,I,k;
struct fileinfo *p;
  p= &finfo[curfile];
  if((n=p->nbLOW)==0) return -1;
  k=getmeminfoindex(curfile,n-1);
  n=meminfo[k].numbytes;
  if(bufbase+n>hbuf) return -2;
  CPfromBLOCK(I=meminfo[k].record,hbuf-n,n);
  hbuf -= n;
  rlBLOCK(I);
  --(p->nbLOW);
  --(p->nbTOT);
  killinfo(k);
  return 0;
}

/*
 * STARTUP
 *
 * Purpose:
 *              Move in lines to the screen buffer
 *              and do an initial start on the editor.
 */
void startup() {
extern char *cd;
  UnPackScreenBuffer(0);
  curoff = col = row = 0;
  statblank=TRUE;
  lncount=1;
  dirsplit(sysfile,cd);
  drawall();
}

/*
 * TOPFIL
 *
 * Purpose:
 *              pack the buffer, go to top of file,
 *              refresh screen with cursor home.
 * Notes:

 *              o Fails if the screen cannot be packed on the low end.
 */
void topfile() {
  if(PackScreenBuffer(0)) {noroom(); return;}
  LowToHighBlockMove(1);
  startup();
}

/*
 * EOFILE
 *
 * Purpose:
 *              Cursor home on last line of file.
 * Notes:
 *              Fails if the buffer cannot be packed to low end.
 */
void eofile() {
int i,j,N;
char *p;

  if(PackScreenBuffer(ROWMAX+1)){noroom(); return;}
  HiToLowBlockMove(1);
  j=lncount=0;
  while((i=getmeminfoindex(curfile,j)) != -1){
    lncount += meminfo[i].numlines; ++j;
  }
  for(p=bufbase;p<=lbuf;p += 1+strlen(p)) ++lncount;
  if(lncount == 0) lncount = 1;
  col = 0;
  row = middle((LLONG)0); /* (lncount > ROWMAX) ? ROWMAX : lncount-1; */
  UnPackScreenBuffer(row+1);
  drawall();
}

/*
 * MASSDE
 *
 * Purpose:
 *              Delete many lines. Deleted lines are copied
 *              to the push buffer, if possible.
 * Returns:
 *              FALSE - Failure to copy all lines to push buffer.
 *              TRUE  - It worked as planned.
 * Notes:
 *              o A positive lx causes deleted lines to be added
 *                onto the end of the push buffer.
 *              o A negative lx causes the old push buffer to
 *                be over-written with the new deleted lines.
 *              o pushlines() now returns the number of lines
 *                actually moved.
 */
int massdelete(n,lx)
int n;  /* n==1 for delete and append notepad, n==0 for delete only */
LLONG lx;
{
int i;
int j;
int k;
LLONG kk;
struct bigarray XX;
struct bigarray far *PXXbigarray= &XX;

  kk=((lx>=0) ? lx : -lx);
  if(kk==0 || (n && !curfile)) return FALSE;
  i=(kk==1);
  if(i) strcpy(sysvv,line[row]);
  XX.str=(i ? sysvv : 0);
  if(n && (kk = pushlines(0,0,PXXbigarray,lx)) == 0)
    return FALSE;
  j = (ROWMAX+1) - row; /* at most j screen lines to delete */
  if(kk>j) {
    while(kk>j && hiPull(sysv)) --kk;
  } else j = (int)kk;
  if(j){
    PUSHnodisp();
    curses();
    if(!HASDELETELINE && !HASSCROLL) nodisp=TRUE;
    for(i=0;i<j-1;++i) delln(sysvv);
    POPnodisp();
    delln(sysvv);
  }
  return TRUE;
}

#if MSDOS
maketempfilename(s) char *s; {
unsigned x; FILEP fp;
char *p,*q;
  p=ffname(s);
  if((q=MYstrchr(p,'.'))!=(char *)0) q[0]='#';
  strcat(p,"########");
  p += 7;
  for(x=0;x<(unsigned)0xFFFF;++x){
   char2asciiBASE((unsigned char)(x / 256),p  ,16,0);
   char2asciiBASE((unsigned char)(255 & x),p+3,16,0);
   p[2]=p[1]; p[1]='.';
   if((fp=fopenbread(s))==(FILEP)0) break;
   closedfile(fp);
  }
  return 0;
}
#endif

#if SUN350 || DEC3100 || IBMAIX /* sun 3/50, sun 386/i, BSD4.x*/
int maketempfilename(s) char *s; {
unsigned x; FILEP fp;
char *p,*q;
  p=ffname(s);
  if(p[0]=='.') p[0]='#';               /* Don't want invisible files */
  if(strlen(s)>MAXFNAME-10) s[MAXFNAME-10]=0;
  strcat(p,".");
  p = endof(s);
  for(x=0;x<(unsigned)0xFFFF;++x){
   char2asciiBASE((unsigned char)(x / 256),p  ,16,0);
   char2asciiBASE((unsigned char)(255 & x),p+2,16,0);
   if((fp=fopenbread(s))==(FILEP)0) break;
   closedfile(fp);
  }
  return 0;
}
#endif


/*
 * CLOSEF
 *
 * Purpose:
 *              Dump buffer to disk.
 * Notes:
 *              o Uses the screen buffer as a disk buffer.
 *              o Requires screen buffer to be packed on high end.
 *              o Blank lines can be written to the output file,
 *                provided all text is above the current cursor
 *                position. Otherwise, blank lines are stripped
 *                from the end of the file.
 */

#if MSDOS
/* does not use return value of x=2. Child fork not implemented. */
int closef(filename) char *filename; {
char *p;
FILEP fp;
char tmpname[MAXFNAME];
char bakname[MAXFNAME];
int flag,backflag;

  killstatus();
  flag = FALSE;
  if(filename[0]==EOS ||
     MYstrcmp(filename,NOTEPAD)==0 ||
     MYstrcmp(filename,UNTITLED)==0) {
    bell();
    if(!getfname("Enter output file name: ",tmpname) || exists(tmpname)){
      lcount(); return FALSE;
    }
    strcpy(filename,tmpname);
  }
  if(!PackScreenBuffer(row)) {
    killstatus();
    strcpy(tmpname,filename);
#if MSDOS
    backflag=(BACKUPS && (classify(filename)!=2));
    if(backflag){
      strcpy(bakname,tmpname);
      maketempfilename(tmpname);
    }
#endif
    fp = fopenbwrite(tmpname);          /* WMODE */
    bbanner("Saving: ",filename);
    if(fp != (FILEP)0) {
      flag=FILEtoDISK(fp);
    } else flag=0;
    if(flag==0) bellpressany("File not saved");
#if MSDOS
    else if(backflag){
      extern char *ffname();
      maketempfilename(bakname);
      rename(filename,bakname); /* Could fail for new file */
      rename(tmpname,filename); /* Probably never fails */
      /* Move bakname to backup directory */
      if(BACKUPS){
        getbackupfname(tmpname); /* $HOME\PIbackups or .\PIbackups */
        mymkdir(tmpname);          /* Usually this does nothing */
        addslash(tmpname);
        strcat(tmpname,ffname(filename));
        maketempfilename(tmpname);      /* New name in subdirectory */
        rename(bakname,tmpname);        /* Move old file to directory */
        /* All of these can fail, no space, CD rom, protection error */
      }
    }
#endif
    /* Restore main buffer */
    UnPackScreenBuffer(row);
  }
  lcount();
  return flag;
}
#endif

#if SUN350 || ARDENT || DEC3100 || IBMAIX /* sun 3/50, sun 386/i, BSD4.x*/
/* Returns x=2 if file being edited is .mm.... from MM mail program */
/* In this case, we assume PI being run under MM, so should not suspend */
/* editor because it is a child process. Probably a better way, but this */
/* indeed works most of the time. */

int closef(filename) char *filename; {
FILEP fp;
char *p;
char tmpfilename[MAXFNAME];
char tmpname[MAXFNAME];
STATIC int flag;
char tempname[MAXFNAME];
char bakname[MAXFNAME];
STATIC int temp;
int fail,child;       
extern char *getenv();

temp = 0;

top:
  killstatus();                   
  if(filename[0]==EOS ||
     MYstrcmp(filename,NOTEPAD)==0 ||
     MYstrcmp(filename,UNTITLED)==0) {
    bell();
    if(!getfname("Enter output file name: ",tmpname) || exists(tmpname)){
      lcount(); return FALSE;
    }
    strcpy(filename,tmpname);
  }                                 
  else
  if(outtexttype==BINtexttype){
    {char *msg[]={
      "      **WARNING**",
      "Binary output file type",
      "writes null (0) instead",
      "of record delimiters cr",
      "(0x0D) and lf (0x0A).", 
      0};          
      WarningMessage(msg); /* memory released; erase screen later */
    }
    if(!getyesno("Proceed")) goto quit;
  };                               
  if(MYstrstr(filename,".mm-outgoing")!=(char *)0) child=1; else child=0;
  modeSetup(filename);
  killstatus();
  /* Build a TEMP file name for saving the file */
  strcpy(tmpname,(temp==1 ? "/tmp/" : filename));
  strcpy(ffname(tmpname),"piXXXXXX");
  mktemp(tmpname);
  fp = fopenbwrite(tmpname);  /* WMODE */
  if(fp == (FILEP)0) {   /* bad open */
  /* Bad file name, no inode, file name on CD or no permission. */
     strcpy(bakname,"Can't open to write: "); strcat(bakname,tmpname);
      bellpressany(bakname);
      strcpy(filename,UNTITLED);
      goto top;
  }
  flag = 0;
  if(!PackScreenBuffer(row)) {
    bbanner("Saving: ",filename);
    flag=FILEtoDISK(fp);
    UnPackScreenBuffer(row);  /* Restore main buffer */
  }
  if(flag==0) bellpressany("File not saved");
  /* If no disk space, then try /tmp */
  if(flag == 0 && temp == 0) { temp = 1; unlink(tmpname); goto top;}
  if(flag == 0 || temp != 0) {
    cleardisplay();
    ePrint("Out of disk space:"); crlf(); bell();
    ePrint("The editor image has been partially saved in ");
    ePrint(tmpname); crlf();
    ePrint("Disk file "); ePrint(filename); ePrint(" is unchanged.");
    crlf();
    goto quit;
  }
  /* Construct backup file name */
  strcpy(tempname,filename);
  strcpy(ffname(tempname),"piXXXXXX");
  mktemp(tempname);
  fail = (classify(filename)==1 ? rename(filename,tempname) : 0);
  if(fail != 0 || rename(tmpname,filename) != 0) {
    cleardisplay();
    if(fail){
      ePrint("Original file "); ePrint(filename); crlf();
      ePrint("Could not be renamed to "); ePrint(tempname); crlf();
    }
    ePrint("The file was saved as: "); ePrint(tmpname); crlf();
    ePrint("Disk file "); ePrint(filename); ePrint(" is unchanged."); crlf();
    goto quit;
  }
  /* Put identical file modes onto saved file */
  modeFix(filename);
  /* try to put backup file away into directory ./PIbackups */
  /* If it does not exist or option is off, then .. Nov 1998 */
  /* try to put backup file away into directory $HOME/PIbackups */
  if(BACKUPS){                 
    if(BACKUPS==2){
#if CYGWIN || LCCWIN32
      p=".";
#else                   
      extern char *cwd();
      p=cwd(); /* Get the current working directory */
      if(p == (char *)0) p=".";
#endif
      strcpy(bakname,p); addslash(bakname);
      strcat(bakname,PIBACKUPS);
      mymkdir(bakname);
      chmod(bakname,0777);
 /*     if(classify(bakname)!=1) p=getenv("HOME");*/ /* Use $HOME otherwise */
    } else p=getenv("HOME");
    strcpy(bakname,(p == (char *)0)? "." : p);
#if !ARDENT /* Do a better bookkeeping job for BSD long filenames */
    addslash(bakname);
    strcat(bakname,PIBACKUPS); addslash(bakname);
    strcat(bakname,(char *)ffname(filename));
    maketempfilename(bakname);
#else
    addslash(bakname);
    strcat(bakname,PIBACKUPS);
    strcat(bakname,"/PIXXXXXX");
    mktemp(bakname);
#endif
    fail = (classify(tempname)==1 ? rename(tempname,bakname) : 0);
    if(fail!=0){
      strcpy(tmpname,bakname); p=MYstrrchr(tmpname,'/');
      if(p != (char *)0) p[0]=0;
      mymkdir(tmpname);
      chmod(tmpname,0777);
      fail = (classify(tempname)==1 ? rename(tempname,bakname) : 0); /* try again */
    }
    if(fail){
      cleardisplay();
      ePrint("Original file is now "); ePrint(tempname); crlf();
      ePrint("Not renamed to "); ePrint(bakname); crlf();
      goto quit;
    }
  } else {
    /* erase backup file, because user doesn't want any clutter */
    unlink(tempname);
  }
  lcount();
  return ((child!=0 && flag==1) ? 2: flag);
quit:
    pressany("");
    drawall();
    return -1;
}

#endif

int exists(filename)
char *filename;
{
int getupper();
  if(classify(filename)==1){
      bbanner(filename," exists, Overwrite? "); bell();
      if(getupper() != 'Y') { lcount(); filename[0] = EOS; return 1;}
  }
  return 0;
}

/* Is it a disk file, a new file or a tty? */
int classify(filename)
char *filename;
{
int flag; FILEP fp;
  flag=0;
  if((fp = fopenbread(filename)) != (FILEP)0) {
    if(!isatty(fileno(fp))) flag=1; else flag=2;
    closedfile(fp);
  }
  return flag;  /* 0=new file, 1=already exists, 2=tty */
}

/*
 * FILEtoDISK
 *
 * Writes the currently edited file to disk. Assumes the screen buffer
 * has been pushed onto the low and high buffers. Returns 0 on failure,
 * otherwise nonzero.
 */
int FILEtoDISK(fp)
FILEP fp;       /* open file pointer */
{
int nbl;        /* number of low buffer blocks in use */
int i,j,flag,k; int I;
char far *q;
  nbl=finfo[curfile].nbLOW;
  flag=unload((FILEP)0,(char *)0,(char *)0);        /* initialize */
  j=0;
  while(1){                                    /* write low blocks first */
    if(j==nbl){                                /* and then */
      unload(fp,bufbase,lbuf);                 /* write low buffer */
      flag=unload(fp,hbuf,ebuf-1);             /* write high buffer */
    }                                          /* then high blocks */
    i=getmeminfoindex(curfile,j);
    if(i == -1) break;
    ++j;
#if MSDOS
    CPfromBLOCK(meminfo[i].record,lbuf+1,k=meminfo[i].numbytes);
    if((flag=unload(fp,lbuf+1,lbuf+k)) == 0) break;
#else
    q=getmemblock(meminfo[i].record);
    k=meminfo[i].numbytes;
    if((flag=unload(fp,q,q+k-1)) == 0) break;
#endif
  }
  flag=unload(fp,(char *)0,(char *)0);        /* flush buffer, fclose(fp) */
  return flag;
}

/*
 * UNLOAD
 *
 * Purpose:
 *              Unload a buffer to an open disk file, using the
 *              screen buffer as a temporary disk buffer. Close
 *              the output file on exit if lo==0. Initialize
 *              i=0,flag=TRUE if fp==0.
 * Returns:
 *              TRUE  -  Operation worked
 *              FALSE -  Operation failed.
 * Notes:
 *              o A screen buffer of SIZEBLOCK + MAXLN + 1 characters
 *                is required.
 */
int unload(fp,lo,hi)
FILEP fp;
char *lo,*hi;
{
register char *p;
register int f;
static int i;      /* i must be saved between calls! */
static int flag;   /* flag must be saved between calls */
static int ff,flg; /* ditto for these */

#define WSIZE SIZEBLOCK              /* SIZEBLOCK+1+MAXLN < screen size */

  if(fp==0){
    i=0;
    flg = (outtexttype==CRLFtexttype) ? 1 : 0;
    ff = (outtexttype==CRtexttype) ? 13 : 10;
    if(outtexttype==BINtexttype) ff=0;
    /* Obsolete static var 1995 */
    /*  flgi = (intexttype == CRLFtexttype) ? 1 : 0; */
    return (flag = TRUE);
  }
  if(lo==0){
    if(i && flag) {
      if(ewrite(fileno(fp),(char far *)scrbuf,i)<i) flag = FALSE;
    }
    if(closedfile(fp)) flag=FALSE;
    return flag;
  }
  if(flag==FALSE) return FALSE;

  p=lo;
  while(p<=hi){
    f=strmov(scrbuf+i,p);       /* strmov copy to null, ret count */
    i += f; p += f+1;
    if(flg) scrbuf[i++]=13;
    scrbuf[i++] = ff;
    if(i>=WSIZE) {
      if(ewrite(fileno(fp),(char far *)scrbuf,i)<i) {
        flag = FALSE; break;
      }
      i=0;
    }
  }
  return flag;
}
/*
 * V5.C
 *
 */

#include "pie.h"

void CenterRefresh(){
  if(row==ROWMAX) Crefreshh((LLONG)0);     /* refresh, cursor center */
}

/*
 * EDITLN
 *
 * Purpose:
 *              Add or delete a character from a line
 *
 *              o Clumsy (but accurate) routines are used to refresh
 *                the screen. Cleaner I/O is possible with more
 *                capable terminals.
 *              o Insert mode is primitive.
 *              o Character delete is primitive.
 */
void editln(c)
int c;
{
char *p;
int i;
int j,k,off,n;
char r[2];
int mydisp;
extern int margin,nodisp;

  r[0] = ' '; r[1] = '\0';
  PUSHnodisp();
  p = line[row];
  switch(c) {
  case   8:     /* Delete char left */
  case 127: if(col == 0 && curoff==0) {
              (insmode ? join() : bell());
              return;
            }
            if(insmode || (curoff>0)) {edleft(); curses();}
            /* and fall through to case 0 */
            else { /* col>0 && insmode==0 && curoff==0 */
              --col; if(*curchar()) { *curchar()=' '; erachar();}
              goto done;
            }
  case   0:       /* delete char right */
            if(*(p=(char *)curchar())!=0) {
              strcpy(p,p+1);
              eraseol();
              xPrint(p,row,col);
            }
            goto done;
  };
  c &= 255;                     /* strip to 8 bits */
  if(c==0) c=128;               /* can't put NULL into strings */
  if(insmode){
    if(WRDWRP==0 && curoff+col>=MAXLN-1){
      bell(); eddn(); homeit(); curses(); lineinsert();
    }
    if(strlen(line[row]) >= MAXLN-1){bell(); goto done;}
      /* tried to extend line too far */
      /* WRDWRP margin is at most MAXLN-2 */
  }
  i = strlen(p=line[row]);
  if(col>=COLMAX){ edright(); edleft(); }
  off=MYmin(col+curoff,MAXLN-1);
  while(i<off) p[i++] = ' ';     /* add blanks */
  if(i<=off) p[off+1] = EOS;
  if(insmode && i>off) moveMEM(p+off+1,p+off,(LLong)(i-off+1));
  r[0] = p[off] = c;            /* add char to line, or writeover */
  xPrint(r,row,col);
  if(col<COLMAX) {
    ++col; ++off;
    if(insmode && i>=off) { xPrint(p+off,row,col); }
  } else bell();
  if(margin==0) goto done;
  if(WRDWRP) {
    if(strlen(p=line[row])>margin && *sob(&p[margin]) != 0){
      for(k=col,j=curoff+col,i=MYmax(j-1,margin);i>=0;--i) {
        if(p[i] == ' ' && p[i+1] != ' ' && i<=margin) {
          n=0; off=i+1;
          if(testindent()) { /* add blanks at front? */
            while(p[n] == ' ') sysx[n++] = ' ';
          }
          strcpy(&sysx[n],&p[off]); p[off]=0;  /* split line here */
          if(off < curoff+COLMAX){
            putcursor(row,MYmax(off-curoff,0)); eraseol();
          }
          eddn(); curses(); addln(sysx,TRUE);
          if(j<off) {col=k; edup();}
          else curfix(1+n+(j-off),0);           /* get col right */
          break;
        }
      }
    }
    CenterRefresh();    /* refresh, cursor center */
  }
done:
curses();
POPnodisp();
}

static char old[40]={0};
void lnumcount() {
int i;
char v[40];

  if(!nodisp){
    if(DOSTATUS==1){
#if LLONGislong
#define STATUSFMT "Line %ld [%u,%u]     "
#else
#define STATUSFMT "Line %u [%u,%u]     "
#endif
      ssprintf(v,STATUSFMT,(LLONG)lncount,row+1,curoff+col+1);
      i=0; if(!statblank) while(v[i] && v[i]==old[i]) ++i;
      if(i<strlen(v)) {      /* Save it */
        strcpy(old,v);
        xPrint(&v[i],-1,MYmax(0,COLMAX-20+i));
      }
    }
  }
}

void recordbanner(){
char *p; char d[128];
  if(!record){record=1; banner("Recording Started ");}
  strinvvideo("Stop memorize ");
  identifykey(RUNMEMORIZE,d);
  strinvvideo(d);
  eraseol();
  lnumcount();
}

/*
 * LCOUNT
 *
 * Purpose:
 *              Display line count on the STATUS line.
 */
void lcount() {
char v[256];
int j;
int width;
extern int macrun,repeats,savcount;

  if(macrun || savcount || repeats>1) goto docurses;
  if(!nodisp) {
#if (BSDunix || SYS5) && !LCCWIN32
    {extern int syscolor;
      if(syscolor!=1) setcolorsUNIX(1);
    }
#endif
    if(testkey()||(macrun != 0 && (macrep > 1 || macind < macmax-1)))
      goto docurses;
    if(DOSTATUS) {
      if(DOSTATUS==3){
        if(makeasciistatus()) goto docurses;
        statblank=TRUE;
      }
      if(statblank) {
        putcursor(-1,0);
        ssprintf(v,"File %u",curfile);
#if LCCWIN32
        setLCCWIN32menucolors();
        etype(v);
        setLCCWIN32colors();
#else
        strinvvideo(v);
#endif
        ePrint(": ");
        if(record) recordbanner();
        else
        if(drawline) strinvvideo(LINEDRAWMODE);
        else
        if((width=COLMAX-9-20)> 0) {
          /* COLMAX-9-20 = num chars left for file name, etc */
          strcpy(v,sysfile);
#if MSDOS
          strcat(v,HELPSTR);
/*          if(MYstrcmp(sysfile,UNTITLED)==0) strcat(v,HELPSTR); */
#else
          if(MYstrcmp(sysfile,UNTITLED)==0){
            if(KEYMODE==1) strcat(v," (Emacs)");
            if(KEYMODE==2) strcat(v," (WDstar)");
            if(KEYMODE==3) strcat(v," (WDperf)");
          }
          strcat(v,HELPSTR);
/*          if(KEYMODE<1 || KEYMODE >3) strcat(v,HELPSTR); */
#endif
          if(!insmode) strcat(v,INSERTMODE);
          j=strlen(v);
          if(j<width) j=0;
          else {
            j -= width; moveMEM(v+j,".. ",3);
          }
          ePrint(v+j);
          eraseol();
        }
      }
      lnumcount();
      statblank = FALSE;
    }
docurses:
    curses();
  }
}

void char2asciiBASE(x,s,base,terminator)
unsigned char  x; int base; char *s; char terminator; {
int y,big;
/* x = x0*base + x1*base^2 + x2*base ^3 + ... + xn*base^n */
/* For character data, x <= 256, so we can find base^n cheaply */
     switch(base){
     case 2: big= /*  2^7 */ 128; break;
     case 8: big= /*  8^2 */  64; break;
     case 16:big= /* 16^1 */  16; break;
     case 10:
     default:big= /* 10^2 */ 100;
     /* strip leading zeros on base 10 */
     while((x / big) == 0 && big!=1)  big /= base;
     break;
     }
/* Convert to ascii string */
     while(big){
        y = x/big; x %= big;  big /= base;
        *s++ = y + (y>=10 ? ('A'-10) : '0');
     }
     *s=terminator;
}


int makeasciistatus() {
char s[60];
unsigned char x;
  if((x= *curchar())!=0){
    strcpy(s,"Char=' ' Oct=777 Hex=FF Bin=11111111 Dec=    ");
    s[6]=x;
    char2asciiBASE(x,s+13, 8,' ');
    char2asciiBASE(x,s+21,16,' ');
    char2asciiBASE(x,s+28, 2,' ');
    char2asciiBASE(x,s+41,10,' ');
    banner(s);
    return 1;
  }
  return 0;
}

/*
 * PUSHLI
 *
 * Purpose:
 *              Push lines from main buffer to push/pop buffer.
 *              The number of lines to push is held in lx.
 *              with the first line at the cursor position.
 * Returns:
 *              0       Failure
 *              n       n lines processed to push buffer
 * Idea:
 * Appends to the NotePad are allowed. Other operations are illegal.
 * Appends do not affect the cursor in the NotePad.
 *
 * The basic algorithm:
 *
 *   a. If lx<0 or the argument was '0', then scrap the NotePad.
 *   b. If lx<0, then let lx = -lx.
 *   c. If lx==0, then return 0.
 *   d. Turn off the display: nodisp=1.
 *   e. Call newedit() to edit the kill ring. This puts the
 *      current file out to external blocks. If it fails, then
 *      call noroom() and return 0.
 *   f. Do HiToLowBlockMove(). This positions the NotePad to
 *      the end. There is nothing to do if the NotePad is already
 *      at the end of file.
 *   g. Repeatedly, copy a high block from the entry file, then
 *      loPush lines until lx lines have been processed. The
 *      screen buffer is used to copy the blocks. All loPush()
 *      operations apply to the NotePad.
 *   h. Set new values for row, col, lncount for the NotePad.
 *   i. Do newedit() on the entry file. UnPackScreenBuffer(row).
 *      Turn the display back on.
 *   j. Return sysarg if there is no error, otherwise do noroom()
 *      and return the number j of lines actually processed.
 */
char RECMARK[2]={'\1',0};
/* buff[0].str==(char *)0 implies use file buffer. */
/* Otherwise, buff[] is an array of pointers of size sysarg. */
LLONG pushlines(START,COLUMNS,buff,lx)
int START,COLUMNS; struct bigarray far *buff; LLONG lx; {
int n,i,j,k,kk,r,m,off; extern char *sysx;
struct fileinfo *p;
LLONG jj,rr;

  jj=0;
  PUSHnodisp();
  if((n=curfile)==0 && buff==0) goto quitzero;
  if(lx==0) lx=1;
  if(lx<0){ lx = -lx; scrapfile(0); } /* Strange way to erase notepad */
  nodisp=1;
  /* try to do something simple for non-null array buff[] */
  /* Edit the notepad */
  if(newedit(0)){ noroom(); goto quitzero; }
  HiToLowBlockMove(0);
  if(loPull(sysx)) {
    if(MYstrcmp(sysx,RECMARK)) loPush(sysx);
    if(COLUMNS || lx!=1) loPush(RECMARK);
  }
  if(buff[0].str != 0) { /* Copy buff[jj].str to notepad */
    while(jj<lx){
     fartonearstrcpy(sysx,buff[(int)jj].str);
     if( loPush(sysx) ) ++jj; else break;
    }
  }
  else {
    /* Warning:  info arrays change during the main loop */
    off=finfo[n].nbLOW;                 /* Copy high blocks only */
    while(jj<lx){
      if((r=getmeminfoindex(n,off++)) == -1) break;
      CPfromBLOCK(meminfo[r].record,scrbuf,m=meminfo[r].numbytes);
      i=0;
      while(i<m){
        if(COLUMNS==0){ /* copy lines */
          if(!loPush(scrbuf+i)) {noroom(); goto failed;}
        } else { /* copy matrix */
          strcpy(sysx,scrbuf+i);
          kk=strlen(scrbuf+i);
          if(START>=kk) sysx[0]=0; else strcpy(sysx,scrbuf+i+START);
          kk=strlen(sysx);
          while(kk<COLUMNS) sysx[kk++]=' ';
          sysx[COLUMNS]=0;
          if(!loPush(sysx)) {noroom(); goto failed;}
        }
        if(++jj >= lx) break;
        i += 1+strlen(scrbuf+i);
      }
    }
  }
  loPush(RECMARK);
failed:
  stowaway();
  lbuf=bufbase-1; hbuf=ebuf;
  setfile(n);
  UnPackScreenBuffer(row);
  rr=kk=0;
  while((i=getmeminfoindex(0,kk++)) != -1){
    rr += meminfo[i].numlines;
  }
  if(!rr) rr = 1;
  p = &finfo[0];
  p->linec = rr+1;
  p->colc = 0;
  p->rowc = MYmin((int)rr,ROWMAX/2);
  if(jj) p->dirty=1;
  if(!n) drawall();
quitzero:
  POPnodisp();
  return jj;
}

/*
 * POPLIN
 *
 * Purpose: Pop lines from the notepad buffer onto the screen.
 */
LLONG poplines(POPMETHOD,INSmode,target,maxline,badmatrix)
int POPMETHOD,INSmode,target; int *maxline; int *badmatrix;
{
int n,i,j,r,m,failed,maxlen,offset,off; char *p;
LLONG nlines;
char far *f; int I;

  offset=col+curoff;
  badmatrix[0]=maxlen=failed=n=nlines=0;  /* n=file number of the NotePad */
  off=m=(finfo[n].nbTOT)-1;
  j= -1;
  while(off>=0){
    /* don't need to copy it to search */
    if((r=getmeminfoindex(n,off)) == -1) break;
    f=getmemblock(meminfo[r].record); i= meminfo[r].numbytes;
    if(off == m){++j; --i;}
    while(i>=2){
      if(f[i-1]==0 && f[i-2]==RECMARK[0] && (i==2 || f[i-3]==0)) ++j;
      if(j==target) goto foundit;
      --i;
    }
    --off;
  }
  off=i=0;/* Didn't find the record mark requested */
  if(++j != target) goto end; /* Count first record too (++j) */
foundit:

  if(PackScreenBuffer(row)){failed=1; goto nospace;}
  markblock((!POPMETHOD) ? 0 : offset);
  while(!failed){
    if((r=getmeminfoindex(n,off++)) == -1) break;
    CPfromBLOCK(meminfo[r].record,scrbuf,m=meminfo[r].numbytes);
    ++r;
    while(i<m){
      if(MYstrcmp(p=scrbuf+i,RECMARK)==0) goto done;
      j=strlen(p);
      if(POPMETHOD){                    /* Copy lines? Or matrix? */
        hiPull(sysx);                   /* Copy matrix ... */
        if(j!=maxlen && maxlen && INSmode){badmatrix[0] = -1; break;}
        addstring(p,sysx,FALSE,INSmode);
        p=sysx;
      }
      maxlen=MYmax(maxlen,j);
      if(!loPush(p)) {failed=1; break;}
      i += 1+j;
      ++nlines;
    }
    i=0;
  }
done:
  UnPackScreenBuffer(i=row);
  lncount += nlines;

#if 0   /* Buggy. Lost the screen display. Fixed May 2000 */
  PUSHnodisp();
  nodisp=1;
#endif
  if(nlines) edup();
  curfix(1+offset+maxlen,0); if(maxlen) edleft();
  setcorner();
  while(row<i){ lineup(); incrROW(); }
#if 0
  POPnodisp();
#endif
  drawall();

nospace:
  if(failed) noroom();
end:
  maxline[0]=maxlen;
  return nlines;
}

/*
 * DIVIDE
 *
 * Purpose:
 *              Break a line into two lines at cursor.
 * Notes:
 *              o Fails if addln() fails
 */
int divide() {
char *p;
int i,j;
extern char *sysv;
  /* divide at front of a line should just insert line! */
  if(!(col+curoff)){addln("",TRUE); return 0;}
  j = 0;
  if(INDENAUTO && insmode) {    /* match indent */
    while(line[row][j] == ' ') sysv[j++] = ' ';
  }
  strcpy(&sysv[j],curchar());
  *curchar()=0; eraseol(); eddn(); curses();
  addln(sysv,TRUE);
  edup();
  return j;
}

int testindent(){
 return (HangingIndent && INDENAUTO && insmode);
}

#if 0           /* debug: memory fragmentation */
                /* Warning: this code assumes no xms */
void defrag_debug()
{
int n,j,k; long v;
  for(j=0;j<maxinfo;++j){
    if(finfo[j].active==0) continue;

    clearscreen();
    fprintf(stderr,"File=%s\n",finfo[j].fname);
    i=startindex(j);
    k=i+finfo[j].nbLOW;
    n=i+finfo[j].nbTOT; v=0;
    fprintf(stderr,(i==k ? "No low blocks\n" : "Low blocks\n"));
    while(i<n){
      if(i==k) fprintf(stderr,"High blocks\n");
      fprintf(stderr,
      "[File=%d] %d. rec=%d, maxsize=%d, bytes=%d, lines=%d\n",j,i,
      meminfo[i].record,SIZEBLOCK,meminfo[i].numbytes,meminfo[i].numlines);
      v += meminfo[i].numbytes;
      ++i;
    }
    if(n==k) fprintf(stderr,"No high blocks\n");
    fprintf(stderr,"Uses %ld bytes, %d blocks\n",v,finfo[j].nbTOT);
    pressany("");
  }
}
#endif
/*
 * V6.C
 */

#include "pie.h"

/*
 * JOIN
 *
 * Purpose:
 *              Join two lines in insert mode.
 * Notes:
 *              o Must be in insert mode to join two lines.
 *                Otherwise, the command reverts to backspace.
 *              o If cursor at col=0 and key=backspace, then
 *                join this line onto previous, cursor at break.
 */
void join() {
char r[1];
char *p,*q;
int i,j;

  if(row) {
    j = strlen(p=line[row]);
    i = strlen(q=line[row-1]);
    if(i+j<=MAXLN) {
      strcpy(q+i,p);
      p[0] = EOS; delln(r);
      decrROW();
      curfix(i+1,0);
      xPrint(&line[row][i],row,col);
      lcount();
      return;
    }
  }
  bell();
}

/*
 * curfix
 *
 * Fix up cursor and screen offset for tabs, off-screen searches.
 * k=abs col position on line (1=first position);
 */
int curfix(k,len) int k,len; {
int j,flg;
    flg=0;
    if(k<=0) return FALSE;
    col=k-1;
    if((j=col-curoff-COLMAX)>0){/* target off screen right */
      curoff += j+len;       /* this always works */
      /* but we must show COLMAX columns */
      /* and the whole search string too! */
      if(MAXLN-1 < COLMAX+curoff) curoff = MAXLN-1-COLMAX;
      if(curoff>col) curoff=col;
      col -= curoff;
    }
    else
    if(col<curoff){     /* target off screen left */
      if(col+len < COLMAX) curoff=0; else {curoff=col; col=0;}
    }
    else {      /* target on screen, but might over-hang */
      col -= curoff;
      flg=1;
    }
    /* now have 0 <= col <= COLMAX */
    if((j=len+col-COLMAX)>0){  /* string not all on screen */
       curoff += j; col -= j; flg=0;
    }
    if(flg==0) drawall();
    return TRUE;
}

/*
 * FFIND
 *
 * Purpose:
 *              Find a string forward in file from current
 *              position plus one.
 * Returns:
 *              TRUE  - if found
 *              FALSE - not found
 * Notes:
 *              o Matching line brought to center of screen.
 *              o Cursor on first character of match. More
 *                than one match per line possible.
 *              o midstr(s) updates taberr variable.
 *                On return, ++taberr for
 *                each tab found.
 */
int ffind() {
int k,m,j,flag;
int i;
LLONG v;
int offset,off;
char far *p;
int midstr();
int Crefreshh();
extern int zero;

  flag=v = 0; offset=col+curoff; i=strlen(line[row]); j=MYmin(offset+1,i);
  if(offset<i){
    if(zeroleft) goto dorows; /* can't match beginning of line*/
    k = midstr((char far *)line[row],&j);
    if(k){k += offset+1; goto quit; }
  }
dorows:
    /* Search the screen rows after the current row */
    for(i=row+1;i<=ROWMAX;++i) {
      j=0; ++v;
      if((k = midstr((char far *)line[i],&j)) != 0) goto quit;
    }
    m=ebuf-hbuf; p=hbuf; j=0; flag=1;
    while(j<m) { ++v; if((k = midstr(p,(int *)&j)) != 0) goto quit; }
    /* search upper blocks */
    off=finfo[curfile].nbLOW;
    while(1){
      /* don't need to copy it to search */
      if((i=getmeminfoindex(curfile,off++))== -1) break;
      m=meminfo[i].numbytes; /* don't need to copy it to search */
      p=getmemblock(meminfo[i].record);
      j=0;
      while(j<m){ ++v;
      /* DEBUG: k=0; do { sysv[k]=p[j+k]; } while(sysv[k++]);
      getnumquery(sysv,&j,0,m); */
      if((k = midstr(p, &j)) != 0) goto quit; }
    }
  goto failed;
quit:
  if(Crefreshh((LLONG)v)) {
    j=0;
    if(flag) k = midstr((char far *)line[row],&j);
    return showmatch(k);
  }
failed:
  return FALSE;
}

int middle(v) LLONG v; {
LLONG x; int y;
 x=lncount-1+v; y=ROWMAX/2;
 if(x<=y) y=(int)x;
 return y;
}

/* Return 0 if no match otherwise nonzero */
/* Applies to current line from current position only! */
/* It is supposed to match blank lines! */
/* If the cursor is in col=0 and line="abc" and srcharg="a" */
/* then srcharg matches in a foward search at position k=1 */
/* however it does not match in a backward search. */
int findlinematch(n) int n; {
int i,j,k,sl,offset; char *p;
extern char *MYstrchr(); extern int zeroleft,zeroright;
 sl=strlen(p=line[row]);
 j=offset=col+curoff;
 k=0;
 if(n==4) { /* Search reverse for match on same line */
  strcpy(sysv,p); sysv[offset]=0;
  if(zeroright && offset < sl-1) goto quit;
  k=findlastmatch((char far *)sysv);    /* k==0 if no match */
 }
 if(n==3) { /* Search forward for match on same line */
  if(offset>sl) goto quit;           /* Nothing here to match */
  if(zeroleft && offset>0)   /* can't match beginning of line*/
    goto quit;
  k = midstr((char far *)p,&j); /* See if srcharg[] matches */
  if(k) k += offset;
 }
quit:
  return (k? showmatch(k):FALSE);
}

/*
 * RFIND
 *
 * Purpose:
 *              Find a string in reverse direction.
 * Returns:
 *              TRUE  - if found
 *              FALSE - not found
 * Notes:
 *              o midstr() updates taberr variable.
 *                On return, ++taberr for
 *                each tab found.
 */
int rfind() {
int k,w;
int i,off,j;
LLONG v;
char far *p;

  strcpy(sysv,line[v=row]); sysv[j=col+curoff]=0;
  k=findlastmatch((char far *)sysv);   /* k==0 if no match */
  if(k!=0 && k!=j+1) goto quit;        /* k==j+1 does not move cursor */
  while(--v != -1){ /* Back up on screen rows */
    if((k=findlastmatch((char far *)line[(int)v])) != 0) goto quit;
  }
  w= 1+lbuf-bufbase; p=bufbase;
  while(w>0) {
  w=startof(p,w-1);
  if((k = findlastmatch(p+w)) != 0) goto quit; --v; }
  off=finfo[curfile].nbLOW-1;
  while(off>=0){
    /* don't need to copy it to search */
    if((i=getmeminfoindex(curfile,off--))== -1) break;
    p=getmemblock(meminfo[i].record); w= meminfo[i].numbytes;
    while(w>0){
    w=startof(p,w-1);
    if((k = findlastmatch(p+w)) != 0) goto quit; --v;}
  }
  goto failed;
quit:
  if(k && Crefreshh((LLONG)(v-row))) {
    if(v<0) k=findlastmatch((char far *)line[row]);
    return showmatch(k);
  }
failed:
  return FALSE;
}

/* Find matches left-to-right, return last match, if any */
int findlastmatch(p) char far *p; {
int k,j,n; extern int zeroleft;
  n=k=0;
  if(zeroleft){j=0; return midstr(p,&j);}
  do{
    n += k; j=n;
    k = midstr(p,&j);
  } while(k);
/*    n==0 for no match at all */
/*    n>=1 means match at n-1 offset into string*/
  return n;
}

void showsearch(n) int n;{
 char v[256];
  if(nodisp) return;
  ssprintf(v,"%sSearch%s: %s",
             (n ? ((n==1 || n==3) ? "Forward " : "Backward "):""),
             (n? "" : " Failed"),srcharg);
  banner(v); if(n==0) bell();
}


int search(n) int n;{
int i;
char *MYstrchr(); extern int DETABBER,taberr;
  i=0;
  if(srcharg[0]==0) goto quit;
  if(FOLDCASE) cvupper(srcharg);
  taberr = DETABBER && (MYstrchr(srcharg,' ') != (char *)0);
  showsearch(n);
  switch(n){
  case 1: i=ffind(); break;
  case 2: i=rfind(); break;
  case 4:
  case 3: i=findlinematch(n); break;
  }
quit:
  if(!i) showsearch(0);
  lnumcount();
  curses();
  return i;
}

/* Show string match to srcharg at col=k-1 */
int showmatch(k) int k; {
extern char *sysx;
extern int srchcount();
int c;
  if(!k) return FALSE;
  curfix(k,srchcount());
  /* Don't print off screen */
  strnzcpy(sysx,&line[row][k-1],MYmin(srchcount(),COLMAX-col));
#if BSDunix || SYS5
  curses(); putcursor(row,col+1); strinvvideo(sysx+1); curses();
#else
  curses(); putcursor(row,col); strinvvideo(sysx); curses();
#endif
  return TRUE;
}

void unshowmatch(){
  curses(); ePrint(sysx);
}

/* Count number of real search chars in srcharg[] */
int srchcount(){
extern int zeroleft,zeroright; extern char *srcharg;
int n;
  n=strlen(srcharg)-zeroleft-zeroright;
  return (n? n : 1);
}

void setinputoutput(){
extern int intexttype,outtexttype,blockflag,ReportExportImport;
  getnumquery("(1=DOS,2=MAC,3=UNIX,4=BINARY) Input",&intexttype,1,4);
  getnumquery("(1=DOS,2=MAC,3=UNIX,4=BINARY) Output",&outtexttype,1,4);
  getnumquery("(0=OFF,1=ON) Report import type",&ReportExportImport,0,1);
  getnumquery("(0=OFF,1=prompts ON) Block read",&blockflag,0,1);
  finfo[curfile].ReadFtype=intexttype;
  finfo[curfile].WriteFtype=outtexttype;
  /* curfile==0 resets the editor defaults read from .pirc */
}

/* transpose 2 chars before cursor */
int chartranspose(){
unsigned char r[3];
unsigned char *p; int n;
unsigned char *curchar();
  n=curoff+col;
  if(n>1){
    p=curchar()-2;
    r[2]=0; r[0]=p[1]; r[1]=p[0]; p[0]=r[0]; p[1]=r[1];
    if(col==1) xPrint(&r[1],row,0);
    if(col>1)  xPrint(r,row,col-2);
    return 1;
  }
  return 0;
}

/* Transpose line at cursor with previous line. */
void linetranspose(){
  delln(sysvv); edup(); curses(); addln(sysvv,TRUE); eddn();
}

/* Formats a buffer for output to a printer */
/* Using global variables from printer setup */
void printformat(){
extern int PRTleft,PRTtop,PRTbot,PRTlpp,PRTff;
int i,tmp; LLONG total;
  PUSHnodisp();
  nodisp=1;
  eofile(); total=lncount;
  topfile();
  fillchr(sysvv,' ',PRTleft); sysvv[PRTleft]=0;
  while(0<total){        /* do all lines in file */
    for(i=0;i<PRTtop;++i) {addln("",FALSE); eddn();}
    for(i=0;i<PRTlpp;++i,--total){
      addstring(sysvv,line[row],FALSE,1); eddn(); homeit();
    }
    for(i=0;i<PRTbot;++i) {addln("",FALSE); eddn();}
    if(PRTff){addln("\f",FALSE); eddn();}
  }
  POPnodisp();
  topfile();
}

void printsetup(){
extern int PRTleft,PRTtop,PRTbot,PRTlpp,PRTff;
  getnumquery("Left spaces",&PRTleft,0,MAXLN-1);
  getnumquery("Top blank lines",&PRTtop,0,-1);
  getnumquery("Bottom blank lines",&PRTbot,0,-1);
  getnumquery("Text lines per page",&PRTlpp,1,-1);
  getnumquery("Use formfeeds",&PRTff,0,1);
}

/* set block marker at cursor position */
void markblock(offset) int offset;{
extern LLONG markline,lncount;
  markset=1; markcol=offset; markline=lncount;
}

LLONG getoffset(){
extern LLONG markline,lncount;
  return (markline-lncount);
}
/* Swap current cursor position with marked position. */
void swapmarker(){
#if 0
extern int markcol;
extern LLONG lncount;
extern LLONG getoffset();
#endif
int ii;
LLONG x,iii; extern int Crefreshh();

  iii=getoffset();           /* iii=offset to target */
  ii=markcol;                /* ii=col of target */
  markblock(col+curoff);     /* Change marks */
  curfix(ii+1,0);            /* Put cursor on other mark */
  x=row+iii;
  if(0 <= x && x <= ROWMAX) {lncount += iii; row=(int)x;}
  else { Crefreshh(iii); }
}

/* Show marked region in inverse video. Only affects screen. */
/* flag=0 is for regular chars, flag=1 for inverse video chars */
void showregion(n,flag) int n,flag;{
int i,j,start,end,startcol,endcol;
LLONG ii;
extern LLONG lncount;
extern LLONG getoffset();

  ii=getoffset();
  if(ii>=0){
    start=row;
    end= (ROWMAX<row+ii) ? ROWMAX : (int)(row+ii);
    startcol=col;
  } else {
    start=(0>row+ii) ? 0 : (int)(row+ii);
    end=row;
    startcol=MYmax(0,markcol-curoff);
  }
  if(start>end) return;
  if(!n) startcol=0;
  endcol=MYmax(markcol,col+curoff);
  for(i=start;i<=end;++i){
    strcpy(sysx,line[i]);
    if(n){j=strlen(sysx); while(j<=endcol) sysx[j++]=' '; sysx[endcol+1]=0;}
    sysv[curoff+COLMAX+1]=0;
    putcursor(i,startcol);
    if(flag) strinvvideo(sysx+curoff+startcol);
    else
    ePrint(sysx+curoff+startcol);
  }
  curses();
}

/*
 * set cursor at upper left corner of rectangle,
 * mark at lower right corner of rectangle.
 */
void setcorner(){
extern LLONG getoffset();
int x;
  if(getoffset() <= 0){
    if(col+curoff<markcol){x=col+curoff; curfix(1+markcol,0); swapmarker(); curfix(1+x,0);}
    else swapmarker();
  } else {
    if(markcol<col+curoff) curfix(1+markcol,0);
    else return;
  }
  curses();
}

void badregion(){
  banner("Block not marked. On menu use: ");
  strinvvideo("Begin block set mark");
}

/* Show marked region, get query, restore region, return query char. */
int regionbanner(msg,options) char *msg; char *options; {
register int n;
int i; LLONG x;
char *endof();
char *MYstrchr();
extern int regdef;
extern LLONG lncount;
extern LLONG getoffset();
extern LLONG absLL();

  n=regdef;
top:
  showregion(n,1);
  x=absLL(getoffset());
#if LLONGislong
#define REGIONFMT "Region: %ld %s "
#else
#define REGIONFMT "Region: %u %s "
#endif
  ssprintf(sysv,REGIONFMT,(LLONG)(x+1),(n? "by":"lines."));
  if(n)
    ssprintf(endof(sysv),"%u. ",(unsigned)(MYabs(markcol-col)-curoff+1));
  ssprintf(endof(sysv),"SPACE toggles view. %s: ",msg);
  if(options[0]) ssprintf(endof(sysv)-2,"? [%s]: ",options);
  banner(sysv);
  while(MYstrchr(options,i=getupper())==(char *)0){
    if(MYstrchr("\r\n\t ",i)!=(char *)0) break;
    bell();
  }
  showregion(n,0);
  if(i==' ') {n=1-n; goto top;}
  regdef=n;
  return i;
}

void killline(){            /* Emacs style kill line */
char r[2]; int i,j,flag; char *p;
extern int killedcrlf;  /* Flag which tells us if we killed a crlf */
  /* erase to end of line */
  /* and save a copy of the line */
  i=curoff+col;
  j=strlen(p=line[row]);
  flag=secondkill();
  if(j > i){
    if(addtolist(p+i,flag,0)){
      p[i] = EOS;
      eraseol();
      goto done;
    }
    goto failed;
  } else if(i>0 || (flag && !killedcrlf)) {
    killedcrlf=1;
    homeit(); eddn(); curses(); join();
  } else { /* i=j=0 */
    if(addtolist(p,flag,1)) delln(r); else goto failed;
  }
  goto done;
failed:
done:
  curses();
  return;
}

#if 0
/* The killerlist is an array of MAXKILLER far addresses. */
/* Each address is the base of a null-terminated string in the far data */
/* segment. The killerlist has killerlen active strings. */
/* When the list must be reused, then the strings  must be purged. */
/* This process must copy all of the string data and the structure */
/* killerlist[killerlen], then save the address of the structure and */
/* its length in the structure blocklist[]. */

typedef char far * FARSTR;      /* FARSTR data is 4 bytes */
struct bigarray {FARSTR str;};  /* killerlist points to this structure */
struct biglist {                /* Container structure to hold saves */
  struct bigarray far *
      blockbase;                /* Address of the array of far strings */
  int
      blocklen;                 /* Number of strings in that array */
};
#endif

#define NKiller 10     /* near data segment contains these arrays */
struct biglist blocklist[NKiller]={0,0,0,0,0,0,0,0,0,0};  /* 60 bytes */
int killedcrlf=0;

#define MKiller 50     /* near data segment contains these arrays */
char *srchlist[MKiller]={0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0,
                         0,0,0,0,0,0,0,0,0,0};    /* 100 bytes */

extern struct bigarray far *killerlist;
extern int killerlen;                   /* Active length of killerlist */
extern int MAXKILLER;                   /* Maximum length of killerlist */
extern int killedcrlf;  /* Flag which tells us if we killed a crlf */

int getkillercore(){
int i;
#if MSDOS
    killerlist=(struct bigarray far *)
               farmalloc((unsigned long)MAXKILLER*sizeof(char far *));

#else
    killerlist=(void *)malloc((int)MAXKILLER*sizeof(char far *));
#endif
if(killerlist==0) bellpressany("bad farmalloc in getkillercore ");
    return ((killerlist==0) ? 0 : 1);
}

void addfailed() {
extern char *MYstrchr();
extern void purgelist2notepad();
unsigned x;
  macabort(); repeats=1;
  bellpressany("Purging strings to NotePad");
  purgelist2notepad(1); poplist(1); killerlen=0;
  curses();
}

int addtolist(s,flag,crlf) char *s; int flag,crlf; {
FARSTR p;
  killedcrlf=crlf;
top:
  if(!flag && !savelist())
     goto failed;               /* Out of memory */
  /* Add string into list */
  if(killerlen<MAXKILLER){
    if(farsafecore(&p,s)){killerlist[killerlen++].str=p; return 1;}
  }
  if(flag){flag=0; goto top;}
failed:
  addfailed();
  return 0;                     /* failed */
}

void killblk(q,n) struct bigarray far *q; int n; {
int j;
  for(j=0;j<n;++j) farfree(q[j].str);
  farfree(q);
}


int savelist() {
struct bigarray far * q;
int i,j,n;
  q=blocklist[NKiller-1].blockbase; n=blocklist[NKiller-1].blocklen;
  if(q) killblk(q,n);
  for(i=NKiller-1;i>=1;--i){     /* Shuffle pointers */
    blocklist[i].blockbase=blocklist[i-1].blockbase;
    blocklist[i].blocklen=blocklist[i-1].blocklen;
  }
  blocklist[0].blockbase=0; blocklist[0].blocklen=0;
  if(!killerlen) return 1;
#if MSDOS
  {
  q=(struct bigarray far *)
    farmalloc((unsigned long)sizeof(struct biglist)*killerlen);}
#else
  {
  q=(void *)malloc((int)sizeof(struct biglist)*killerlen);}
#endif
  if(q){
    blocklist[0].blockbase=q; blocklist[0].blocklen=killerlen;
    for(i=0;i<killerlen;++i) q[i].str=killerlist[i].str;
    killerlen=0;
    return 1;
  }
  bellpressany("farmalloc failed in savelist");
  return 0;
}

void purgelist2notepad(flag) int flag;{
int i,j,n;
struct bigarray far *q;
 i=NKiller;while(--i>=0){
     if((q=blocklist[i].blockbase)==0) continue;
     n=blocklist[i].blocklen;
     if(flag) pushlines(0,0,q,(LLONG)n);
     killblk(q,n);
     blocklist[i].blockbase=0; blocklist[i].blocklen=0;
  }
 i=MKiller;while(--i>=0){
     freeup(srchlist[i]); srchlist[i]=(char *)0;
  }
}


/* paste the kill list at the cursor position */
/* or paste onto the end of the notepad */
int poplist(flag) int flag; {
int i,j,k,n; FARSTR p;
extern unsigned char *curchar();
   if((n=killerlen)!=0){
     k=0;
     if(flag) pushlines(0,0,killerlist,(LLONG)killerlen);
     else {
       if(*curchar() || line[row][0]==0) divide();
       fartonearstrcpy(sysv,killerlist[0].str);
       if(addstring(sysv,line[row],TRUE,0)== -1) return 0;
       if(--n){eddn(); curses();}
       k=1;
     }
     for(j=ROWMAX-row+1,i=killerlen-1;i>=k;--i) {
       p=killerlist[i].str;
       if(flag){
          farfree(p);
       } else {
         fartonearstrcpy(sysv,p);
         addln(sysv,(i<=j));
       }
     }
     if(k && n){edup(); curses();}
   }
   return killerlen;
}

/* stack for previous search arguments */
/* look through it with uparrow, downarrow on status line - see v1.c */
/* Remove last item in stack, add srcharg, copy new item to srcharg */
/* Detect special features of begin/end line matches */
/* List always contains current search arg on top */

static int firstone=0;          /* where to insert next save */
static int gotfirstone=0;

void copysrcharg(s) char *s; {
int n,m,k;
char *p;
extern int zeroleft,zeroright;
  if(s[0]==0) goto done;
  gotfirstone=1;
  for(n=0;n<MKiller;++n){
    if(srchlist[n]!=0 && MYstrcmp(srchlist[n],s)==0)
      goto duplicate; /* already there */
  }
  freeup(srchlist[firstone]);
  safecore(&srchlist[firstone],s);              /* 0=fail,1=worked */
  firstone=circularlist(firstone,-1);           /* Next one to retire */
done:
  zeroleft=zeroright=0;                 /* No special position on line */
  if(s[0]==zero) zeroleft=1;            /* Match start of line */
  if(tailchar(s+1)==zero) zeroright=1;  /* Match end of line */
  strcpy(srcharg,s);                    /* Top of list and srcharg match */
  return;
duplicate:
  k=circularlist(firstone,1);   /* Most recent position */
  if(n==k) goto done;           /* All done. */
  p=srchlist[n];                /* Move it to the most recent position */
  while(1){
    m=circularlist(n,-1);
    srchlist[n]=srchlist[m];    /* Fold the array over on itself */
    if(m==k) break;
    n=m;
  }
  srchlist[k]=p;
  goto done;
}

int circularlist(n,flag)
int n,flag;
{
  if(flag<0){ if(++n >= MKiller) n=0;}
  else { if(--n <0) n=MKiller-1;}
  return n;
}


/* p=source string. */
/* d=direction, 1=backward, -1=forward */
/* Copy to p either srcharg or else next search string from list */
/* Works with cursor keys on status line. If UPARROW, then cycle */
/* backwards in the list until the end is reached. If DOWNARROW, */
/* then cycle forward until srcharg is reached. */
void getsrcharg(p,d) char *p; int d; {
int m,n;
static int lastone=0;           /* Marker for where to look first */

  if(gotfirstone==0){p[0]=0; return;}
  if(!p[0]) goto std;
  if(MYstrcmp(p,srcharg)==0) { /* Always, srcharg matches top of list */
    if(d<0) return;          /* Can't move further in minus direction */
    lastone=firstone;        /* Start at top of list again */
    goto search;
  }
/* Detect when at the bottom of the list or the tp of the list */
search0:
  n=lastone;
  m=circularlist(n,d);
  if(d>0){
    if(srchlist[m]==0) return; /* partially-filled list */
    if(n==firstone) return;  /* next one wraps around */
  } else { /* d < 0 */
    if(srchlist[m]==0) goto std; /* partially-filled list */
    if(n==circularlist(firstone,1)) goto std;
  }
search:
  n=lastone;
search1:
  while(1){
    n=circularlist(n,d);
    if(srchlist[n]!=0){
      if(MYstrcmp(srchlist[n],p)!=0) goto finished;
    }
    if(n==lastone) break;
  }
  /* All list entries are different, by construction. Two possible */
  /* events: (1) Empty list. (2) List has 1 entry and it matches p. */
  return;
std:
  n=circularlist(firstone,1);    /* index of top of list */
  strcpy(p,srchlist[n]);
  lastone=n;
  return;
finished:
  strcpy(p,srchlist[n]);
  lastone=n;
}

/* Yank all kill list members at the cursor position, interactive */
/* Yank occurs one at a time in reverse chronological order */
/* with interactive query about keep, ignore, quit */
int yanklist() {
int i,j,k,n,total; extern int regdef;
extern struct biglist blocklist[NKiller];

  for(j=ROWMAX-row+1,total=i=0;i<NKiller;++i){
   k=blocklist[i].blocklen; n=0;
   markblock(0);
   while(--k>=0){
     fartonearstrcpy(sysv,blocklist[i].blockbase[k].str);
     if(addln(sysv,(k<=j))) ++n;
   }
   if(n){
     markline += n-1; regdef=0;
     k=regionbanner("Recover","YNQ");
     if(k=='Y'){total += n; continue;}
     massdelete(0,(LLONG)n);
     if(k=='Q') goto end;
   }
  }
end:
  lcount();
  return total;
}

/*
 * ZAP
 *
 * Remove match to optbuf from current line
 */
int zap(optbuf) char *optbuf;{
int n; char *p;
  if(strlen(p=line[row])>col+curoff){
    p += (col+curoff);
    n=strlen(optbuf);
    if(MYstrncmp(p,optbuf,n)==0){
      strcpy(p,p+n);
      xPrint(p,row,col); eraseol();
      return TRUE;
    }
  }
  return FALSE;
}

void dozap(rows,cols)
int rows,cols;
{
char *p;
int i,j,offset;
  offset=curoff+col;
  for(i=0;i<rows;++i){
    j=strlen(line[row]);
    if(j>offset){
      p = &line[row][offset];
      curses(); eraseol();
      if(offset+cols >= j) p[0]=0;
      else {strcpy(p,p+cols); ePrint(p);}
    }
    curses();
    eddn();
  }
  for(i=0;i<rows;++i) {curses(); edup();}
}

int MYisalpha(c) int c; {
  if(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')) return 1;
  return 0;
}

/* return end of string address or address of first upper or lower char */
char *sobl(s) char *s; {
  while(s[0] && !MYisalpha(s[0])) ++s;
  return s;
}

/* nextword() contains KILL WORD for n==1. */
/* For n==0 it is normal word ahead and word back. */
int nextword(n) int n;{
int k,x,y,flag,wasvisible; char *p,*q;
FARSTR r;
char *sobl(); char *sob();
  y=col+curoff;
  flag=0;
  if(n && !secondkill()) addtolist("",0,0);
  if(strlen(q=line[row])>y){
    p=sob(q+y);
    if(q[y]!=' ') {
      k=0;
      while((x=p[0])!=0 && MYisalpha(x)){++k; ++p;} /* Skip over alpha */
      if(k==0 && p[0]!=0) ++p;            /* No alpha chars, then take one */
    } else x=p[0];
    wasvisible = (*sob(q+y)!=0);  /* wasvisible==1 if we removed visible chars*/
    k=(int)(p-q);
    if(n){      /* KILL WORD for n==1 */
      k -= y;
      if(k){strnzcpy(sysvv,q+y,k); zap(sysvv); flag=1;}
      if(killedcrlf) addtolist("",1,0);
      if(flag) {
        strcpy(sysx,sysvv);
        r=killerlist[--killerlen].str;
        fartonearstrcpy(sysvv,r); farfree(r);
        strcat(sysvv,sysx);
        addtolist(sysvv,1,0);
      }
      if(!wasvisible) goto nextline;
      goto end;
    }
    if(x){ curfix(1+k,0); goto end;}
  }
nextline:
  homeit(); eddn();
  if(n){curses(); join(); killedcrlf=flag=1;}
end:
  return flag;
}

void prevword(){
int n;
char *p,*q; char *sobl();
  n=strlen(q=line[row]);
  if(n){
    p = q + ((n>col+curoff) ? (col+curoff) : n);
    if(p==q) goto prevline;
    if(MYisalpha(p[0]) && p!=q && !MYisalpha(p[-1])) --p;
    else {
     while(p!=q && !MYisalpha(p[0])) --p;     /* skip over non-alpha */
     while(p!=q && MYisalpha(p[-1])) --p;     /* skip until non-alpha */
    }
    curfix(1+(int)(p-q),0); return;
  }
prevline:
  if(lncount==1) {homeit(); return;}
  edup(); endit();
}
/*
 * V7.C
 *
 */

#include "pie.h"

int getfile(filename) char *filename; {
int x;
char tmp[MAXFNAME];
  strcpy(tmp,filename);
  if(dir(tmp,-1,0,(char *)0,(char **)0) == 1) /* complete file name */
    strcpy(filename,tmp);
  if((x=isinfo(filename)) != 0) {
    bell(); strcat(tmp," already loaded. Load anyway");
    if(!getyesno(tmp)) {sysarg = x-1; return -3;}
  }
  return getafile(filename);
}

void setfinfo(p) struct fileinfo *p; {
  p->rowc=p->colc=p->linec=p->nbLOW=p->nbTOT=p->dirty=p->curoffset=
  p->setmark = p->setmarkcol = p->setmarkline;
  p->ReadFtype=finfo[0].ReadFtype;
  p->WriteFtype=finfo[0].WriteFtype;
}

/*
 * KILLFINFO
 *
 * releases memory and junks finfo[] slot.
 */
void killfinfo(p) struct fileinfo *p;{
  setfinfo(p);
  freeup(p->fname);
  p->fname = (char *)0;
  p->active=0;
}

/*
 * SCRAPFILE
 *
 * Junks a file and all its meminfo[] blocks.
 *
 */
void scrapfile(NN) int NN;{
 int i,j,jj; struct fileinfo *p;
  jj=startindex(NN); p = &finfo[NN];
  j=p->nbTOT;
#if 0
  for(i=0;i<j;++i){
    rlBLOCK(meminfo[jj].record);
    killinfo(jj);
  }
#else
  for(i=jj+j-1;i>=jj;--i){
    rlBLOCK(meminfo[i].record);
    killinfo(i);
  }
#endif
  if(NN) killfinfo(p); /* don't kill off finfo[0] */
  else setfinfo(p);
}


#if macOSX || SOLARIS || CYGWIN || LINUX
#if 0 /* macOSX */ 
ssize_t  read(int, void *, size_t);
ssize_t  write(int, const void *, size_t);
int eread(int, char far *, int);     
int ewrite(int, char far *, int);
#endif
int eread(n,s,rdsize) int n,rdsize; char far *s; {
   int x;
   x=(int)read(n,(void *)s,(size_t)rdsize);
   return x;
}
/*    k=eread(fileno(fp),q+orphans,RDSIZE); */
int ewrite(n,s,rdsize) int n,rdsize; char far *s; {
   int x;
   x=(int)write(n,(void *)s,(size_t)rdsize);
   return x;
}
#endif

int getafile(filename) char *filename; {
FILEP fp;
int start;
int amt;
STATIC                     
int RDSIZE,OFFSET,i,k,kk,m,mm,eof,N,NN,inbytes,orphans;
int f,flag,kind;
char *p; char far *q;
char t[24];
int eread();
char *MYstrchr(); char *utoa();
extern int blockflag;           /* prompt for block read params */
extern int splitlines;
extern int filekind;
extern char *lltoa();

  if(filename[0] == EOS) return -1;
  mm=splitlines=amt = 0; start = 1;
  if(blockflag){         /* Request for partial read */
    getnumquery("Start block",&start,1,0);
    getnumquery("Count",&amt,0,-1);
  }
#if MSDOS      /* Uppercase file names */
  cvupper(filename);
#endif
  /* Ready to read file from disk. See algorithm above */
  /* 1,2. Get open slot in finfo[] array, open file */
  if((fp = fopenbread(filename)) == (FILEP)0) { /* RMODE */
    return -2;
  }
  if((NN=getslot()) == -1 || !corefinfo(NN,filename)) {
    closedfile(fp); return -1;
  }
  m=startindex(NN);
  /* setup variables f, flag for intexttype */
  flag = (intexttype==CRLFtexttype) ? 1 : 0;
  f = (intexttype==CRtexttype) ? 13 : 10;
  if(intexttype==BINtexttype) f=0;
  /* 3. Process disk file into GAP */
  sysx[0]=eof=orphans=kind=0;
  OFFSET = 6+2*(SIZEBLOCK/MAXLN); /* max number split lines times 2 */

/* Main READ loop */

  bbanner("    Reading ",filename);
  while(!eof){
      if((N=crBLOCK()) == -1) goto stop;
      q=getmemblock(N);
      RDSIZE=SIZEBLOCK-orphans-OFFSET;
      /* fill input buffer, partial line plus disk read */
      if(orphans>0) segcopy(q,(char far *)sysx,orphans);

      k=eread(fileno(fp),q+orphans,RDSIZE);
      if(k<=0) k=0;
#if 0
      home(); ePrint(lltoa((LLONG)(mm+1),t));
#endif

      if(k<RDSIZE) eof=1;
      inbytes = orphans + k;
      if(eof) { /* buffer contains remaining bytes */
          if(inbytes>0 && (kk=(q[inbytes-1])) != f && kk != 0) {
              if(flag) q[inbytes++] = '\r';
              q[inbytes++] = f;
          }
          orphans=0;
      }
      else {  /* Have added more bytes on top of orphans */
        q[i=inbytes]=orphans=0;
        /* see if we can remove partial line */
        /* Search for 10 or 13. Admit 0 is also end of line. */

        while(--i >= 0){
          if((kk=q[i])==0) {kind |= 8; break;} /* BINARY FILE? */

          if(kk==10){ /* End of line marker? */
            if(i && q[i-1]=='\r') kind |= 1;
            else
            /* Probably a unix file without CR chars */
            kind |= 4;     /* might be a unix file */
            break;
          }

          if(kk==13){  /* Could be part of split cr/lf combo */
            if(orphans){ /* Don't consider it on very end */
              kind |= 2;      /* might be a MAC file */
              break;
            }
          }

          /* kk is an orphan character, but we save it later */
          if(++orphans >= MAXLN-3) { /* no f's or long line */
            if(flag) q[inbytes++] = '\r';
            q[inbytes++] = f;
            q[inbytes++] = 2; /* mark splitline with ctrl-B */
            orphans=1; /* Put at front of next buffer*/
split:
            ++splitlines;       /* Only do it for long lines */
            break;
          }
        }
        if(orphans) { /* Copy partial line from end to sysx[] buffer */
          segcopy((char far *)sysx,q+inbytes-orphans,orphans);
          inbytes -= orphans;
        }
      }
      /* Skip over some initial blocks if start > 1 */
      if(start>1){--start; inbytes=0;}
            
      /* log info on file into info[] array */
      if(inbytes>0){

        addinfo(N,0,inbytes,1,m+(mm++)); /* Mark it as unconverted */
        if(amt != 0 && eof == 0 && --amt <= 0) {
           eof=1; bellpressany("Partial Read"); filename[0]=0;
        }
      } else rlBLOCK(N);
  }
stop: /* N == -1 if we got troubles, or ran out of buffer space */
  closedfile(fp);
  finfo[NN].active=1; finfo[NN].nbTOT=mm;
  if(N == -1){
    strcpy(filename,UNTITLED);
    if(!getyesno("No blocks. Proceed"))
      { scrapfile(NN); return -4; }
  }
  if(kind==4) kind=3; if(kind>4) kind=4;
  if(kind && kind != intexttype){
    filekind=kind;
  }
  return NN; /* report file kind found */
}

static char *KindofFile[]={ "MSDOS","MAC","UNIX","BINARY"};
int splitlines=0;
int filekind=0;
int ReportExportImport=1;
    
int getfilekind(){
extern int filekind; int x=filekind;
 if(x){filekind=0; return (x-1);}
 if((x=intexttype)>=1 && x<=4) return (x-1);
 return 3;
}

void reportfilekind(){
extern int filekind,ReportExportImport;
  char msg1[128],msg2[128],msg3[128];
  char *msg[]={"","","","Change using Export-Import (File menu).",(char *)0};
  if(filekind || (intexttype!=finfo[0].ReadFtype && ReportExportImport)){
   ssprintf(msg1,"Detected file type %s",KindofFile[getfilekind()]);
   ssprintf(msg2,"Default or NotePad type is %s",KindofFile[finfo[0].ReadFtype-1]);
   ssprintf(msg3,"File type set to %s",KindofFile[getfilekind()]);
   msg[0]=msg1; msg[1]=msg2; msg[2]=msg3;
   WarningMessage(msg);
  }
}

void reportsplit() {
char tmp[100];
extern int splitlines;
  if(splitlines){
    char *msg[]={"Long lines split","",0};
    /* getnumquery("Long lines split at ^B",&splitlines,0,0); */
    msg[1]=tmp;
    ssprintf(tmp,"Inserted ^B [%u times]",splitlines);
    WarningMessage(msg);
    splitlines=0;
  }
}

/* After reading in a file, the first screen is displayed */
/* and this function begins executing. The call to cvblk() */
/* is inside getmeminfoindex(). */
/* Convert all blocks */
void convertall(NN) int NN; {
int off;
extern int splitlines;
  off=0; while(getmeminfoindex(NN,off)!= -1){ /* the engine */
           ++off;
           if(!splitlines && ccBIOS(2)) break;
         }
  reportsplit();
}

void determineFtype(p,nbytes) char far *p; int nbytes; {
  while(nbytes !=0){
    if(p[0]=='\r'){
      if(p[1]!='\n') intexttype=outtexttype=CRtexttype; /* MAC */
      else
      intexttype =outtexttype = CRLFtexttype;           /* MSDOS */
      return;
    }
    if(p[0]=='\n'){
      intexttype=outtexttype=LFtexttype;                /* UNIX */
      return;
    }
    if((p[0]&128)!=0) break;
    ++p; --nbytes;
  }
  /* NOT CRLFtexttype, LFtexttype nor CRtexttype */
  intexttype=outtexttype=BINtexttype;                   /* BINARY */
}

void cvblk(N,off)
int N; int off;
{
int x,y; int nlines,nbytes; char far *dest; char far *src;
extern int killcrlf();
extern void slowkillcrlf();
struct bucket far *q;
extern int splitlines;

  q= &meminfo[N]; if(!(q->flag)) return;
  x=q->record;
  dest=getmemblock(x);
  nbytes = q->numbytes;
    /* Another crBLOCK might swap block off to xms memory. */
    memmark[x]=3;  /* Lock the block. Next crBLOCK() leaves x alone. */
    if((y=crBLOCK()) != -1){
      src=dest;
      dest=getmemblock(y);
    }
    else { /* ugh, no memory, must copy over it */
    segcopy(src=dest+SIZEBLOCK-nbytes,dest,nbytes);
    }
    memmark[x]=2; /* Unlock the block */
    if(!off) determineFtype(src,nbytes);
    slowkillcrlf(dest,src,&nlines,&nbytes,&splitlines);
    if(y != -1){ rlBLOCK(x); q->record = y; }

  q->numbytes = nbytes;
  q->numlines = nlines;
  q->flag=0;           /* Mark as converted */
}

/*
 * INJECT
 *
 * Purpose:
 *              Inject a file into the text buffer at cursor.
 * Returns:
 *              TRUE  - It worked.
 *              FALSE - It failed.
 *
 * To inject a file into the edit buffer:
 *
 *     1. Free the screen buffer for I/O use. Call PackScreenBuffer().
 *        Return -1 on failure.
 *     2. Using putHIblock(), move the upper buffer out to blocks. A partial
 *        block will result for the move of the last of the data. If it fails,
 *        then goto step 5.
 *     3. Read in a file off disk, as outlined above. If it fails, then
 *        goto step 5.
 *     4. Link in the blocks of the file of item 3, to the high blocks of the
 *        current edit. This process always succeeds because it is just
 *        bookkeeping. Note: the total block count is affected, and
 *        the array meminfo[] is updated. Information about the injected file
 *        is lost at this point. The link of blocks can leave one partially
 *        filled block in the upper chain. If files are injected in a clever
 *        order, then many partially-filled blocks can occur. A seek to the
 *        end of the file will recover as many blocks as possible. To make this
 *        completely bug-free requires the trash compactor to be used on the
 *        high block chain (a lot of overhead).
 *     5. Call routine UnPackScreenBuffer(). Call drawall().
 *     6. If the inject worked, then return 0, otherwise return -1.
 *
 */
int inject(file) char *file; {
int i,rec,nlines,nbytes,flg,NN;
int PackScreenBuffer();
int getafile();
struct bucket far *q;

  if(PackScreenBuffer(row)) return -1;
  NN = -1;
  while(1){
    i=putHIblock();
    if(i == 0) continue;
    if(i == -1) break;
    if(i == -2) goto failed;
  }
  if((NN=getafile(file)) < 0) goto failed;
  /* File now in external blocks, but unconverted */
  /* bookkeeping on meminfo[] array - subtle memory overlap problems */
  while(finfo[NN].nbTOT > 0){
    i=startindex(NN) + (--(finfo[NN].nbTOT));
    q= &meminfo[i];
    rec=q->record;
    nlines=q->numlines;
    nbytes=q->numbytes;
    flg=q->flag;
    killinfo(i);
    i=startindex(curfile)+finfo[curfile].nbLOW;
    addinfo(rec,nlines,nbytes,flg,i);
    ++(finfo[curfile].nbTOT);
  }
  killfinfo(&finfo[NN]);
failed:
  UnPackScreenBuffer(row);
  drawall();
  return ((NN < 0) ? -1 : 0);
}

/*
 * GETSLOT
 *
 * Find an open slot in the finfo[] array.
 *
 * Returns index N on success, -1 on failure.
 */

int getslot(){
struct fileinfo *p;
int i;
  for(i=0;i<maxinfo;++i){
    p = &finfo[i];
    if(p->active == 0) {
      p->fname=(char *)0;
      setfinfo(p);
      return i;
    }
  }
  return -1;
}

int corefinfo(NN,s)
int NN;         /* index into finfo[] array */
char *s;        /* file name */
{
char **q;
  q = (char **)&(finfo[NN].fname);
  freeup(q[0]);
  if(!safecore(q,s)){
    return (finfo[NN].active=0);                                /*0=failed*/
    /*failed*/
  }
  return 1;                                                     /*1=worked*/
}

/*
 * STARTINDEX
 *
 * Finds the index j in the meminfo[] array where block
 * info is stored for the file whose info is kept in finfo[N].
 *
 * Returns index j or -1 for failure.
 */
int startindex(N) int N;{
int i,tot;
  if(N>=maxinfo) return -1;
  for(tot=i=0;i<N;++i) tot += finfo[i].nbTOT;
  return tot;
}


int getmeminfoindex(n,off) int n; int off; {
int k;
    if(off>=finfo[n].nbTOT) return -1;
    k=startindex(n)+off;
    if(meminfo[k].flag) cvblk(k,off);  /* Convert block */
    return k;
}

/*
 * ADDINFO
 *
 * Inserts an entry at index j into meminfo[] array.
 *
 */

int addinfo(rec,nlines,nbytes,flg,j)
register int rec,nlines,nbytes,flg,j; {
struct bucket far *q;
int r,nl,nb,f;
/*  if(j >=MAXBLOCK || memmark[MAXBLOCK-1] != 0) return -1; bug fix Aug95 */
  if(j >=MAXBLOCK) return -1;
  do {
    q= &meminfo[j];
    r=q->record;  /* save record info */
    nl=q->numlines;
    nb=q->numbytes;
    f=q->flag;
    q->record = rec;
    q->numlines = nlines;
    q->numbytes = nbytes;
    q->flag = flg;
    if(memmark[j]==0) break;
    rec=r; nlines=nl; nbytes=nb; flg=f;
  } while(++j < MAXBLOCK);
  return 0;
}

/*
 * KILLINFO
 *
 * Kills an entry at index j in meminfo[] array.
 *
 */
int killinfo(j) int j; {
struct bucket far *s,*d;
int i;
  if(j>=MAXBLOCK) return -1;
  for(i=j+1;i<MAXBLOCK;++i){
#if 0
    d= &meminfo[i-1]; s= &meminfo[i];
    d->numbytes = s->numbytes;
    d->numlines = s->numlines;
    d->record   = s->record;
    d->flag     = s->flag;
#else
 segcopy((char far *)&meminfo[i-1],(char far *)&meminfo[i],sizeof(struct bucket));
/*    moveMEM((char *)meminfo[i-1],(char *)meminfo[i],sizeof(struct bucket)); */
#endif
    if(memmark[i]==0) break;
  }
  return 0;
}


void initfinfo(){                        /* Initialize finfo[] array */
extern struct fileinfo *finfo;
extern int maxinfo;
extern char *core();
int i;
    finfo=(struct fileinfo *)core(i=maxinfo*sizeof(struct fileinfo));
    fillchr((char *)&finfo[0],'\0',i);
}

void savefinfo(fiddle) int fiddle; {
extern int markset,markcol; extern LLONG markline;
struct fileinfo *p;
  p= &finfo[curfile];
  p->dirty=fiddle;
  p->setmark=markset;
  p->setmarkcol=markcol;
  p->setmarkline=markline;
  p->ReadFtype=intexttype;
  p->WriteFtype=outtexttype;
}

void getfinfo(fiddle) int *fiddle; {
struct fileinfo *p;
  p= &finfo[curfile];
  fiddle[0]=p->dirty;
  markset=p->setmark;
  markcol=p->setmarkcol;
  markline=p->setmarkline;
  intexttype=p->ReadFtype;
  outtexttype=p->WriteFtype;
}

/*
 * STOWAWAY
 *
 * Stows current file into external blocks, so that another file can
 * be edited.
 *
 * To release the current file and save it for future editing:
 *
 *      1. Set NN=curfile.
 *      2. Call routine PackScreenBuffer(). If this fails, then return
 *         -1.
 *      3. Apply putLOWblock() until the low buffer is empty. Apply
 *         putHIblock() until the high buffer is empty. If anything
 *         goes wrong, then goto 6. We assume that curfile==NN and that
 *         the routines are smart enough to update meminfo[] and finfo[].
 *      4. Set finfo[NN] variables for row, col, line using the current
 *         system variables.
 *      5. Set file active: finfo[NN].active=1.
 *         Return 0.
 *      6. Failed. Call UnPackScreenBuffer() to reload data items to the
 *         screen buffer. Drawall(). Return -1.
 */
int stowaway(){
int i;
struct fileinfo *p;
  while(1){
    i=putHIblock();
/*    if(i == 0) continue; */
    if(i == -1) break;
    if(i == -2) goto failed;
  }
  while(1){
    i=putLOWblock();
/*    if(i == 0) continue; */
    if(i == -1) break;
    if(i == -2) goto failed;
  }
  p = &finfo[curfile];
  p->rowc=row;
  p->colc=col;
  p->curoffset=curoff;
  p->linec=lncount;
  p->active=1;
  return 0;
failed:
  return -1;
}

void setoffset(n) int n; {
  finfo[curfile].curoffset=n;
}

/*
 * SETFILE
 *
 * Sets the file being currently edited to NN
 *
 */
void setfile(NN) int NN;{
register struct fileinfo *p;
  p = &finfo[NN];
  p->active=2;
  row=MYmin(p->rowc,ROWMAX);      /* 1996: Window can change size */
  col=MYmin(p->colc,COLMAX);
  if((lncount=p->linec)==0) lncount=1;
  strcpy(sysfile,p->fname);
  curoff = p->curoffset;
  curfile=NN;
}


/*
 * NEWEDIT
 *
 * Edit a new file already stowed away in external blocks.
 *
 * To edit file NN in finfo[]:
 *
 *      1. Test NN in range 0...(maxinfo-1). Return -1 if invalid.
 *      2. Release current file. Return -1 if it cannot be released.
 *         Set curfile=NN. Set finfo[NN].active=2.
 *         Set row=finfo[NN].rowc, col=finfo[NN].colc, lncount=finfo[NN].linec.
 *      3. Set buffer empty: lbuf=bufbase-1, hbuf=ebuf.
 *         Return 0.
 */
int newedit(NN) int NN;{
  if(NN<0 || NN>=maxinfo) return -1;
  if(PackScreenBuffer(row)) return -1;
  if(stowaway()){
    UnPackScreenBuffer(row);
    drawall();
    bellpressany("No more blocks");
    return -1;
  }
  /*doPURGE();*/
  setfile(NN);
  lbuf=bufbase-1; hbuf=ebuf;
  return 0;
}

/*
 * PACKSCREENBUFFER
 *

 * Copies screen lines to low and high buffers as appropriate.
 * This implies purges to external blocks, hence failure is possible.
 * The routine cleans up the mess in case of failure.
 *
 * Fragmentation can occur because of this routine. The way it happens
 * is for the low or high buffer to be empty, so that no active block
 * can be used to pick up the fragmentation. A clever way to prevent the
 * trouble is to insure that the low and high buffers are non-empty, if
 * possible.
 *
 */
int PackScreenBuffer(row) int row; {
int i;
  deFRAG();
  for(i=0;i<row;++i) {
    if(!loPush(line[i])) {
      while(i--) loPull(sysx);
      return -1;
    }
  }
  for(i=ROWMAX;i>=row;--i) {
    if(!hiPush(line[i])){
      while(i++ <= ROWMAX) hiPull(sysx);
      i=row; while(i--) loPull(sysx);
      return -1;
    }
  }
  return 0;
}

/*
 * deFRAG
 *
 * Insure that both low and high buffers are loaded, to prevent
 * fragmentation.
 */
void deFRAG() {
  if(loPull(sysx)) loPush(sysx);  /*insure low buffer does not fragment */
  if(hiPull(sysx)) hiPush(sysx);  /*insure high buffer does not fragment */
}

/*
 * UNPACKSCREENBUFFER
 *
 * Used after a previous call to PackScreenBuffer() in order to
 * restore lines to the screen. Depends on the cursor row. It should
 * always succeed.
 */
int UnPackScreenBuffer(row) int row; {
register int i;
  for(i=row-1;i>=0;--i) loPull(line[i]);
  for(i=row;i<=ROWMAX;++i) hiPull(line[i]);
  return 0;
}

/*
 * PUTLOWBLOCK
 *
 * Purges a low buffer hunk out to external blocks. Returns 0 on
 * success, -1 if no more low buffer to purge, -2 if an external block
 * cannot be created for the copy. Updates lbuf and meminfo[], finfo[]
 * arrays on success.
 *
 * The routine putLOWblock():
 *
 *      1. Test lbuf<bufbase. If true, then return failure -1.
 *      2. Let N=crBLOCK(). If N == -1, then no block is available, so
 *         in this case return -2.
 *      3. Open a slot in the meminfo[] array and save the record I.
 *      4. Determine the number n of bytes to write (<= SIZEBLOCK) and
 *         the number of lines m. Update meminfo[] with this information
 *         and call CPtoBLOCK(N,bufbase,n) to move the data.
 *      5. Block move: moveMEM(bufbase,bufbase+n,lbuf-bufbase-n)
 *      6. Change lbuf -= n to reflect the purge of data. Return 0.
 */
int putLOWblock(){
int k;
int i,j,lines,N;
struct fileinfo *p;
  if(lbuf<bufbase) return -1;
  if((N=crBLOCK()) == -1) return -2;
  p = &finfo[curfile];
  k=lbuf+1-bufbase; i=0;
  if(k>SIZEBLOCK){
    i=k-SIZEBLOCK; k=SIZEBLOCK;
    while(k>0){ if(bufbase[k-1] == 0) break; --k; ++i;}
  }
  lines=CPtoBLOCK(N,bufbase,k);
  j=startindex(curfile)+p->nbLOW;
  addinfo(N,lines,k,0,j);   /* Such blocks are already converted */
  ++(p->nbLOW);
  ++(p->nbTOT);
  moveMEM(bufbase,bufbase+k,i);
  lbuf -= k; *lbuf=0;
  return 0;
}

/*
 * PUTHIBLOCK
 *
 * Purges a high buffer hunk out to external blocks. Returns 0 on
 * success, -1 if no more high buffer to purge, -2 if an external block
 * cannot be created for the copy. Updates hbuf and meminfo[], finfo[]
 * arrays on success.
 *
 * The routine putHIblock():
 *
 *      1. Test hbuf>=ebuf. If true, then return failure -1.
 *      2. Let I=crBLOCK(). If I == -1, then no block is available, so
 *         in this case return -2.
 *      3. Open a slot in the meminfo[] array and save the record I.
 *      4. Determine the number n of bytes to write (<= SIZEBLOCK) and
 *         the number of lines m. Update meminfo[] with this information
 *         and call CPtoBLOCK(I,ebuf-n,n) to move the data.
 *      5. Block move: moveMEM(hbuf+n,hbuf,ebuf+1-hbuf-n)


 *      6. Change hbuf += n to reflect the purge of data. Return 0.
 *


 */
int putHIblock(){
register int i,j; int r,lines,N;
  if(hbuf>=ebuf) return -1;
  if((N=crBLOCK()) == -1) return -2;
  j=ebuf-hbuf; i=0;
  if(j>SIZEBLOCK){
    i = j-SIZEBLOCK; j=SIZEBLOCK;
    while(j>0){
      if(hbuf[i-1]==0)break; ++i; --j;
    }
  }
  lines=CPtoBLOCK(N,hbuf+i,j);
  r=startindex(curfile)+finfo[curfile].nbLOW;
  addinfo(N,lines,j,0,r);  /* Such blocks are already converted */
  ++(finfo[curfile].nbTOT);
  if(i) moveMEM(hbuf+j,hbuf,i);
  hbuf += j;
  if(hbuf>ebuf) hbuf=ebuf; *ebuf=0;
  return 0;
}

/*
 * GETLOWBLOCK
 *
 * Copies the near low block from external memory into the low buffer.
 * The low work buffer is assumed to be empty.
 *
 * The routine getLOWblock():
 *
 *      1. Let m = finfo[curfile].nbLOW. If m==0, then return -1 for failure.
 *         Let N=startindex(curfile)+m-1, n=meminfo[N]->numbytes,
 *         I=meminfo[N]->record.
 *      2. Test bufbase+n+HEADROOM<hbuf. If false, then return -2.
 *      3. Call CPfromBLOCK(I,bufbase,n). Update lbuf = bufbase+n.
 *         Note that *lbuf == 0 is required by other routines.
 *      4. Call rlBLOCK(I). We are not using it anymore, so it has to be
 *         put back into the free pool.
 *      5. Decrement finfo[NN].nbLOW and nbTOT. Remove array element N from
 *         meminfo[]
 *      6. Return 0 to report success.
 */
int getLOWblock(){
int n,I,k;
struct fileinfo *p;
  p= &finfo[curfile];
  if((n=p->nbLOW)==0) return -1;
  k=getmeminfoindex(curfile,n-1);
  n=meminfo[k].numbytes;
  if(bufbase+n>=hbuf) return -2;
  I=meminfo[k].record;
  CPfromBLOCK(I,bufbase,n);
  lbuf = bufbase+n-1; *lbuf=0;
  rlBLOCK(I);
  --(p->nbLOW);
  --(p->nbTOT);
  killinfo(k);
  return 0;
}

/*
 * GETHIBLOCK
 *
 * Copies the near high block from external memory into the high buffer.
 * The high work buffer is assumed to be empty.
 *
 * The routine getHIblock():
 *
 *      1. Let N = startindex(NN)+finfo[NN].nbLOW. Let I =
 *         meminfo[N]->record. If finfo[NN].nbTOT==finfo[NN].nbLOW, then
 *         return -1 for failure. Let n = meminfo[N]->numbytes.
 *      2. Test lbuf+HEADROOM+n<ebuf. If false, then return -2.
 *      3. Call CPfromBLOCK(I,hbuf=ebuf-n,n).
 *      4. Call rlBLOCK(I). We are not using it anymore, so it has to be
 *         put back into the free pool.
 *      5. Decrement finfo[NN].nbTOT. Remove array element N from
 *         meminfo[].
 *      6. Return 0 to report success.
 *
 */
int getHIblock(){
int n,I,N;
  if((N=getmeminfoindex(curfile,finfo[curfile].nbLOW)) == -1) return -1;
  n=meminfo[N].numbytes;
  if(lbuf+n>=ebuf) return -2;
  I=meminfo[N].record;
  CPfromBLOCK(I,hbuf=ebuf-n,n);
  *ebuf=hbuf[-1]=0;
  rlBLOCK(I);
  --(finfo[curfile].nbTOT);
  killinfo(N);
  return 0;
}

/*
 * NEXTFILE
 *
 * Computes the index 0...(maxinfo-1) of the next file to be edited,
 * measured from the current file index (curfile), in a circular loop.
 * If it can't find anything to edit, it returns 0, the index of the
 * kill ring. So this routine always returns something useful.
 */
int nextfile(n) int n;{
register
int i,flag;
  flag=(n>=0);
  i=MYabs(n); if(i>=maxinfo) i=0;
  n=i;
  while(1){
    if(finfo[i].active) return i;
    if(flag) ++i; else --i;
    if(i<0) i=maxinfo-1; if(i>=maxinfo) i=0;
    if(i == n) break;
  }
  return 0;
}

/*
 * PRESSANY
 *
 * Wait for a return key.
 */
void pressany(s)char *s;{
extern char *MYstrchr();
  if(s[0]) {bbanner(s," - ");} ePrint("Press return: ");
  clrcon();
  while(MYstrchr("\r\n",getbyte())==(char *)0) ;
}

void bellpressany(s)char *s;{
  bell(); pressany(s);
}

/*
 * ISDIRTY
 *
 * Tests for any dirty files that should be written.
 */
int isdirty(){
register
int i;
  for(i=0;i<maxinfo;++i){
    if(finfo[i].dirty) return 1;
  }
  return 0;
}

/*
 * SHOWDIRTY
 *
 * Displays any dirty files that should be written.
 */
int showdirty(){
register
int i;
  if(!isdirty()) return 0;
  cleardisplay();
  ePrint("[Changes exist]"); crlf();
  for(i=0;i<maxinfo;++i){
    if(finfo[i].dirty) {ePrint(finfo[i].fname); crlf();}
  }
  return (!getyesno(QUIT_PROMPT));
}

/*
 * USEDFILES
 *
 * Returns the number of active files.
 */
int usedfiles(){
register
int i,j;
  for(j=i=0;i<maxinfo;++i) if(finfo[i].active) ++j;
  return j;
}

/*
 * ISINFO
 *
 * Tests finfo array to see if there is a file name match
 */
#if !MSDOS
int isinfo(name) char *name; {
int i,j; char *p;
char myname[128];
  strcpy(myname,name);
  j=strlen(myname);
  for(i=0;i<maxinfo;++i){
    if(finfo[i].active != 0){
      if(strlen(p=finfo[i].fname) != j) continue;
      if(MYstrncmp(p,myname,j)==0) return 1+i;
    }
  }
  return 0;
}
#endif

#if MSDOS
int isinfo(name) char *name; {
int i,j; char *p;
char myname[128];
  strcpy(myname,name);
  cvupper(myname);
  j=strlen(myname);
  for(i=0;i<maxinfo;++i){
    if(finfo[i].active != 0){
      if(strlen(p=finfo[i].fname) != j) continue;
      if(strccmp(p,myname,j)==0) return 1+i;
    }
  }
  return 0;
}
#endif
/*
 * V8.C
 *
 *              This file contains special OS functions and
 *              standard C library functions missing from the
 *              main library for the target C compiler.
 *
 *              For supported OS, see header file V.H.
 */

#include "pie.h"

#define COLUMNS 1       /* using 1-column output */
/*
 * FREESP
 *
 * Purpose:
 *              Computes the number of free kilobytes on the disk.
 * Returns:
 *              Message to print on status line, in area sysv[].
 */
#if BSDunix || SYS5
VOIDS freespace() {
char buf[512];
int x,y,NCOLS;
char *p;
LLong ln;
char *endof(),*MYstrchr();
LLONG atoiLL();

  x = ln = 0;
  if(sysv[0]) {
    if((p = MYstrchr(sysv,',')) != 0) {
      *p = '\0'; NCOLS = (int)atoiLL(p+1);
    } else NCOLS = COLUMNS;
    strcpy(buf,sysv);
    x=dir(buf,NCOLS,0,(char *)0,(char **)0);
    ssprintf(sysv,"%u File%s",(unsigned)x,(x == 1 ? "":"s"));
  }
  return ( (x <= 0) ? 0 : 1);
}

char *getwdir(buf) char *buf; {
extern char * getcwd();
 return getcwd(buf,MAXFNAME-1);
}
#endif  


#if MSDOS
int freespace() {
char buf[128];
int y,NCOLS;
char *p;
char *endof(),*MYstrchr();
LLONG atoiLL();

  y=0;
  if(sysv[0]) {
    if((p = MYstrchr(sysv,',')) != (char *)0) {
      *p = '\0'; NCOLS = (int)atoiLL(p+1);
    } else NCOLS = COLUMNS;
    strcpy(buf,sysv);
    y = dir(buf,NCOLS,0,(char *)0,(char **)0);   /* reads default disk */
    if(y != -1)
      ssprintf(sysv,"%u File%s",(unsigned)y,(y == 1 ? "":"s"));
  }
  return ( (y <= 0) ? 0 : 1);
}
#endif

/*
 * DIR
 *
 * Purpose:
 *              Inject disk directory in five-column format.
                Uses currently logged disk.
 * Notes:
 *
 */
#if (BSDunix || SYS5) && !LCCWIN32

#include <sys/types.h>
#include <dirent.h>

DIR *fphandle=0;

int findfirst(t,s) char *t,*s; {
struct dirent *dp;
if(fphandle==0) return 1;
for(dp = (struct dirent *)readdir(fphandle);
    dp != (struct dirent *)0;
    dp = (struct dirent *)readdir(fphandle)){
     if(matches(t,dp->d_name)==0) {strcpy(s,dp->d_name); return 0;} /* found it */
    }
strcpy(s,"");
rewinddir(fphandle);
return 1; /* not found */
}

matches(t,s) char *t; char *s; {
int i,status;
status=0;
while(1) {
  if(t[0]==0) break; /* end of string */
  if(s[0]==0) break; /* end of string */
  if(t[0]=='*') {
    /* Does s match a substring of t+1? */
    i=0; while(t[1+i]==s[i] && s[i]!=0) ++i;
    if(s[i]==0){ s += i; t += 1+i; break;}
    if(t[1+i]=='*' || t[1+i]=='?'){
      t += 1+i; s += i;
    } else
    /* Otherwise, leave t[0]=='*' and match current char s[0] */
    ++s;
  } else {
    if(t[0] != '?' && t[0] != s[0]) break;
    ++t; ++s;
  }
}
while(t[0]=='*') ++t;
if(t[0] != 0 || s[0]!=0) status=1;
return status;
}

int isadir(s) char *s; {
DIR *fp;
char t[MAXFNAME];
  strcpy(t,s);
  if(t[0]=='/' && t[1]==0) strcat(t,".");   
  else
  if(tailchar(t)=='/') *MYstrrchr(t,'/')=0;
#if 0 /* Not very efficient */
  if(stat(t,(struct stat *)&buf)==0 && (buf.st_mode & S_IFDIR)!=0) 
    return 1;
#endif
#if 1
  if((fp=(DIR *)opendir(t)) != (DIR *)0) {
    closedir(fp); return 1;
  }
#endif
  return 0;
}

int iswild(s) char *s; {
char *MYstrchr();
  if(MYstrchr(s,'*') != (char *)0) return 1;
  if(MYstrchr(s,'?') != (char *)0) return 1;
  return 0;
}

void backslashes(s) char *s; {
int i; char *p;
  for(i=0;s[i]!=0;++i) {if(s[i]=='/') s[i]='\\';}
}

void addslash(s) char *s; {
  if(tailchar(s) != '/') strcat(s,"/");
}

void stripstars(buf) char *buf;
{
char *p;
  if((p=MYstrstr(buf,"/*"))!=0 && p[2]==0) p[1]=0;
}

/* split off dir, copy to cd[] */
void dirsplit(s,cd) char *s,*cd; {
extern char *MYstrrchr();
  if(s[0]==0 || MYstrcmp(s,NOTEPAD)==0 || MYstrcmp(s,UNTITLED)==0) return;
  strcpy(cd,s);
  if((s=MYstrrchr(cd,'/'))!=(char *)0) s[1]=0; else cd[0]=0;
}

char *ffname(buf) char *buf; {
char *s;
extern char *MYstrrchr();
  if((s=MYstrrchr(buf,'/')) == (char *)0) s=buf;
  else ++s;
  return s;
}

char *statbyte(s) char *s; {
 return(s+strlen(s)+1);
}

/* Search Directory */
/* Only BSDunix or SYS5 and not LCCWIN32 */

int dir(buf,COLUMN,flag,dirbuff,buffer)
char *buf;      /* file name to match */
int COLUMN;     /* flag for file name completion, or # cols to output */
int flag;       /* 0=dir or lookup, 1=directory list */
char *dirbuff;  /* name of directory in lookup */
char **buffer;  /* file list, max=MAXDIRENT */
{
int n,k;
DIR *fp;
char dirbuf[MAXFNAME]; char testdir[MAXFNAME];
char  s[MAXFNAME]; char t[MAXFNAME]; char work[MAXFNAME];
char *p; char *cwd(), *endof(), *getenv();
extern char *statbyte();

  if((p=MYstrstr(buf,"//"))!=(char *)0){  /* Double slash gets rid of */
    strcpy(buf,p+1);                    /* previous string */
    if(COLUMN==-1) return 1;
  }
  if(!dirbuff) dirbuff="";
  if(buf[0]==0) {strcpy(buf,cwd()); addslash(buf);}
  /* assume connected dir */
  if(buf[0]=='/' && buf[1]==0) strcat(buf,".");
  if(tailchar(buf)=='/') strcat(buf,"*");
  strcpy(s,buf);
  /* Is is like ~/tmp/class/xx.c? */
  if(buf[0]=='~'){
    p=getenv("HOME");
    if(p != (char *)0){ strcpy(s,p); strcat(s,buf+1);}
  }
  else  
  /* Is is like $HOME/tmp/class/xx.c? */
  if(buf[0]=='$'){
    strcpy(t,buf+1);
    p=MYstrchr(t,'/'); if(p!=(char *)0) p[0]=0;
    p=getenv(t);
    if(p!=(char *)0){
      strcpy(s,p);
      p=MYstrchr(buf,'/'); if(p==(char *)0) p="";
      strcat(s,p);
    }   
  }
  strcpy(dirbuf,s);
  fp=(DIR *)opendir(dirbuf);
  if(fp==(DIR *)0){ /* It had a file name on the end */
    p=MYstrrchr(s,'/'); if(p==(char *)0) {strcpy(t,s); strcpy(s,cwd());}
    else {strcpy(t,p+1); p[0]=0;}
    /* s=directory string, t=file name pattern */
    strcpy(dirbuf,s);
    fp=(DIR *)opendir(dirbuf);
    if(fp==(DIR *)0) return 0;
  } else t[0]=0; /* no file name given */
  if(t[0]==0) strcpy(t,"*"); /* no file name? then assume all files */
  strcpy(work,dirbuf);
  addslash(dirbuf);
  strcpy(buf,dirbuf); strcat(buf,t);
  n=0;
/* debug */
  fphandle=fp;
  k=findfirst(t,s);

  /* Lookup a directory entry and report the effort */
  if(COLUMN == -1){
    if(k) {
      strcat(t,"*");
      strcat(buf,"*");
      closedir(fp); fp=(DIR *)opendir(work);
      fphandle=fp;
      k=findfirst(t,s);
    }
    if(k==0){
      n=1;
      strcpy(work,dirbuf); strcat(work,s); /* Save last find */
      if(findfirst(t,s)==0) ++n;        /* Oops, can't find */
      else strcpy(buf,work);
    }
    goto quit;
  }
        
  /* Make a list of directory entries, return the list */
  if(flag) {
    if(dirbuf[0]) strcpy(dirbuff,dirbuf);
    else {strcpy(dirbuff,cwd()); addslash(dirbuff);}
  }
  if(k==0 && flag==0){
      addln(dirbuf,TRUE); eddn(); curses();
  }
  while(k==0) {
    strcpy(work,dirbuff); addslash(work); strcat(work,s);
    if(isadir(work)) addslash(s);
    if(flag==0){
      ++n; addln(s,TRUE); eddn(); curses(); if(ccBIOS(2)) break;
    } else {
      if(MYstrcmp(s,"./")==0) goto nextmatch;        
      if(n==0 && MYstrcmp(s,"../")!=0) squirrel(&buffer[n++],"./"); 
      if(!squirrel(&buffer[n],s) || n>=MAXDIRENT-1) goto quit;
      ++n;
    }   
nextmatch:
    k = findfirst(t,s);
  }
quit:
  closedir(fp);
  return n;
}

int squirrel(dest,src) char **dest,*src; {
int flag=0;
      if((*dest=(char *)malloc(2+strlen(src)))==(char *)0)
        goto quit;
      strcpy(*dest,src);
      *statbyte(*dest)=' ';
      flag=1;
quit:
     return flag;
}

int getbufferlist(s,last) char **s; int *last; {
register
int i,v;
char ss[MAXFNAME];

  for(v=i=0;i<maxinfo;++i){
    if(finfo[i].active) {
      if(i==curfile) last[0]=v;
      ssprintf(ss,"%s%u. %s",(i<10 ? "0":""),i,finfo[i].fname);
      if(!squirrel(&s[v],ss)) goto quit;
      ++v;
    }
  }
quit:
  return v;
}

#endif

#if (LCCWIN32 && SYS5)

void stripstars(buf) char *buf;
{
char *p;
  if((p=MYstrstr(buf,"\\*"))!=0 && p[2]==0) p[1]=0;
}

/* split off dir, copy to cd[] */
void dirsplit(s,cd) char *s,*cd; {
extern char *MYstrrchr();
  if(s[0]==0 || MYstrcmp(s,NOTEPAD)==0 || MYstrcmp(s,UNTITLED)==0) return;
  strcpy(cd,s);
  if((s=MYstrrchr(cd,'\\'))!=(char *)0) s[1]=0;
  else cd[0]=0;
}
                
char *ffname(buf) char *buf; {
char *s;
extern char *MYstrrchr();
extern void fixslashes();
  fixslashes(buf);
  if((s=MYstrrchr(buf,'\\')) == (char *)0) s=buf;
  else ++s;
  return s;
}

char *statbyte(s) char *s; {
 return(s+strlen(s)+1);
}

int squirrel(dest,src) char **dest,*src; {
int flag=0;
      if((*dest=(char *)malloc(2+strlen(src)))==(char *)0)
        goto quit;
      strcpy(*dest,src);
      *statbyte(*dest)=' ';
      flag=1;
quit:
     return flag;
}

int getbufferlist(s,last) char **s; int *last; {
register
int i,v;
char ss[MAXFNAME];

  for(v=i=0;i<maxinfo;++i){
    if(finfo[i].active) {
      if(i==curfile) last[0]=v;
      ssprintf(ss,"%s%u. %s",(i<10 ? "0":""),i,finfo[i].fname);
      if(!squirrel(&s[v],ss)) goto quit;
      ++v;
    }
  }
quit:
  return v;
}

#include <direct.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>
#include <string.h>

void fixslashes(s) char *s; {
while(s[0]){
  if(s[0]=='/') s[0]='\\';
  ++s;
}
}

void addslash(s) char *s; {
  fixslashes(s);
  if(tailchar(s) != '\\') strcat(s,"\\");
}

void backslashes(s) char *s; {
int i;
  for(i=0;s[i]!=0;++i) {if(s[i]=='/') s[i]='\\';}
}

void purgedoubledot(s) char *s; {
int i; char *p,*q;
loop:
  p=MYstrstr(s,"\\..");  
  if(p==(char *)0) return;
  p[0]=0;
  q=MYstrrchr(s,'\\');
  if(q==(char *)0){p[0]='\\'; return;}
  strcpy(q+1,p+3);
  goto loop;
}

#if 0
char *lastchar(s) char *s; {
int i;
i=strlen(s); if(i == 0) i=1;
return (s+i-1);
}
#endif

/* Some directory strings: "c:", "c:\", "c:/", "/" */
int isadir(s) char *s; {
int flag,k,isaDIR;
char *p;
long ffhandle;
struct _finddata_t info;
  isaDIR=0;
  backslashes(s);
  purgedoubledot(s);
  k=strlen(s);
  if(k==0) goto quit;            /* null string is not a directory */
  if(tailchar(s) == ':') addslash(s);
  /* s[] == new filename or directory */
  p=ffname(s);
  if(MYstrcmp(p,".")!=0 && p[0]!=0){
    struct _stat buf; int k;
    k= _stat(s,(struct _stat *)&buf);
    if(k!=1 && (_S_IFREG & buf.st_mode) != 0) goto quit;
/*    if(classify(s)==1) goto quit; File open takes too long, can't use. */
/* Unfortunately, _S_IFDIR is set for nonexistent dir names, so can't use. */
  }
  if(MYstrcmp(p,".")!=0){ /* Might be a directory name */
    addslash(s); strcat(s,(k>3 ? "." : "*"));
  }
  ffhandle= _findfirst(s, (struct _finddata_t *)&info);
  if(ffhandle != (long)-1){
    _findclose(ffhandle);
    isaDIR=1;     /* Found a directory */
  }
quit:
  s[k]=0;     /* restore string */
return isaDIR; 
}


#if 0
struct _finddata_t {
    unsigned    attrib;
    unsigned long       time_create;    /* -1 for FAT file systems */
    unsigned long       time_access;    /* -1 for FAT file systems */
    unsigned long       time_write;
    unsigned long       size;
    char        name[260];
};
/* int findfirst(t,s) char *t,*s;
   RETURN 0 if string t matches a file in directory fp, then s=match, else
   RETURN 1 for failure.
*/
#endif

static long khandle= -1;

int findfirst(dirbuf,s) char *dirbuf, *s; {
long k;
struct _finddata_t info;
if(khandle!= -1) _findclose(khandle);
khandle = (long)  _findfirst(dirbuf, (struct _finddata_t *)&info);
if(khandle != (long)-1) strcpy(s,info.name);
return (khandle== -1 ? 1 : 0);
}

int findnext(s) char *s; {
int k;
struct _finddata_t info;
k = _findnext(khandle, (struct _finddata_t *)&info);
if(k == 0) strcpy(s,info.name);
return (k==0 ? 0 : 1);
}

int iswild(s) char *s; {
char *MYstrchr();
  if(MYstrchr(s,'*') != (char *)0 || MYstrchr(s,'?') != (char *)0) return 1;
  return 0;
}


/* Search Directory LCCWIN32 */

int dir(buf,COLUMN,flag,dirbuff,buffer)
char *buf;      /* file name to match */
int COLUMN;     /* flag for file name completion -1, or # cols to output */
int flag;       /* 0=dir or lookup, 1=directory list */
char *dirbuff;  /* name of directory in lookup */
char **buffer;  /* file list, max=MAXDIRENT */
{
int n,k;
char dirbuf[MAXFNAME]; char testdir[MAXFNAME];
char  s[MAXFNAME]; char t[MAXFNAME]; char work[MAXFNAME];
char *p,*q;
/* extern char *cwd(), *endof(), *getenv(); */
/* extern char *statbyte(); */

  khandle= -1; /* From _findfirst() */
  if(!dirbuff) dirbuff="";
  fixslashes(buf);
  if((p=MYstrstr(buf,"\\\\"))!=(char *)0){  /* Double slash gets rid of */
    strcpy(buf,p+1);                    /* previous string */
    return 1;
  }
  if(tailchar(buf)==':') addslash(buf);
  strcpy(s,buf);
  switch(buf[0]){
  case 0:    /* Empty string means connected working directory */
    strcpy(s,cwd()); addslash(s); break;

  case '~':  /* Is buf[] like ~/tmp/class/xx.c? */
    if(buf[1]=='\\'){
      p=getenv("HOME");
      if(p != (char *)0){ strcpy(s,p); strcat(s,buf+1); fixslashes(s);}
    }
    break;

  case '$':  /* Is buf[] like $HOME/tmp/class/xx.c? */
    strcpy(t,buf+1);
    p=MYstrchr(t,'\\'); if(p!=(char *)0) p[0]=0;
    q=getenv(t);
    if(q!=(char *)0){
      strcpy(s,q);
      if((p=MYstrchr(buf,'\\'))!=(char *)0) strcat(s,p);
      fixslashes(s);
    }
    break;
  }; /* end switch */
  /* Maybe changed s[], so copy it back to buf[] */
  strcpy(buf,s);

  /* Is there a file name on the end? */
  p=MYstrrchr(s,'\\');
  if(p==(char *)0) {strcpy(t,s); strcpy(s,cwd()); fixslashes(s);}
  else {strcpy(t,p+1); p[1]=0;}

  /* s=directory string, t=file name pattern */
  addslash(s);
  strcpy(dirbuf,s);

  if(isadir(dirbuf)==0) return 0;

  /* t[0]==0 means no file name given */
  if(t[0]==0) strcpy(t,"*"); /* no file name? then assume all files */

  strcpy(buf,dirbuf); strcat(buf,t);
  /* Rebuilt buf[] to always contain a directory name */
  n=0;
  k=findfirst(buf,s);
  if(COLUMN == -1){ /* Do a search for buf[] */
    if(k) {  /* No match found. Try a wildcard search */
          n=0;
      if(isadir(buf)) addslash(buf);
      if(iswild(buf)==0) strcat(buf,"*");
      k=findfirst(buf,s);
    }
    if(k==0){ /* Found a match */
      n=1;
      strcpy(work,dirbuf); strcat(work,s); /* Save last find */
      if(findnext(s)==0) ++n;        /* Oops, can't find unique one */
      else strcpy(buf,work);         /* Copy unique one to buf[] */
    }
    goto quit; /* buf[]==match and n==1 success or n==2 fail */
  }
  if(flag) {
    if(dirbuf[0]) strcpy(dirbuff,dirbuf);
    else {strcpy(dirbuff,cwd()); addslash(dirbuff);}
  }
  if(k==0 && flag==0){
      addln(dirbuf,TRUE); eddn(); curses();
  }
  while(k==0) {
   if(MYstrcmp(s,".")!=0){
     strcpy(work,dirbuff); addslash(work); strcat(work,s);
     if(isadir(work)!=0) addslash(s);
     if(flag==0){
        ++n; addln(s,TRUE); eddn(); curses(); if(ccBIOS(2)) break;
      } else {
        if(MYstrcmp(s,".\\")==0) goto nextmatch;        
        if(!squirrel(&buffer[n],s) || n>=MAXDIRENT-1) goto quit;
        ++n;
      }
   }
nextmatch:
   k = findnext(s); 
  }
quit:
  if(khandle!= -1) { _findclose(khandle); khandle= -1;}
  return n;
}

#endif


#if MSDOS
char *ffname(buf) char *buf; {
char *s;                 
extern char *MYstrrchr(),*MYstrchr();
  s=buf; while((s=MYstrchr(s,'/'))!=(char *)0) s[0]='\\';
  if((s=MYstrrchr(buf,'\\')) == (char *)0){
    if((s=MYstrchr(buf,':')) != (char *)0) ++s; else s=buf;
  }
  else ++s;
  return s;
}

/* split off dir, copy to cd[] */
void dirsplit(s,cd) char *s,*cd; {
extern char *MYstrrchr(),*MYstrchr();
char *p;
char buf[MAXFNAME];
  strcpy(buf,s);
  if((p=MYstrrchr(buf,'\\'))!=(char *)0) p[1]=0;
  else
  if((p=MYstrchr(buf,':'))!=(char *)0){p[1]=0; addslash(buf);}
  else buf[0]=0;
  strcpy(cd,buf);
}

int iswild(s) char *s; {
char *MYstrchr();
  if(MYstrchr(s,'*') != (char *)0 || MYstrchr(s,'?') != (char *)0) return 1;
  return 0;
}

void addslash(s) char *s; {
  if(tailchar(s) != '\\') strcat(s,"\\");
}

void backslashes(s) char *s; {
int i;
  for(i=0;s[i]!=0;++i) {if(s[i]=='/') s[i]='\\';}
}

#include <dir.h>
/*
int isadir(s) char *s; {
  return ((tailchar(s)=='\\') ? 1 :0);
}
*/
int isadir(s) char *s; {
struct ffblk sysfcb;
  if(tailchar(s)=='\\') return 1;
  if(getmod(s)==16) return 1;
  return 0;
}

static void putln(){
  addln(sysv,TRUE); eddn(); curses();
}

char *statbyte(s) char *s; {
 return(s+strlen(s)+1);
}

int getmod(s) char *s; {
extern void __int__();
_DX = (unsigned)s;
_AL=0; _AH=0x43; __int__(0x21);
return (_CX);
}

void pad15(ss) char *ss; {
int j;
      j=strlen(ss); while(j<15) ss[j++] = ' '; ss[j] = EOS;
}


/* Search Directory MSDOS */

int dir(buff,COLUMN,flag,dirbuff,buffer)
char *buff;      /* file name to match */
int COLUMN;     /* flag for file name completion, or # cols to output */
int flag;       /* 0=dir or lookup, 1=directory list */
char *dirbuff;  /* name of directory in lookup */
char **buffer;  /* file list, max=MAXDIRENT */
{
struct ffblk sysfcb;
char *s,*p;
int i,j,k,v,curdrv,destdrv;
char dirbuf[MAXFNAME],destbuf[MAXFNAME],dest[MAXFNAME],ss[MAXFNAME];
char buf[MAXFNAME];
#define NAME sysfcb.ff_name

  v=0;
  cvupper(buff);
  ffname(buff);                  /* change '/' to '\' */
  if((s=MYstrstr(buff,"\\\\"))!=(char *)0){  /* Double slash gets rid of */
    strcpy(buff,s+1);                      /* previous string */
    return 1;
  }
  curdrv=getdrive();  /* bdos(25,0,0) & 255 */
  getwdir(dirbuf);
  if(buff[0]==0) strcpy(buff,cwd());
  destdrv = ('A' <= buff[0] && buff[1]==':') ? (buff[0]-'A') : curdrv;
  if(destdrv == curdrv) strcpy(destbuf,dirbuf);
  else {
    setdrive(destdrv); /* bdos(14,destdrv,0); */
    getwdir(destbuf);
  }
  /* get file name pointer */
  if(isadir(buff)) addslash(buff);
  strcpy(buf,buff);
  k=findfirst(buf,&sysfcb,16);  /* Seek file or directory */
  s=ffname(buf);
  if(!iswild(s)){
    if(k==0 && (sysfcb.ff_attrib & 16) != 0) {addslash(buf); strcat(buf,"*.*");}
    if(k) strcat(buf,"*");
  }
  if(k && iswild(s) && MYstrchr(s,'.')==(char *)0) strcat(buf,".*");
  strcpy(ss,buf);
  if(COLUMN == -1){ /* file name completion */
    strcpy(buff,buf);
    if(k) k=findfirst(buf,&sysfcb,16);       /* look up again */
    if(k==0){
      v=1;
      s[0]=0;
      if(buf==s || NAME[0]=='.') {
        strcpy(buf,destbuf);
        addslash(buf);
      }
      if(MYstrcmp(NAME,".") != 0) strcat(buf,NAME);
      if((sysfcb.ff_attrib & 16) != 0) addslash(buf);
      if(findnext(&sysfcb)==0) ++v; else strcpy(buff,buf);
    } else bell();
    goto quit; /* return with file name in buff[] */
  }

  /* s=filename with path split off */
  s[0]=0; i=strlen(buf); if(i>1 && buf[i-1]=='\\') buf[i-1]=0;
  if(tailchar(buf)==':') addslash(buf);
  chdir(buf);
  getwdir(dest);
  addslash(dest);
  v=0;
  k=findfirst(ss,&sysfcb,16);       /* look up again */
  if(flag) {
    strcpy(dirbuff,dest);
/*    if(safecore(&buffer[0],"..\\0 ")){ ++v; *MYstrrchr(buffer[0],'0')=0;} */
  } else j=sysv[0]=0;
  while(k==0) {
    if(flag==0){
      if(j >= COLUMN) {
        putln();
        j=sysv[0]=0;
        if(ccBIOS(2)) break;
      }
      if(COLUMN==1)
       if(NAME[0] == '.' || destdrv != curdrv) strcpy(sysv,dest);
      strcpy(s,NAME);
      if((sysfcb.ff_attrib & 16) != 0) addslash(s);
      if(COLUMN>1) pad15(s);
      strcat(sysv,s); ++j; ++v;
    } else {  /* make file list into array buffer[n] */
      if(MYstrcmp(NAME,".")==0) goto nextone;        
      strcpy(ss,NAME);
      if((sysfcb.ff_attrib & 16) != 0) addslash(ss); 
      if(!squirrel(&buffer[v],ss) || v>=MAXDIRENT-1) goto quit;
      ++v;
    }
nextone:
    k = findnext(&sysfcb);
  }
  if(j && flag==0) putln();
quit:
  if(curdrv != destdrv) {setdrive(destdrv); chdir(destbuf);}
  setdrive(curdrv); chdir(dirbuf);
  return v;
}

void stripstars(buf) char *buf;
{
char *p;
  if((p=MYstrstr(buf,"\\*.*"))!=0 && p[4]==0) p[1]=0;
}

int squirrel(dest,src) char **dest,*src; {
int flag=0;
      strcat(src,"0");   /* make space for status byte */
      if(!safecore(dest,src)) goto quit;
      *MYstrrchr(*dest,'0')=0; *statbyte(*dest)=' ';
      flag=1;
quit:
     return flag;
}

int getbufferlist(s,last) char **s; int *last; {
register
int i,v;
char ss[MAXFNAME];

  for(v=i=0;i<maxinfo;++i){
    if(finfo[i].active) {
      if(i==curfile) last[0]=v;
      ssprintf(ss,"%s%u. %s",(i<10 ? "0":""),i,finfo[i].fname);
      pad15(ss);
      if(!squirrel(&s[v],ss)) goto quit;
      ++v;
    }
  }
quit:
  return v;
}

#endif

/*
 * ETYPE
 *
 * Purpose:
 *              Print a string to console, raw mode.
 */
void etype(s)
char *s;
{
#if (BSDunix || SYS5) && !LCCWIN32
extern FILE *termout;
  if(nodisp) return;
  ewrite(fileno(termout),s,strlen(s));
  fflush(termout);
#endif
#if SYS5 && LCCWIN32
  if(nodisp) return;
  ewrite(fileno(stdout),s,strlen(s));
#endif
#if MSDOS  /* most anything else can do this */
  if(nodisp) return;
  ewrite(2,(char far *)s,strlen(s));    /* fast for turbo-c */
                                        /* no fflush needed */
#endif
}

/*
 * term setup
 *
 * Purpose:
 *              System setup - puts terminal in proper mode
 *              for binary 8-bit I/O.
 */


#if TURBOC
void termDoExit(){
   /* Happens in case of shell out to system or exit() */
    clears(); setcursortype(1); ctrlbreak(1);
#ifdef MOUSEBUILTIN
    mousereset();
#endif
}
#endif


#if BSDunix || SYS5
#if IBMAIX && !LCCWIN32
#if LINUX
#if macOSX
#include <sys/termios.h>
#include <sys/ioctl.h>
#include <sys/ioctl_compat.h>
#else                           
#include <termio.h>
#endif
#else
#include <sys/termio.h>
#include <sys/ioctl.h>
#endif
#endif
#if ARDENT
#include <sys/43ioctl.h>
#endif
#if SUN350
#include <sys/ttold.h>
#include <sys/filio.h>
#define RAW O_RAW
#define ECHO O_ECHO
#endif
#if ALPHA
#include <sys/ioctl.h>
#include <sys/ioctl_compat.h>
#else
#if DEC3100
#include <sys/ttold.h>
#endif
#endif

#if (ARDENT || SUN350 || DEC3100)
FILE *termout=0;
static struct sgttyb tmp1;

void termcanon(){
   tmp1.sg_flags &= ~RAW;
   tmp1.sg_flags |= ECHO;
   ioctl(fileno(stdin), TIOCSETP, &tmp1);
}

void termraw(){ /* Set terminal to raw mode */
   tmp1.sg_flags |= RAW;
   tmp1.sg_flags &= ~ECHO;
   ioctl(fileno(stdin), TIOCSETP, &tmp1);
}

void termSetup(){
/* Get terminal characteristics */
  ioctl(fileno(stdin), TIOCGETP, &tmp1);
  termraw();    /* set it up the way we want it */
#if defined(SIGWINCH)
    signal(SIGWINCH, win_change);
#endif
    termout=fopen("/dev/tty","wb");
}

void termDoExit(){
    fclose(termout); termout=0;
    termcanon();          /* restore terminal settings */
#if defined(SIGWINCH)
    signal(SIGWINCH, SIG_DFL);
#endif
}
#endif

#if IBMAIX && !LCCWIN32
#if macOSX
#include <sys/ioctl.h>
FILE *termout=0;
static struct termios tmp1;
static struct termios tmp2;

void termcanon(){
   tmp1=tmp2;  /* restore terminal settings */
   ioctl(fileno(stdin), TIOCSETAF, &tmp1);
}

void termraw(){
    /* set it up the way we want it */
    tmp1.c_iflag &= ~ICRNL;
    tmp1.c_iflag &= ~IXON;
    tmp1.c_iflag &= ~ISTRIP;    /* Insure 8-bit char input */
    tmp1.c_lflag &= ~ISIG;
    tmp1.c_lflag &= ~ICANON;
    tmp1.c_lflag &= ~ECHO;
#ifdef OPOST
    tmp1.c_oflag |= OPOST;     /* Enable output postprocessing */
    tmp1.c_oflag &= ~ONLCR;    /* Disable map of NL to CR-NL on output */
#endif
#ifdef NLDLY
    tmp1.c_oflag &= ~(NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
 /* No output delays */
#endif
#ifdef ASCEDIT
      tmp1.c_iflag &= ~ASCEDIT;
#endif
      tmp1.c_iflag &= ~IGNBRK;
      tmp1.c_iflag &= ~BRKINT;
#ifdef CDISABLE
      tmp1.c_cc[VERASE] = CDISABLE;       /* disable erase processing */
      tmp1.c_cc[VKILL] = CDISABLE;        /* disable kill processing */
#endif
#ifdef IEXTEN
      tmp1.c_lflag &= ~IEXTEN;      /* Disable other editing characters.  */
#endif
#if 0                           /* Don't matter */
    tmp1.c_cc[VMIN]=1;
    tmp1.c_cc[VTIME]=0;
#endif
    ioctl(fileno(stdin), TIOCSETAF, &tmp1);
}


#endif
#if !macOSX
#include <sys/fcntl.h>
FILE *termout=0;
static struct termio tmp1;
static struct termio tmp2;

void termcanon(){
   tmp1=tmp2;  /* restore terminal settings */
   ioctl(fileno(stdin), TCSETAF, &tmp1);
}

void termraw(){
    /* set it up the way we want it */
    tmp1.c_iflag &= ~ICRNL;
    tmp1.c_iflag &= ~IXON;
    tmp1.c_lflag &= ~ISIG;
    tmp1.c_lflag &= ~ICANON;
    tmp1.c_lflag &= ~ECHO;
#ifdef OPOST
    tmp1.c_oflag |= OPOST;     /* Enable output postprocessing */
    tmp1.c_oflag &= ~ONLCR;    /* Disable map of NL to CR-NL on output */
#endif
#ifdef NLDLY
    tmp1.c_oflag &= ~(NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
 /* No output delays */
#endif
#ifdef ASCEDIT
      tmp1.c_iflag &= ~ASCEDIT;
#endif
      tmp1.c_iflag &= ~IGNBRK;
      tmp1.c_iflag &= ~BRKINT;
#ifdef CDISABLE
      tmp1.c_cc[VERASE] = CDISABLE;       /* disable erase processing */
      tmp1.c_cc[VKILL] = CDISABLE;        /* disable kill processing */
#endif
#ifdef IEXTEN
      tmp1.c_lflag &= ~IEXTEN;      /* Disable other editing characters.  */
#endif
#if 0                           /* Don't matter */
    tmp1.c_cc[VMIN]=1;
    tmp1.c_cc[VTIME]=0;
#endif
    ioctl(fileno(stdin), TCSETAF, &tmp1);
}
#endif

#if CYGWIN
static volatile sig_atomic_t received_signal = 0;
static void
ctlc_handler(int sig)
{
/* mark ctrl-C received, set termio to return from getchar() call. */
        received_signal = 1;
        fcntl(fileno(stdin),F_SETFL,O_NONBLOCK);
        signal(SIGINT, ctlc_handler);
}
#endif

void termSetup(){
/* save terminal characteristics */
#if macOSX
   ioctl(fileno(stdin), TIOCGETA, &tmp1);
#else
   ioctl(fileno(stdin), TCGETA, &tmp1);
#endif
    tmp2 = tmp1;        /* copy structure */
    termraw();          /* Set raw terminal */
#if defined(SIGWINCH)
    signal(SIGWINCH, win_change);
#endif
#if CYGWIN
    signal(SIGHUP, SIG_IGN);
    signal(SIGTERM, SIG_IGN);
    signal(SIGINT, ctlc_handler);
    signal(SIGPIPE, SIG_IGN);
#endif
    termout=fopen("/dev/tty","wb");
    setvbuf(termout, NULL, _IONBF, 0 ); /* no buffering */
}

void termDoExit(){
    if(termout != 0) fclose(termout); termout=0;
    termcanon();
#if defined(SIGWINCH)
    signal(SIGWINCH, SIG_DFL);
#endif
#if CYGWIN
    signal(SIGHUP, SIG_DFL);
    signal(SIGTERM, SIG_DFL);
    signal(SIGINT, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);
#endif
}
#endif /* IBMAIX */
#endif /* BSDunix || SYS5 */


#if IBMAIX && LCCWIN32
#include <io.h>
#include <fcntl.h>

void termcanon(){
}

void termraw(){ /* Set terminal to raw mode */
/*    setmode(fileno(stdin), _O_BINARY); */
}
                  
void termSetup(){
HANDLE get_the_io_handle();
    termraw();
    setcursortype(1);
    signal(SIGINT, SIG_IGN);
    SetConsoleMode(get_the_io_handle(),0); /* Enable ctrl-c passthrough */
}

void termDoExit(){
    signal(SIGINT, SIG_DFL);
}
#endif /* IBMAIX && LCCWIN32 */




/*
 * ccBIOS
 *
 * Purpose:
 *              Direct bios call.
 */
#if MSDOS || BSDunix || SYS5

#if BSDunix || SYS5
#define SAVBUFSIZE 1023
#else
#define SAVBUFSIZE 511
#endif

char savbuf[1+SAVBUFSIZE];
int savcount = 0;
int curchr = 0;
int abortkeycount=0;

unsigned bufget() {
unsigned x;

if(savcount==0) bufput(ccBIOS(0));
while(ccBIOS(2) != 0 && savcount < SAVBUFSIZE) bufput(ccBIOS(0));
x = (unsigned)savbuf[curchr++];
if(curchr >= savcount) curchr=savcount=0;
return x;
}

#if BSDunix || SYS5
#define SLEEP sleep(1)
#endif

void clrcon() {
  abortkeycount=curchr=savcount=0;
  while(ccBIOS(2) != 0) ccBIOS(0);
}

void bufput(x) int x; {
  if(savcount < SAVBUFSIZE) savbuf[savcount++] = x;
  if(x == abortkey) {   /* abortkey == ctrl-U normally */
    if(++abortkeycount <= 2) return; /* Three in a row cause abort */
    curchr=0; macabort();
    savbuf[1]=savbuf[0] = x; savcount = 2;
#ifdef SLEEP
    SLEEP;
#endif
    clrcon();
  }
  abortkeycount=0;
}

#if MSDOS
LLong ccBIOS(f)
int f;
{
static unsigned lastscancode=1;
static unsigned lastlowbyte=1;
unsigned x;
int y;
extern int oldbios;

  switch(f) {
  case 2: return bioskey(1+oldbios);
  case 0:
          if(lastlowbyte==0){
            lastlowbyte=1; return lastscancode;
          }
          while((x=bioskey(1+oldbios))==0) ;
          /* Wait for char available */
          /* handle dark plus, ast and enter keys */
          if((bioskey(2)& 0x20)) goto skipGREY; /* Shift status 00100000 */
          switch(x){                            /* i.e., numlock on */
          case 0x4e2b: /* If grey plus */
            x = GREYplus; break; /* Map to Enter key, "ALT =" */
          case 0x372a: /* if grey asterisk */
            x = GREYast; break; /* Map to Menu key, "CTRL underline" */
          case 0xe00d: /* if grey enter key, enhanced keyboard */
            x = GREYent;  /* Map to Enter key, "ALT ="  */
          }
skipGREY:
          lastscancode = (x >> 8);
          lastlowbyte = (0xFF & x);
          /* Change all extended keys to XT keys */
          if(lastlowbyte == 0xe0 && lastscancode != 0) lastlowbyte=0;
            bioskey(oldbios);  /* get char and bump keybuffer pointers */
          return lastlowbyte;
  case 3: return (255 & bufget());
/*  default: banner("ccBIOS not implemented\n"); */
  }
  return 0;
}
#endif

#if (BSDunix || SYS5) && !LCCWIN32
#if CYGWIN
LLong ccBIOS(f)
int f;
{
long ln;
unsigned got;
  switch(f) {
  case 2:
          return (LLong)(0);
  /* Ctrl-C unblocked termio, so block it again, fetch char */
  case 0:
again:    got=getchar();
          if(received_signal){
            received_signal=0; got=3; fcntl(fileno(stdin),F_SETFL,0);
          }
          if(got == -1) goto again;
          return (LLong)(255&(unsigned)got);
  case 3: return (LLong)(255&(unsigned)bufget());
  default: banner("ccBIOS not implemented\n");
          return 0;
  }
}
#else
LLong ccBIOS(f)
int f;
{
long ln;
  switch(f) {
  case 2:
          ioctl(fileno(stdin), FIONREAD, &ln);
          return (LLong)ln;
  case 0:
          return (LLong)(255&(unsigned)fgetc(stdin));
  case 3: return (LLong)(255&(unsigned)bufget());
  default: banner("ccBIOS not implemented\n");
          return 0;
  }
}
#endif
#endif

#if  SYS5 && LCCWIN32
#include <conio.h>
LLong ccBIOS(f)
int f;
{
LLong ln;
  switch(f) {
  case 2:
          ln=(LLong)(_kbhit() ? 1 : 0);
          return (LLong)ln;
  case 0:
          return (LLong)(255&(unsigned)getchGNU());
  case 3: return (LLong)(255&(unsigned)bufget());
  default: banner("ccBIOS not implemented\n");
          return 0;
  }
}
#endif

#endif


int testkey(){
extern int savcount,curchr,halfdone;
  while(ccBIOS(2) != 0) bufput(ccBIOS(0));
  return savcount;
}

#if !HASMOVEMEM
/*
 * MOVEME
 *
 * Purpose:
 *              Memory move without overlap problems.
 */
#if TURBOC
void moveMEM(dest,source,num)
char *dest,*source;
LLong num;
{
  movmem(source,dest,num);
}
#endif

#if !TURBOC
void moveMEM(dest,source,num)
char *dest,*source;
LLong num;
{
char *d,*s;
LLong n;
  d = dest; s = source; n = num;
  if(d <= s || s+n <= d) {
#if HASMEMCPY
    memcpy(d,s,n);
#else
    while(n--) *d++ = *s++;
#endif
  }
  else {
    d += n;
    s += n;
    while(n--) *--d = *--s;
  }
}
#endif
#endif

#if !MSDOS

FILEP fopenbread(s) char *s; {
  return fopen(s,RMODE);
}

FILEP fopenbwrite(s) char *s; {
  return fopen(s,WMODE);
}
     
int closedfile(fp) FILEP fp; {
  return fclose(fp);
}
#endif

#if MSDOS
FILEP fopenbread(s) char *s; {
  return fopen(s,RMODE);
}

FILEP fopenbwrite(s) char *s; {
  return fopen(s,WMODE);
}

closedfile(fp) FILEP fp; {
  return fclose(fp);
}
#endif

#if BSDunix || SYS5

int updateWinsize=0;

void
win_change(sig)
int     sig;
{
extern int updateWinsize;
#if defined(SIGWINCH)
        signal(SIGWINCH, SIG_IGN);
        updateWinsize=1;        /* Schedule an update */
        signal(SIGWINCH, win_change);
#else
        updateWinsize=1; bell();
#endif
}

void resetWinSize(rrow,ccol)int *rrow,*ccol; {
int Rows,Cols;
extern int MAXLN,COLMAX,ROWMAX,updateWinsize;

# if defined(TIOCGWINSZ)
struct winsize win;
    Rows=Cols=0;
    if(ioctl (0, TIOCGWINSZ, &win) == 0){
      if (win.ws_col) Cols = win.ws_col-1;
      if (win.ws_row) Rows = win.ws_row-1;
    }
#else
    Rows=Cols=0;
    banner("TIOCGWINSZ not defined"); sleep(1);
#endif
    if (Rows <= 0 || Cols <= 0){
       if(rrow[0]<=0 && ccol[0] <= 0){
         banner("Must know the screen size, assuming 24x80");
         bell(); sleep(1);
         Rows=24; Cols=80;
       } else { Rows=rrow[0]; Cols=ccol[0];}
    }
    if(MAXLN <= Cols) Cols=MAXLN-10;
    Cols--;   /* ULTRIX wraps in last column, safety factor 1 */
    Rows--;   /* don't use final line, in case it is terminal status */
                /* line (e.g. line 25 on VT100 display) */
                /* ULTRIX scrolls when cursor enters last line */

    updateWinsize=0;
    rrow[0]=Rows; ccol[0]=Cols;
}

int doWinUpdate(){
  return updateWinsize;
}
#endif
/*
 * V9.C
 *
 */

#include "pie.h"

/*
 * TERMINAL-DEPENDANT SOURCE CODE
 *
 * Conventions:
 *
 *      ROWMAX   -  The number of lines on the screen minus 1.
 *      Rows are numbered 0 to ROWMAX.
 *
 *      COLMAX   -  The number of columns on the screen minus 1.
 *      Columns are numbered 0 to COLMAX.
 *
 *      MAXLN    -  The length in characters of the longest
 *      storable string for a screen line.
 *
 */

/*
 * QUIT
 *
 * Purpose:
 *              Elegant exit
 * Notes:
 *              o Cleans up the special keypad settings.
 *              o Clears the screen.
 *              o Exits without a file save.
 */
#if TURBOC
void quit() {
  termDoExit();
  XMSfree();
  _exit(0);
}
#endif

#if BSDunix || SYS5
#if IBMAIX && !LCCWIN32
#include <sys/ioctl.h>
#endif
#if ARDENT
#include <sys/43ioctl.h>
#endif

#if SYS5
void quit(n) int n;{
  if(n==100){termDoExit(); exit(0);} /* n=0 is init, n=1 normal, n=100 abort */
  exitterminal();
  termDoExit();
  if(n==2){
#if !defined(SIGTSTP) || !defined(SIGSTOP)
#define SIGTSTP 18      /* stop signal from tty */
#define SIGSTOP 19      /* stop signal from tty */
#endif
#if !LCCWIN32
    kill(0, SIGSTOP);
#endif
    setup();
    initterminal();
    return;
  }
  exit(0);
}
#endif

#if BSDunix
void quit(n) int n;{
#if !defined(SIGTSTP)
#define SIGTSTP 18      /* stop signal from tty */
#endif
  exitterminal();
  termDoExit();
  /* n=0 is init, n=1 normal, n=100 abort */
  if(n==2){
#if SOLARIS
    killpg(getpgrp(), SIGTSTP);
#else
    killpg(getpgrp(0), SIGTSTP);
#endif
    setup();
    initterminal();
    return;
  }
  exit(0);
}
#endif


#endif

/*
 * INITTERMINAL
 */
#if PIINILOAD && TERMCAP

#if TURBOC
void initterminal(){
  etputs(rs_termcap,1);
#ifdef MOUSEBUILTIN
  if(mousereset()) mousehook(); /* Hook mouse interrupt if hsens nonzero */
#endif
  clearscreen();
  etputs(ks_termcap,1);
  /* Fixup for bad DEC termcap (they invented the vt100?) */
  if(MYstrcmp(ks_termcap,"\033[?1h")==0) etputs("\033=",1);
}

void exitterminal(){
 statblank=TRUE;
 setcolorsUNIX(4);
 drawall();
 clearlines(-1);
 etputs(rs_termcap,1);  etputs(ke_termcap,1);
}
#endif

#if (BSDunix || SYS5) && !LCCWIN32
void initterminal(){
  etputs(rs_termcap,1);
  if(mouseexists() && mousekeys()) setmouse(1);
  setansicolors();                    /* Set up screen colors */
  setcolorsUNIX(1);
  clearscreen();
  etputs(ks_termcap,1);
  /* Fixup for bad DEC termcap (they invented the vt100?) */
  if(MYstrcmp(ks_termcap,"\033[?1h")==0) etputs("\033=",1);
}

void exitterminal(){
 if(mouseexists()) setmouse(0);
/*  setcolorsUNIX(0); */
/* clearscreen(); */
 statblank=TRUE;
 setcolorsUNIX(4);
 drawall();
/* clearlines(-2); */       /* Erase one-by-one */
 clearlines(-1);
 etputs(rs_termcap,1);  etputs(ke_termcap,1);
}
#endif

#else

#if TURBOC
void initterminal(){
#ifdef MOUSEBUILTIN
  if(mousereset()) mousehook(); /* Hook mouse interrupt if hsens nonzero. */
#endif
}
void exitterminal(){}
#endif

#if SYS5 && LCCWIN32
void initterminal(){ setansicolors(); clrscr();}
void exitterminal(){textmode(LASTMODE); clrscr(); setcursortype(1);}
#endif

#if (BSDunix || SYS5) && !LCCWIN32
void initterminal(){clearscreen();}
void exitterminal(){clearscreen();}
#endif


#endif

#if (BSDunix || SYS5) && !LCCWIN32
  /* Set background and foreground colors */
int syscolor=1;

void setcolorsUNIX(n) int n; {
syscolor=n;
  /* Reset foreground and background colors to default */
  switch(n){
  case 2:
  case 0:  etype("\033c"); etype(UNIXdefaultfgbg);
           break;
  /* Set edit screen foreground and background colors */
  case 1:  etype(UNIXtextfgbg); break;
  /* Set foreground and background colors of popup windows */
  case 3:  etype(UNIXmenufgbg); break;
  case 4: /* etype("\33[1m\33[4m\33[7m\33[0;0m"); */
    freeup(UNIXtextfgbg);
    safecore(&UNIXtextfgbg,"\33[0;0m");
    break;
  }
}

void setansicolors() {
char *s;
  if(ansimode) {
    ssprintf(sysv,"\033[0;%d;%dm",textfgcolor,textbgcolor);
    freeup(UNIXtextfgbg);
    safecore(&UNIXtextfgbg,sysv);
    ssprintf(sysv,"\033[0;%d;%dm",menufgcolor,menubgcolor);
    freeup(UNIXmenufgbg);
    safecore(&UNIXmenufgbg,sysv);
    UNIXdefaultfgbg="\033[0m";
  } else {
    UNIXtextfgbg=UNIXmenufgbg=UNIXdefaultfgbg="";
  }
}
#endif

/*
 * SETUP
 *
 */
#if TURBOC
void setup(){
  equipment();     /* find color/mono, set whole screen to black! */
  ctrlbreak(0);           /* save state, set no ctrl-break */
  nearmemorybaseinit();  /* Get near memory base, just once */
}
#endif

#if BSDunix || SYS5
void setup(){termSetup();}
#endif

/*
 * CLEARS
 *
 * Purpose:
 *              Screen clear and cursor home.
 * Notes:
 *              o An erase page takes time.
 *              o We must clear the background on the VT52
 *                to insure highlighted areas are erased.
 */
#if (BSDunix || SYS5) && !LCCWIN32
void cleardisplay() {
  clearlines(0);
}
void clearlines(n) int n; {
int i;
int flag;
  fflush(stderr);
  setcolorsUNIX(1);
  if(cd_termcap[0]){
    putcursor(n,0);
    etputs(cd_termcap,1);
  }
  flag = (n < -1 || cd_termcap[0]==0);
  if(n<-1) n= -1;
  if(flag){ /* must erase lines one by one */
    for(i=0;i<=ROWMAX;++i){putcursor(n+i,0); eraseol();}
    putcursor(n,0);
  }
}
void clearscreen() {
  clearlines(-1);
}
#endif

#if SYS5 && LCCWIN32
#include <tcconio.h>
void putcursor(rrow,ccol) int rrow,ccol; {
extern void gotoxy();
  gotoxy(ccol+1,rrow+1+1);
}

void dellin(){
  delline();
}

void inslin(){
  insline();
}

void eraseol(){
  setLCCWIN32colors();
  clreol();
}

void cleardisplay() {
int i;
  clearlines(0);
}

void clearlines(n) int n; {
int i;
  if(n<-1) n= -1;
  /* must erase lines one by one */
  setLCCWIN32colors();
  for(i=0;i<=ROWMAX;++i){putcursor(n+i,0); clreol();}
  putcursor(n,0);
}

void clearscreen() {
  setLCCWIN32colors();
  clrscr();
}

int getLCCWIN32color(ansicode,defaultcolor) int ansicode,defaultcolor;{
  switch(ansicode-1){
  case 0: return BLACK;
  case 1: return BLUE;
  case 2: return GREEN;
  case 3: return CYAN;
  case 4: return RED;
  case 5: return MAGENTA;
  case 6: return BROWN;
  case 7: return LIGHTGRAY;
  case 8: return DARKGRAY;
  case 9: return LIGHTBLUE;
  case 10: return LIGHTGREEN;
  case 11: return LIGHTCYAN;
  case 12: return LIGHTRED;
  case 13: return LIGHTMAGENTA;
  case 14: return YELLOW;
  case 15: return WHITE;
  };
  return defaultcolor;
}

  /* Set background and foreground colors */
int syscolor=1;
int lccfg=BLACK, lccbg=LIGHTCYAN, lccmenubg=BLUE, lccmenufg=WHITE;


void setcolorsUNIX(n) int n; {
}

void setansicolors() {
  if(ansimode==2) {
    lccfg = getLCCWIN32color(textfgcolor,BLACK);
    lccbg = getLCCWIN32color(textbgcolor,LIGHTCYAN);
    lccmenufg = getLCCWIN32color(menufgcolor,WHITE);
    lccmenubg = getLCCWIN32color(menubgcolor,BLUE);
  } else {
    lccfg=BLACK; lccbg=LIGHTCYAN; lccmenubg=BLUE; lccmenufg=WHITE;
  }
}

void setLCCWIN32colors(){
  textbackground(lccbg); textcolor(lccfg);
}

void setLCCWIN32menucolors(){
  textbackground(lccmenubg); textcolor(lccmenufg);
}

void setLCCWIN32inversecolors(){
  textbackground(lccfg); textcolor(lccbg);
}

void home(){
  putcursor(-1,0);
}

void killstatus(){
  statblank = TRUE; home();
/*  if(syscolor!=1) setcolorsUNIX(1); */
eraseol();
}

#endif

/*
 * CRLF
 *
 * Purpose:
 *              Issue carriage return and linefeed.
 * Notes:
 *              o Some terminals have auto-linefeed switches.
 *                If you can't turn it off in hardware, try
 *                to fix it here.
 */
void crlf(){
  etype("\r\n");
}

/*
 * LINEDE
 *
 * Purpose:
 *              Delete line at current cursor position.
 * Notes:
 *              o Needs long delay with some terminals to insure
 *                delete command gets completed.
 */
#if 0           /* This is the prototype for old terminals */
void linedelete(){
  etype(dl_termcap);
}
#endif
#if MSDOS || LCCWIN32
void linedelete(){
  if(row == ROWMAX) { putcursor(row,0); eraseol(); }
  else dellin();
}
#endif
#if (BSDunix || SYS5) && !LCCWIN32
void linedelete(){
int i;
extern char *toprint();
  if(row==ROWMAX) goto quit;
  if(HASDELETELINE) etputs(dl_termcap,1);
  else {
    if(row == 0 && HASSCROLL == 0) {
      fwdindex(); statblank = TRUE; lcount();
    }
    else {
      if(HASSCROLL) {
        setscroll(1+row);
        putcursor(ROWMAX,0);
        etputs(sf_termcap,ROWMAX-row);
        setscroll(0);
        curses();
      } else {
        for(i=row;i<=ROWMAX;++i) {
          putcursor(i,0); eraseol();
          if(i+1<=ROWMAX) ePrint(toprint(line[i+1]));
        }
      }
    }
  }
quit:
  putcursor(ROWMAX,0); eraseol();
  putcursor(row,col);
}

/*
 * ERASEO
 *
 * Purpose:
 *              Erase to end of line from current cursor position.
 */
void eraseol(){
  etputs(ce_termcap,1);
}

/*
 * KILLST
 *
 * Purpose:
 *             Erase the STATUS line
 */
void killstatus(){
  statblank = TRUE; putcursor(-1,0);
  if(syscolor!=1) setcolorsUNIX(1);
eraseol();
}
#endif

/*
 * BELL
 *
 * Purpose:

 *             Ring terminal bell.
 */
#if TURBOC
void bell(){
/*  etype("\7"); */
 if(!nodisp) shortclick(135,15,10000);
}
#endif

#if SYS5 && LCCWIN32
void bell(){
  etype("\7");
}
#endif

#if (BSDunix || SYS5) && !LCCWIN32
void bell(){
  etputs(bl_termcap,1);
}

/*
 * PUTCUR
 *
 * Purpose:
 *              Place cursor on specified row and column.
 * Notes:
 *              No error check for on-screen.
 */
#if 0           /* prototype for vt52 terminal */
void putcursor(rrow,ccol) int rrow,ccol; {
char s[5]={'\033','Y','0','0',0};
  /* VT52 style custom cursor */
  s[2]=(rrow+' '+1);
  s[3]=(ccol+' ');
  etype(s);
}
#endif

void putcursor(rrow,ccol) int rrow,ccol; {
extern char *tgoto();
  etputs(tgoto(cm_termcap,ccol,rrow+1),1);
}

/*
 * HOME
 *
 * Purpose:
 *              Put cursor at home position (0,0).
 * Notes:
 *              This is the physical home position,
 *              which may not be the first text line.
 */
void home(){
  putcursor(-1,0);
}
#endif

/*
 * LINEIN
 *
 * Purpose:
 *              Insert a line with hardware command.
 */
#if MSDOS || LCCWIN32
void lineinsert(){
  if(row == ROWMAX) {
    putcursor(row,0); eraseol();
  } else
  inslin();
}
#endif

#if 0   /* VT52 prototype */
void lineinsert(){
int i;

  if(row == 0) {
    revindex();
  } else
  if(row == ROWMAX) {
    putcursor(row,0); eraseol();
  }
  else {
    etype(al_termcap);
    putcursor(row,0);
  }
}
#endif

#if (BSDunix || SYS5) && !LCCWIN32
void lineinsert(){
int i;
extern char *toprint();

  putcursor(ROWMAX,0); eraseol();
  putcursor(row,0);
  if(row == 0) {
    revindex();
  } else {
    if(HASINSERTLINE) {
      etputs(al_termcap,1); eraseol();
    } else {
      if(HASSCROLL) {
        setscroll(1+row);
        putcursor(row,0);
        etputs(sr_termcap,ROWMAX-row);
        setscroll(0);
      } else {
        putcursor(i = row,0); eraseol();
        for(i = row+1;i<=ROWMAX;++i) {
              putcursor(i,0);
              eraseol();
              ePrint(toprint(line[i-1]));
        }
      }
    }
  }
  putcursor(row,0);
}

/*
 * SETSCR
 *
 * Purpose:
 *              Set scroll region for a VT100 terminal
 *              From row=r to ROWMAX
 *              Uses low level termcap routines (source included)
 */
void setscroll(r) int r; {
char buf[128];
extern char *tgoto();
  etputs(tgoto(cs_termcap,ROWMAX+1,r),1);
}
#endif

/*
 * REVIND
 *
 * Purpose:
 *              Performs a hardware reverse index to put a blank line
 *              on the top of the screen. Causes all current lines on
 *              screen to be shifted down one, with the last line lost.
 *              The status line is reprinted.
 */
#if MSDOS
void revindex(){
  putcursor(0,0); inslin();
  lcount();
}
#endif

#if SYS5 && LCCWIN32
void revindex(){
  putcursor(0,0); inslin();
  lcount();
}

void fwdindex(){
    putcursor(0,0); dellin();
    putcursor(ROWMAX,0); eraseol();
}

void invvideo(x) int x; {
char *p;
char buf[2];
  buf[0] = x + '@'; buf[1]=0;
  setLCCWIN32inversecolors();  etype(buf); setLCCWIN32colors();
}

void erachar(){
  etype("\b\040\b");
}
#endif

#if (BSDunix || SYS5) && !LCCWIN32
void revindex(){
int i;
extern char *toprint();

   putcursor(ROWMAX,0); eraseol();
  if(HASINSERTLINE) {
    putcursor(0,0);
    etputs(al_termcap,1); eraseol();
  } else {
    if(HASSCROLL){
       setscroll(1); putcursor(0,0);
       etputs(sr_termcap,ROWMAX-1); setscroll(0);
    } else {
      clearscreen(); putcursor(0,0);
      for(i=0;i<ROWMAX;++i) {
        crlf(); ePrint(toprint(line[i]));
      }
    }
  }
  lcount();
}

/*
 * FWDIND
 *
 * Purpose:
 *
 *              Performs a hardware forward scroll. The top line is
 *              lost and a new blank line is issued at ROWMAX.
 */
void fwdindex(){
  if(HASDELETELINE) {
    putcursor(0,0); etputs(dl_termcap,1);
    putcursor(ROWMAX,0); eraseol();
  } else {
    if(HASSCROLL){
      setscroll(1); putcursor(ROWMAX,0);
      etputs(sf_termcap,ROWMAX-1); setscroll(0);
    } else {
     killstatus(); putcursor(ROWMAX,0); crlf();
    }
  }
}

/*
 * INVVID
 *
 * Purpose:
 *              Output a byte in inverse video. This may be
 *              implemented as background or foreground on certain
 *              terminals.
 * Notes:

 *              o We assume 0 <= x < 32 so that x+'@' is printable.
 */
void invvideo(x) int x; {
char *p;
char buf[256]; char *endof();
  strcpy(buf,REVVIDEO); p=endof(buf); *p++ = x + '@';
  if(UNIXtextfgbg[0]==0) strcpy(p,NORMVIDEO);
  else
  strcat(buf,UNIXtextfgbg);
  etype(buf);
}

/*
 * ERACHA
 *
 * Purpose:
 *              Erase character left of cursor
 *
 * Notes:
 *              o Usually, "\b\40\b" will work.
 *                Termcap uses BC for the backspace char.
 */
void erachar(){
  etype(ERACHR);
}
#endif

/*
 * BANNER
 *
 * Purpose:
 *              Print a banner on the STATUS line, start col=0.
 * Notes:
 *              Erases the STATUS line first.
 *              Types the message s[] starting at the first
 *              column of the STATUS line.
 *              DOES NOT restore the cursor. Use curses().
 */
void banner(s) char *s; {
  killstatus(); ePrint(s);
}

void bbanner(s1,s2) char *s1,*s2; {
 banner(s1); ePrint(s2);
}

/*
 * NOROOM
 *
 * Purpose:
 *              Print NO ROOM message on STATUS line.
 * Notes:
 *              Restores cursor.
 *              Returns FALSE.
 */
int noroom() {
  banner("Out of room"); bell(); curses();
  return FALSE;
}

/*
 * CURSES
 *
 * Purpose:
 *             Restore cursor to its saved position.
 * Notes:
 *             Uses global variables row & column.
 */
void curses() {
  putcursor(row,col);
}
/*
 * V10.C
 */
/*  This file contains TERMCAP code for DOS and UNIX machines, and
 *  any other operating system that can support terminal types. No
 *  termcap databases are supplied, except for DOS, because it is
 *  preferable to get the termcap file from the local system. Warning:
 *  DOS and WIN32 systems cannot use terminal types unless they run
 *  underneath a terminal emulator like ansi.sys.
 *
 *  This file contains KEYMAP code for mapping keys on startup and
 *  also on the fly. The keymap code does not use TERMCAP.
 *
 */

#include "pie.h"

#if PIINILOAD

#undef BUFSIZE
#define BUFSIZE 1536

#if BSDunix || SYS5
#if LCCWIN32
#define TERMFILE  "/usr/lccpie/termcap"
#ifndef PIEROOT
#define PIEROOT "/usr/lccpie/"
#endif
#else
#define TERMFILE  "/etc/termcap"
#ifndef PIEROOT
#define PIEROOT "/usr/local/lib/pie/"
#endif
#endif
char *SETEMACS=0;
char *SETWDSTAR=0;
char *SETWDPERF=0;
char * SRCDIR=0;
char * VT100key=0;
char * KEYMAP=0;
char * SYSUSERFILE=0;
char * USERFILE=0;
char * DUMPKEY=0;                             
char * PIMACROS=0;
char * PIHLPDOC=0;              
                    
#define PIDUMPKEY  "pi-dump.key"
#define PIMACROKEY "pi-macro.key"
#define PISET      ".pirc"
#define LOCALPISET "./.pirc"
struct dataset {char *name; char **addr;};
struct dataset unixnames[]={
{"emacs.keys", &SETEMACS},
{"wdstar.keys", &SETWDSTAR},
{"wdperf.keys", &SETWDPERF},
{"keymap.dat",&KEYMAP},
{"SYSpi.rc",&SYSUSERFILE},
{"pihelp.htm",&PIHLPDOC}
};
#define SIZEunixnames sizeof(unixnames)/sizeof(unixnames[0])
      
void getprogramname(s) char *s; {
extern char *progname;
char *p;
#if !(LCCWIN32 || CYGWIN)
extern char *realpath();
  if(realpath(progname,s) ) return;
  if( realpath("/bin/pi",s) ) return;
  if( realpath("/usr/local/bin/pi",s) ) return;
#endif
  strcpy(s,PIEROOT); addslash(s); strcat(s,"pi");
}

/* Create file names with HOME as the directory */
/* Unix has the trouble of no write permission in various places */
/* So the files have to be kept in a read/write location */
/* File ./.pirc is special. If it exists, then assume it is read/write. */
/* Always, pi-macro.key is written to the current directory, but that */
/* can fail. */
void setfiledefaults(){
char *getenv(),*endof(),*ffname();
char *p;
int i;
char homedir[MAXFNAME];
  /* source ./pi-macro.key */
  newcore(&PIMACROS,"pi-macro.key");
                    
  /* source $HOME/pi-dump.key */
  p=getenv("HOME");
  strcpy(homedir,(p? p:"."));
  addslash(homedir);
  p=endof(homedir);
  strcpy(p,PIDUMPKEY); newcore(&DUMPKEY,homedir);

  /* sources ./.pirc or  $HOME/.pirc */
#if LCCWIN32
  if(classify(LOCALPISET)==1) strcpy(homedir,"./");
  else strcpy(homedir,PIEROOT);  /* Use the system-wide .pirc */
  addslash(homedir);
  strcat(homedir,PISET); newcore(&USERFILE,homedir);
#else
  if(classify(LOCALPISET)==1) {strcpy(homedir,"./"); p=endof(homedir);}
  strcpy(p,PISET); newcore(&USERFILE,homedir);
#endif

  /* Find the SRCDIR by lookup of the program name. */
  getprogramname(homedir);       /* Worst case is PIEROOT */
  *ffname(homedir)=0;            /* Strip off the exe name */
  if(!SRCDIR) newcore(&SRCDIR,homedir);

  /* source $PIE/keymap.dat and others */
  strcpy(homedir,SRCDIR); addslash(homedir); p=endof(homedir);
  for(i=0;i<SIZEunixnames;++i){
    strcpy(p,unixnames[i].name);
    newcore(unixnames[i].addr,homedir);
  }
}

void queryLocalPiSet(){
char *p;
  ssprintf(sysvv,"%s%s",cwd(),ffname(USERFILE));
  if(classify(sysvv)==1) return;
  if(getyesno("Create local .pirc"))
    if(CreateFILEcopy(USERFILE,sysvv)) newcore(&USERFILE,sysvv);
}                   
#endif

#if MSDOS
#define DUMPKEY   "pi-dump.key"
#define PIMACROS  "pi-macro.key"
char * SRCDIR=0;
char * KEYMAP;
char * USERFILE;
char *PIHLP="pihelph";

struct dataset {char *name; char **addr;};
struct dataset dosnames[]={
{".dat",&KEYMAP},
{".set",&USERFILE}
};
#define SIZEdosnames sizeof(dosnames)/sizeof(dosnames[0])

void setfiledefaults(){
char *ffname(),*endof(),*MYstrchr();
char *p;
int i;
char homedir[MAXFNAME];
char dest[MAXFNAME];
  getprogramname(homedir);
  strcpy(dest,ffname(homedir));
  if((p=MYstrchr(dest,'.'))!=(char *)0) p[0]=0;
  *ffname(homedir)=0;
  if(!SRCDIR){
    newcore(&SRCDIR,homedir);
  } else strcpy(homedir,SRCDIR);
  addslash(homedir);
  strcat(homedir,dest);
  p=endof(homedir);
  for(i=0;i<SIZEdosnames;++i){
    strcpy(p,dosnames[i].name);
    newcore(dosnames[i].addr,homedir);
  }
}                   

void queryLocalPiSet(){
char *p;
  ssprintf(sysvv,"%s%s",cwd(),ffname(USERFILE));
  if(classify(sysvv)==1) return;
  if(getyesno("Create local .pirc"))
    if(CreateFILEcopy(USERFILE,sysvv)) newcore(&USERFILE,sysvv);
}

#endif


/* find file name after first colon, if any */
char *
fpick(pat,s)
char *pat;      /* pattern to match */
char *s;        /* string to search */
{
   s=sob(s);   /* 2002: found error of not skipping over spaces */
   if(MYstrncmp(s,pat,strlen(pat))==0){
        s=skipto(":",s);
        if(s[0] && s[1]) return s+1;
   }
   return "";
}

/* Use core() to build memory blocks not to be released by free() */
#if MSDOS
char *core(n) int n;{
char *p;
  if((p=(char *)sbrk(n))==(char *)-1){
    etype("Out of memory"); quit(100);
  }
  fillchr(p,0,n);
  return p;         
}
#else                   /* DEC3100 needs word alignment - use malloc() */
char *core(n) int n;{
char *p;
  if((p=(char *)malloc(n))==(char *)0){
    etype("Out of memory"); quit(100);
  }
  fillchr(p,0,n);
  return p;
}
#endif

/* Get core, copy string */
void newcore(d,s) char **d; char *s; {
  strcpy(d[0]=core(strlen(s)+1),s);     /* Uses sbrk() under MSDOS */
}

int safecore(d,s) char **d; char *s; {
  if((d[0]=(char *)malloc(strlen(s)+1))==(char *)0) return 0;
  strcpy(d[0],s);
  return 1;
}                   

void fartonearstrcpy(d,s) char *d; char far *s;
{
  while((*d++ = *s++) != 0) ;
}


#if MSDOS
void far * farcore(n) unsigned long n; {
void far *p;
  if((p=(void far *)farmalloc(n))==0){
    bellpressany("farmalloc failed");
    exit(0);
  }
  return p;
}

int farsafecore(d,s) char far **d; char *s; {
char far *p;
  if((p=(char far *)farmalloc((unsigned long)strlen(s)+1))==(char far
  *)0){
    bellpressany("farmalloc failed");
    return 0;
  }
  d[0]=p;
  while((*p++ = *s++) != 0) ;
  return 1;
}


#else
int farsafecore(d,s) char far **d; char far *s; {
  return safecore(d,s);
}

#endif

void getcorememblock(){
int i;
#if !MSDOS
    memmark=(char *)core(MAXBLOCK); /* core() nulls out the memory block */
    meminfo=(struct bucket *)core(sizeof(struct bucket)*MAXBLOCK);
    memblock=(char far **)core(sizeof(char far *)*MAXBLOCK);
#else
    memmark=farcore((unsigned long)MAXBLOCK);
    for(i=0;i<MAXBLOCK;++i) memmark[i]=0;
    meminfo=farcore((unsigned long)sizeof(struct bucket)*MAXBLOCK);
    memblock=farcore((unsigned long)sizeof(char far *)*MAXBLOCK);
#endif
}

#if MSDOS
#if defined(__SMALL__) || defined(__MEDIUM__) || defined(__LARGE__)
/* TURBOC. Used for debugging */

static char *watermark=0;

void nearmemorybaseinit(){
 if(watermark==0) watermark=(char *)sbrk(0);
}
#endif
#endif


#if MSDOS
void freeup(s) char *s; {
extern void free();
  /* Try not to "free" a memory address for compiled-in data */
  if(s!=(char *)0 && s[0]!=0 && (long)s >= (long)watermark) free(s);
}
#else
void freeup(s) char *s; {
extern void free();
  if(s!=(char *)0 && s[0]!=0) free(s);
}
void farfree(s) void far *s; {
  freeup((char far *)s);
}
#endif

/* copy string to 127 bytes, null terminate */
void grabtoken(d,s) char *d,*s;{
char *fnb();
  moveMEM(d,s,127); d[127]=0; *fnb(d)=0;
}

void strnzcpy(d,s,n) char *d,*s; int n; {
  moveMEM(d,s,n); d[n]=0;
}



#if MSDOS
#if defined(__SMALL__) || defined(__MEDIUM__)
/* TURBOC. Used for debugging */
void displaymemoryleft(){           /* Debugging memory */
char s[128];
extern unsigned coreleft();  /* small and medium models only TURBOC */
lltoa((LLONG)farcoreleft(),s);  /* LLONG is long for MSDOS */
ssprintf(sysv,"Far memory = %s, Low memory=%u",s,coreleft());
ePrint(sysv); crlf();
lltoa((LLONG)MAXBLOCK*SIZEBLOCK,s);  /* LLONG is long for MSDOS */
ssprintf(sysv,"File size max = %s bytes",s);
ePrint(sysv); crlf();
}
#endif
#endif
#if !MSDOS
void displaymemoryleft(){           /* Debugging memory */
#if BSDunix || LINUX
char s[256];
sprintf(s,"File size max = %lu bytes",(long)farcoreleft());
ePrint(s); crlf();
getusageBSD(s);
ePrint(s); crlf();
#endif
}
#endif

int CreateFILEcopy(src,dest) char *src,*dest; {
FILE *fi; FILE *fp; char buff[512]; int status=0;
  if(!MYstrcmp(src,dest)) goto quit; /* name conflict */
  fi=fopenbread(src);
  if(!fi) goto quit;
  fp=fopenbwrite(dest);
  if(fp){
    while(fgetLN(buff,fi)>0) fputsline(buff,fp);
    closedfile(fp);
    status=1;
  }
  closedfile(fi);
quit: return status;
} 

/* Load global variables file to set dynamic dimensions */
void loadinifile(argc,argv) int argc; char **argv; {
char *p,*q;
int i,flag,flagsrc;
char s[128];
extern char *ffname();
extern char *fnb();
    /* should be able to pass resource file -set: on command line */
    /* (-dir:xxx -key:xxx -set:xxxx -term:xxxxx). Environment=defaults. */
/*
 * Ready to load up the resource files
 */
   flag=flagsrc=0;
   for(i=1;i<argc;++i){ /* scan command line for -dir: */
     getsetupsDIR(argv[i],&flagsrc);
   }
   /* hit environment to get more, but don't override command line */
   if(SRCDIR==0 || SRCDIR[0]==0){
     if((p=getenv("PIEINIT"))==(char *)0) p="";
     while(p[0]){
       getsetupsDIR(p,&flagsrc);
       p=fnb(p);
     }
   }
   setfiledefaults();   /* All file names determined */
   for(i=1;i<argc;++i){ /* scan command line for -set: */
     getsetupsSET(argv[i],&flag);
   }
   /* hit environment to get more, but don't override command line */
   if(USERFILE==0 || USERFILE[0]==0){
     if((p=getenv("PIEINIT"))==(char *)0) p="";
     while(p[0]){
       getsetupsSET(p,&flag);
       p=fnb(p);
     }
   }
   if(flag) return;
   /* Get all integer setup variables. No keys yet. */
#if MSDOS
   if(classify(ffname(USERFILE))==1)
     loadrc(ffname(USERFILE));  /* File in connected directory */
   else
     loadrc(USERFILE);          /* $HOME or connected directory */
#else      /* unix */
   if(classify(ffname(USERFILE))==1) /* Get setup from connected dir */
     loadrc(ffname(USERFILE));
   else
   if(!loadrc(USERFILE)){
     loadrc(SYSUSERFILE);   /* Otherwise use system default */
     /* Try to copy SYSUSERFILE to USERFILE */
     CreateFILEcopy(SYSUSERFILE,USERFILE);
   }
#if LCCWIN32
   if(boxcharset == 0) boxcharset =1;
#endif
#endif
}                              

void getsetupsDIR(p,flagsrc) char *p; int *flagsrc; {
char *q;
     if(*(q=fpick("-d",p))!=0){
       newcore(&SRCDIR,q);
       flagsrc[0]=(SRCDIR[0]==0) ? 0 : 1;
     }
}

void getsetupsSET(p,flag) char *p; int *flag; {
char *q;
     if(*(q=fpick("-s",p))!=0){
       if(!flag[0]){
         if((flag[0] = loadrc(q))!=0){
           newcore(&USERFILE,q);
           flag[0]=(USERFILE[0]==0) ? 0 : 1;
         }
       }
     }
}

/* Load termcap entry for user terminal */
#if TERMCAP
static char    *eebuf = (char *)0;       /* pointer to entry buffer */

char mybuf[BUFSIZE];            /* Put termcap entry into here */
void loadtcap(argc,argv) int argc; char **argv; {
char *p,*q;
int i,stat;
char s[128];
    /* should be able to pass terminal type on command line */
    /* (-key:xxx -set:xxxx -term:xxxxx). Use environment for defaults. */

    s[0]=0;
    for(i=1;i<argc;++i) {       /* User switch -term: on command line? */
      if(*(q=fpick("-t",argv[i]))!=0) strcpy(s,q);
    }
    if(!s[0]){         /* look for -term: in environment */
      if((p=getenv("PIEINIT"))==(char *)0) p="";
      while(p[0]){
        if(*(q=fpick("-t",p=sob(p)))!=0){
          grabtoken(s,q);
        }
        p=fnb(p);
      }
    }
    eebuf = &mybuf[0]; eebuf[0]=0;
    if(!s[0]) { /* No TERM variable found on cmdline or in PIEINIT */
      if((p = getenv("TERMCAP")) != (char *)0) {
        /* flesh out TERM variable from TERMCAP */
        p=skipto("|:",sob(p));
        if(p[0]=='|'){
          strnzcpy(s,p+1,127);
          *skipto("|:",s)=0;
        }
      }
    }
    if(!s[0]) { /* No term variable found yet, look in TERM */
      if((p = getenv("TERM")) != (char *)0) strcpy(s,p);
    }
    /* Find TERMCAP entry ... */
gettermcap:
    stat=tgetent(eebuf, s);
    /* mybuf[] loaded with TERMCAP entry, ready to decode. */
    /* get cm,al,dl,cs,sf,sr,ce,cd,rs,si,so,se,md,me */
    /* May have to assume internal VT100 terminal */
    decodetermcap(stat,s);
#if BSDunix || SYS5
    resetWinSize(&ROWMAX,&COLMAX);     /* Use SIG to find window size parameters */
#endif
    return;    /* Success! */
}
#endif

#if !MSDOS
char *setkeydefault() {
     switch(KEYMODE){
     case 1: return SETEMACS;
     case 3: return SETWDPERF;
     default:
     case 2: return SETWDSTAR;
     }
}

void setMode() {
char *keyfile;
char *setkeydefault();
    keyfile=setkeydefault();
    loadkeymap(keyfile);
    safecore(&INIFILE,keyfile); /* Save last keyfile name */
    sorttable();
}

void setEmacs(){ KEYMODE=1; setMode(); EMACSMODE=TABINSERT=1;}

void setWdstar(){ KEYMODE=2; setMode(); EMACSMODE=TABINSERT=0;}

void setWdperf(){ KEYMODE=3; setMode(); EMACSMODE=TABINSERT=1;}
#endif

/* Load key files to set up user keys */
void loadkeys(argc,argv) int argc; char **argv; {
char *p,*q;
static char *keyfile="";
int i,flag;
char s[128];
char *ffname();
    /* should be able to pass keyboard type on command line */
    /* (-key:xxx -set:xxxx -term:xxxxx). Use environment for defaults. */

    flag=0;
/*
 * Load up key files.
 */
   for(i=1;i<argc;++i){ /* command line first choice for -key: */
     if(*(q=fpick("-k",argv[i]))!=0) flag += loadkeymap(keyfile=q);
   }
   if(!flag){   /* command line eliminates environment */
    /* scan environment for -key: */
    if((p=getenv("PIEINIT"))==(char *)0) p="";
     while(p[0]){
       if(*(q=fpick("-k",p=sob(p)))!=0){
         grabtoken(s,q);
         if(loadkeymap(s)){keyfile=s; flag=1;}
       }
       p=fnb(p);
     }
   }
#if TERMCAP
   { extern char *setkeydefault();
   if(!flag) {   /* No keyfile found, choose system default */
     keyfile=setkeydefault();
     if(KEYMODE==2) {flag=1; keyfile="Internal WS Keys";} else
     flag = loadkeymap(keyfile);
   }/* end if */
   }
#endif
   if(keyfile==0 || keyfile[0]==0) keyfile="Internal WS keys";
   safecore(&INIFILE,keyfile);            /* Save last keyfile name */
   loadkeymap(USERFILE);                  /* Load pi.set or ~/.pirc */
   loadkeymap(ffname(PIMACROS));          /* Load macros from current dir */
   /* Sort table before use! */
   /* Call sorttable() after this routine. */
}

/* get a key sequence definition */
int getkeyseq(prompt,buf) char *prompt,*buf; {
char d[128];
char *p;
int x;
  bell();
  macabort();
  p=buf;
  ssprintf(d,"%s: ",prompt);
  while(1){
    banner(d);
    x=getbyte();
    if(x=='\r') break;
      if(x=='P'-'@') *p++ = getbyte();
        else
      if((x==127 || x==8) && p>buf) --p;
        else
      *p++ =x;
    p[0]=0;
    /* translation printed if s[0]!=' ' */
    printseq("End with RETURN: ",buf,d);
  }
  if(p==buf) return 0;
  return 1;
}

int getkeyslot(){
int i;
  for(i=0;i<MAXKEYS;++i) {
    if(ourkeys[i].value== -1) return i;
  }
  return -1;
}

/* Find a keyslot matching macro text in macbuf[], else an empty slot */
/* Return -1 on error. */
int getmacroslot(slot) int *slot; {
int j;
extern int MACNUM,*macromax;
for(j=1;j<=MACNUM;++j){
  if(macromax[j]==0) continue;
  if(MYstrcmp(macbuf,macrobuf[j])==0){slot[0]=j; return 0;}
}
for(j=1;j<=MACNUM;++j){
  if(macromax[j]==0){slot[0]=j; return 1;}
}
return -1;      /* return -1 on error */
}

/* Print key alias list to string d[] */
int lastidentifykey;

int identifykey(key,d) unsigned key; char *d; {
int i,j; char *endof();
  strcpy(d,"Keymap");
  lastidentifykey=-1;
  for(j=i=0;i<MAXKEYS;++i){
    if(ourkeys[i].value == key) {
        printseq(":",ourkeys[i].seq,endof(d)); ++j; /* translation printed */
        lastidentifykey=i;
    }
  }
  return j;
}

/* kill duplicates in identify key string */
#if MSDOS
void killduplicates(){}
#endif
#if !MSDOS
void killduplicates(s) char *s; {
char *skipto(); char *p,*r;  char t[128]; int i;
  r=skipto(":",s);
  while(r[0]){
      p=MYstrchr(r+1,':');
      if(p==(char *)0) break;
      p[0]=0; strcpy(t,r); p[0]=':';
      i=strlen(t);
      while((p=MYstrstr(r+1,t))!=(char *)0) fillchr(p,' ',i);
      r += i;
  }

  p=s;
  while(p[0]){ /* remove blanks */
    s[0]=p[0];
    ++p;
    if(s[0]!= ' ') ++s;
  }
  s[0]=0;
}
#endif

/* Insert key sequence and copy the current macro. Then sort the table */
void copymacro(){
char buf[256];
  if(getkeyseq("Macro key",buf)) keyinsert(buf,1,0);
}

int addkey(value) int value; { /* Attach a key to an existing command */
char buf[256];
  if(value){
    if(getkeyseq("New key",buf)){
      return(keyinsert(buf,0,value)); /* return -1 on failure */
    }
  }
  return -1;
}
      

/* Remove a key sequence from the key table. Does not remove functions. */
int killkey(seq) char *seq; {
int i,x,xx;
char buf[256];
extern char *endof();
  xx=keyconflict(seq,&x,&i,sysv);  /* xx==0 for no conflict */
  if(xx == -3){ /* one key definition exists */
    strcpy(buf,ourkeys[i].seq);
    goto killkey;
  }
  if(xx != -4) goto err;
  bell();
  strcpy(sysv,"Delete which ");
  identifykey(x,endof(sysv));
  if(!getkeyseq(sysv,buf)) goto errminus1;
killkey:
  return(keyinsert(buf,2,x)); /* return -1 on failure */
err:  
  bellpressany("Some error in killkey");
errminus1:
  return -1;
}

void showkeys(){
  getkeyseq(" Press keys",sysv);
}

int isinside(s,t) char *s,*t; { /* 0=Ok string, 1=s inside t, 2=s equals t */
int xx;
  xx=0;
  if(MYstrncmp(s,t,strlen(t))==0) ++xx;
  if(MYstrncmp(t,s,strlen(s))==0) ++xx;
  return xx;
}

static void nulloutkey(i) int i; {
/* Macro keys have extra text elsewhere, in macbuf[] */
/* Tries to do memory freeup, but some key sequences are compiled in */
#if MSDOS
  freeup(ourkeys[i].seq);       /* What will happen with this? */
#else                           /* Macro keys not compiled in */
  if(ourkeys[i].value>MACROKEY) freeup(ourkeys[i].seq);
#endif
  ourkeys[i].value= -1;
  ourkeys[i].seq="";             /* Nullstring is important, because freeup */
                                /* will not try to free it. */
}     

int keyinsert(buf,kind,keyvalue) /* Returns index into ourkeys[] or -1 on fail */
char *buf;    /* chars in key sequence */
int kind;     /* Regular (0) or Macro (1) or Kill (2) key possible */
int keyvalue; /* Key code to insert for new key sequence, kind==0 */
{
char d[256],dd[256];
int i,j,x,xx,flag,slot;
char **q;
char *macrosfile();
top:
  xx=keyconflict(buf,&x,&i,d);  /* xx==0 for no conflict */
    /* char  *buf;  chars in key sequence */
    /*     int *x;  Keycode */
    /*     int *i;  Index i of key match or new key slot */
    /*    char *d;  Error message */
  strcpy(dd,"Can't define key. "); strcat(dd,d);
  if(kind==0){  /* Else, regular key slot request, kind==0 */
    if(xx < 0 || xx >0){
      /* xx == -4 */ /* "Can't define key. Key sequence in use" */
      /* xx == -3 */ /* "Can't define key. Key sequence in use" */
      /* xx == -1 */ /* "Can't define key. Key table full" */
      /* xx == -2 */ /* "Can't define key. Key conflict" */
      goto quit;
    }
    /* if(xx == 0) */
    x=keyvalue;
    ourkeys[i].value=x; safecore((char **)&(ourkeys[i].seq),buf);
    sorttable();
    macrosfile(x,buf,2); /* document added key in PI.SET */
    goto sortexit;
  }
  if(kind==1){ /* Macro key slot request, kind==1 */
    /* Use old macro value or else hunt up a new one */
    flag=((xx == -3 || xx == -4) &&(x<MACROKEY)); /* keycode is not a macro */
    flag=(flag || xx == -1 || xx == -2);
    if(flag){
      /* xx == -1 */ /* "Can't define key. Key table full" */
      /* xx == -2 */ /* "Can't define key. Key conflict" */
      /* xx == -3 */ /* "Can't define key. Key sequence in use" */
      /* xx == -4 */ /* "Can't define key. Key sequence in use" */
      goto quit;
    }
    if(xx == -4){
      /* Then x is an active macro keycode with keys already assigned */
      /* If xx == -4, then one other key sequence is mapped. */
      /* It is a bad idea to redefine the other key; quit if xx == -4 */
      goto quit;
    }
    if(xx == -3){
      /* If xx == -3, then just this key sequence is mapped. */
      /* In this case (xx == -3) the request should delete the key */
      /* and then add it using the new macro text. The old macro text */
      /* will be saved in the file PI.SET; the new macro text */
      /* is located in the default macro buffer, macbuf[]. */

      j=x-MACROKEY;   /* Index into the macrobuf[] array */
      if(macromax[j]!=0){ /* Something in the buffer? */
        if(MYstrcmp(macbuf,macrobuf[j])!=0){
          freeup(macrobuf[j]);  /* Throw away any existing macro text */
          macromax[j]=0;        /* to free up space for writing */
        } else { /* macro text and keymap already there */
          goto sortexit;
        }
      }
      /* j=Index into array macrobuf[], x=keycode for the macro */
      goto storemacro;
    }
    if(xx == 0){  /* Use new unused key slot required, key unmatched . */
    /* Store macro text in next macro slot numbered 1..MACNUM */
    /* But if macro text already exists, then just add a key to it */
    /*     int *i;  Index i of the new key slot */
      switch(getmacroslot(&j)){
      case 0:  /* Macro text matches macrobuf[j] */
        x=MACROKEY+j;
        goto storemacro1;
      case 1:  /* Macro text unmatched, got new slot j */
        x=MACROKEY+j;
        break;
      case -1:
      default:
        strcat(dd,"No macro slots");
        goto quit;
      }
      /* j=Index into array macrobuf[], x=keycode for the macro */

storemacro:
      /* A macro has a keycode 15XXX, a key sequence, and its TEXT */
      /* Other functions just have the keycode and key sequence */

      q=(char **) &macrobuf[j];
      macbuf[macmax]=0; /* terminate source string for safety */
      if(safecore(q,macbuf)){ /* safecore terminates the copied string */
         macromax[j]=macmax;
storemacro1:
         ourkeys[i].value=x;
         safecore((char **)&(ourkeys[i].seq),buf);
         sorttable();
         macrosfile(x,buf,2);  /* rewrite the new macro into PI.SET */
      }
      goto sortexit;
    }
    goto quitminus1;
  }   
  if(kind==2){ /* Delete the keymap from the table */
    if(xx == -3 || xx == -4){
      /* if(x != keyvalue) then there might be an error ... */
      /* don't know how to process this error, yet. */
      if(x != keyvalue){
        bell(); if(!getyesno("Kill this key")) goto quitminus1;
      }
      macrosfile(x,buf,1);  /* Document killed key in PI.SET */
      nulloutkey(i);
      sorttable();
      goto sortexit;
    }
    if(xx == 0 || xx == -1 || xx == -2){
      strcpy(dd,"Can't kill key. Key not found");
      goto quit;
    }
  }
  goto quit;
sortexit:
  return 0;
quit:
  bellpressany(dd);
quitminus1:
  return -1;  /* -1 on fail */
}

/*
Test for buf[] equal to a current key sequence
Returns:  0  Not found, new key slot available at index i
         -1  Not found and No new slot available for a key
         -2  Key conflict, substring error
         -3  It matches a definition. Keycode match has one definition.
         -4  It matches a definition. Keycode match has two definitions.
 key[0]     == keycode, on a match
 slot[0]    == Which array slot produced match, or else new slot
 err        == error message
*/
int keyconflict(buf,key,slot,err)
char  *buf;             /* chars in key sequence */
int *key;               /* Keycode, on a match */
int *slot;              /* Index i of key match or new key slot */
char *err;              /* Error message */
{
int i,j,x,y,xx;
char s[256];
char *p; int flag;
  for(y=flag=i=0;i<MAXKEYS;++i) {
    if((y=ourkeys[i].value)== -1) continue;
    xx=isinside(ourkeys[i].seq,buf);
    /* isinside(s,t): 0=mismatched, 1=s inside t or reverse , 2=s equals t */
    if(xx){
      /* */
      /* Count (x) how many keys use function code y */
      /* x==1 && xx==1 ==> key conflict, return -2; */
      /* x >1 && xx==1 ==> key conflict, return -2 */
      /* x==1 && xx!=1 ==> unique key in use, return -3 */
      /* x >1 && xx!=1 ==> key has two defs, return -4 */
      /* */
      x=identifykey(y,s);
      flag = (xx==1 ? -2 : (x==1 ? -3 : -4));
      break;
    } /* end if(xx) */
  } /* end for() */
  slot[0]=i;            /* Index of match, if any */
  key[0]=y;             /* keycode of match, if any */
  err[0]=0;
  switch(flag){
  case  0:        /*   0  Not found, see if new key slot is available */
        if((slot[0]=getkeyslot()) == -1) flag = -1;
        else break;
  case -1:        /*  -1  No new slot available for a key */
        strcpy(err,"Key table full"); break;
  case -2:        /*  -2  Key conflict, substring error */
        p="Key conflict"; goto keys;
  case -3:        /*  -3  Keycode has one key definition and it matches */
  case -4:        /*  -4  Keycode has two key definitions. One matches. */
        p="Key sequence in use";
keys:   ssprintf(err,"%s [%u]. %s",p,y,s);
        break;
  }
quit:
  return flag;
} 

int readkeymap(){
  bell();
  if(getfname("Key map file: ",sysv)){
    loadkeymap(sysv); sorttable();
    return 1;       
  } else return 0;        
}                         
                          
                          
/* dump keys to a file. Returns 1=worked, 0=failed */
int pidumpkey()           
{                         
FILEP fp; FILEP fi;       
unsigned target;          
int i,j,flag;             
char *keyseq;             
char buff[512];           
LLONG atoiLL();           
char *endof();            
                          
  flag=0;                 
  fi=fopenbread(KEYMAP);  
  if(fi!=(FILEP)0){       
    fp=fopenbwrite(DUMPKEY);
    if(fp!=(FILEP)0){
      while(fgetLN(buff,fi)>0){
        fputsline(buff,fp);
        if(buff[0]==';' || buff[0]=='#') continue;
        target=(int)atoiLL(buff);
        if(target==0) continue;
        for(i=0;i<MAXKEYS;++i){
          if(target==ourkeys[i].value){ /* termcap sequence printed */
            printseq(" key=",keyseq=ourkeys[i].seq,buff);
            fputsline(buff,fp);
            printseq("#decoded=",keyseq,buff);  /* translation printed */
            if(!isSeqMatch(keyseq,buff+9)) fputsline(buff,fp);
          }
        }/* end for() */
      }/* end while */
      macrodump(fp);
      closedfile(fp);
      flag=1;
    }
    closedfile(fi);
  }

#if 0           /* DEBUG Set to 1 to dump all keys to "loadset.key" */
/* Write out the keys as currently sorted */

  {int i; char *endof();
  fp=fopenbwrite("loadset.key");
   for(i=0;i<MAXKEYS;++i){
        buff[0]='#';  buff[1]=0;
        target=ourkeys[i].value;
        ssprintf(buff,"%u #",target);
        if(target != (unsigned)-1) identifykey(target,endof(buff));
        fputsline(buff,fp);
        printseq(" key=",ourkeys[i].seq,buff); /* termcap sequence printed */
         fputsline(buff,fp);
   }
   closedfile(fp);
  }
#endif          /* DEBUG */

  return (flag);
}
      
int pimacrodump(){
FILEP fp;
  fp=fopenbwrite(PIMACROS);
  if(fp!=(FILEP)0){
    macrodump(fp);
    closedfile(fp);
    return 1;
  }
  return 0;
}
              
int isSeqMatch(keyseq,decoded)
char *keyseq;   /* Raw key sequence */
char *decoded;  /* decoded sequence */
{
char buf[512];
  printseq(" ",keyseq,buf);
  /* printseq emits text " " */
  if(MYstrcmp(buf+1,decoded)==0) return 1;
  return 0;
}

void macrodump(fp) FILEP fp; {
char buff[512];
char s[128];
int i,j,k,keycode;
char *keyseq,*macseq;
extern char *endof();
  for(k=0,j=1;j<=MACNUM;++j){
    if(macromax[j]==0) continue;  /* No macro defined */
    keycode=j+MACROKEY;
    macseq=macrobuf[j];

    getMacroTitle(keycode,s);
    ssprintf(buff,"%u %s [MACROKEY+%u]",keycode,s,j);
    fputsline(buff,fp);
    printseq(" macro=",macseq,buff);  /* termcap sequence printed */
    fputsline(buff,fp);
    printseq("#decoded=",macseq,buff); /* translation printed */
    if(!isSeqMatch(macseq,buff+9)) fputsline(buff,fp);

    for(i=0;i<MAXKEYS;++i){
      if(ourkeys[i].value != keycode) continue;
      ++k;                              /* Count keys assigned */
      keyseq=ourkeys[i].seq;
      printseq(" key=",keyseq,buff); /* termcap sequence printed */
      fputsline(buff,fp);
      printseq("#decoded=",keyseq,buff); /* translation printed */
      if(!isSeqMatch(keyseq,buff+9)) fputsline(buff,fp);
    }
    strcpy(buff,"#Multiple ");
    if(identifykey(keycode,endof(buff))>1) fputsline(buff,fp);
  }
  ssprintf(buff,"# Defined %d key sequences [MAXKEYS=%d,MACNUM=%d]",k,MAXKEYS,MACNUM);
  fputsline(buff,fp);
  printseq("# LeadinSTR=",LeadinSTR,buff);
  fputsline(buff,fp);
}


int moveFileToPIbackups(src,dest)
char *src;              /* Existing file to be moved */
char *dest;             /* filename for PIbackups directory */
{
char buff[MAXFNAME];
char *ffname();
int n=0;
    if(BACKUPS){
      getbackupfname(buff);     /* $HOME\PIbackups or .\PIbackups */
      mymkdir(buff);
      addslash(buff);
      strcat(buff,ffname(dest));
      maketempfilename(buff);   /* New name in subdirectory */
      n=rename(src,buff);      /* Move old file to directory */
      /* All of these can fail, no space, CD rom, protection error */
    }
    return n;
}

char *macrosfile(keycode,buf,flag)
int keycode;    /* keycode, >15000 a macro keycode */
char *buf;      /* key sequence */
int flag;       /* 0=remake file, 1=document kill key, 2=document add key */
{
static int test=0;
FILEP fp;
FILEP fi;
char s[MAXFNAME];
char buff[512];
extern LLONG atoiLL();
  if(!test){
     test=1;
     queryLocalPiSet();
  }
  strcpy(s,USERFILE);    /* Make a temp name for PI.SET file */
  maketempfilename(s);
  /* Insert killed key or added key info into temp PI.SET */
  documentAddKill(keycode,buf,flag,s);
  /* Move old PI.SET to a safe place */
  if(s[0]) {
     moveFileToPIbackups(USERFILE,USERFILE);
     rename(s,USERFILE);
     /* If this fails, then pi.set is unchanged */
     /* and the new one has a backup name. */
  }
  return USERFILE;
}

/* Test a PI.SET file for a comment line */
/* If a line starts with ; or # then it is a comment line */
int isacommentline(buff)
char *buff; {
  return (MYstrchr(";#",buff[0])!=0) ? 1 : 0;
}

/* Test a PI.SET file for a header line */
int isaheaderline(buff,keycode)
char *buff;
int *keycode;
{
  return ((keycode[0]=(int)atoiLL(buff)) != 0);
}

static void putKeyLines(keyseq,token,fo)
char *keyseq;  /* Key sequence */
char *token;   /* Either " key=" or " kill=" */
FILEP fo;      /* Output file */
{
char bigbuf[512];
  printseq(token,keyseq,bigbuf);
  fputsline(bigbuf,fo);
  printseq("#decoded=",keyseq,bigbuf);
  if(!isSeqMatch(keyseq,bigbuf+9)) fputsline(bigbuf,fo);
}

static void putMacroLines(macrotext,fo)
char *macrotext;  /* macro text */
FILEP fo;      /* Output file */
{
char bigbuf[512];
  printseq(" macro=",macrotext,bigbuf);  /* termcap sequence printed */
  fputsline(bigbuf,fo);
  printseq("#decoded=",macrotext,bigbuf);  /* termcap sequence printed */
  if(!isSeqMatch(macrotext,bigbuf+9)) fputsline(bigbuf,fo);
}

void getMacroTitle(keycode,s)
int keycode;
char *s;     /* Buffer of size 512 or larger */
{
char buff[512];
FILEP fi;
int j;
extern char *fnb(),*sob();
extern char *USERFILE;
  if((fi=fopenbread(USERFILE)) == (FILEP)0) goto notitle;
  while(fgetLN(buff,fi)>0){
     j=(int)atoiLL(buff)-MACROKEY;
     if(1 <= j && j <= MACNUM) {
       if(j+MACROKEY != keycode) continue;
       strTruncate(buff,'[');
       strncpy(s,sob(fnb(buff)),MACROTRUNC);
       if(*sob(s)!=0) goto quit;  /* Got a title */
       s[0]=0;
     }
  }
notitle:
  strcpy(s,"Untitled");
quit:
  if(fi) closedfile(fi);
}

void getToolTitle(keycode,title)
int keycode; char *title;
{
char buff[512];
int j,KEYcode;
FILEP fi;
char *p;
extern char *USERFILE;
  title[0]=0;
  if((fi=fopenbread(USERFILE)) == (FILEP)0){
     banner("can't open userfile "); ePrint(USERFILE); pressany("");
     return;
  }
  while(fgetLN(buff,fi)>0){
     if( isacommentline(buff) ) continue;
     if(isaheaderline(buff,&j) != 0) KEYcode=j;
     if(KEYcode==keycode){
       if((p=MYstrstr(buff,"tool="))!=0){
         strcpy(title,p+5); title[MACROTRUNC]=0;
         break;
       }
     }
  }
  closedfile(fi);
}




/* Return 0 for no title entered */
int newmacrotitle(keycode) int keycode;
{
   if(keycode<=MACROKEY) return 0;
   return getTitle(keycode,0);
}

/* Return 0 for no title entered */
int newtooltitle(keycode) int keycode;
{
   if(keycode<=0) return 0;
   return getTitle(keycode,1);
}

int getTitle(keycode,titlekind)
int keycode,titlekind; /* titlekind=0 for macro, =1 for tool */
{
char title[2+MACROTRUNC];
char buff[512];
char tmp[128];
char s[MAXFNAME];
char *p;
int gotit,KEYcode;
FILEP fo;
FILEP fi;
extern LLONG atoiLL();
   if(titlekind==0) getMacroTitle(keycode,title);
   if(titlekind==1) getToolTitle(keycode,title);
   strcpy(buff,title);
   ggetstr("[40 char] New title:",buff);
   if(MYstrcmp(title,buff)==0) return 0;                    /* No title entered */
   strncpy(title,buff,MACROTRUNC);
   if((fi=fopenbread(USERFILE))==0) goto fileError;
   gotit=0;
   strcpy(s,USERFILE);    /* Make a temp name for PI.SET file */
   maketempfilename(s);
   if((fo=fopenbwrite(s))==0) goto writeError;
   while(fgetLN(buff,fi)>0){
     if( isaheaderline(buff,&KEYcode)!=0 && KEYcode==keycode){
       gotit=1;
       if(titlekind==0) /* Macro header */
         ssprintf(buff,"%u %s [MACROKEY+%d]",keycode,title,keycode-MACROKEY);
       if(titlekind==1){  /* Tools header */
         fputsline(buff,fo); goto getTools;
       }
     }
loop:
     fputsline(buff,fo);
loop1:;
   }
goto outerexit;
getTools:
   while(fgetLN(buff,fi)>0){ /* Goto outerexit on break */
     if( isacommentline(buff) ) goto write1;
     if((p=MYstrstr(buff,"tool="))!=0){
        strcpy(p+5,title); gotit=2;
        if(title[0]) goto loop;
        goto loop1;
     }
     if(isaheaderline(buff,&KEYcode)!=0 && KEYcode!=keycode){
loop2:
        ssprintf(tmp," tool=%s",title);
        if(title[0]) fputsline(tmp,fo); gotit=2;
        goto loop;
     }
write1:
     fputsline(buff,fo);
   }
   if(gotit != 2) goto loop2;
outerexit:
   closedfile(fi);
   closedfile(fo);
   if(!gotit){
     remove(s);
   } else {    /* Move old PI.SET to a safe place */
     moveFileToPIbackups(USERFILE,USERFILE);
     rename(s,USERFILE);
     /* If this fails, then pi.set is unchanged */
     /* and the new one has a backup name. */
  }
  goto err1;
writeError:
   closedfile(fi);
   ssprintf(buff,"Can't write %s",s);
   goto err;
fileError:
   ssprintf(buff,"%s not found",USERFILE);
err:
   bellpressany(buff);
err1:
   return 1;
}

void documentAddKill(keycode,buf,flag,s)
int keycode; /* keycode, >15000 is a macro code */
char *buf;   /* key sequence */
int flag;    /* 1=kill, 2=add */
             /* e.g. " kill=" plus "\E[1~" becomes " kill=\E[1~" */
char *s;     /* Temp file name for writing new copy of PI.SET */
             /* On return, s[0]==0 if something failed */
{
char bigbuf[512];
char buff[512];
FILEP fi;
FILEP fo;
int j;
int macrocode;
int macroDone;
int keyDone;
int killDone;
int KEYcode;
char *p;
extern LLONG atoiLL();
extern char *endof();

  macrocode=keycode-MACROKEY;
  if(macrocode<1 || macrocode > MACNUM) macrocode=0;
  keyDone=killDone=macroDone=0;

  fi=fopenbread(USERFILE);

  fo=fopenbwrite(s);
  if(fo==(FILEP)0){
    if(fi!=(FILEP)0) closedfile(fi);
    ssprintf(buff,"Write error on %s",USERFILE);
    bellpressany(buff); return;
  }

  if(fi==(FILEP)0) goto notdocumented;

  /* Read and write lines until the keycode matches on a header */
top:
  while(fgetLN(buff,fi)>0){
    fputsline(buff,fo);
    if( isaheaderline(buff,&KEYcode) ){
      if(KEYcode==keycode) goto keymap;
    }
  }
  closedfile(fi); fi=0; /* No header matched */
  goto notdocumented;     /* Then file at end, maybe no hit. */

keymap:
  /* Examine lines after header until next header */
  while(fgetLN(buff,fi)>0){
    if( isacommentline(buff) ) goto write;
    if(isaheaderline(buff,&KEYcode)){
      if(keycode == KEYcode) continue;       /* Duplicate header, skip it */
      if(flag==2 && keyDone==0){
        putKeyLines(buf," key=",fo);
        keyDone=1;
      }
      if(flag==1 && killDone==0){
        putKeyLines(buf," kill=",fo);
        killDone=1;
      }
    if(flag==2 && macrocode && macroDone==0){
      putMacroLines(macrobuf[macrocode],fo);
    }
      fputsline(buff,fo);                    /* Done with this block */
      goto top;
    }
    /* The buffer does not contain a header nor a comment */
    /* Test for "key=" or "kill=" */
    if((p=MYstrstr(buff,"key=")) != 0){
      tcap_to_string(p+4,bigbuf);
      if(MYstrcmp(buf,bigbuf)==0){
        if(flag==1) killDone=1;
        continue; /* Don't document this key */
      }
    }
    if((p=MYstrstr(buff,"kill=")) != 0){
      tcap_to_string(p+5,bigbuf);
      if(MYstrcmp(buf,bigbuf)==0){
         continue; /* Don't document this key */
      }
    }
    if(!macrocode) goto write;

macrotest:
    /* If its a macro header, then test for "macro=" */
    if(macromax[macrocode]==0) goto write;  /* No macro in memory */
    if((p=MYstrstr(buff,"macro=")) == 0) goto write;
    tcap_to_string(p+6,bigbuf);
    /* This macro might be the same as the one in memory. */
    if(MYstrcmp(macrobuf[macrocode],bigbuf) == 0){
      if(flag==2) goto write0;
      goto write1;
    }
    /* No, it's a different macro definition */
    buff[0]='#';  /* Comment out this macro, in case it's needed */
    fputsline(buff,fo);
write0:
    if(macroDone) continue;
    printseq(" macro=",macrobuf[macrocode],buff);  /* termcap sequence printed */
write1:
    macroDone=1;
write:
    fputsline(buff,fo);
  } /*  while(fgetLN(buff,fi)>0) */

notdocumented:
  if(getHeaderLine(keycode,buff)){ /* Got a header */
    if(flag==1 && killDone==0){
      fputsline(buff,fo);
      putKeyLines(buf," kill=",fo);
    }
    if(flag==2 && keyDone==0){
      fputsline(buff,fo);
      putKeyLines(buf," key=",fo);
    }
    if(macrocode && macroDone==0){
      putMacroLines(macrobuf[macrocode],fo);
    }
  }
alldone:
  closedfile(fo);
  if(fi) closedfile(fi);   /* Can be that fi==0 */
}

int getHeaderLine(keycode,buff)
int keycode;
char *buff;
{
char s[2+MACROTRUNC];
int macrocode;
FILEP fi;
int gotit;
int KEYcode;
  gotit=0;
  macrocode=keycode-MACROKEY;
  if(macrocode<1 || macrocode>MACNUM) macrocode=0;
  if(macrocode){
    getMacroTitle(keycode,s);
    ssprintf(buff,"%u %s [MACROKEY+%u]",keycode,s,macrocode);
    gotit=1;
  } else {
    /* Find the first match in the KEYMAP file to the given keycode */
    fi=fopenbread(KEYMAP);
    if(fi!=(FILEP)0){
       while(fgetLN(buff,fi)>0){
         if( isaheaderline(buff,&KEYcode) ) {
           if(keycode != KEYcode) continue;
           gotit=1;  break;
         }
       }
       closedfile(fi);
    }
  }
  return gotit;
}
/* Print termcap key sequence if msg[0]==' ' else translate function keys */
void printseq(msg,p,s) char *msg; char *p; char *s; {
int x,i,j,wasoctal; char *endof();
  strcpy(s,msg); i=strlen(s);
  wasoctal=0;
  while(p[0]){
    x=p[0]; ++p;
    if(0<x && x<' ' && x!='\33') {s[i++]='^'; s[i++]=x+'@';}
    else if(' '<=x && x <= '~' && !(wasoctal && '0'<=x && x <= '9')){
      s[i++]=x;
      if(x=='\\') s[i++]=x;
    }
    else if(msg[0] != ' ' && (j=functionkey(p-1,s+i))!=0) {
      p += j-1;         /* skipover used chars */
      i = strlen(s);    /* skip to end of string */
    }
    else if(x == '\33'){s[i++]='\\'; s[i++]='E';}
    else {
     wasoctal=1;
     s[i++]='\\';
     s[i++]=((x&0300) >> 6)+'0';
     s[i++]=((x&0070) >> 3)+'0';
     s[i++]=((x&0007))+'0';
     continue;
    }
    wasoctal=0;
  }
  s[i]=0;
}

int istrchr(s,x) char *s; int x;{
char *p; char *MYstrchr();
  if((p=MYstrchr(s,x))==(char *)0) x = 0; else x = 1+(int)(p-s);
  return x;
}

#if MSDOS
int functionkey(pp,s)
char *pp;  /* source string address */
char *s;   /* dest string */
{
char v[32];
char *p; int i,j,x;
char *endof(),*lltoa();
  x=pp[0];      /* leadin char of escape sequence? */
  if(x == '\200'){ /* IBM function keys */
    x=pp[1];    /* look for match to second char in string */
    p="";
    if((i=istrchr("\017TUVWXYZ[\\]\207\210",x))!=0)  /* shift TAB, F1 to F12 */
      p="Shift-";
    else
    if((i=istrchr("\224^_`abcdefg\211\212",x))!=0)   /* ctrl TAB, F1 to F12 */
      p="Ctrl-";
    else
    if((i=istrchr("\225hijklmnopq\213\214",x))!=0)   /* alt TAB, F1 to F12 */
      p="Alt-";
    else
    if((i=istrchr("\001;<=>?@ABCD\205\206",x))!=0)   /* placeholder, F1 to F12 */
      p="";

    if(i){
copynumber:
      lltoa((LLONG)(i-1),v);
      if(i==1) v[0]=0;
      ssprintf(s,"{%s%s%s}",p,(v[0]? "F" : "Tab"),v);
      goto quit;
    }
    /* alt 0 to 9 */
    if((i=istrchr("\201\170\171\172\173\174\175\176\177\200",x))!=0){
      j='0'+i-1;
copyALT:
      ssprintf(s,"{Alt-%c}",j);
      goto quit;
    }
    if((i=istrchr("\036\060\056\040\022\041\042\043\027\044\045\046\062\061\030\031\020\023\037\024\026\057\021\055\025\054",x))!=0){   /* alt a to z */
      j='A'+i-1; goto copyALT;
    }
    if((i=istrchr("\202\203\053\032\033\047\050\063\064\065",x))!=0){
    /* alt -=\[];',./ */
      {
      char *s0="-=\\[];',./";
      j=s0[i-1]; goto copyALT;
      }
    }
    {   /* special IBM function keys */
#if 0
    KEY    norm ctrl
    Home   71   119
    Up     72   141
    PgUp   73   132
    Left   75   115
    Right  77   116
    End    79   117
    Down   80   145
    PgDn   81   118
    Ins    82   146
    Del    83   147
#endif

      static char *ibmf[]={ "Home", "Up", "PgUp", "Left", "Right",
                            "End", "Down", "PgDn", "Ins", "Del" };
      if((i=istrchr("GHIKMOPQRS",x))!=0) p="";
      else
      if((i=istrchr("w\15\04stu\21v\22\23",x))!=0) p="Ctrl-";
      if(i){
        ssprintf(s,"{%s%s}",p,ibmf[i-1]); goto quit;
      }
    }
  }/* end if(x == '\200') */

return 0;       /* tell caller it failed */
quit:
return 2;       /* tell caller it worked */
}
#endif
#if !MSDOS
struct alias {char *string; char *key;};
static
struct alias sunterm[]={
{"R8}:{UP",    "A"},
{"R14}:{DOWN",  "B"},
{"R10}:{LEFT",  "D"},
{"R12}:{RIGHT", "C"},
{"L1",  "192z"},
{"L2}:{F12",  "193z"},
{"L3}:{F13",  "194z"},
{"L4}:{F14",  "195z"},
{"L5}:{HELP",  "196z"},
{"L6}:{DO",  "197z"},
{"L7}:{F17",  "198z"},
{"L8}:{F18",  "199z"},
{"L9}:{F19",  "200z"},
{"L10}:{F20", "201z"},
{"F1",  "224z"},
{"F2",  "225z"},
{"F3",  "226z"},
{"F4",  "227z"},
{"F5",  "228z"},
{"F6",  "229z"},
{"F7",  "230z"},
{"F8",  "231z"},
{"F9",  "232z"},
{"F10", "233z"},
{"R1",  "208z"},
{"R2",  "209z"},
{"R3",  "210z"},
{"R4",  "211z"},
{"R5",  "212z"},
{"R6",  "213z"},
{"R7",  "214z"},
{"R8",  "215z"},
{"R9",  "216z"},
{"R10", "217z"},
{"R11", "218z"},
{"R12", "219z"},
{"R13", "220z"},
{"R14", "221z"},
{"R15", "222z"},
/* SUN TERMINAL KEY CODES "xterm +sf" or "xterm" */
{"L1",  "23~"},
{"L2",  "24~"},
{"L3",  "25~"},
{"L4",  "26~"},
{"L5",  "28~"},
{"L6",  "29~"},
{"L7",  "31~"},
{"L8",  "32~"},
{"L9",  "33~"},
{"L10", "34~"},
{"F1",  "11~"},
{"F2",  "12~"},
{"F3",  "13~"},
{"F4",  "14~"},
{"F5",  "15~"},
{"F6",  "17~"},
{"F7",  "18~"},
{"F8",  "19~"},
{"F9",  "20~"},
};
#define SUNTERMSIZE sizeof(sunterm)/sizeof(sunterm[0])

/* DEC 3100 KEYBOARD "xterm -sf" */
/* F11 \33 */
static
struct alias decterm[]={
{"FIND",         "1z"},
{"INSERT",       "2z"},
{"REMOVE",       "3z"},
{"SELECT",       "4z"},
{"PREV",         "5z"},
{"NEXT",         "6z"},
/* change to \33OXXXX */
{"R8}:{UP",    "A"},
{"R14}:{DOWN",  "B"},
{"R10}:{LEFT",  "D"},
{"R12}:{RIGHT", "C"},
{"PF1",  "P"},
{"PF2",  "Q"},
{"PF3",  "R"},
{"PF4",  "S"},
{"KP0", "p"},
{"PERIOD",  "n"},
{"ENTER",  "M"},
{"COMMA",  "l"},
{"MINUS",  "m"},
{"KP1", "q"},
{"KP2", "r"},
{"KP3", "s"},
{"KP4", "t"},
{"KP5", "u"},
{"KP6", "v"},
{"KP7", "w"},
{"KP8", "x"},
{"KP9", "y"},
};
#define DECTERMSIZE sizeof(decterm)/sizeof(decterm[0])

int functionkey(pp,s)
char *pp;  /* source string address */
char *s;   /* dest string */
{
char v[32];
char *p; int i,x;
char *endof();
  /* pp[0] == leadin char of escape sequence? */
  if(pp[0] == '\33'){ /* SUN, DEC function keys */
    if(pp[1] == '['){
      for(i=0;i<SUNTERMSIZE;++i){
        if(MYstrcmp(pp+2,sunterm[i].key)==0){
          strcpy(s,"{"); strcat(s,sunterm[i].string); strcat(s,"}");
          return (strlen(pp));
        }
      }
    }
    else
    if(pp[1] == 'O'){
      for(i=0;i<DECTERMSIZE;++i){
        if(MYstrcmp(pp+2,decterm[i].key)==0){
          strcpy(s,"{"); strcat(s,decterm[i].string); strcat(s,"}");
          return (strlen(pp));
        }
      }
    }
  }/* end if(x == '\33') */

return 0;       /* tell caller it failed */
}
#endif

char *
skipto(charset,p) char *charset,*p; {
extern char *MYstrchr();
 while(p[0] && MYstrchr(charset,p[0])==(char *)0) ++p;
 return p;
}

int loadkeymap(keyfile) char *keyfile; {
int target,x;
FILEP fp;
char buff[512]; char *p; char *q;
char *sob(),*endof();
LLONG atoiLL();
/* struct swapchar {unsigned char old,new;}; */
extern struct swapchar *accentchars;
extern int NUMACCENT;

  fp=fopenbread(keyfile);
  if(fp!=(FILEP)0){
  /* Do the dirty work of reading a keymap source file */
  /* and changing the key dispatch table. */

    while(fgetLN(buff,fp)>=0){
      if(buff[0]==';' || buff[0]=='#') continue;
      x=(int)atoiLL(sob(buff));
      if(x != 0){
        target=x;
      } else {
        if((p=MYstrstr(buff,"key="))!= (char *)0) tblinsert(target,p+4);
        else
        if((p=MYstrstr(buff,"kill=")) != (char *)0) tblkill(target,p+5);
        else
        if((p=MYstrstr(buff,"macro=")) != (char *)0) {
          x=target-MACROKEY;
          if(x>0 && x<=MACNUM){
            tcap_to_string(p+6,buff);
            macromax[x]= safecore(&(macrobuf[x]),buff) ? strlen(buff):0;
            /* safecore terminates the copied string */
          }
        }
        else
        if((p=MYstrstr(buff,"foreign=")) != (char *)0) {
            if(p[8]==0){
              freeup((char *)&accentchars[0]); NUMACCENT=0;
            } else {
              tcap_to_string(p+8,buff);
              if(NUMACCENT){strcat(buff,(char *)&accentchars[0]);
              freeup((char *)&accentchars[0]);}
              NUMACCENT= safecore((char **)&accentchars,buff) ?
                       strlen(buff)/sizeof(struct swapchar) : 0;
            }
        }
        else
        if((p=MYstrstr(buff,"browser=")) != (char *)0) {
            if(p[8]!=0){
              safecore(&BROWSER,p+8);
            }
        }/* else do nothing*/
      }
    }
    closedfile(fp);
    return 1;
  }
  return 0;
}

int loadrc(userfile) char *userfile; {
/* Load user options from resource file, if found. */
#if 0
Here is what we can change on the fly at run time:
; Default resource file "~/.PIrc" for PI editor. Comment lines start with ';'.
; Do not change text in this file - only the numbers after '='!
; No leading white space on any line!
annoying.startup.message=1; None=0, Annoyance=1 (1=default)
select.emulation=2;   None==0, Emacs==1, WordStar==2, WordPerfect==3. (1=default)
auto.indent.toggle=1; Toggle auto-indent word wrap (1=on,0=off).
hang.indent.toggle=1; Toggle hanging indent mode (1=on,0=off)
word.wrap.toggle=1;   Toggle word wrap at right margin (1=on,0=off).
bit.strip.toggle=0;   Toggle stripping of 128-bit on input file (1=on,0=off).
detab.toggle=1;       Toggle auto-detab of input during scrolling (1=on,0=off).
fold.case.toggle=1;   Toggle case-insensitive search mode (1=on,0=off).
insert.toggle=1;      Toggle insert mode (1=on,0=off).
backup.toggle=1;      Toggle backup (0=off,1=~/PIbackups,2=./PIbackups)
; numerical settings
status.line.toggle=1; Print status line during edit (2=all,1=no numbers,0=off).
screen.tab.stop=8;    Screen tab stop increment for TAB key (8=normal).
file.detab.stop=8;    Tab stop increment for detabbing a file (8=default).
trailing.space=0;     Remove trailing space from lines (0=off, 1=on).
import.type=1;        Import text line delimiter code (1=MSDOS,2=MAC,3=UNIX).
export.type=1;        Export text line delimiter code (1=MSDOS,2=MAC,3=UNIX).
drawing.style=1;      Default line/box drawing style (1 to 5,1=default).
tab.insert.mode=0;    Tab key inserts spaces and/or TAB (1=on,0=off)
search.token.lf=-128; Token in search string used for begin or end line
return.insert.crlf=0; EMACS-like split on RETURN (1=on,0=off)
right.margin=72;      Right margin for word wrap and centering (72=normal).
max.macro.size=256;   Size in chars of a macro recording buffer (256=normal).
available_macros=25;  Maximum number of key slots for macros (25=normal)
; COLOR INFO for IBM-PC
; dark colors: BLACK=0, BLUE=1, GREEN=2, CYAN=3, RED=4, MAGENTA=5,
;              BROWN=6, LIGHTGRAY=7
; light colors: DARKGRAY=8, LIGHTBLUE=9, LIGHTGREEN=10, LIGHTCYAN=11,
;               LIGHTRED=12, LIGHTMAGENTA=13, YELLOW=14, WHITE=15
; Example: foreground=BLACK, background=LIGHTCYAN uses attribute 48
;          because (foreground+(background)*16) - 128 = 48.
;          Warning: Attributes are in the range 0 to 127.
;                   Add 128 to an attribute to make it blink.
other.attributes=7;   Screen attribute for edit screens (7=mono,48=color)
title.attribute=15;   Window title attribute flag (31=boldface,15=mono).
window.attribute=7;  Window text attribute (19=blue /w yellow text,7=mono).
border.attribute=31;  Window border attribute flag (31=boldface,15=mono).
box.characterset=0;   Character set for box border (0=Text,1=vga1,2=vga2,3=iso1,4=iso2)
ansi.mode.unix=1;     Ansi text mode for the window, 1=on, 0=off
tfg.text.fg.color=30; Foreground text color ansi unix (30)
tbg.text.bg.color=46; Background text color ansi unix (46)
mfg.menu.fg.color=37; Foreground menu color ansi unix (37)
mbg.menu.bg.color=44; Background menu color ansi unix (44)
killed.line.max=500;  Maximum killed lines that will be saved (500=normal).
line.max.chars=384;   Maximum allowed line length (160=normal).
most.lines.count=25;  Most lines per screen >= 1+(row-max-index) (40=normal).
cga.noretrace.video=1; Set noretrace mode for CGA video (1=mono/ega/vga).
; Printer functions next six lines
printer.number=0;     set printer 0-6:prn,com1,com2,com3,lpt1,lpt2,lpt3
lpp.printer=55;       lines printed per page, 55 normal (printer)
left.spaces.printer=0; number of spaces printed before line (printer)
top.lines.printer=5;  number of blank lines at top (printer)
bot.lines.printer=5;  number of blank lines at bottom (printer)
formfeed.printer=1;   use formfeed between pages, 1=on,0=off (printer)
; The abort key interrupts macros and stops type-aheads (editor halt).
abort.editor.char=21;  Bailout/abort key is normally ctrl-U (21 decimal)
; Next two handled by TERMCAP - changes declare you an expert! (Win3)
row.max.index=23;     Rows numbered 0..ROWMAX (ROWMAX=23 is normal).
col.max.index=79;     Columns numbered 0..COLMAX (COLMAX=79 is normal).
xmouse.toggle=0;      X-windows mouse for Xterm. 0=off, 1=on.
horizontal.mouse.sens=0; Mouse sensitivity. Normal=8, Off=0. MSDOS
vertical.mouse.sens=0;   Mouse sensitivity. Normal=16, Off=0. MSDOS
chigh.cursor.shape=0;  Set IBM cursor to vga (1) or cga (6) or mono (12).
clow.cursor.shape=15;  Set IBM cursor to vga (13) or cga (7) or mono (13)
xms.maximum.blocks=1024; Block maximum msdos xms (512) or unix (1024)
block.buffer.size=16; Block size msdos [2k->10k] (4) or unix [2k->32k] (16)
string.reserve.blocks=6; Blocks reserved msdos for internal line copying (6)
action.on.startup=1; Menu on initial startup (1) or UnTitled (0).
suspend.on.exit=1;  Suspend PI on SAVE THEN EXIT ctrl-KX.
; other set options
; browser=iexplore
; foreign=
#endif
int i;
FILEP fp; char *p;
char buff[512];
struct setopts {char *option; int *setting;};
static struct setopts setup[]={
{"ann", /*annoying.startup.msg */   &ANNOYING},
{"sel", /*select-emulation     */   &KEYMODE},
{"aut", /*auto-indent-toggle   */   &INDENAUTO},
{"han", /*hanging indent toggle*/   &HangingIndent},
{"wor", /*word-wrap-toggle     */   &WRDWRP},
{"rig", /*right-margin         */   &margin},
{"bit", /*bit-strip-toggle     */   &BITSTRIP},
{"det", /*detab-toggle         */   &DETABBER},
{"fol", /*fold-case-toggle     */   &FOLDCASE},
{"ins", /*insert-toggle        */   &INSMD},
{"bac", /*backup-toggle        */   &BACKUPS},
{"sta", /*status-line-toggle   */   &DOSTATUS},
{"scr", /*screen-tab-stop      */   &TSTOPS},
{"fil", /*file-detab-stop      */   &STOPS},
{"dra", /*drawing-style        */   &drawstyle},
{"imp", /*import-type          */   &intexttype},
{"exp", /*export-type          */   &outtexttype},
{"rep", /*report-export-import */   &ReportExportImport},
{"max", /*max-macro-size       */   &MAXMB},
{"inf", /*info-max-size        */   &maxinfo},
{"ava", /*available_macros     */   &MACNUM},
{"oth", /*other-attributes     */   &SYSATT},
{"tit", /*title-attribute      */   &titlattr},
{"win", /*window-attribute     */   &windattr},
{"bor", /*border-attribute     */   &bordattr},
{"box", /*box-characterset     */   &boxcharset},
{"lin", /*line-max-chars       */   &MAXLN},
{"mos", /*most-lines-count     */   &MAXROWS},
{"row", /*row-max-index        */   &ROWMAX},
{"col", /*col-max-index        */   &COLMAX},
{"ret", /*return-insert-crlf   */   &EMACSMODE},
{"cga", /*cga-noretrace-video  */   &_egavga},
{"abo", /*abort-editor-char    */   &abortkey},
{"lpp", /*lpp-printer          */   &PRTlpp},
{"lef", /*left-spaces-printer  */   &PRTleft},
{"top", /*top-lines-printer    */   &PRTtop},
{"bot", /*bottom-lines-printer */   &PRTbot},
{"for", /*formfeed-printer     */   &PRTff},
{"tab", /*tab-insert-mode      */   &TABINSERT},
{"sea", /*search-token-lf      */   &zero},
{"kil", /*killed-line-max      */   &MAXKILLER},
{"xms", /*xms-maximum-blocks   */   &MAXXMS},
{"blo", /*block-buffer-size    */   &SIZEKBLOCK},
{"act", /*action-on-startup    */   &ACTIONSTART},
{"tra", /*trailing.space       */   &BLANKSTRIP},
#if BSDunix || SYS5
{"xmo", /*X-windows Xterm mouse*/   &mouseswitch},
{"ans", /*ansi-mode-unix       */   &ansimode},
{"tfg", /*tfg-text-fg-color    */   &textfgcolor},
{"tbg", /*tbg-text-bg-color    */   &textbgcolor},
{"mfg", /*mfg-menu-fg-color    */   &menufgcolor},
{"mbg", /*mbg-menu-bg-color    */   &menubgcolor},
#endif
#if MSDOS  /* Mapping of GREY keys on the keypad to other keycodes */
           /* Value is the msdos keycode replacement for the key */
           /* The code appears in routine ccBIOS (v8.c) */
{"plu", /*plus.grey.keycode    */   &GREYplus},
{"ast", /*asterisk.grey.keycode*/   &GREYast},
{"ent", /*enter.grey.keycode   */   &GREYent},
{"str", /*Mem blocks for str   */   &STASHmin},
{"sus", /*Suspend on ctrl-KX   */   &SUSPENDexit},
{"hor", /*horizontal-mouse-sens*/   &hsens},
{"clo", /*IBM cursor shape CL  */   &cursorCL},
{"chi", /*IBM cursor shape CH  */   &cursorCH},
{"ver", /*vertical-mouse-sens  */   &vsens},
#endif
};
int *x;
LLONG atoiLL();
#define OPSIZE sizeof(setup)/sizeof(setup[0])
#define KEYSIZE 3               /* size of key string in search */

  fp=fopenbread(userfile);
  if(fp!=(FILEP)0){
    while(fgetLN(buff,fp) >= 0){
      if(buff[0]==';' || buff[0]=='#') continue;
      for(i=0;i< (OPSIZE);++i){
        if(MYstrncmp(buff,setup[i].option,KEYSIZE)==0){
          p=MYstrchr(buff,'=');
          x=setup[i].setting;
          if(p != (char *)0) x[0] = (int)atoiLL(p+1);
        }
      }/* end for() */
    }/* end while() */
    closedfile(fp);
    return 1;
  }
  return 0;
}

void tblkill(x,buff) unsigned x; char *buff; {
int i;
char buf[128];
  /* Wipe out all references that match buff[] for code x */
  tcap_to_string(buff,buf);
  for(i=0;i<MAXKEYS;++i) {
    if(ourkeys[i].value == -1) continue;
    if(MYstrcmp(buf,"*all")==0) nulloutkey(i);
    else
    if(ourkeys[i].value == (int)x){
      if(buff[0]=='*') nulloutkey(i); /* wildcard match keys to code */
      else
      if(MYstrcmp(ourkeys[i].seq,buf)==0){ nulloutkey(i); return; }
    }
  }
}

void tblinsert(x,buff) unsigned x; char *buff; {
int i,u;
char buf[128]; char s[128]; char *p;
  tcap_to_string(buff,buf);
  /* A key cannot have two actions - check for conflicts */
  for(i=0;i<MAXKEYS;++i) {
    if(ourkeys[i].value == -1) continue;
    u=isinside(ourkeys[i].seq,buf);
    if(u==2 && ourkeys[i].value==x) return;     /* key already coded */
    if(u){ printseq("Bad key ignored: ",buf,s); goto fail; }
  }
  /* Add a new key into table */
  if((i=getkeyslot()) != -1) {
     if(safecore((char **)&(ourkeys[i].seq),buf)) ourkeys[i].value = (int)x;
     return;
  }
  strcpy(s,"Key table full");
fail:
  bellpressany(s);
}

void fputsline(buf,fp) char *buf; FILEP fp; {
  ewrite(fileno(fp),(char far *)buf,strlen(buf));
#if CRLFBOX
  ewrite(fileno(fp),(char far *)"\r\n",strlen("\r\n"));
#else
  ewrite(fileno(fp),(char far *)"\n",strlen("\n"));
#endif
}

/*
 * FGETLN
 *
 * Purpose:
 *              Read in a line of text, binary mode.
 * Returns:
 *              -1 for end of file
 *              Number of chars read, otherwise
 */
int fgetLN(buf,fp)
char *buf;
FILEP fp;
{
int i; extern int fgetc(); int x;
  for(i=0;i<511;++i){
    if((x=fgetc(fp)) == -1) goto err;
    buf[i]=x;
    if(x=='\n') break;
  }
  buf[i]=0;
  if(i>0 && buf[i-1]=='\r') buf[--i]=0;
  return (i+1);
err:
  buf[0]=0;
  return -1;
}

#if 0  /* Not used anymore */
int getln(p,m) char *p; int m; {
int c,n;

  n = 0;
  while(n<m) {
    c = ccBIOS(0);
    if(c == '\r' || c == '\n') break;
    if(c == 8 || c == 127) {
      if(n) {
        erachar(); --n;
      } else bell();
      continue;
    }
    p[n++] = c;
    p[n] = 0;
    etype(&p[n-1]);
  }
  p[n] = 0;
  return n;
}
#endif

void tcap_to_string(s,buf)   /* convert termcap string to C string */
char *s;                /* termcap-style string, no white space */
unsigned
char *buf;              /* destination string */
{                       /* PI uses termcap strings from ini-files */
int i,x;

  /* remove leading and trailing spaces */
  while(s[0]==' ') ++s;
  i=0;
  while((x=s[0]) != 0){
    ++s;
    if(x=='\\'){
      if('0'<=s[0] && s[0]<='7'){
        x=0;
        while('0'<=s[0] && s[0]<='7') {x = 8*x + (s[0] - '0'); ++s;}
      } else {
        x=s[0];
        ++s;
        switch(x){
        case 'E': x='\033'; break;
        case 'r': x='\r'; break;
        case 'n': x='\n'; break;
        case 't': x='\t'; break;
        case 'b': x='\b'; break;
        case 'f': x='\f'; break;
        }
      }
    } else {
      if(x == '^'){
        x=s[0]; if('a'<=x && x<='z') x=x-'a'+'A';
        x=x-'@';  /* make a control char */
        ++s;
        if(x==0) x=128;
      }
    }
    buf[i++]=x; if(x==0) break;
  }
  buf[i]=0;
}

#if TERMCAP
/* Read in termcap variables into editor system variables */
int decodetermcap(stat,s)
int stat; char *s;
{
/* get co,li,cm,al,dl,cs,sf,sr,ce,cd,rs,is,so,se,md,me,ks,ke,bl */
char *p;
char buf[512];
char *buff;
int x,error;

  error=0;
top:
  if(stat != 1){
    if(strcmp(s,"vt100internal")!=0){
#if macOSX || CYGWIN || SOLARIS
#else
      etype("Environment variables TERM and TERMCAP unset.\r\n");
      etype("TERM=vt100internal uses defaults to eliminate this message.\r\n");
      etype("pi -term:vt100internal does the same.\r\n");
      etype("Assuming VT100 internal terminal.\r\n");
      error=1;
#endif
    }
#define VT100eebuf \
"vt100|vt100internal:cm=\\E[%i%d;%dH:co#80:li#24:al=\\E[L:cd=\\E[J:\
ce=\\E[K:cl=\\E[H\\E[2J:dl=\\E[M:md=\\E[1m:me=\\E[m:mr=\\E[7m:\
sc=\\E7:rc=\\E8:sf=\n:so=\\E[7m:se=\\E[m:sr=\\EM:ku=\\EOA:\
ks=\\E[?1h\\E=:ke=\\E[?1l\\E>:"
    strcpy(eebuf,VT100eebuf);
    strcpy(s,"vt100internal");
  }
  buff= &buf[0];
  if(!egetstr("cm",&cm_termcap)){
    etype("Bad TERMCAP cursor motion CM.\r\n");
    stat = -1;
    goto top;
  }
  egetstr("al",&al_termcap);
  egetstr("dl",&dl_termcap);
  egetstr("ce",&ce_termcap);
  egetstr("cd",&cd_termcap);
  if(ce_termcap[0]==0 && cd_termcap[0]==0){
    etype("Bad TERMCAP CE and CD.\r\n");
    stat = -1;
    goto top;
  } else {
    egetstr("cs",&cs_termcap);
    if(0==egetstr("sf",&sf_termcap)) sf_termcap="\n";
    egetstr("sr",&sr_termcap);
    if(0==egetstr("pc",&pc_termcap)) pc_termcap="\200";
    egetstr("rs",&rs_termcap);
    egetstr("is",&is_termcap);
    egetstr("ks",&ks_termcap);
    egetstr("ke",&ke_termcap);
    egetstr("so",&REVVIDEO);
    egetstr("se",&NORMVIDEO);
    egetstr("md",&BOLDF);
    if(!egetstr("bl",&bl_termcap)) bl_termcap="\7";
    if(egetstr("me",&buff) && buff[0]) newcore(&NORMVIDEO,buff);
  }
  /* Set rows and columns on screen, reset max line if needed */
  x = tgetnum("co"); x -= 2; if(x <= -1) x=79; COLMAX=x;
  x = tgetnum("li"); x -= 2; if(x <= -1) x=22; ROWMAX=x;
  if(MAXLN <= COLMAX) MAXLN=COLMAX+10;

  /* Define variables HASSCROLL, HASDELETELINE, HASINSERTLINE */
  HASINSERTLINE=HASDELETELINE=HASSCROLL=0;
  if(al_termcap[0]) HASINSERTLINE=1;
  if(dl_termcap[0]) HASDELETELINE=1;
  if(cs_termcap[0] != 0 && sf_termcap[0] != 0 && sr_termcap[0] != 0)
    HASSCROLL=1;
  if(error){
    etype("Press RETURN to continue (ctrl-C aborts): ");
    if(getbyte()==3) {crlf(); quit(100);}
  }
  if(s[0]) newcore(&termtype,s);
  return 0;
}

char * skipdelay(s) char *s; {
char *p;
  p=s;
  while(MYisdigit(p[0])) ++p;
  if(p[0]=='.') ++p;
  while(MYisdigit(p[0])) ++p;
  if(p[0]=='*' && p!=s) ++p;
  return p;
}

int egetstr(s,d) char *s; char **d; {
char *p;
char buff[512];
  p=buff;
  p=tgetstr(s,&p); if(p==(char *)0){d[0]=""; return 0;}
  newcore(d,skipdelay(buff));      /* 2/96 Skip over stinking delays */
  return 1;
}

/* Typical termcap entry: d0|vt100|vt100-am|vt100am|dec vt100:\ */
/* First use: exact match to the terminal name string (c=='|'). */
/* eg, "|vt100-am|" appears in the string and we want to match "vt100-am" */
/* Second use: match token in the termcap string (c==':'). */
/* eg, ":tc=vt100" is in the string and we want to pick off "=vt100" */

char *findName(buf,name,c)
char *buf;              /* null-terminated string from termcap database */
char *name;             /* terminal name to locate */
int  c;                 /* left delimiter */
{
int n,i; char *p;
  p=buf;
  do {
    if(p[0]==0) break;
    i=0;
    if(c == '|'){ /* Use exact token length in match */
      while(p[i] != 0 && p[i] != '|' && p[i] != ':') ++i;
    }
    n=MYmax(i,strlen(name));
    if(MYstrncmp(p,name,n)==0) goto gotit;
    if((p=MYstrchr(p+1,c))!=(char *)0) ++p;
  } while(p!=(char *)0);
  return (char *)0;
gotit:
  if(c==':') return p+n;        /* Point past match */
  return p;                     /* Point to exact match */
}

/*   g e t E n t r y
 *  Gets the named entry from the already-opened termcap file into
 *  the buffer. The size of the buffer is 1024, and it is
 *  considered to be an error if the size of the entry equals or
 *  exceeds this. We place a terminating NULL character at the end
 *  of the entry. Call error() on any irregularities. Return 0 if
 *  the named entry not found, else 1. Removes terminal names and
 *  all newlines from the entry. If this is called for a 2nd time
 *  from tgetent(), then the length checking is useless.
 */
static int getEntry(fp,outbuf,name)
FILEP  fp;              /* Stream pointer for termcap file*/
char  *outbuf;          /* where we put the entry */
char  *name;            /* terminal type name to locate */
{
char    *p;             /* scrap pointer variable */
char    inbuf[BUFSIZE]; /* temporary termcap file buffer 1024 bytes */

  outbuf[0]=0;
  if(name==(char *)0 || name[0]==0) goto quit;
  while(fgetLN(inbuf,fp)>=0){
     if(inbuf[0]==0) continue;
     if(MYstrchr("#\t\r\n\f\b\040",inbuf[0])!=(char *)0) continue;
     /* must be a terminal line entry */
#if 0
 etype(inbuf); crlf(); /* this prints out all termcap terminal names! */
#endif
     if(findName(inbuf,name,'|')!=(char *)0){  /* found terminal name */
       do {
         if(inbuf[0]!='#' && *sob(inbuf)!=0){
            strcat(outbuf,sob(inbuf));
            p=endof(outbuf);
            if(p[-1]=='\\') p[-1]=0; /* continuation line found */
            else break;              /* Go check tc= capability */
         }
         if(fgetLN(inbuf,fp)<0) goto quit;      /* file format error */
       } while(inbuf[0]==0 || MYstrchr("#\t\r\n\f\b\040",inbuf[0])!=(char *)0);
       /* Check for tc= capability done by caller */
       goto success;    /* got termcap entry in outbuf */
     }/* end if(found terminal name) */
  }/* end while on reading stream=fp */
  return 0;     /* Could not find name in termcap file */
quit:           /* Get here on error, so set retval = -1 */
  return -1;
success:        /* Get here if outbuf[] successfully filled, retval=1 */
  return 1;
}

/*   t g e t e n t
 *  Extracts the entry for terminal name into the buffer at bp. Bp
 *  should be a character array of size 1024 and must be retained
 *  through all subsequent calls to tgetnum(), tgetflag(), and
 *  tgetstr(). Returns -1 if it cannot open the termcap file, 0 if
 *  the terminal name given does not have an entry, and 1 if all
 *  goes well. Looks in the environment for a TERMCAP variable. If
 *  found, and the value does not begin with a slash, and the
 *  terminal type name is the same as the environment string TERM,
 *  the TERMCAP string is used instead of reading the termcap file.
 *  If it does begin with a slash, the string is used as a pathname
 *  rather than /etc/termcap. This can speed up entry into programs
 *  that call tgetent(), as well as to help debug new terminal
 *  descriptions or to make one for your terminal if you can't write
 *  the file /etc/termcap.
 */
int tgetent(bp, name)
char  *bp;              /* pointer to buffer */
char  *name;           /* terminal type name string */
{
char  *termcap;         /* pointer to environment TERMCAP string */
FILEP fp;               /* file pointer for termcap file */
int   retval;           /* return value */
char *p;
char  newname[128];

  p = getenv("TERMCAP");
  termcap = TERMFILE;  /* in case :tc capability found */
  retval = -1;
  if(p != (char *)0){
    if(p[0]=='/' || (p[0]!=0 && p[1]==':')) termcap=p;
    else {
      if(findName(p,name,'|')!=(char *)0){
        /* use TERMCAP as the entry */
        strnzcpy(bp,p,MYmin(BUFSIZE,strlen(p)+1));
        retval =1;          /* using TERMCAP variable, no files needed */
      }
    }
  }
  if(retval == -1) {              /* look for entry in termcap file */
    if((fp = fopenbread(termcap))!=(FILEP)0) {
      retval = getEntry(fp, bp, name); closedfile(fp);
    }
  }
  if(retval == 1){
    /* consider :tc= capability */
    p = findName(bp,"tc",':');
    if(p != (char *)0) {
      retval = -1;
      strnzcpy(newname,&p[1],127);
      *skipto(":",newname )=0;
      if((fp = fopenbread(termcap))!=(FILEP)0){
        retval = getEntry(fp,&p[-2],newname); closedfile(fp);
      }
    }
  }

  return retval;
}



/*   t g e t n u m
 *  Gets the numeric value of capability id, returning -1 if it is
 *  not given for the terminal.
 */
int tgetnum(id)
char  *id;                /* capability name */
{
char  *p;
LLONG atoiLL();

  p = findName(eebuf,id,':');
  return ((p==(char *)0 || p[0] != '#') ? (-1) : (int)atoiLL(p+1));
}

/*   t g e t f l a g
 *  Returns 1 if found, 0 otherwise
 */
int tgetflag(id)
char  *id;                /* capability name */
{
char  *p;

  p = findName(eebuf,id,':');
  return (p != (char *)0  &&  p[0] == ':');
}



/*   t g e t s t r
 *  Returns the string value of the capability id, places it in the
 *  buffer at area, and advances the area pointer [past the
 *  terminating '\0' char]. It decodes the abbreviations for this
 *  field described in termcap(5), except for cursor addressing and
 *  padding information. Returns NULL if the capability was not
 *  found.
 */
char *tgetstr(id, area)
char  *id;              /* capability name */
char  **area;           /* pointer to output pointer */
{     
char    *retval;        /* return value */
char    *p;             /* pointer into capability string */
int     x;

  p = findName(eebuf,id,':');
  if(p == (char *)0 || p[0] != '=') retval = (char *)0;
  else {
    retval = area[0]; ++p;
    while(p[0] != ':') {
      if (p[0] == '\\'){
        ++p;
        if(MYstrchr("01234567",p[0])!=(char *)0){
          x=0;
          while(MYstrchr("01234567",p[0])!=(char *)0) x = 8*x + (*p++ - '0');
          x  =  (x & 255);
          --p;
          /** will \200 really end up as \000 like it should ? **/
          /** see termcap(5), top of page 6 **/
        } else {
          switch (p[0]) {
          case 'e': x  =  '\033';  break;
          case 'E': x  =  '\033';  break;
          case 'b': x  =  '\b';  break;
          case 'f': x  =  '\f';  break;
          case 'n': x  =  '\n';  break;
          case 'r': x  =  '\r';  break;
          case 't': x  =  '\t';  break;
          default:  x  =  (255&p[0]);  break;
          }
        }
      }
      else if(p[0] == '^')
        x  =  *++p - '@';  /* control */
      else
        x  =  p[0];        /* normal */
      *(*area)++  =  x;
      ++p;
    } /* end while */
    *(*area)++ = '\0';          /* NULL-terminate the string */
  } /* end else */
  return retval;
}



/*   t g o t o
 *  Returns a cursor addressing string decoded from cm to go to
 *  column destcol in line destline. It uses the external variables
 *  UP (from the up capability) and BC (if bc is given rather than
 *  bs) if necessary to avoid placing \n, ^D, or ^@ in the returned
 *  string. (Programs which call tgoto() should be sure to turn off
 *  the XTABS bit(s), since tgoto() may not output a tab. Note that
 *  programs using termcap should in general turn off XTABS anyway
 *  since some terminals use control I for other functions, such as
 *  non- destructive space.) If a % sequence is given which is not
 *  understood, then tgoto() returns "OOPS".
 *
 *  Output buffer is static - recursion will not work.
 *  No error checking here.
 */
char *tgoto(cm, destcol, destline)
char  *cm;              /* cm capability string */
int   destcol;          /* destination column (left is 0) */
int   destline;         /* destination line (top is 0) */
{
char    *outp;          /* pointer into answer[] */
static char  answer[88];/* result stashed here */
int  reversed;          /* ==1 when should send col 1st */
int     value;          /* next value to output */
extern char *edigits();


  reversed = 0;
  value = destline;
  outp = answer;
  while(cm[0]){
    if (*cm == '%') {
      switch (*++cm) {
      case '%': *outp++ = '%';
            break;
      case 'd':
            strcpy(outp,edigits(value,1));      /* no lead zeros unless "0" */
            outp += strlen(outp);
            goto rev;
      case '2': strcpy(outp,edigits(value,2));
            outp += 2;
            goto rev;
      case '3': strcpy(outp,edigits(value,3));
            outp += 3;
            goto rev;
      case '.': *outp++ = (value==0 ? 128 : value);
            goto rev;
      case '+': *outp++ = value + *++cm;
rev:        value = (reversed) ? destline : destcol;
            break;
      case '>': if(value > *++cm) value += *++cm; else ++cm;
            break;
      case 'r': value = (reversed) ? destline : destcol;
            reversed ^= 1;
            break;
      case 'i': ++value; ++destline; ++destcol;
            break;
      case 'n': destcol  ^= 0140;
            destline ^= 0140;
            break;
      case 'B': value = (16 * (value / 10))  +  (value % 10);
            break;
      case 'D': value = (value - (2 * (value % 16)));
            break;
      default:  strcpy(outp,"OOPS");
            outp += 4;
            break;
      }
    } else *outp++ = *cm;
    ++cm;
  }
  *outp = '\0';
  return answer;
}



/*   e t p u t s
 *  Decodes the leading pad information of the string cp; affcnt
 *  gives the number of lines affected by the operation, or 1 if
 *  this is not applicable. Outc is a routine which is called with
 *  each character in turn. The external variable ospeed should
 *  contain the output speed of the terminal as encoded by stty(3).
 *  The external variable PC should contain a pad character to be
 *  used (from the pc capability) if a null (^@) is inappropriate.
 */
void etputs(cp, affcnt)
char  *cp;              /* leading pad information string */
int   affcnt;           /* number lines affected, or 1 */
{
char    *p;
int  delay;
/* milliseconds per character, for each of the possible baud rates of ospeed */
/* table is multiplied by 10 to eliminate decimal arithmetic */
static unsigned  delayFactor[] = {
     0,   /* B0 */  /* hang up  or network device */
  1920,   /* B50 */
  1280,   /* B75 */
   872,   /* B110 */
   730,   /* B134 */
   640,   /* B150 */
   480,   /* B200 */
   320,   /* B300 */
   160,   /* B600 */
    80,   /* B1200 */
    50,   /* B1800 */
    40,   /* B2400 */
    20,   /* B4800 */
    10,   /* B9600 */
     5,   /* EXTA (19200 here) */
     2,   /* EXTB (34800 here) */
};
LLONG atoiLL();

  etype(skipdelay(cp));               /* output string, delay later */
  /* calculate delay and output pad characters, if any */
  delay=(int)atoiLL(cp);
  if(delay){
    if(ospeed){                         /* nonzero tty speed? */
      delay = 10*delay;       /* delay factor is in 10ms units*/
      while(MYisdigit(cp[0])) ++cp;
      if(cp[0] == '.'){                 /* decimal point? */
        delay += (5 + (int)atoiLL(++cp)*100)/10;
        while(MYisdigit(cp[0])) ++cp;
      }
      if(cp[0] == '*') delay *= affcnt;
      /* output delay characters */
      while(delay>0){ etype(pc_termcap); delay -= delayFactor[ospeed];}
    }/* end if */
  }/* end if */
}

/*
 * EDIGITS
 *  Purpose:   special itoa() with zero-fill to n-digits.
 */
char *
edigits(r,n)
int r,n;
{
char d[11];
static char e[11];
register int i,j;
  if(n > 10) n=10;
  i = r; j = 0;
  do{
     d[j++] = '0' + i%10; i /= 10;
  } while(i);
  i=0;
  while(j<n) d[j++]='0';
  while(--j>=0) e[i++] = d[j];
  e[i]=0;
  return e;
}

#endif          /* TERMCAP */
#endif
/*
 * V11.C
 *
 *      Center line module.
 *      Line reform module.
 *      Help routines for LOADINI option.
 */
#include "pie.h"

/*
 * REFORM
 *
 *      The purpose of this subroutine is to act on a given line pair.
 *      These actions are taken:
 *
 *        1. Processing starts at the current cursor position.
 *        2. Leading spaces are replaced by the number of spaces
 *           in the current tab setting unless INDENAUTO!=0. See 8.
 *        3. Spaces after the leading spaces are reduced to a
 *           single space, except for ".!?" followed by two or
 *           more spaces, which are reduced to two spaces.
 *        4. If the line becomes full as a result of copying
 *           characters, then the line is split into two lines.
 *        5. If the line does not fill, then the characters on the
 *           next line are used to help fill the line. If that
 *           line is completely blank, then processing stops.
 *        6. At most one line back or ahead is accessed.
 *        7. The cursor remains at the margin column of the current
 *           line, or it is placed at the start of the next line if
 *           that line has characters other spaces, or it is placed at
 *           the end of the line, in case the next line is blank.
 *        8. The indent rules are fairly complex. The idea is to do an
 *           indent differently in col==0 than for a col>0. If col==0,
 *           then indent happens. Otherwise, the indent has no effect.
 *           The amount of indent depends upon the setting of variable
 *           INDENAUTO:
 *             case 1. INDENAUTO==0
 *             Then indent=0, if there is no white space at the front of
 *             the line. Otherwise indent=TSTOPS.
 *             case 2. INDENT != 0
 *             The indent will equal the indent of the previous line, if
 *             that makes sense, otherwise TSTOPS is used, unless the
 *             current line is without leading space, in which case the
 *             indent is zero.
 *        9. Reform from a blank line onto a full line will go to the
 *           start of the full line. This prevents repeat keys from
 *           corrupting a previously reformed paragraph. The effect is
 *           undone by INDENAUTO==0.
 */
#if !HASSOB
char *sob(p) char *p; {
  while(p[0] == ' ') ++p;
  return p;
}
#endif

#if !HASFNB
char *fnb(p) char *p; {
  while(p[0] != EOS && p[0] != ' ') ++p;
  return p;
}
#endif

/* center text within margins */
void textcenter(){
int i; char *p; char *sob();
  strip(p=sob(line[row]));                      /* trim off blanks */
  if((i=strlen(p)) <= margin){
    i=(margin-i)/2;                             /* add leading blanks */
    fillchr(sysvv,' ',i);                       /* to target string */
    strcpy(&sysvv[i],p);                        /* copy tail */
    fixline();                                  /* fixup screen */
    eddn(); curses();
  } else bell();
}

void reform() {
char *r;
int ind;
char safe[512];
int oldcol;
extern char *ju(),*sob();
extern unsigned char *curchar();
extern char *sysvv;
extern int firstnonblank();

  if(ROWMAX <= 1) return;
/* A mistake? Need to add curoff support someday. */
  if(curoff>0){setoffset(curoff=0); drawall();}
  curchar();                                 /* Add blanks as needed. */
  ind=(!col && line[row][0]==' ') ? TSTOPS:0;/* default indent */
  if(INDENAUTO){                             /* set indent for AUTO */
    if(col) ind=firstnonblank(line[row]);    /* use line's indent */
    else if(lncount>1) {
      edup(); eddn(); curses();              /* on row 2 or greater */
      if(*sob(r=line[row-1]))                /* Use indent of */
        ind=firstnonblank(r);                /* previous line. */
    }
    /* Otherwise, use the default indent already set. */
  }
  if(row == ROWMAX) { eddn(); edup(); curses();}
  if(*sob(line[row]) == EOS) {               /* Line all blanks */
    eddn(); col=firstnonblank(line[row]); goto quit;
  }
  if(*sob(line[row+1]) == EOS && *sob(curchar()) == EOS)
    goto nextline;                           /* At end, next line blank */
  if(ind>=margin) goto nextline;             /* Indent too large */
  /* Search the line for a word boundary on which to begin reform. */
  r=line[row];
  oldcol=col;
  while(col<margin && r[col] && r[col] != ' ') ++col;
  if(r[col] && r[col] != ' '){
    col=MYmin(col,margin);
    while(col>0 && r[col] != ' ') --col;
    while(r[col] == ' ' && col>0 && r[col-1] == ' ') --col;
    if(col==0) {bell(); goto nextline;}
  }
  strnzcpy(sysvv,r,col);                     /* Copy up to cursor */
  if(!col) fillfront(sysvv,ind,"");          /* Nothing? Then indent.*/
  r = ju(sysvv,r+col,margin);                /* Justify into sysvv[] */
  if(r[0]) {                                 /* from cursor forward.*/
    if(INDENAUTO){
      if(ind+strlen(r)<MAXLN) fillfront(safe,ind,r);
      else { fixline(); goto setcursor; }
    } else
    strcpy(safe,r);
    fixline();
    eddn(); curses();
    addln(safe,TRUE);
    col = firstnonblank(safe); goto quit;
  }
  else {
    r = line[row+1];
    if(*sob(r)==0 || (!INDENAUTO && (r[0] == EOS || r[0] == ' '))) {
       /* new paragraph */
       if(oldcol<strlen(line[row])){fixline(); goto setcursor;}
       goto nextline;
    }
    else {                              /* next line has chars */
      r = ju(sysvv,r,margin);           /* add more onto sysvv[] */
      fixline();                        /* print what we got */
      /* now do the next line, from the spillover */
      if(INDENAUTO){
        if(ind+strlen(r) < MAXLN) fillfront(sysvv,ind,r);
        else goto setcursor;
      } else
      strcpy(sysvv,r);
      eddn(); curses();
      if(r[0]) {fixline(); col = firstnonblank(sysvv); goto quit;}
      else { delln(sysvv); edup();}
    }
  }
goto setcursor;
nextline:
  eddn();
setcursor:
  col = MYmin(strlen(line[row]),margin);
  CenterRefresh();      /* if on last line then refresh to center */
quit:
  return;
}

/*
 * FIXLIN
 *
 * Purpose:
 *              Erases the current line, replaces it with the
 *              line held in sysvv[].
 */
void fixline()
{
extern char *toprint();
  xPrint(toprint(sysvv),row,col=0); eraseol();
  strcpy(line[row],sysvv);
}

/*
 * FIRSTNONBLANK
 *
 * Find string index to first nonblank char or to end of string
 */
int firstnonblank(s) char *s; {
extern char *sob();
  return (int)(sob(s)-s);
}

/*
 * FILLFRONT
 *
 * Fills a string with blanks then appends another string.
 *
 */
void fillfront(d,ind,s) char *d,*s; int ind; {
  fillchr(d,' ',ind); strcpy(d+ind,s);
}

/*
 * JU
 *
 * Purpose: Does single line justification.
 *
 The basic algorithm is to begin this routine with sysvv[]
 filled. The contents of array buf[] are added to the end of
 sysvv[] until the margin is exceeded. The position in buf[]
next to be processed is returned (hopefully end of string).

 */
char *ju(d,s,margin)
char *d;        /* destination buffer */
char *s;        /* source buffer */
int margin;     /* right margin limit */
{
char *r,*p;
int i,j,flag;
char *sob(),*fnb(),*MYstrchr();

  j = strlen(d);
  p = fnb(r=sob(s));
  while(r[0]) {
    if((i = (int)((fnb(r))-r)) == 0) break;
    flag = (j > 0 && d[j-1] != ' ');
    if(flag){
      ++i;
      if(MYstrchr(".!?",d[j-1])!=(char *)0) {  /* Worcester add on 1992 */
        if(p[0]==' ' && p[1]==' ') d[j++]=' ';
      }
    }
    if((i+j) <= margin) {
      if(flag) { d[j++] = ' '; --i; }   /* add space after token*/
      while(i--) d[j++] = *r++;         /* copy token */
      r = sob(p=r);                     /* position to next token */
    } else break;
  }
  d[j] = EOS;
  return r;
}

#if PIHELPDOC
/* PIHELPH.C  - color or mono version of pihelp for HTML source     */
extern long ftell();

#define SLASHCHAR '/'
void setcolors(){}
void setcolormono(){}
void setcolorinverse(){}
void helpcputs(s) char *s; { ePrint(s);}
void clrscrn(){ clearscreen(); home();}

void
cr(){ etype("\r"); }

int isHTML(s) char *s; {
  if(s[0]=='<') return 1;
  return 0;
}
    
/* Detect HTML line  <a NAME="blah blah blah"></a> */
int isaheader(s) char *s; {
char *p;
char t[256];
  if(! isHTML(s) ) return 0;
  if(MYstrchr("aA",s[1])==0) return 0;
  if(MYstrstr(s,"name=")!=0 || MYstrstr(s,"NAME=")!=0){
    p=MYstrchr(s,'=');
    if(p==0) return 0;
    p=MYstrchr(s,'"');
    if(p[1]=='"') ++p;
    if(p==0) return 0;
    ++p;
    strcpy(t,p);
    p=MYstrchr(t,'"');
    if(p==0) return 0;
    p[0]=0;
    strcpy(s,t);
    return 1;     /* Header title in string s[] */
  }
  return 0;
}

void
getReturnKey(){
  pressany("");
}
    
void
killLinefeed(s) char *s; {
extern char *MYstrchr();
char *p;
  if((p=MYstrchr(s,'\n'))!=(char *)0) p[0]=0;
  if((p=MYstrchr(s,'\r'))!=(char *)0) p[0]=0;
}

int fgetline(s,fp) char *s; FILE *fp; {
  if(fgets(s,128,fp)==(char *)0) return 0;
  killLinefeed(s);
  return 1;
}

#define ROWS 8          /* Use 8 rows of items per help screen */
#define MAXSTRING 256   /* Largest string expected in helpfile */
#define MAXLPS 22       /* Max lines per screen */

#define INFILE "pihelp.htm"     /* Name of the helpfile */
#define TBLSIZ 500              /* Max number of index items */


void help() {
long *table;
char **token;
char *p,*q;
FILE *fp;
char file[128];
char s[MAXSTRING],keep[MAXSTRING];
long place;
int i,n,j,k,m,x,base,nlines;
int looping,maxlines;
char header[128];
char leader[128];
extern char *MYstrchr(),*MYstrrchr();
extern char *sobl();
extern char *PIHLPDOC;
    
  if(ROWMAX<13 || COLMAX<72){
    pressany("Screen size too small for manual");
    return;
  }

  if((table =(long *)malloc(sizeof(long)*TBLSIZ))==(long *)0) goto bad;
  if((token =(char **)malloc(sizeof(char *)*TBLSIZ))==(char **)0){
     free(table); goto bad;
  }
    
  strcpy(file,PIHLPDOC);
  fp = fopenbread(file);
  if(fp == (FILE *)0) {
   helpcputs("Can't open file "); helpcputs(file); crlf();
   goto bad;
  }

  maxlines = MAXLPS;
  setcolors();

  s[0]=n=0;
  while(n<TBLSIZ) {
    place = (long)ftell(fp);
    if(fgetline(s,fp) <= 0) break;
    if(isaheader(s)){                  /* Changes s[] to header text */
      if(!safecore(&token[n],s)) break;
      table[n++] = place;
    }
  }

  base = 0;
  looping = 1;
  while(looping) {

   clrscrn();
   m = (n < base+ROWS) ? n : (base+ROWS);
   k = n/ROWS; if((n%ROWS)!=0) ++k;
   ssprintf(header,"%s %d of %d","        HELP TOPICS",1+base/ROWS,k);
   helpcputs(header); crlf(); crlf();
   for(k=0,i=base;i<m;++i,++k) {
      ssprintf(leader,"        %d  -  ",1+k);
      helpcputs(leader);
      helpcputs(token[i]); crlf();
   }
   crlf();
   helpcputs("        N  -  Next Screen");
   crlf();
   helpcputs("        P  -  Previous Screen");
   crlf();
   helpcputs("        Q  -  Quit");
   crlf();
   crlf();
   helpcputs("Select: ");

   x = getupper();
   switch(x) {
   case '1': case '2': case '3': case '4':
   case '5': case '6': case '7': case '8':
             j = x - '1' + base; break;
   case 0: case ' ': case '\r': case '\n': case 'N': case 'N'-'@':
             base = base + ROWS; if(base >= n) base = 0;
             j = -1; break;
   case 127: case 8: case 'B': case 'P': case 'P'-'@':
             base = base - ROWS; if(base < 0) { base = n - n%ROWS;}
             if(base < 0) base = 0;
             j = -1; break;
   case 3:   case 'X': case 'Q':
             looping = 0; j = -1; break;
   default: bell(); j = -1; break;
   }
repeat:
   if(0 <= j && j < n) {
      clrscrn();
      s[0]=nlines=0;
      fseek(fp,table[j],0);
      if(fgetline(s,fp) <=0) goto badseek;
      if(!isaheader(s)) goto badseek;
      strinvvideo(s); crlf();
      strcpy(keep,s);
      while(1) {
          if(fgetline(s,fp) <= 0) break;
          if(isaheader(s)){
            while(nlines++ < maxlines) crlf();
            break;
          }
          if(isHTML(s)) continue;
          helpcputs(s); crlf(); ++nlines;
          if(nlines >= maxlines) {
             helpcputs("--MORE--"); x = getupper();
             if(MYstrchr("QXESN\3\32",x) != (char *)0) break;
             clrscrn();
             strinvvideo(keep); crlf();
             nlines=1;
          }
      }
      cr(); helpcputs("END OF SECTION. Press Q to quit: ");
      if(MYstrchr("QqXxSs\3\32",getupper())==(char *)0){++j; goto repeat;}
   }
  }

stop:
  closedfile(fp);
  goto normalexit;
badseek:
 helpcputs("Bad seek in "); helpcputs(file); crlf();
 getReturnKey();
normalexit:
  for(i=0;i<n;++i) free(token[i]);
  free(token);
  free(table);
normal1:
  setattribute(0);
  return;
bad:
  getReturnKey();
  goto normal1;
}

#endif /* end PIHELPDOC */
/* V16.C  Sources for getchGNU() and lccwin32 */
#if LCCWIN32
#include <windows.h>

#define VK_GREYDIV 191                                                
#define VK_GREYENT 13                                                 
#if 0
struct {                                                      
  int vk;
  const char *val[5];
} keytable[] = {
/*           NORMAL      SHIFT        CTRL         ALT        SHIFTCTRL   */
{VK_LEFT,    {"\033[D",  "\033[D;2~", "\033[D;5~", "\033[D",  "\033[D;6~"}},
{VK_RIGHT,   {"\033[C",  "\033[C;2~", "\033[C;5~", "\033[C",  "\033[C;6~"}},
{VK_UP,      {"\033[A",  "\033[A;2~", "\033[A;5~", "\033[A",  "\033[A;6~"}},
{VK_DOWN,    {"\033[B",  "\033[B;2~", "\033[B;5~", "\033[B",  "\033[B;6~"}},
{VK_PRIOR,   {"\033[5~", "\033[5;2~", "\033[5;5~", "\033[5~", "\033[5;6~"}},
{VK_NEXT,    {"\033[6~", "\033[6;2~", "\033[6;5~", "\033[6~", "\033[6;6~"}},
{VK_HOME,    {"\033OH",  "\033OH;2~", "\033OH;5~", "\033OH",  "\033OH;6~"}},
{VK_END,     {"\033OF",  "\033OF;2~", "\033OF;5~", "\033OF",  "\033OF;6~"}},
{VK_INSERT,  {"\033[2~", "\033[2;2~", "\033[2;5~", "\033[2~", "\033[2;6~"}},
{VK_DELETE,  {"\033[3~", "\033[3;2~", "\033[3;5~", "\033[3~", "\033[3;6~"}},
{VK_F1,      {"\033[11~","\033[11;2~","\033[11;5~","\033[11~","\033[11;6~"}},
{VK_F2,      {"\033[12~","\033[12;2~","\033[12;5~","\033[12~","\033[12;6~"}},
{VK_F3,      {"\033[13~","\033[13;2~","\033[13;5~","\033[13~","\033[13;6~"}},
{VK_F4,      {"\033[14~","\033[14;2~","\033[14;5~","\033[14~","\033[14;6~"}},
{VK_F5,      {"\033[15~","\033[15;2~","\033[15;5~","\033[15~","\033[15;6~"}},
{VK_F6,      {"\033[17~","\033[17;2~","\033[17;5~","\033[17~","\033[17;6~"}},
{VK_F7,      {"\033[18~","\033[18;2~","\033[18;5~","\033[18~","\033[18;6~"}},
{VK_F8,      {"\033[19~","\033[19;2~","\033[19;5~","\033[19~","\033[19;6~"}},
{VK_F9,      {"\033[20~","\033[20;2~","\033[20;5~","\033[20~","\033[20;6~"}},
{VK_F10,     {"\033[21~","\033[21;2~","\033[21;5~","\033[21~","\033[21;6~"}},
{VK_F11,     {"\033[23~","\033[23;2~","\033[23;5~","\033[23~","\033[23;6~"}},
{VK_F12,     {"\033[24~","\033[24;2~","\033[24;5~","\033[24~","\033[24;6~"}},
{VK_NUMPAD5, {"\033OE",  "\033OE;2~", "\033OE;5~", "\033OE",  "\033OE;6~"}},
{VK_CLEAR,   {"\033OE",  "\033OE;2~", "\033OE;5~", "\033OE",  "\033OE;6~"}},
{VK_GREYDIV, {"\033Oo",  "\033Oo",    "\033Oo",    "\033Oo",  "\033Oo"}},
{VK_MULTIPLY,{"\033Oj",  "\033Oj",    "\033Oj",    "\033Oj",  "\033Oj"}},
{VK_SUBTRACT,{"\033Om",  "\033Om",    "\033Om",    "\033Om",  "\033Om"}},
{VK_ADD,     {"\033Ok",  "\033Ok",    "\033Ok",    "\033Ok",  "\033Ok"}},
{VK_GREYENT, {"\033OM",  "\033OM",    "\033OM",    "\033OM",  "\033OM"} },
{0,          {NULL,      NULL,        NULL,        NULL,      NULL}}
};                                                                     
#endif
struct {                                                      
  int vk;
  const char *val;
} keytable[] = {
/*           NORMAL     */
{VK_LEFT,    "\033[D"},
{VK_RIGHT,   "\033[C"},
{VK_UP,      "\033[A"},
{VK_DOWN,    "\033[B"},
{VK_PRIOR,   "\033[5~"},
{VK_NEXT,    "\033[6~"},
{VK_HOME,    "\033OH"},
{VK_END,     "\033OF"},
{VK_INSERT,  "\033[2~"},
{VK_DELETE,  "\033[3~"},
{VK_F1,      "\033[11~"},
{VK_F2,      "\033[12~"},
{VK_F3,      "\033[13~"},
{VK_F4,      "\033[14~"},
{VK_F5,      "\033[15~"},
{VK_F6,      "\033[17~"},
{VK_F7,      "\033[18~"},
{VK_F8,      "\033[19~"},
{VK_F9,      "\033[20~"},
{VK_F10,     "\033[21~"},
{VK_F11,     "\033[23~"},
{VK_F12,     "\033[24~"},
{VK_NUMPAD5, "\033OE"},
{VK_CLEAR,   "\033OE"},
{VK_GREYDIV, "\033Oo"},
{VK_MULTIPLY,"\033Oj"},
{VK_SUBTRACT,"\033Om"},
{VK_ADD,     "\033Ok"},
{VK_GREYENT, "\033OM"},
{0,          NULL}
};                                                                     
                                                                       
const char *                               
get_nonascii_key (INPUT_RECORD &input_rec, char *tmp)
{
#define control_key_state (input_rec.Event.KeyEvent.dwControlKeyState)
#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
#define NORMAL  0       /* NO modifier keys */
#define SHIFT   1       /* LEFT or RIGHT SHIFT */
#define CONTROL 2       /* LEFT or RIGHT CONTROL */
#define ALT     3       /* LEFT or RIGHT ALT */
#define SHIFTC  4       /* SHIFT-CONTROL */
  int i;
  int modifier_index = NORMAL;

  if (control_key_state & SHIFT_PRESSED){
    modifier_index = SHIFT;
    if(control_key_state & CTRL_PRESSED)
      modifier_index = SHIFTC;
  }
  else if (control_key_state & CTRL_PRESSED)
    modifier_index = CONTROL;
  else if (control_key_state & ALT_PRESSED)
    modifier_index = ALT;
    for (i = 0; keytable[i].vk; i++){
      if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk){
        strcpy(tmp,keytable[i].val);
#if 0
        switch(modifier_index){
        case NORMAL:  /* NO modifier keys */
        case ALT:     /* LEFT or RIGHT ALT */
          break;
        case SHIFT:   /* LEFT or RIGHT SHIFT */
          strcat(tmp,";2~"); break;
        case CONTROL: /* LEFT or RIGHT CONTROL */
          strcat(tmp,";5~"); break;
        case SHIFTC:  /* SHIFT-CONTROL */
          strcat(tmp,";6~"); break;
        };
#endif
        return tmp;
      }
    }

  if ((i=input_rec.Event.KeyEvent.uChar.AsciiChar)!=0){
      tmp[0] = i;
      tmp[1] = '\0';
      if(modifier_index == ALT && isalpha(i)){
       tmp[2]='\0';
       tmp[1]=i;
       tmp[0]='\033';
      }
      return tmp;
  }
  return NULL;
}

/* Distinguish 10-key or shifted keypad and enhanced keys. Detect ALT. */
int isSpecialKey(unsigned vk, DWORD cks) {
  if((cks & NUMLOCK_ON)!=0) return 0;  /* keypad is 10-key */
  if((cks & ENHANCED_KEY)!=0) return 1;
  if((cks & ALT_PRESSED)!=0) return 1;
  if(vk==VK_MULTIPLY || vk==VK_SUBTRACT || vk==VK_ADD) return 1;
  return 0;
}

HANDLE get_the_io_handle(void){
static HANDLE h= (HANDLE)-1;
if(h == (HANDLE)-1){
  h = GetStdHandle(STD_INPUT_HANDLE);
#if 0
SECURITY_ATTRIBUTES sa;
 sa.bInheritHandle=1;
 h=CreateFile((LPCTSTR)"CONIN$",GENERIC_READ,
              FILE_SHARE_READ | FILE_SHARE_WRITE,&sa,
              OPEN_EXISTING,(DWORD)0,(HANDLE)0);
#endif
 SetConsoleMode(h,0); /* Enable ctrl-c passthrough */
}
return h;
}

int getchGNU(void){
static  char tmp[60]={0};
  HANDLE h = get_the_io_handle();
  int ch;

top:
  if (tmp[0]){
    ch=tmp[0]; strcpy(tmp,tmp+1);
    return ch;
  }

  for (;;)
    {
      DWORD nread;
      INPUT_RECORD input_rec;
      const char *toadd = NULL;

      if (!ReadConsoleInput (h, &input_rec, 1, &nread))
          return -1;            /* seems to be failure */

      /* check the event that occurred */
#define virtual_key_code (input_rec.Event.KeyEvent.wVirtualKeyCode)
#define control_key_state (input_rec.Event.KeyEvent.dwControlKeyState)
#define ich (input_rec.Event.KeyEvent.uChar.AsciiChar)
#define wch (input_rec.Event.KeyEvent.uChar.UnicodeChar)
#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
      switch (input_rec.EventType)
        {
        case KEY_EVENT:

          if (!input_rec.Event.KeyEvent.bKeyDown)
            continue;

          if (wch == 0 || isSpecialKey(virtual_key_code,control_key_state))
            { /* arrow/function/enhanced/alt keys */
              toadd = get_nonascii_key (input_rec, tmp);
              if (!toadd) continue;
              strcpy(tmp,toadd);
              break;
            }
          else
            {
              tmp[0] = ich; tmp[1]=0; toadd=tmp; break;
            }
          break;

        default:
          continue;
        }

      if (toadd) goto top; else break;
    }
  return -1;
}
#undef ich
#undef wch
#undef ALT_PRESSED
#undef CTRL_PRESSED
#undef ich
#endif  /* end LCCWIN32, V16.C */
#endif  /* end of pie1.c source file */
#ifndef PIE1_C_SRC
/* pie2.c, second half of pie.c that requires turboc asm compile.  */
#if 0
#define KEYDEFS 1       /* include key definitions from v.h */
#include "pie.h"        /* Set options here for OS and compiler */
#include "pieproto.h"   /* Prototypes for all functions in pie.c */
#endif

/*
 * V12.C
 *
 *      The idea behind this module is to manage the text buffer
 *      as external blocks. For speed, these blocks are kept in
 *      main memory. Code must provide fast copies to and from the
 *      main text buffer, and of course, fast loads from disk.
 */
#include "pie.h"

#if TURBOC
#pragma inline
void CPfromBLOCK(I,P,n) int I; char *P; int n;{
/* This code by Steve Worcester, Nov 1988 */
getmemblock(I);         /* Load external block to low memory */
#if 0
asm     mov     ax,es   /* save es and ds */
asm     mov     dx,ds
asm     mov     cx,n    /* count */
asm     mov     bx,I    /* offset into memblock[] */
asm     shl     bx,1    /* element width 4 */
asm     shl     bx,1
asm     add     bx,word ptr DGROUP:_memblock
asm     mov     di,P    /* dest is es:di */
asm     lds     si,dword ptr [bx]  /* source ds:si */
asm     mov     es,dx   /* swap in segment for dest */
asm     cld
asm     rep     movsb   /* move it all */
asm     mov     ds,dx   /* restore ds */
asm     mov     es,ax   /* restore es */
#else
asm     push    es
asm     push    ds
asm     mov     dx,ds
asm     mov     ax,I    /* offset into memblock[] */
asm     shl     ax,1    /* element width 4 */
asm     shl     ax,1
asm     les     bx,dword ptr DGROUP:_memblock
asm     add     bx,ax
asm     lds     si,dword ptr es:[bx]  /* source ds:si */
asm     mov     di,P    /* dest is es:di */
asm     mov     es,dx   /* swap in segment for dest */
asm     mov     cx,n    /* count */
asm     cld
asm     rep     movsb   /* move it all */
asm     pop     ds
asm     pop     es
#endif
}

#if 0
/* This routine requires a block move of the data before the copy */
/* in order to succeed. The problem is that dest[] and src[] must */
/* be so many chars apart for this to work. */
void slowkillcrlf(dest,src,nlines,nbytes,splits)
char far *dest; char far *src;
int *nlines,*nbytes,*splits;
{
extern int intexttype;
register int x,nbyte;
int nline;
int lc;
register char far *s;
register char far *d;
extern int MAXLN;

    d=dest; s=src;
    lc=MAXLN-1; nbyte=nbytes[0];
    s[nbyte]=26; /* 26 chosen because it's not equal to 13, 10, 0, 255 */
    nline=0;
    while(nbyte-- > 0){
      x=(s[0]);
      /* Read MSDOS, MAC, UNIX files by default */
sk1:
      if(x==13){
        x=s[1];           /* works because s[nbyte]==26 */
        if(x==0 || x==10) {++s; --nbyte;}
        goto newline;
      }
sk2:
      if(x==0 || x== 10) {
newline: *d++ = 0; ++s; lc=MAXLN-1; ++nline; continue;
      }
sk3:
      if(!lc) {
splitline:  *d++ = 0; ++nline; *d++ = 2; lc=MAXLN-1-1; splits[0] += 1;
      }
sk4:
      *d++ = *s++; --lc;
    }
    nbytes[0]=((int)(d-dest)); nlines[0]=nline;
}
#endif

#if 1
void slowkillcrlf(dest,src,nlines,nbytes,splits)
char far *dest; char far *src;
int *nlines,*nbytes,*splits;
{
int dsreg;
/*extern int MAXLN;*/
  asm   mov     cx,MAXLN        /* cx == MAXLN-1 */
  asm   dec     cx                              /* cx=MAXLN-1 */
  asm   mov     bx,nbytes
  asm   mov     dx,word ptr [bx]                /* dx==number of bytes*/
  asm   les     di,src
  asm   add     di,dx
  asm   mov     byte ptr es:[di],26             /*  src[nbytes]=26; */
  asm   les     di,dest                         /* di=dest */
  asm   xor     bx,bx                           /* nlines=0; line count */
  asm   push    ds
  asm   push    bx
  asm   push    cx
  asm   lds     si,src                          /* si=src */
slowloop:
/* while(nbyte-- > 0) */
  asm   or      dx,dx                           /* test nbyte-- == 0 */
  asm   je      slowkillquit
  asm   dec     dx                      /* --nbyte */
  asm   mov     al,byte ptr ds:[si]     /* al == s[0] */
  asm   mov     ah,al
/*  if(x==13){if(s[1]==0 || s[1]==10) {++s; --nbytes;}; goto newline;}*/
/*  This works because s[nbytes]==26 */
sk1:
/*  if(x==13){ */
  asm   cmp     al,13
  asm   jne     sk2                     /* Go test 0, 10 */
/*  if(s[1]==0 || s[1]==10) */
  asm   inc     si                      /* ++s */
  asm   mov     al,byte ptr ds:[si]     /* Works because s[nbytes]==26 */
  asm   cmp     al,10                   /* s[1] == 10? */
  asm   je      slowkill4
  asm   cmp     al,0                    /* s[1]==0? */
  asm   je      slowkill4
  asm   dec     si
  asm   jmp     short newline           /* got a line with CR only   */
/* if(x==0||x==10){*dest++ =0; ++src; lc=MAXLN-1; ++nlines; continue;} */
slowkill4:
  asm   or      dx,dx                   /* Only decrement dx if dx>0 */
  asm   je      newline
  asm   dec     dx
newline:
  asm   mov     byte ptr es:[di],0
  asm   inc     di              /* *dest++ = 0 */
  asm   inc     si              /* ++src; */
  asm   pop     cx              /* cx == MAXLN-1 */
  asm   push    cx
  asm   inc     bx              /* count lines */
  asm   jmp     slowloop
sk2:
  asm   cmp     al,10           /* x == 10 */
  asm   je      newline
  asm   cmp     al,0            /* x==0 */
  asm   je      newline
/* if(!lc) {*dest++ =0; ++nlines; *dest++ =2; lc=MAXLN-1-1; splits[0]+=1;} */
sk3:
  asm   mov     byte ptr es:[di],ah
  asm   or      cx,cx
  asm   jne     sk4                     /* No, then continue... */
  asm   mov     byte ptr es:[di],0      /* break the line */
  asm   inc     di
  asm   inc     bx
  asm   mov     byte ptr es:[di],2      /* Marker is ctrl-B */
  asm   inc     di
  asm   mov     byte ptr es:[di],ah
  asm   pop     cx                      /* cx == MAXLN-1 */
  asm   pop     ax
  asm   inc     ax                      /* splits[0] += 1 */
  asm   push    ax
  asm   push    cx
  asm   dec     cx                      /* lc = MAXLN-2 */
/*  *d++ = *s++; --lc; */
sk4:
  asm   inc     di                      /* *dest++ = *src++ */
  asm   inc     si
  asm   dec     cx                      /* --lc */
  asm   jmp     slowloop

slowkillquit:
  asm   pop     cx
  asm   pop     dx
  asm   pop     ds
  asm   mov     ax,bx
  asm   mov     bx,nlines
  asm   mov     word ptr [bx],ax        /* return nlines[0]==ax */
  asm   mov     bx,splits
  asm   mov     ax,word ptr [bx]
  asm   add     ax,dx
  asm   mov     word ptr [bx],ax        /* splits[0] updated */
  asm   mov     ax,di                   /* nbytes=((int)(dest-destbase)); */
  asm   les     di,dest
  asm   sub     ax,di
  asm   mov     bx,nbytes
  asm   mov     word ptr [bx],ax        /* return nbytes[0]==ax */
}
#endif

          
#if 0
/* For MAC or UNIX files, only one pass is needed for a text file, */
/* in which the line terminator is replaced by NULL. */
/* The routine fails if another end of line char is found, unless */
/* it happens to be '\r' and the file type is supposed to be CRLF. */
/* On success, for a CRLF file, it calls dropCRnull() to remove the */
/* CR+NULL combinations (a little slow). */
int killcrlf(buf,nlines,nbytes)
char far *buf;
int *nlines,*nbytes;
{
extern int MAXLN;
extern int intexttype;
register int i,j;
char f,g,flag,x;
extern void dropCRnull();

  i=nbytes[0];
  nlines[0]=0;
  flag = (intexttype==CRLFtexttype);
  f='\n'; g='\r';
  if(intexttype==CRtexttype) {f='\r'; g='\n';}
  while(i--){
    if((buf[i]) != f) goto quit;           /* Wrong end of line char */
    buf[i]=0;                              /* End of line == NULL */
    nlines[0] += 1;                        /* Count lines */
    j=i;
    while(i){
      if((x=buf[i-1])==0) goto quit;       /* BINARY file? */
      if(flag==0 && x==g) goto quit;       /* Wrong file type? */
      if(x==f) break;                      /* Found end of line */
      --i;
      if(j-i >= MAXLN) goto quit;          /* Long line */
    }
  }
  if(flag) dropCRnull(buf,nbytes);         /* Low overhead for DOS */
  return 1;                                /* It worked. */
quit:
  return 0;                                /* It failed. */
}

/* This routine is only applied in case proc killcrlf() was a */
/* success **AND** the file type is CRLF.  It copies the block */
/* over itself, folding out the extra CR's, assuming all LF's */
/* have been replaced by NULL. */
void dropCRnull(dest,nbytes) char far *dest; int *nbytes; {
int n,m; char far *s; char far *d;
  m=nbytes[0]; n=0; s=d=dest;
  while(m--){
   if((*d++ = *s++)!=0) ++n;
   else {
     if(n){ /* Then 2 chars were copied, last was a null */
       if(d[-2]==13){d[-2]=0; --d;} /* Make 2 nulls, copy over second */
       n=0;
     }
   }
  }
  nbytes[0]=(int)(d-dest);
}
#endif

int CPtoBLOCK(I,P,n) int I; char *P; int n;{
/* Following code by Steve Worcester, Nov 1988 */
/* MOVE BLOCK */
getmemblock(I);         /* Load external block to low memory */
#if 0
asm     mov     dx,es
asm     mov     bx,I
asm     shl     bx,1
asm     shl     bx,1
asm     add     bx,word ptr DGROUP:_memblock
asm     les     di,dword ptr [bx]
asm     mov     si,P
asm     mov     cx,n
asm     cld
asm     rep     movsb
asm     mov     es,dx
#else
asm     push    es
asm     push    ds
asm     mov     dx,ds
asm     mov     ax,I
asm     shl     ax,1
asm     shl     ax,1
asm     lds     bx,dword ptr DGROUP:_memblock
asm     add     bx,ax
asm     les     di,dword ptr ds:[bx]
asm     mov     si,P
asm     mov     ds,dx
asm     mov     cx,n
asm     cld
asm     rep     movsb
asm     pop     ds
asm     pop     es
#endif
/* COUNT NULLS */
asm     push    es      /* save es */
asm     mov     ax,ds   /* scas uses es:di */
asm     mov     es,ax   /* es=ds */
asm     xor     al,al   /* al=byte to be matched */
asm     xor     dx,dx   /* dx=match counter */
asm     mov     di,P    /* di=base address to search */
asm     mov     cx,n    /* cx=number of bytes to search */
CPtloop:
asm     repnz   scasb   /* scan until cx=0 or [di] == al */
asm     inc     dx      /* increment null count */
asm     or      cx,cx   /* test cx=0 */
asm     jnz     CPtloop /* no, then loop */
asm     dec     di      /* yes, then check last byte == al */
asm     cmp     byte ptr [di],al
asm     jz      CPtend  /* Matched, correct count in dx */
asm     dec     dx      /* else adjust counter by 1 */
CPtend:
asm     pop     es      /* restore es to match entry */
asm     mov     ax,dx   /* return count of nulls */
return (_AX);
}

/* copy one segment to another, no overlap troubles */
void segcopy(d,s,n)
char far *d;
char far *s;
int n;
{
if(n==0) return;
if(d <= s || s+n <= d){
asm     mov     bx,ds
asm     mov     dx,es
asm     les     di,d            /* dest = es:di */
asm     lds     si,s            /* src = ds:si */
asm     mov     cx,n            /* count to move */
asm     cld
asm     rep     movsb           /* move it all */
asm     mov     ds,bx
asm     mov     es,dx
}
else {
asm     mov     bx,ds
asm     mov     dx,es
asm     les     di,d            /* dest = es:di */
asm     lds     si,s            /* src = ds:si */
asm     mov     cx,n            /* count to move */
asm     add     si,cx
asm     add     di,cx
asm     dec     si
asm     dec     di              /* move backwards */
asm     std
asm     rep     movsb           /* move it all */
asm     cld
asm     mov     ds,bx
asm     mov     es,dx
}
}

/* Read in a block off disk to far pointer memory */
int eread(fd, buf, len)
int     fd;
char  far *buf;
int     len;
{
asm     mov     ax,ds
asm     mov     cx,len
asm     mov     bx,fd
asm     lds     dx,dword ptr buf
asm     push ax /* save ds */
asm     mov     ah,3fh
asm     int     21h
asm     pop     ds      /* restore ds */
asm     jc      readerr
return (_AX);
readerr:
return (-1);
}

/* Write far pointer memory to disk */
int ewrite(fd, buf, len)
int     fd;
char  far *buf;
int     len;
{
asm     mov     ax,ds
asm     mov     cx,len
asm     mov     bx,fd
asm     lds     dx,dword ptr buf
asm     push    cx
asm     push ax /* save ds */
asm     mov     ah,40h
asm     int     21h
asm     pop     ds      /* restore ds */
asm     pop     cx      /* Amt we were supposed to write */
asm     jc      error
asm     cmp     ax,cx
asm     je      quit
error:
asm     mov     ax,-1
quit:;
return (_AX);
}

#endif

#if BSDunix || SYS5 || (TURBOC && !UseXMS)
/*
 * getmemblock(n)  -- get address of memory block
 */
char far *getmemblock(n) int n; {
/* extern char far **memblock; */
  return memblock[n];
}

/*
 *      Create block by OS function call. Return record
 *         I on success, -1 on failure.
 *
 */
int crBLOCK(){
register int i,j;

  for(i=0;i<MAXBLOCK;++i) {
/*    if((j=memmark[i]) == 2) continue; */
    if((j=memmark[i]) == 1) goto remark; /* re-use free block */
    if(j == 0) {
#if !TURBOC
      if((memblock[i]=(char far *)malloc((int)SIZEBLOCK))==0) goto quit;
#endif
#if TURBOC
extern int STASHmin;
      if((long)STASHmin*SIZEBLOCK>farcoreleft()) goto quit;
      if((memblock[i]=farmalloc((unsigned long)SIZEBLOCK))==0) goto quit;
#endif
      goto remark;
    }
  }
quit:
  return -1;
remark:
      memmark[i]=2; ++USEDBLOCK; return i;
}

/*
 *      Release block I. Return 0
 *         on success, nonzero on failure,
 *
 */
int rlBLOCK(I) unsigned I;{
  if(I<MAXBLOCK && memmark[I] ==2){
    --USEDBLOCK; memmark[I]=1; return 0;
  }
  return -1; /* failed */
}

void XMSinit(){      /* Used in v0.c */
    MAXBLOCK=MAXXMS;
#if MSDOS               /* No XMS memory, using only 640k */
    /* Only main memory can be used for blocks */
    MAXBLOCK=MYmin((int)(farcoreleft()/SIZEBLOCK),MAXBLOCK);
#endif
    MAXBLOCK=MYmax(5,MAXBLOCK);   /* Sanity check, zero blocks can't work */
}
void XMSfree(){}     /* Used at exit; see v9.c */
#endif

#if (TURBOC && UseXMS)

#define XGetVersion     00h   /* commands to the XMS driver*/
#define XRequestHMA     01h
#define XReleaseHMA     02h
#define XGlobalE20      03h
#define XGlobalD20      04h
#define XLocalE20       05h
#define XLocalD20       06h
#define XQuery20        07h
#define XGetMemSize     08h
#define XAllocEMB       09h
#define XFreeEMB        0ah
#define XMoveEMB        0bh
#define XLockEMB        0ch
#define XUnlockEMB      0dh
#define XGetHandleInfo  0eh
#define XReallocEMB     0fh
#define XRequestUMB     10h
#define XReleaseUMB     11h

#define ENone           00h            /* "no error" error code */
#define ENotInitd       01h            /* XMSSetup was never  */
                                        /* called before other XMS funcs */

struct EMMMoveStruct {
unsigned long TransferLength; /* 32-bit number of bytes to transfer */
unsigned      SourceHandle;   /* Handle of source block */
unsigned long SourceOffset;   /* 32-bit offset into source block */
unsigned      DestHandle;     /* Handle of dest block */
unsigned long DestOffset;     /* 32-bit offset into dest block */
};

unsigned XMSInitd=0;    /* Flag which detects presence of XMS */
unsigned freeXMSblk=0;  /* Amount of largest free XMS block in kilobytes */
unsigned freeXMSmem=0;  /* Amount of free XMS memory in kilobytes */
int SWAPBL=0;
unsigned long XMSDriver=0;   /* far address of XMS driver */

struct EMMMoveStruct moverec; /* extended memory move          */

void XMS_Setup(){
asm     mov     ax,4300h                /* XMS Driver installation check */
asm     int     2fh
asm     cmp     al,80h                  /* returns 80h if installed */
asm     mov     ax,0                    /* XMS Driver not present */
asm     jne     short exit
asm     mov     ax,4310h                /* get address of XMS driver */
asm     int     2fh
asm     mov     word ptr _XMSDriver,bx            /* store offset */
asm     mov     word ptr _XMSDriver+2,es          /* store segment */
asm     mov     ax,1
exit:
asm     mov     XMSInitd,ax              /* 0=No Init, 1=Init */
}

int XMS_LargestFreeBlock(freeblk,freemem)
unsigned int *freeblk,*freemem;
{
asm     cmp     XMSInitd,0              /* has XMSSetup been called? */
asm     mov     al,ENotInitd            /* no, return error code */
asm     je      short exit
hasinitd:
asm     mov     ah,XGetMemSize          /* function to get free/total memory */
asm     call    dword ptr _XMSDriver    /* call the XMS driver */
asm     push    bx
asm     mov     bx,freeblk
asm     mov     word ptr [bx],ax
asm     mov     bx,freemem
asm     mov     word ptr [bx],dx
asm     pop     ax                      /* XMS error code to al */
exit:
asm     mov     ah,0
return (_AX);
}

unsigned XMS_AllocEMB(mysize,handle)
unsigned int mysize; unsigned int *handle;
{
asm     cmp     XMSInitd,0              /* has XMSSetup been called? */
asm     mov     al,ENotInitd            /* no, return error code */
asm     jz      short exit
asm     mov     ah,XAllocEMB            /* function code */
asm     mov     dx,mysize               /* number of kb to allocate */
asm     call    dword ptr _XMSDriver    /* call the XMS driver */
asm     or      ax,ax
asm     mov     al,bl                   /* bl==error code */
asm     jz      exit
asm     mov     bx,handle
asm     mov     word ptr [bx],dx        /* dx == handle */
asm     xor     al,al
exit: ;
asm     mov     ah,0
return (_AX);
}

int XMS_MoveEMB()
{
extern struct EMMMoveStruct moverec; /* extended memory move          */
asm     cmp     XMSInitd,0              /* has XMSSetup been called? */
asm     mov     al,ENotInitd            /* no, return error code */
asm     jz      short exit
hasinitd:
asm     mov     ah,XMoveEMB             /* function code */
asm     mov     si,offset moverec       /* Offset to si, segment is ds:*/
asm     call    dword ptr _XMSDriver    /* call the XMS driver */
asm     or      ax,ax
asm     mov     al,bl                   /* transfer XMS error code to al */
asm     jz      exit
asm     xor     al,al
exit:
asm     mov     ah,0
return (_AX);
}

int XMS_FreeEMB(handle)
unsigned int handle;
{
asm     cmp     XMSInitd,0              /* has XMSSetup been called? */
asm     mov     al,ENotInitd            /* no, return error code */
asm     je     short exit
hasinitd:
asm     mov     ah,XFreeEMB             /* function code */
asm     mov     dx,handle               /* handle number to free */
asm     call    dword ptr _XMSDriver    /* call the XMS driver */
asm     or      ax,ax
asm     mov     al,bl                   /* XMS error code to al */
asm     jz      exit
asm     xor     al,al
exit:
asm     mov     ah,0
return (_AX);
}

/*
 *      Release block I. Return 0
 *         on success, nonzero on failure,
 *
 */
int rlBLOCK(I) unsigned I;{
  if(I<=MAXBLOCK && memmark[I] ==2){
    --USEDBLOCK; memmark[I]=1; return 0;
  }
  return -1;
}

/*
 * getmemblock(n)  -- get address of memory block in low memory.
 * This routine has to succeed, it is not allowed to fail.
 */
char far *getmemblock(n) int n; {
int M;
  if(handles[n]==0) goto quit;
  if((M=crBLOCK()) == -1) goto noblock;
  XMSmoveblock(M,n);  /* Copy XMS block to low memory block. */
  swapblockP(M,n);    /* Swap pointers. Block n is low, block M is xms */
  rlBLOCK(M); /* Release the XMS block. It doesn't affect block n. */
  goto quit;  /* Return the low memory address */

noblock:
  M=getfirstused(2);
  XMSmoveblock(SWAPBL,M);  /* Copy block M to block SWAPBL. Now M is free. */
  XMSmoveblock(M,n);       /* Copy block n to block M. Now n is free. */
  swapblockP(n,M);         /* Swap pointers. Block M is xms, block n is low. */
  swapblockP(SWAPBL,M);    /* Swap pointers. Blocks SWAPBL snd M are xms. */
                           /* Data in M unchanged. Data in n unchanged. */
quit:
  return memblock[n]; /* Return the low memory address */
}

/*
 *      Create block by function call. Return record
 *      I on success, -1 on failure.
 *      Always sets memmark[I]==2 on success.
 *      WARNING: Two calls to crBLOCK might swap out the first
 *               block to XMS memory.
 *      To ***lock*** this block
 *      temporarily, set memmark[I]=3 after the call, then reset
 *      back to memmark[I]=2 before calling rlBLOCK(I).
 */
int crBLOCK(){
int i,j;
char far *tmp; unsigned handle;
extern int STASHmin;
static char washere=0;
  if((i=getfirstused(1)) != -1) goto worked; /* Re-use low memory? */
  if((i=getfirstused(0)) != -1){  /* Use new low memory block? */
      if((long)STASHmin*SIZEBLOCK <= farcoreleft()){
      if((memblock[i]=(void far *)farmalloc((unsigned long)SIZEBLOCK))!=0)
        goto worked;
      }
  }
  if(!washere) {
    XMSloadSWAPBL();    /* Set up XMS swap block */
    washere=1;          /* Do it only once. */
  }
  for(i=0;i<MAXBLOCK;++i){ /* Re-use an old XMS block? */
    if(memmark[i]==1 && handles[i]!=0) goto swapxms;
  }
  if((i=getfirstused(0))!= -1){ /* Create a new XMS block? */
      if(get_XMS_block(i,SIZEKBLOCK)) goto failed;
      goto swapxms;
  }
failed:
  return -1;
swapxms:
  j=getfirstused(2);    /* Get first used low memory block j */
  XMSmoveblock(i,j);    /* Move data in block j to empty XMS block i */
  swapblockP(i,j);      /* Swap pointers in the two blocks */
  /* Now block j points to XMS, block i is low mem */
worked:
  handles[i]=0; /* Using low memory block */
  memmark[i]=2; ++USEDBLOCK;
  return i;
}

/* Return low memory index or unused array index */
int getfirstused(x) int x; {
int i;
  for(i=0;i<MAXBLOCK;++i) {
    if(memmark[i]==0){ if(x==0) return i; break; }
    if(memmark[i]==x && handles[i]==0) return i; /* low block index */
  }
return -1;
}

void XMSmoveblock(dest,src) int dest,src; {
extern struct EMMMoveStruct moverec; /* extended memory move          */
/* copy low block to extended memory */
    moverec.TransferLength = SIZEBLOCK;
    moverec.SourceHandle = handles[src];   /* handle of 0 means direct memory */
    moverec.SourceOffset = (unsigned long)memblock[src];
    moverec.DestHandle = handles[dest];    /* the XMS handle */
    moverec.DestOffset=(unsigned long)memblock[dest];
#if 0
    if(XMS_MoveEMB()){
     char **msg={"XMS move error","Send email to GBG",0};
      WarningMessage(msg);
    }
#else
    XMS_MoveEMB();
#endif
}

void swapblockP(n,i) int n,i; {
char far *tmp; unsigned int x;
  tmp=memblock[i]; memblock[i]=memblock[n]; memblock[n]=tmp;
  x=handles[i]; handles[i]=handles[n]; handles[n]=x;
}

int get_XMS_block(n,amount)
    int n;                      /* Index into handles[], memblock[] */
    unsigned int amount;        /* Amount to allocate*/
{
static unsigned int blocksize= 0;
static unsigned int lasthandle= -1;
static unsigned long lastoffset=0;
unsigned x;

/* Nonzero blocksize? */
if(blocksize != 0){
  if(amount <=blocksize){
    handles[n]=lasthandle;
    goto success;
  } else blocksize=0;
}
if(blocksize == 0){
    if(XMS_LargestFreeBlock(&freeXMSblk,&freeXMSmem)) goto fail;
    if(amount>freeXMSblk) goto fail;
    blocksize=MYmin(MYmax((freeXMSmem >> 3),amount),MAXXMS);
    blocksize=MYmin(blocksize,freeXMSblk);
    if(XMS_AllocEMB(blocksize,&x)){  /* allocate memory */
       if(XMS_AllocEMB(blocksize=freeXMSblk,&x)) goto fail;
    }
    lasthandle=handles[n]=x;
    lastoffset=0;
    goto success;
}

fail: blocksize=0; return(1);
success:
    memblock[n]=(char far *)lastoffset;
    lastoffset += 1024L*amount;
    blocksize -= amount;
return(0);
}

void XMSinit() {
static char washere=0;
extern char *core();
extern unsigned freeXMSmem,freeXMSblk;
    if(washere) return;
    washere=1;
    XMS_Setup();
    /* XMSInitd */    /* Flag which detects presence of XMS */
    /* XMS_Setup must be called before using any other XMS function */
    if(XMSInitd){
        XMS_LargestFreeBlock(&freeXMSblk,&freeXMSmem);
        MAXBLOCK += MYmin(MAXXMS,freeXMSmem/SIZEKBLOCK);
    }
    SWAPBL=MAXBLOCK-1;
#if 0
    handles=(unsigned far *)core(sizeof(unsigned int)*MAXBLOCK);
#else
    handles=(void far *)farmalloc((unsigned long)sizeof(unsigned int)*MAXBLOCK);
#endif
}

void XMSloadSWAPBL(){        /* Save one xms block for swapping */
    if(XMSInitd){
       if(get_XMS_block(SWAPBL,SIZEKBLOCK)==0){
         --MAXBLOCK; memmark[SWAPBL]=2;
       }  else XMSInitd=0;
    }
}


void XMSfree(){
int j;
  if(XMSInitd){
    for(j=0;j<=MAXBLOCK;++j){
      XMS_FreeEMB(handles[j]); /* free memory */
    }
  }
}
#endif

/*
 *      Copy n bytes from block I to pointer p. Always
 *         works. Returns nothing.
 *
 */
#if BSDunix || SYS5
void CPfromBLOCK(II,P,n) int II; char *P; int n;{
#if SEGMEMORY
register char far *q; register int i;
  q=getmemblock(II);
  for(i=0;i<n;++i) P[i]=q[i];
#endif
#if !SEGMEMORY
#if !HASMEMCPY
  moveMEM(P,getmemblock(II),n);
#endif
#if HASMEMCPY
  memcpy(P,getmemblock(II),n);
#endif
#endif
}
#endif

/*
 *      Copy n bytes to block I from pointer P. Always
 *         works. Returns number of nulls found during
 *         transfer.
 *
 */
#if BSDunix || SYS5
/* C code seems to be slow */
int CPtoBLOCK(II,P,n) int II; char *P; int n;{
#if HASMEMCPY
register char *r; register int i,j;
  memcpy(getmemblock(II),r=P,i=n);
  j=0; while(i--) if(*r++ == 0) ++j;
#endif
#if !HASMEMCPY
register char far *q; register char *r; register int i,j;
  q=getmemblock(II); r=P;
  i=n; j=0;
  while(i--) if((*q++ = *r++)==0) ++j;
#endif
  return j;
}
#endif

#if BSDunix || SYS5
/* This routine requires a block move of the data before the copy */
/* in order to succeed. The problem is that dest[] and src[] must */
/* be so many chars apart for this to work. */
void slowkillcrlf(dest,src,nlines,nbytes,splits)
char far *dest; char far *src;
int *nlines,*nbytes,*splits;
{
extern int intexttype;
register int x,nbyte;
int nline;
register int lc;
register char far *s;
register char far *d;
extern int MAXLN;

    d=dest; s=src;
    lc=MAXLN-1; nbyte=nbytes[0];
    s[nbyte]=26; /* 26 chosen because it's not equal to 13, 10, 0, 255 */
    nline=0;
    while(nbyte-- > 0){
      x=(s[0]);
      /* Read MSDOS, MAC, UNIX files by default */
sk1:
      if(x==13){
        x=s[1];           /* works because s[nbyte]==26 */
        if(x==0 || x==10) {++s; --nbyte;}
        goto newline;
      }
sk2:
      if(x==0 || x== 10) {
newline: *d++ = 0; ++s; lc=MAXLN-1; ++nline; continue;
      }
sk3:
      if(!lc) {
splitline:  *d++ = 0; ++nline; *d++ = 2; lc=MAXLN-1-1; splits[0] += 1;
      }
sk4:
      *d++ = *s++; --lc;
    }
    nbytes[0]=((int)(d-dest)); nlines[0]=nline;
}
#endif
/*
 * V13.C
 */
/* This file contains routines for MS-DOS using the TURBOC
 * compiler version 1.5. Hardware dependencies are:
 *
 *              int 10h
 *              int 11h
 *              direct video for mono or color
 *              int 21h
 * Also contained here are portable C versions of all the IBM
 * routines, which are used on Unix hardware.
 *
 * See Peter Norton's Programmers Guide to the IBM PC for help on
 * understanding the routines below.
 *
 */
                                /* otherwise use direct video */
#include "pie.h"

#if TURBOC              /* DOS hosts */
#if IBMHARDWARE         /* only /w IBM hardware */
#pragma inline
#define bios 10h

void inslin() {
  insORdel(0x0701);
}

void dellin() {
  insORdel(0x0601);
}

void insORdel(n) unsigned n; {
extern int ROWMAX,COLMAX,SYSATT;
/*      Insert or delelete a line at the current cursor position */
asm     cmp     nodisp,0
asm     jnz     end
asm     mov ah,3                /*  Get cursor position. */
asm     mov bh,0
asm     int bios
asm     push dx
asm     mov ch,dh
asm     xor cl,cl               /*  Start at beginning of row. */
asm     mov dh,byte ptr ROWMAX  /*  dh=number of rows minus 1 */
asm     inc dh                  /* make up for status line */
asm     mov dl,byte ptr COLMAX  /* dl=number of columns minus 1 */
asm     mov ax,n
asm     mov bh,byte ptr SYSATT
asm     int bios                /*  Scroll down one line. */
asm     pop dx
asm     xor dl,dl
asm     mov bh,dl
asm     mov ah,2
asm     int bios
end:
;
}

void fwdindex(){
extern int ROWMAX,COLMAX,SYSATT;
asm     cmp     nodisp,0
asm     jnz     end
asm     mov dx,0100h            /* putcursor (0,0) */
asm     xor bh,bh
asm     mov ah,2
asm     int bios
asm     mov ch,dh
asm     xor cl,cl               /*  Start at beginning of row. */
asm     mov dh,byte ptr ROWMAX
asm     inc dh                  /* make up for status line */
asm     mov dl,byte ptr COLMAX
asm     mov bh,byte ptr SYSATT
asm     mov ax,0601H            /*  Scroll up one line. */
asm     int bios
end:
;
}

void clears(){                          /* clear all screen positions */
        blankit(0x0000,7);
        home();
}

void clearscreen()
{
/* This routine blanks the screen and puts the cursor home [0,0] */
    blankit(0x0000,SYSATT);
    home();
    statblank=1;
}

void cleardisplay(){
/* This routine blanks the screen and puts the cursor on line 2 */
    blankit(0x0100,SYSATT);
    putcursor(0,0);
}

void blankit(corner,attr) unsigned corner; unsigned char attr; {
extern int ROWMAX,COLMAX,nodisp;
asm     cmp     nodisp,0
asm     jnz     end
asm     mov ah,0fh              /* Get current video state */
asm     int bios                /* ah=cols, al=mode, bh=active page */
asm     mov cx,corner           /* point=[chigh+1,clow+1] */
asm     mov dh,byte ptr ROWMAX  /* blank rectangle dh x dl */
asm     inc dh                  /* make up for status line */
asm     mov dl,ah               /* Number of columns */
asm     mov bh,attr             /* attribute to write */
asm     mov ax,600H
asm     int bios
end: ;
}

void eraseol()
{
extern int ROWMAX,COLMAX,SYSATT;
asm     cmp     nodisp,0
asm     jnz     end
asm     mov ah,3
asm     xor bh,bh
asm     int bios        /*  getcursor() */
asm     mov cx,dx
asm     mov dl,byte ptr COLMAX
asm     mov ax,0700h
asm     mov bh,byte ptr SYSATT
asm     int bios        /* scroll() */
end:
;
}

void killstatus()
{
extern int statblank;
statblank = 1;
home();
eraseol();
}

void putcursor(rrow,ccol)
int rrow,ccol;
{
extern int ROWMAX,COLMAX;
asm     cmp     nodisp,0
asm     jnz     end
asm     xor bh,bh       /* page 0 */
asm     mov ax,rrow
asm     cmp ax,ROWMAX       /* off screen ? */
asm     jle putc1
asm     mov ax,ROWMAX
putc1:
asm     inc ax          /* row - offset 1 in this application */
asm     xchg ax,dx
asm     mov ax,ccol     /* column */
asm     cmp ax,COLMAX   /* off screen ? */
asm     jle putc2
asm     mov ax,COLMAX
putc2:
asm     mov dh,dl
asm     mov dl,al
asm     mov ah,2        /* set cursor position */
asm     int bios
end:
;
}

void home()
{
asm     cmp     nodisp,0
asm     jnz     end
asm     xor dx,dx        /*      ; Go to top left corner of screen. */
asm     mov bh,dh
asm     mov ah,2
asm     int bios
end:
;
}

void erachar() {
extern int SYSATT;
asm     cmp     nodisp,0
asm     jnz     end
asm     mov ah,3
asm     xor bh,bh
asm     int bios        /*  getcursor() */
asm     cmp dl,0        /* on column 0? */
asm     jz end          /* can't erase on column 0 */
asm     dec dl          /* column-- */
asm     xor bh,bh       /* page 0 */
asm     mov ah,2        /* set cursor position */
asm     int bios
asm     mov bl,byte ptr SYSATT
asm     xor bh,bh
asm     mov cx,1
asm     mov al,' '      /* move left and print ' ' */
asm     mov ah,9
asm     int bios
end:
;
}

int getcursor() {
asm     xor     bh,bh
asm     mov     ah,3
asm     int     bios
asm     mov     ax,dx
return (_AX);
}

#if BIOSSRC
/* use this for a general version of PI for any clone */
/* Uses int 10h functions only - no direct video */
/* Good for machines like the Z100, and SUN DOS386I */

void ePrint(s) char *s; {
extern int nodisp;
  if(!nodisp){
    eBios(s,getcursor(),0);
  }
}

void eBios(s,n,flag) char *s; int n,flag; {
extern int invvideo();
extern int COLMAX;
unsigned char edge;
        edge= (unsigned char)COLMAX;
asm     mov     dx,n
asm     mov     si,s
bios1:
asm     mov     AL,[SI]
asm     inc     si
asm     cmp     al,0
asm     je      end
asm     mov     ah,al
asm     and     ah,96           /* test for inverse video attribute */
asm     je      bios3
asm     cmp     dl,edge         /* at edge of screen ? */
asm     jne     bios2           /* no, then write char and advance cursor */
asm     mov     ah,10           /* write video without cursor advance */
asm     mov     cx,1            /* because at right edge of screen */
asm     int     bios
asm     jmp     end             /* all done */
bios2:
asm     mov     ah,14           /* write tty */
asm     int     bios            /* with cursor advance */
asm     inc     dl
asm     jmp     bios1
bios3:
asm     push    dx
asm     push    bx              /* char needs to be in */
asm     push    ax              /* inverse video */
asm     call    invvideo
asm     pop     ax
asm     pop     bx
asm     pop     dx
asm     inc     dl
asm     cmp     dl,edge         /* but we might be at edge of screen */
asm     jle     bios1           /* if so, then quit SIGNED BYTE ERROR! */
end:
  if(flag) eraseol();
}
#endif


#if !BIOSSRC

/* use this for a version of PI for any clone with mono or color adapter */
void ePrint(s) char *s; {
extern int nodisp;
  if(!nodisp){
    if(iscga()) waitsync();
    setcursor(eDirect(s,getcursor(),0));
  }
}

/* write direct to video ram - returns new cursor position */

int eDirect(s,n,flag)
char *s;                        /* char buffer, null-terminated */
int n;                          /* dx=n, dl=col, dh=row cursor pos */
int flag;                       /* 0 for no fill, 1 for fill */
{
extern unsigned _screen;
extern int ROWMAX,COLMAX,SYSATT;
asm     mov ah,0fh              /* Get current video state */
asm     int bios                /* ah=cols, al=mode, bh=active page */
asm     mov     al,ah
asm     xor     ah,ah           /* ax== number of columns displayed */
asm     mov     si,s
asm     mov     bx,n            /* Cursor position */
asm     mov     cl,bh           /* row number */
asm     xor     ch,ch
asm     imul    cx              /* uses ax,cx,dx */
asm     mov     cl,bl           /* col number */
asm     xor     ch,ch
asm     add     ax,cx
asm     add     ax,ax           /* ax = offset in ram */
asm     mov     di,ax           /* video ram offset */
asm     mov     ax,_screen      /* video ram segment */
asm     mov     es,ax
asm     mov     cl,byte ptr flag        /* blank-fill flag for end of line */
asm     mov     dx,bx
asm     mov     bl,byte ptr COLMAX
asm     mov     bh,byte ptr SYSATT
loop:
asm     mov     AL,ds:[SI]

asm     cmp     al,0

asm     jne     loop1           /* end of string found */
asm     test    cl,1            /* Are we supposed to fill to end of line ? */
asm     jz      quit            /* no, then just quit */
asm     mov     al,' '          /* yes, then add in a blank */
asm     mov     ah,bh           /*      with reg attrib */
asm     jmp     short noattr
loop1:
asm     inc     si
asm     test    al,96           /* test for inverse video attribute */
asm     mov     ah,bh
asm     jnz     noattr          /* no, then write char with reg attribute */
asm     test    al,128          /* Is the high bit set? */
asm     jnz     noattr          /* yes, then write char with reg attribute */
asm     mov     ah,070h         /* no, change to inverse video */
asm     add     al,'@'          /* convert to printable */
noattr:
/*  mono adaptor processing - requires no port checks */
mono:
asm     mov     es:[di],ax
asm     inc     di
asm     inc     di
asm     cmp     dl,bl             /* at edge of screen ? */
asm     je      quit
asm     inc     dl
asm     jmp     short loop
quit:
end:
asm     mov     ax,dx           /* return new cursor */
return (_AX);
}

#endif

void invvideo(x)
int x;
{
extern int COLMAX;
asm     xor bh,bh               /* display page */
asm     mov ah,3
asm     int bios
asm     mov cx,1                /* write 1 char */
asm     mov ah,9                /* function 9, video services */
asm     mov al,x                /* char to print */
asm     test al,128             /* Signed? */
asm     jnz next
asm     cmp al,' '              /* control char? */
asm     jge next                /* No, then don't add '@' correction */
asm     add al,'@'
next:
asm     mov bl,70h              /* inverse video attribute */
asm     int bios
asm     cmp dl,byte ptr COLMAX
asm     je done                 /* can't move cursor */
asm     mov ah,14               /* overprint as tty */
asm     int bios
done:
;
}

void strinvvideo(s) char *s; {
  if(!nodisp){
    while(*s) invvideo(*s++);
  }
}

void xPrint(p,rrow,ccol) char *p; int rrow,ccol; {
extern int COLMAX,nodisp;
  if(!nodisp) {
    if(ccol > COLMAX+1) ccol = COLMAX+1;
    putcursor(rrow,ccol);
    ePrint(p);
  }
}

void setcursor(n) unsigned n; {
asm     mov     dx,n
asm     mov     ah,2            /* set cursor */
asm     xor     bh,bh
asm     int     bios
}

/* find out if mono or color card or neither */

unsigned _screen = 0xb000;
int oldbios=0;

void equipment() {
#define TEXT16colorHIGHRES80x25  3      /* cga high resolution text mode */
#define TEXTmonoHIGHRES80x25     7      /* mono high resolution text mode */
/* Set video modes AL=3 or 7, AH=0, int bios */
        clears();               /* Erase screen before changing its
size */
#if !BIOSSRC
asm     mov     ax,0500h        /* set active display page */
asm     int     bios
asm     int 11h                 /* do equipment check */
asm     and ax,30h
asm     mov _screen,0b000h
asm     cmp ax,30h
asm     je mono                 /* It's mono */
asm     mov _screen,0b800h      /* It's color (ax==20h)*/
mono:
#endif
asm     mov ax,40h
asm     mov es,ax
asm     test byte ptr [es:96h],10000b  /* enhanced keyboard installed */
asm     mov oldbios,10h
asm     jnz new
asm     mov oldbios,0h
new:
        setcursortype(1);       /* user cursor shape */
}

/*
 * setcursortype()
 *
 * Scan lines numbered 0-7 for CGA, 0-13 for VGA/MONO
 * n=0 : no cursor  ch=32, cl=0
 * n=1 : block cursor ch=1, cl=13 for vga, ch=6,  cl=7  for cga
 */
void setcursortype(n) int n; {
extern int cursorCH,cursorCL;
asm     xor ax,ax
asm     mov ch,32       /* n=0 is set no cursor */
asm     mov cl,al
asm     cmp ax,word ptr n
asm     je setct
asm     int 11h
asm     mov ch, byte ptr cursorCH       /* set cursor shape to user shape */
asm     mov cl,byte ptr cursorCL
setct:
asm     xor al,al
asm     mov ah,1
asm     mov bh,al
asm     int 10h
}

char vmodes[]={0x50,0x54,0x58,  /* 30 rows */
               0x51,0x55,0x59,  /* 43 rows */
               0x52,0x56,0x5a,0x00}; /* 60 rows */
char vmoderows[]={23,28,41,58};         /* 2 less than num rows */

int getvideomode(){
asm     mov ah,0fh              /* Get current video state */
asm     int bios                /* ah=cols, al=mode, bh=active page */
asm     xor ah,ah               /* AX==video mode */
return (_AX);
}

void setvideomode(x) unsigned x; {
unsigned i,j; unsigned char mode;
asm     xor ah,ah
asm     mov al,byte ptr x
asm     int bios
asm     mov ah,0fh              /* Get current video state */
asm     int bios                /* ah=cols, al=mode, bh=active page */
asm     mov byte ptr mode,al    /* Save mode */
/* If mode is 81,84,88 then use 29 rows */
/* If mode is 82,85,89 then use 42 rows */
/* If mode is 86,90 then use 59 rows */
/* AH==num columns, use 39, 79 or 131 by default */
asm     mov bl,39
asm     cmp ah,40               /* 40-col mode? */
asm     je done
asm     mov bl,79
asm     test ah,128             /* Signed ? */
asm     jz done
asm     mov bl,131
done:
asm     xor bh,bh
asm     mov word ptr j,bx
   i=(istrchr(vmodes,mode)+2)/3;
   setwindowsize(vmoderows[i],j);       /* Does equipment() too */
}

void cursorstyleset(hi,lo) char hi,lo; {
asm     mov ch,hi
asm     mov cl,lo
asm     mov ah,1
asm     xor al,al
asm     mov bh,al
asm     int 10h
}

void shortclick(lo,hi,duration) char lo,hi; int duration; {
/* LO and HI frequencies. See MBASIC program */
asm     mov     al,182
asm     out     67,al
asm     mov     al,lo
asm     out     66,al
asm     mov     al,hi
asm     out     66,al
asm     in      al,97
asm     mov     ch,al
asm     mov     cl,al
asm     or      ch,3
asm     mov     al,ch
asm     out     97,al
asm     push    cx
asm     mov     ax,duration

loop:
asm     dec     ax
asm     or      ax,ax
asm     jnz     loop

asm     pop     cx
asm     mov     al,cl
asm     out     97,al
}

void drawall()
{
int i,k;
extern char **line; extern char *toprint();
extern int ROWMAX,nodisp,halfdone;
  if(testkey()) {nodisp=halfdone=1; return;}
  if(!nodisp) {
    nonzeroScreenLines=0;
    k=iscga();
    for(i=0;i<=ROWMAX;++i) {
        if(bioskey(1)) goto quit;
        /* about 460 characters can be written after waitsync() */
#if BIOSSRC
        putcursor(i,0);
        eBIOS(toprint(line[i]),(i+1) << 8,1); /* write 160 characters */
#else
        if(k) if(i % 2 == 0) waitsync();
        eDirect(toprint(line[i]),(i+1) << 8,1); /* write 160 characters */
#endif
    if(line[i][0]) ++nonzeroScreenLines;
    }      
    adjustNonzeroScreenLines();
    newuserAnnoyingMessage();
    lcount();
    halfdone=0;
  }
quit:;
}

int iscga() {
extern int _egavga;
extern unsigned _screen;
asm     xor     ax,ax
#if !BIOSSRC
asm     mov     dx,_screen
asm     cmp     dx,0b800h
asm     jne     short ismono
asm     cmp     ax, _egavga
asm     jne     short ismono
asm     inc     ax
#endif
ismono:
return (_AX);
}

void waitsync() {
#if !BIOSSRC
asm     mov     dx,03DAh
asm     mov     ah,09h          /* retrace mask. Al Williams, p 186 */
asm     cli
cga1:
asm     in      al,dx           /* wait for horizontal sync */
asm     rcr     al,1            /* Adaptor slower than monitor retrace */
asm     jc      cga1            /* This came from AT bios source */
cga2:
asm     in      al,dx           /* wait for vertical retrace */
asm     and     al,ah           /* about every 63 microseconds */
asm     jz      cga2
asm     sti
#endif
cga3:
;
}


/* Return 0 if not in enhanced mode, 1 otherwise */
int isWinEnhanced(){
asm     mov ax,0x1600   /* See if Enhanced mode windows */
asm     int 2fh
asm     mov ah,0
asm     or al,al
asm     jz quit
asm     inc al
quit:
        return (_AX);
}

#endif /* IBMHARDWARE special code ends */


/* setargv -- setup argv with wild card expansion for Turbo-C 1.5 */
#ifdef __TURBOC__               /* only for turboc command line */
/*  Get Program Name - argv[0] - TurboC2.0 setargv.asm does it */
void getprogramname(buffer) char *buffer; {
extern unsigned _envLng,_envseg; extern unsigned char _osmajor;
asm     mov     si,buffer       /* ds:si is buffer */
asm     mov     di, _envLng
asm     add     di, 2           /* DI = Program name offset */
asm     mov     es, _envseg     /* ES = segment address */
asm     mov     cl, 07fh        /* buffer is 256 bytes */
asm     cld
asm     mov     al,_osmajor
asm     cmp     al,3            /* No program name for DOS 2 */
asm     jb      loopexit
loop:
asm     mov     al,byte ptr es:[di]     /* Copy string */
asm     mov     byte ptr ds:[si],al
asm     or      al,al
asm     jz      loopexit
asm     dec     cl
asm     or      cl,cl
asm     jz      loopexit
asm     inc     si
asm     inc     di
asm     jmp     loop
loopexit:
asm     xor     al,al
asm     mov     byte ptr ds:[si],al     /* Terminate string! */
}

#ifndef WILDCARDS       /* WILDCARDS defined in dir.h */
#include <dir.h>
#endif
#ifndef NFDS            /* NFDS defined in dos.h */
#include <dos.h>
#endif

void _setargv()
{
unsigned char far *farcmdtail;
char buffer[129],s[129];
char *q[256];
char *p,*cmdtail,*pathend;
int i,k,n,c,wild;
struct ffblk ffb;
extern int              _argc;
extern char             **_argv;
extern unsigned         _psp;

  farcmdtail = MK_FP(_psp, 0x81);
  n=farcmdtail[-1]; if(n<0 || n>0x80) {n=0; goto quit;}
  for(s[n]=i=0;i<n;++i) s[i]=farcmdtail[i];
  n=0;
  cmdtail=s;
  while(*(cmdtail=sob(cmdtail)) != 0){
    /* find word */
    p = pathend = buffer;
    k=fnb(cmdtail)-cmdtail;             /* how many chars to scan */
    for(wild=i=0;i<k;++i){
      if ((c=cmdtail[i]) == '/') c = '\\';
      wild |= (c == '*' || c == '?');
      *p++ = c;
      if (c == '\\' || c == ':') pathend = p;
    }
    p[0]=0;

    if(wild && cmdtail[0] != '-')
      for(c=findfirst((char *)buffer,&ffb,0);c==0;c=findnext(&ffb)){
        strcpy(pathend,ffb.ff_name);
        if(n < 256) newcore(&q[n++],buffer);
      }
    else
      if(n<256) newcore(&q[n++],buffer);
    cmdtail += k;
  }

quit:
  _argv = (char **)core((n+1)*sizeof(char *));
  for(i=1;i<=n;++i) _argv[i]=q[i-1];
  _argv[0] = "C";
  _argc=n+1;
}
#endif  /* turbo-c command line code only */

#ifdef __TURBOC__
#ifdef __SMALL__
/* malloc.c */
#define MEMINCR 1024                    /* memory to allocate */

#define PAD     1                       /* Extra needed in block */
#define SLOP    4                       /* Extra before split */

struct  header  {
        unsigned           h_size;         /* Size of this free block */
        struct  header  *h_ptr;         /* Pointer to next header */
        };

static  struct  header  Base = { 0, &Base };
                                        /* Empty list to get started */
static  struct  header  *Allocp = &Base;        /* Last allocated block */

extern  void    free();
extern  void    *sbrk();
extern  void    *ssbrk();

void *malloc(nbytes) unsigned nbytes; {
register struct header *p, *q;
register char *cp;

  nbytes += PAD + sizeof p->h_size;
  nbytes &= ~1;
  q = Allocp;
  for (p = q->h_ptr; ; q = p, p = p->h_ptr){
    if (p->h_size >= nbytes){
      if (p->h_size <= nbytes + SLOP) q->h_ptr = p->h_ptr;
      else {
        p->h_size -= nbytes;
        p = (struct header *)((char *)p + p->h_size);
        p->h_size = nbytes;
      }
      Allocp = q;
      return((char *)(&p->h_ptr));
    }
    if (p == Allocp) break;
  }
  cp = ssbrk(nbytes);
  if ((int)cp == -1) return(0);
  p = (struct header *)cp;
  p->h_size = nbytes;
  return((char *)(&p->h_ptr));
}

void free(ap) char *ap; {
register struct header  *p, *q;

  p = (struct header *)(ap - sizeof p->h_size);
  for (q = Allocp; !(p > q && p < q->h_ptr); q = q->h_ptr)
    if (q >= q->h_ptr && (p > q || p < q->h_ptr)) break;

  if ((char *)p + p->h_size == (char *)q->h_ptr){
    p->h_size += (q->h_ptr)->h_size;
    p->h_ptr = (q->h_ptr)->h_ptr;
  }
  else p->h_ptr = q->h_ptr;
  if ((char *)q + q->h_size == (char *)p){
    q->h_size += p->h_size;
    q->h_ptr = p->h_ptr;
    p = q;
  }
  else q->h_ptr = p;

  /* If the block just freed is at the top of memory, use brk */
  if((char *)p + p->h_size == ssbrk(0)){ /* Find the new end of list */
    for (q = p; q->h_ptr != p; q = q->h_ptr) ;
    q->h_ptr = p->h_ptr;
    brk(p);
  }
  Allocp = q;
}

void *ssbrk(n) unsigned n; {
char *p;
/* Could kill the stack, since it's here also */
  p=sbrk(0);
  if(n==0) return p;
  if((unsigned)p > 63488) return (char *)-1;
  if(n>(unsigned)63488 - (unsigned)p) return (char *)-1;
  return sbrk(n);
}
#endif

#undef getwdir
char *getwdir(s) char *s; {
asm     mov     ah,25           /* get current disk */
asm     int     21h
asm     xor     ah,ah
asm     add     ax,'A'
asm     mov     si,s
asm     mov     byte ptr [si],al
asm     inc     si
asm     mov     byte ptr [si],58        /* colon ':' */
asm     inc     si
asm     mov     byte ptr [si],92        /* backslash '\\' */
asm     inc     si
asm     xor     dl,dl
asm     mov     byte ptr [si],dl        /* null */
asm     mov     ah,71
asm     int     21h
return (char *)(_AX);
}
#if 0
char * getenv(name) char *name; {
        int size,i; unsigned envb;
        unsigned psp; unsigned far *tmp;
        char far *env; char far *e; char *v;

        /* _psp is our PSP. */

        psp = _psp;

        /* Now get the address of environment block
           from the PSP, offset 2ch. */

        tmp = MK_FP(psp,44);
        envb=tmp[0];  /* Fetch word at psp+2ch */
        if (!envb) {/* Can't find environment */
            return (char *)0;}

        env = MK_FP(envb,0);

        /* Get the env block size from the MCB which immediately
           preceeds the environment block.  This is a paragraph count,
           which must be multiplied by 16 to get a byte count. */

        tmp = MK_FP(FP_SEG(env)-1,3);
        size = 16 * tmp[0];

        /* The outer loop below searches for an existing environment
           variable of the same name as we have in name[]. */

        while (*env) {  /* 2 zero bytes marks end of the env */
                /* Compare current env variable with name[]. */
                e = env; v = name; i=size;
                while (e[0] == v[0] && i--){ ++v; ++e;}
                if (e[0]=='=' && v[0]==0) {
                        ++e;  /* skip over equal sign */
                        v=sysx; /* Store it in system scratch */
                        while(e[0]!=0) *v++ = *e++;
                        *v=0;
                        return sysx;
                }
                while ((--size) && (*env++));
        }
        return (char *)0; /* failed */
}
#endif

int bioskey(n) char n; {
asm     mov     ah,n
asm     cmp     ah,1
asm     je      charReady
asm     cmp     ah,11h
asm     je      charReady
asm     int     16h
asm     jmp     end
charReady:
asm     int     16h
asm     jnz     end             /* return AX=char available */
asm     mov     ax,0            /* no char available */
end:
return (_AX);
}

int getdrive() {
asm mov ah,25
asm int 21h
return (_AX);
}

void setdrive(drv) int drv; {
asm mov dx,drv
asm mov ah,14
asm int 21h
}

#ifndef SWAPPER                    /* Use these when Ralf Brown's swapper */
long                            /* is disabled. Also works with SPAWNO40. */
farcoreleft(){                  /* Also with MSC version of SPAWNO30. */
asm     mov     bx,0efffh
asm     mov     ah,48h
asm     int     21h
asm     mov     ax,bx
        return (_AX*16L);
}

void far *farmalloc(n) unsigned long n; {
unsigned x;
        x=(unsigned)n;
        x = (unsigned)((x+15)/16);
asm     mov     bx,x
asm     mov     ah,48h
asm     int     21h
asm     jnc     end
asm     mov     ax,0
end: ;
asm     mov     x,ax
        return (char far *)MK_FP(x,0);
}

#endif
#endif


/* same as termraw for DOS386I */

#if DOS386I && !IBMHARDWARE
int nothing(){
extern int nodisp,halfdone;
  bufput(ccBIOS(0)); return (halfdone=nodisp=1);
}

void ctrlbreak(n) int n;{
static int x;
extern void __int__();
  if(n==0){
     ctrlbrk(nothing);
    _AL=0; _AH=0x33; __int__(0x21); x= _DL;
    _DL=0;
  } else _DL=x;
  _AL=1; _AH=0x33; __int__(0x21);
}

#else
void
ctrlbreak(n) int n; {   /* Keeps ctrl-C from aborting HELP */
static int x;
extern void __int__();
  if(!n){ _AL=0; _AH=0x33; __int__(0x21); x= _DL; _DL=0; } else _DL=x;
  _AL=1; _AH=0x33; _DL=x; __int__(0x21);
}
#endif

#ifdef SWAPPER
/*
 * MYSYSTEM.C
 * by GBG, May 1991
 *
 * Special system() swaps the application to disk. Thanks to Ralf Brown
 * at cs.cmu.edu for the swapper __spawnv(). Since putenv() can fragment
 * the environment, it is necessary to build a private copy of the
 * environment and pass it to __spawnv(). Along the way it is cheap to
 * mung the PROMPT to emit an EXIT message (a la 5.1 WordPerfect). Calls
 * to putenv() are recognized but putenv() is not used.
 * This works with spawno20, spawno30, spawno40. Not tested with others.
 *
 * Env Variables:       PROMPT          dos prompt
 *                      COMSPEC         command.com path
 *                      SWAPDRV         where to swap application
 * Externals:
 *                      environ         Turbo-C enviroment **-pointer
 *                      malloc,free,    std library routines
 *                      strcpy,strcat,
 *                      MYstrncmp,getenv,
 *                      strlen
 *                      __spawnv        Ralf Brown's library function
 *
 * The function __spawnv() is Copyright 1990 by Ralf Brown. Ralf does not
 * supply source for the swapper, but he does make the library available
 * free. Get it from wuarchive.wustl.edu anonymous FTP, /mirrors/msdos/c,
 * in file spawno30.zip. I am using the main function __spawnv() from
 * SPAWNS.LIB, in SPAWN_TC.ZIP, extracted as SPAWNVS.OBJ (renamed from
 * the original _spawnvs.obj). Note: MS-C also supported in SPAWNO30.ZIP.
 * For the medium model use _spawnvm.obj. I prefer to use spawno20,
 * because it does not use EMS, only XMS and disk. On modern hardware,
 * EMS exists only because of EMM386, which is generally loaded because
 * of windows, and already himem.sys is there to allow it to happen.
 * In short, XMS is the first thing installed.
 */
/*#include <dos.h>*/
#include <stdarg.h>

extern int pascal
__spawnv(const char *overlay_path,const char *name,va_list args,int env) ;

#define PROMPT "PROMPT=Enter EXIT to resume PI$_$n$g"

static char far *mem;

static void mv(p) char *p; {
   while((*mem++ = *p++)!=0) ;
}

#undef system
void mysystem(s) char *s; {
char **v;
char *p,*q;
char *env;
char file[128];
char tail[128];
char *argv[3];
int envseg;
unsigned int length;
extern char *getenv();
extern char **environ;          /* Turbo-C env pointer */
extern int swapswitch;
struct paramblock {unsigned envseg; char far * cmdline;
char far *fcb1; char far *fcb2; unsigned
childSP,childSS,childIP,childCS;};
struct paramblock pb;
unsigned char far *tmp; unsigned envv;

  if((q=getenv("COMSPEC"))==0) q=SHELL;
  strcpy(argv[0]=file,q);
  if(!swapswitch){ system((s[0]==' ' && s[1]==0) ? file : s); return;}

   argv[1]=argv[2]=(char *)0;
   length=16+1+sizeof(PROMPT);
   v=environ;
   while(v[0] && v[0][0]) length += strlen(*v++) + 1 ;
   env=(char *)malloc(length);
   envseg = FP_SEG(env) + ((FP_OFF(env)+15)>>4) ;
   mem = MK_FP(envseg,0) ;
   v=environ;
   while(v[0] && v[0][0]){
     if(MYstrncmp(p = *v++,"PROMPT=",7)) mv(p);
   }
   mv(PROMPT);
   mem[0] = '\0' ;

  if(s[0] && s[0]!=' ') ssprintf(argv[1]=tail,"/c %s",s);
  if((p=getenv("SWAPDRV"))==0) p="c:\\";
  __spawnv(p,file,argv,envseg);
  free(env);
}
#define system mysystem

#endif

void strip(p) char *p; {
asm     mov     ax,ds
asm     mov     es,ax
asm     mov     di,p
asm     xor     al,al
asm     mov     cx,0ffffh
asm     cld
asm     repnz   scasb
asm     dec     di              /* point at null */
asm     mov     cx,di
asm     sub     cx,p            /* cx=string length */
L1:
asm     or      cx,cx
asm     jz      L2
asm     dec     cx
asm     dec     di
asm     cmp     byte ptr es:[di],32
asm     je      L1
asm     inc     di
L2:
asm     mov     byte ptr es:[di],0
}

/*
 * UNSETBITS
 *
 * Strips parity bit from a string.
 */
void unsetbits(s) char *s;{
register char *p;
  p=s;
  while(*p){ if((127 & p[0]) != 0) p[0] &= 127; ++p;}
}

int startof(s,k)
char far *s;
int k;
{
  asm   les     di,dword ptr s
  asm   mov     cx,word ptr k
  asm   xor     ax,ax
  asm   cmp     cx,ax
  asm   jle     quit
  asm   add     di,cx
mainloop:
  asm   dec     di
  asm   cmp     al,byte ptr es:[di]
  asm   jz      quitplus
  asm   loop    mainloop
quitplus:
  asm   mov     ax,cx
quit:
return (_AX);
}

/* Search source string s=ss+n[0] for a match to srcharg[] */
/* Returns string index of match >=1 or 0 for failure */
/* Array n[] is updated on failure to allow search of next string */
/* in the buffer (assume buffer full of 0-delimited strings). */
/* May 1995: Make search string ASCII 128 match end of line char */

#if 1
int midstr(sss,n)
char far *sss;                /* source string */
int *n;                       /* offset to end of string on fail */
{                             /* offset to start of string on entry */
char far *s;
int nn,mm,xl;
extern int taberr; extern int zeroleft,zeroright;
extern char *lbuf;
  s=sss+n[0]; nn=0;
  if(taberr && mydotabs(s)) {nn=farstrlen(s); sdetab(s,lbuf+1); s=lbuf+1;}
  if(zeroleft){ /* srcharg[] has a zero token at the start */
        xl=strc(s);  /* If xl==1, a match from beginning of line */
        mm= xl ? 0 : farstrlen(s); /* Offset to match or end of str */
  } else  xl=strd(s,&mm);
  n[0] += (nn ? nn+1 : mm+1);
  return (xl ? mm+1 : 0);
}
#endif

#if 0
int farstrlen(s) char far *s; {
register int i;
i=0; while(s[i++]); return i-1;
}
#endif

#if 1
int farstrlen(s) char far *s; {
asm     les     di,s
asm     xor     al,al
asm     xor     cx,cx
asm     dec     cx
asm     repnz   scasb   /* scan until cx=0 or [di] == al */
asm     mov     ax,-2
asm     sub     ax,cx
return (_AX);
}
#endif

#if 0
/* Return 1 if it matches, 0 if it fails to match */
/* Don't mix and match the ASM versions with these! */
int strc(d) char far *d;{
char *s; int x,z;
extern int zero,zeroleft; extern int FOLDCASE; extern char *srcharg;
s=srcharg;
if(zeroleft) ++s; /* First zero matches beginning of line */
while((z= *s++) != 0) {
  x = *d++;
  if(x==0){             /* Reached end of search string */
    if(z==zero) break;  /* Matched */
    /* Otherwise, z != 0 implies z != x and we exit below */
  }
  if(FOLDCASE) x = fold(x);
  if(x!=z) return 0;   /* Failed to match */
}
return 1;       /* It matched */
}
#endif

#if 1
int strc(d) char far *d; {
extern char *srcharg; extern int zero; extern int FOLDCASE;
asm     les     di,dword ptr d
asm     mov     dx,FOLDCASE
asm     mov     si,srcharg
asm     cmp     zeroleft,0
asm     je      loop
asm     inc     si
loop:
asm     mov     ah,byte ptr [si]
asm     inc     si
asm     or      ah,ah
asm     jz      matched                 /* End of search string */
strc5:
asm     mov     al,byte ptr es:[di]
asm     inc     di
asm     or      al,al                   /* End of string? */
asm     jnz     strc5x
asm     mov     al,ah
asm     cbw
asm     cmp     ax,zero                 /* z==zero? */
asm     je      matched                 /* Got a match */
asm     jmp     short mismatched        /* No match */
strc5x:
asm     or      dl,dl                   /* if(FOLDCASE==0) */
asm     jz      strc6
asm     test    al,128                  /* Signed? */
asm     jnz     strc6
asm     cmp     al,'a'
asm     jl      strc6
asm     cmp     al,'z'
asm     jg      strc6
asm     and     al,11011111B            /* fold case to upper */
strc6:
asm     cmp     al,ah                   /* Do they match? */
asm     jz      loop
mismatched:
asm     xor     ax,ax
asm     jmp     short quit
matched:
asm     mov     ax,1
quit:
return (_AX);
}
#endif

#if 0
/* Return 1 if it matches, 0 if it fails to match */
/* Updates n[0] to be the offset to the string match or end of string */
int strd(d,n) char far *d; int *n; {
char far *d1; int flag;
register int x,z; int xl;
extern int zero,zeroleft; extern int FOLDCASE; extern char *srcharg;
 flag=0; d1=d;
 x= zeroleft ? srcharg[1] : srcharg[0]; /* x is already uppercase */
 xl= (FOLDCASE && 'A'<=x && x<='Z') ? (x+' ') : x;
  /* First zero matches beginning of line */
 while((z=d1[0])!=0){
  if((x==z || xl==z) && (flag=strc(d1)) != 0) break; ++d1;
 }
 n[0]=(int)(d1-d);
 return flag;
}
#endif


#if 1
int strd(d,n) char far *d; int *n; {
extern char *srcharg; extern int FOLDCASE; extern int zero,zeroleft;
asm     mov     bx,srcharg
asm     cmp     zeroleft,0
asm     je      strd0
asm     inc     bx
strd0:
asm     mov     ch,byte ptr [bx]        /* Already in uppercase */
asm     mov     cl,ch                   /* x=ch, xl=cl */
asm     cmp     FOLDCASE,0
asm     jz      strd1
asm     test    cl,128                  /* Signed? */
asm     jnz     strd1
asm     cmp     cl,'A'
asm     jl      strd1
asm     cmp     cl,'Z'
asm     jg      strd1
asm     add     cl,' '                  /* xl==cl is in lowercase */
strd1:
asm     les     bx,dword ptr d
loop:
asm     mov     al,byte ptr es:[bx]
asm     or      al,al
asm     je      nomatch                 /* At end of string */
asm     cmp     al,cl
asm     je      strd2
asm     cmp     al,ch
asm     jne     strd3
strd2:
asm     push    es
asm     push    bx
asm     call    strc            /* Doesn't use cx or bx */
asm     pop     bx
asm     pop     es
asm     or      ax,ax           /* 1=match, 0=nomatch */
asm     jnz     match           /* Got  match */
strd3:
asm     inc     bx
asm     jmp     short loop
nomatch:
asm     xor     dx,dx
asm     jmp     short quit
match:
asm     mov     dx,1
quit:
asm     les     cx,d
asm     mov     ax,bx
asm     sub     ax,cx
asm     mov     bx,n
asm     mov     word ptr [bx],ax        /*  n[0]=(int)(d1-d); */
asm     mov     ax,dx                   /*  return flag; */
return (_AX);
}
#endif

int strccmp(d,s,n) char *d,*s; int n; {
int i;
int fold();
while((n-- > 0) && (fold(*d) == *s)) {
  if(*d == '\0' || n == 0) return 0;
  ++d; ++s;
}
return (fold(*d) - *s);
}

int fold(c) int c; {
return (('a' <= c && c <= 'z') ? (c - ' ') : c);
}

/*
 * MYDOTABS
 *
 * Returns 1 if the line needs detabbing, 0 otherwise.
 */
int mydotabs(buf) char far *buf; {
asm     les     di,dword ptr buf
asm     mov     ah,9
tabloop:
asm     mov     al,byte ptr es:[di]
asm     inc     di
asm     or      al,al
asm     jz      notab
asm     cmp     al,ah
asm     jnz     tabloop
return 1;
notab:
return 0;
}

/*
 * DOTABS
 *
 * Returns 1 if the line needs detabbing
 */
int dotabs(buf) char *buf; {
char *MYstrchr();
 if(MYstrchr(buf,'\t') != (char *)0) return 1;
 return 0;
}

void sdetab(s,d)
char far *s; char *d;
{
register int lc; int tc;
  lc=0;
  while(s[0]){
    if(s[0] != '\t') {*d++ = *s++; ++lc;}
    else {
      tc=(lc&(STOPS-1)); tc = tc ? STOPS-tc : STOPS;
      lc += tc; while(tc--) *d++ = ' '; ++s;
    }
  }
  *d=0;
}

/*
 * DETAB
 *
 * Converts tabs on the fly, writing over the input buffer as
 * needed when the conversion is incomplete. Returns the next
 * buffer location to be processed.
 */
char *detab(source,dest)
char *source,*dest;
{
register char *p,*q;
register int lc,tc;
  p=source; q=dest; lc=0;
  while(p[0]){
    if(p[0] != '\t') {if(++lc >= MAXLN-1) break; *q++ = *p++; }
    else {
      tc=(lc&(STOPS-1)); tc = tc ? STOPS-tc : STOPS;
      if(lc+tc>=MAXLN-1) break;
      lc += tc; while(tc--) *q++ = ' '; ++p;
    }
  }
  *q=0;
  if(p[0]) return p;
  return p+1;
}

char *strstart(s) char *s; {

asm     mov     si,s
S3:
asm     dec     si
asm     mov     al,byte ptr [si]
asm     or      al,al
asm     jne     S3
asm     mov     ax,si
return ((char *)_AX);
}

char *endof(s) char *s; {
asm     mov     si,s
L1:
asm     mov     al,byte ptr [si]
asm     or      al,al
asm     je      L2
asm     inc     si
asm     jmp     SHORT L1
L2:
asm     mov     ax,si
return (char *)(_AX);
}

char * sob(p)
char *p;
{
asm     mov     si,p
N3:
asm     mov     al,byte ptr [si]
asm     cmp     al,32
asm     jne     N4
asm     inc     si
asm     jmp     short N3
N4:
asm     mov     ax,si
return (char *)(_AX);
}

char * fnb(p)
char *p;
{
asm     mov     si,p
N7:
asm     mov     al,byte ptr [si]
asm     cmp     al,32
asm     je      N8
asm     or      al,al
asm     je      N8
asm     inc     si
asm     jmp     short N7
N8:
asm     mov     ax,si
return (char *)(_AX);
}

int MYmax(a,b)
int a,b;
{
asm     mov     ax,a
asm     cmp     ax,b
asm     jge     N9
asm     mov     ax,b
N9:
return (_AX);
}

int MYmin(a,b)
int a,b;
{
asm     mov     ax,a
asm     cmp     ax,b
asm     jle     N11
asm     mov     ax,b
N11:
return (_AX);
}

#if 0
unsigned minu(a,b)
unsigned a,b;
{
asm     mov     ax,a
asm     cmp     ax,b
asm     jae     N12
asm     jmp     short N11
N12:
asm     mov     ax,b
N11:
return (_AX);
}
#endif

int MYabs(a)
int a;
{
asm     mov     ax,a
asm     cmp     ax,0
asm     jge     N13
asm     neg     ax
N13:
return (_AX);
}

void fillchr(d,c,n)
char *d; char c; unsigned n;
{
asm     mov     si,d
asm     mov     al,c
asm     mov     bx,n
N17:
asm     or      bx,bx
asm     je      N18
asm     mov     byte ptr [si],al
asm     inc     si
asm     dec     bx
asm     jmp     short N17
N18:
;
}

int strmov(d,s)         /* like strcpy. Copy null. ret count. */
char *d; char *s;
{
asm     mov     si,d
asm     mov     di,s
asm     mov     ax,0
loop:
asm     mov     cl,byte ptr [di]
asm     mov     byte ptr [si],cl
asm     inc     si
asm     inc     di
asm     inc     ax
asm     or      cl,cl
asm     jnz     loop
asm     dec     ax              /* ret count of non-null chars */
return (_AX);
}

int MYisupper(z) int z; {
asm     mov     bx,0
asm     mov     ax,z
asm     cmp     ax,'A'
asm     jl      quit
asm     cmp     ax,'Z'
asm     jg      quit
asm     inc     bx
quit:
asm     mov     ax,bx
return (_AX);
}

int MYislower(z) int z; {
asm     mov     bx,0
asm     mov     ax,z
asm     cmp     ax,'a'
asm     jl      quit
asm     cmp     ax,'z'
asm     jg      quit
asm     inc     bx
quit:
asm     mov     ax,bx
return (_AX);
}

int MYtoupper(z) int z; {
asm     mov     ax,z
asm     mov     bx,ax
asm     cmp     ax,'a'
asm     jl      quit
asm     cmp     ax,'z'
asm     jg      quit
asm     sub     bx,' '
quit:
asm     mov     ax,bx
return (_AX);
}

int MYtolower(z) int z; {
asm     mov     ax,z
asm     mov     bx,ax
asm     cmp     ax,'A'
asm     jl      quit
asm     cmp     ax,'Z'
asm     jg      quit
asm     add     bx,' '
quit:
asm     mov     ax,bx
return (_AX);
}

int tailchar(s) char *s; {
register int j;
  return ((j=strlen(s))==0 ? 0 : s[j-1]);
}

char *toprint(p) char *p; {
 if(strlen(p)<curoff) p=""; else p += curoff;
 return p;
}


#if 0
char *strchr(s, c) char *s; char c; {
   while (*s){ if (*s == c) return(s); s++; } return(0);
}

char *strrchr(s, c) char *s; char c; {
int n;
  n=strlen(s);
  while(n){
    if(s[--n]==c) return (s+n);
  }
  return(0);
}
#endif

char *MYstrchr(s, c) unsigned char *s; unsigned char c; {
/*   while (*s){ if (*s == c) return(s); s++; } return(0); */
asm     mov     ah,byte ptr c
asm     mov     si,word ptr s
loop:
asm     mov     al,byte ptr [si]
asm     or      al,al
asm     jz      fail
asm     cmp     al,ah
asm     je      success
asm     inc     si
asm     jmp     short loop
fail:
asm     xor     ax,ax
asm     jmp     short quit
success:
asm     mov     ax,si
quit:
return (char *)(_AX);
}

char *MYstrrchr(s, c) unsigned char *s; unsigned char c; {
  _AX = strlen(s);
asm     mov     si,s
asm     add     si,ax
asm     mov     bl,c
/*  while(n){ if(s[--n]==c) return (s+n); } */
loop:
asm     or      ax,ax
asm     jz      fail
asm     dec     ax
asm     dec     si
asm     cmp     bl,byte ptr [si]
asm     jne     loop
succeed:
asm     mov     ax,si
asm     jmp     short quit
fail:
/*  return(0); */
asm     xor     ax,ax
asm     jmp     short quit
quit:
return (char *)(_AX);
}

int MYstrncmp(s,t,n) unsigned char *s,*t; int n; {
/*
  while(n-- && s[0]==t[0]){ if(!s[0] || !n) return 0; ++s; ++t;}
  return ((unsigned char)s[0]-(unsigned char)t[0]);
*/
asm     mov     cx,n
asm     mov     si,s
asm     mov     di,t
loop:
asm     cmp     cx,0
asm     jz      end
asm     dec     cx
asm     mov     al,byte ptr [si]
asm     cmp     al,byte ptr [di]
asm     jne     end
asm     cmp     cx,0
asm     je      zero
asm     cmp     al,0
asm     je      zero
asm     inc     si
asm     inc     di
asm     jmp     loop
zero:
asm     mov     ax,0
asm     jmp     exit
end:
asm     mov     al,byte ptr [si]
asm     mov     dl,byte ptr [di]
asm     mov     ah,0
asm     mov     dh,ah
asm     sub     ax,dx
exit:
return (_AX);
}

int MYstrcmp(s,t) unsigned char *s,*t; {
/*
  while(s[0] && s[0]==t[0]){ ++s; ++t;}
  return ((unsigned char)s[0]-(unsigned char)t[0]);
*/

asm     mov     si,s
asm     mov     di,t
loop:
asm     mov     al,byte ptr [si]
asm     cmp     al,0
asm     je      end
asm     cmp     al,byte ptr [di]
asm     jne     end
asm     inc     si
asm     inc     di
asm     jmp     loop
end:
asm     mov     al,byte ptr [si]
asm     mov     dl,byte ptr [di]
asm     mov     ah,0
asm     mov     dh,ah
asm     sub     ax,dx
return (_AX);
}

char *lltoa(n,str) LLONG n; char *str;
{
LLONG i;
char *p;
char q[61];
char flag;
  *(p = &q[60]) = '\0';
  flag=(n<0);
  i = flag ? -n : n;
  do { *--p = '0' + i%10; } while( (i /= 10) > 0);
  if(flag) *--p = '-';
  strcpy(str,p);
  return str;
}

LLONG atoiLL(s) char *s; {
LLONG i;
char flag;
extern char *sob();
  s=sob(s);
  flag=i=0;
  if(s[0]=='+') ++s; else
  if(s[0]=='-'){++s; flag=1;}
  while(MYisdigit(s[0])) i = 10*i + (*s++) -'0';
  return (flag ? -i : i);
}

#endif          /* TURBOC */


#if !TURBOC || (DOS386I && !IBMHARDWARE)
/*
 * Routines below are entirely in C so that
 * they become portable across machines. Most
 * non-portable code for MS-DOS appears above.
 */

#if 1
LLONG atoiLL(ss) char *ss; {
LLONG i;
char flag;
extern char *sob();
extern int MYisdigit();
char *s;
  s=sob(ss);
  flag=i=0;
  if(s[0]=='+') ++s; else
  if(s[0]=='-'){++s; flag=1;}
  while(MYisdigit(s[0])) i = 10*i + (*s++) -'0';
  return (flag ? -i : i);
}
#endif

char *MYstrchr(s, c) unsigned char *s; int c; {
   while (*s){ if (*s == c) return(s); s++; } return(0);
}

char *MYstrrchr(s, c) unsigned char *s; int c; {
int n;
  n=strlen(s);
  while(n){
    if(s[--n]==c) return (s+n);
  }
  return(0);
}

char *lltoa(n,str) LLONG n; char *str;
{
LLONG i;
char *p;
char q[61];
char flag;
  *(p = &q[60]) = '\0'; flag=(n<0);
  i = (flag ? -n : n);
  do { *--p = '0' + i%10; } while( (i /= 10) > 0);
  if(flag) *--p = '-';
  strcpy(str,p);
  return str;
}

#if LCCWIN32
void ePrint(p)       /* no crlf pair issued */
char *p;
{
static
int x,i,j;
extern int COLMAX,nodisp;
extern int MYmin();
char s[1024];
  if(!nodisp) {
    i=j = 0;
    while((p[j] & 96) != 0) ++j;
    if(p[j] && !(p[j] & 96)) j = MYmin(strlen(p),COLMAX+1);
    else
/*    if(j>COLMAX) j = COLMAX+1; */
    if(j>COLMAX) j = COLMAX;
    while(j--) {
      if((x = *p++) & 96) s[i++] = x; else {
        if((x & 128)!=0) s[i++] = x; else {
          s[i]=0; etype(s); invvideo(x); i=0;
          /* s[i++]= x + '@'; */
        }
      }
    }
    s[i]=0; etype(s);
  }
}
#else
void ePrint(p)       /* no crlf pair issued */
char *p;
{
static
int x,i,j;
extern int COLMAX,nodisp;
extern int MYmin();
char s[1024];
  if(!nodisp) {
    i=j = 0;
    while((p[j] & 96) != 0) ++j;
    if(p[j] && !(p[j] & 96)) j = MYmin(strlen(p),COLMAX+1);
    else
    if(j>COLMAX) j = COLMAX+1;
    while(j--) {
      if((x = *p++) & 96) s[i++] = x; else {
        if((x & 128)!=0) s[i++] = x; else {
        strcpy(&s[i],REVVIDEO); i += strlen(REVVIDEO);
        s[i++]= x + '@';
#if BSDunix || SYS5
        if(UNIXtextfgbg[0]==0) {
          strcpy(&s[i],NORMVIDEO); i += strlen(NORMVIDEO);
        } else {
          strcpy(&s[i],UNIXtextfgbg); i += strlen(UNIXtextfgbg);
        }
#else
        strcpy(&s[i],NORMVIDEO); i += strlen(NORMVIDEO);
#endif
        }
      }
    }
    s[i]=0; etype(s);
  }
}

#endif

#if !LCCWIN32
int eePrint(p,s)      /* issue crlf pair before string print */
register char *p; register char *s;
{
int x,i,j;
extern int COLMAX,nodisp;
extern int MYmin();
extern char *skipdelay();
  if(!nodisp) {
    i = j = 0;
    s[i++]= '\r'; s[i++]= '\n';
    while((p[j] & 96) != 0) ++j;
    if(p[j] && !(p[j] & 96)) j = MYmin(strlen(p),COLMAX+1);
    else
    if(j>COLMAX) j = COLMAX+1;
    while(j--) {
      if((x = *p++) & 96) s[i++] = x; else {
        if((x & 128)!=0) s[i++] = x; else {
        strcpy(&s[i],REVVIDEO); i += strlen(REVVIDEO);
        s[i++]= x + '@';
#if BSDunix || SYS5
        if(UNIXtextfgbg[0]==0) {
          strcpy(&s[i],NORMVIDEO); i += strlen(NORMVIDEO);
        } else {
          strcpy(&s[i],UNIXtextfgbg); i += strlen(UNIXtextfgbg);
        }
#else
        strcpy(&s[i],NORMVIDEO); i += strlen(NORMVIDEO);
#endif
        }
      }
    }
#if SYS5 && LCCWIN32
#else
    strcpy(&s[i],skipdelay(ce_termcap));
    i += strlen(skipdelay(ce_termcap));
#endif
    s[i]=0;
  }
  return i;
}

#endif

#if SYS5 && LCCWIN32
void strinvvideo(s) char *s; {
  setLCCWIN32inversecolors(); etype(s); setLCCWIN32colors();
}
#else
void strinvvideo(s) char *s; {
  etype(REVVIDEO);
  etype(s);
#if BSDunix || SYS5
  if(UNIXtextfgbg[0]==0) etype(NORMVIDEO); else etype(UNIXtextfgbg);
#else
  etype(NORMVIDEO);
#endif
}
#endif

char *drawbuff=(char *)0;

#if LCCWIN32
void setdrawbuff(){}
#else
void setdrawbuff(){
int n; extern char *core();
  freeup(drawbuff);
  n=(strlen(REVVIDEO)+strlen(NORMVIDEO)+1)*(3+COLMAX)*(1+ROWMAX);
  drawbuff=core(n);
}
#endif

#include <errno.h>
extern int errno;
void drawall()
{
register
int i,n;
extern char **line; extern char *toprint();
extern int nodisp,halfdone;
extern FILE *termout;

if(testkey()) {nodisp=halfdone=1; return;}
if(!nodisp){
  nonzeroScreenLines=0;
  setcursortype(2);
#if LCCWIN32
  setLCCWIN32colors();
  statblank=TRUE; clrscr();
  for(i=0;i<=ROWMAX;++i) {
    crlf(); ePrint(toprint(line[i]));
    if(line[i][0]) ++nonzeroScreenLines;
  }
#else
  n=0;
  for(i=0;i<=ROWMAX;++i) {
    n += eePrint(toprint(line[i]),&drawbuff[n]);
    if(line[i][0]) ++nonzeroScreenLines;
  }
  home();
#if (BSDunix || SYS5) && !LCCWIN32
    write(fileno(termout),drawbuff,n);
    fflush(termout);
#endif
#if MSDOS
    etype(drawbuff);
#endif
#endif
  halfdone=0;
  lcount();
  adjustNonzeroScreenLines();
  newuserAnnoyingMessage();
  curses();
  setcursortype(1);
  }
}

#if LCCWIN32
void xPrint(p,rrow,ccol) char *p; int rrow,ccol; {
static
int i,x,j;
extern int COLMAX,nodisp;
extern int MYmin();
char s[1024];
  if(!nodisp) {
    if(ccol > COLMAX) ccol = COLMAX;
    i=j = 0;
    while((p[j] & 96) != 0) ++j;
    /* lcc library messes up in last column of console */
    if(p[j] && !(p[j] & 96)) j = MYmin(strlen(p),COLMAX-ccol);
    else
    if(j>COLMAX-ccol) j = COLMAX-ccol;
    putcursor(rrow,ccol);
    while(j--) {
      x = *p++;
      if(x & 96) s[i++] = x; else {
        if((x & 128)!=0) s[i++] = x; else {
          s[i]=0; etype(s); invvideo(x); i=0;
        }
      }
    }
    s[i]=0; etype(s);
  }
}
#else
void xPrint(p,rrow,ccol) char *p; int rrow,ccol; {
static
int i,x,j;
extern int COLMAX,nodisp;
extern int MYmin();
char s[1024];
  if(!nodisp) {
    if(ccol > COLMAX+1) ccol = COLMAX+1;
    i=j = 0;
    while((p[j] & 96) != 0) ++j;
    if(p[j] && !(p[j] & 96)) j = MYmin(strlen(p),COLMAX+1-ccol);
    else
    if(j>COLMAX-ccol) j = COLMAX+1-ccol;
    putcursor(rrow,ccol);
    while(j--) {
      x = *p++;
      if(x & 96) s[i++] = x; else {
        if((x & 128)!=0) s[i++] = x; else {
        strcpy(&s[i],REVVIDEO); i += strlen(REVVIDEO);
        s[i++]= x + '@';
#if BSDunix || SYS5
        if(UNIXtextfgbg[0]==0) {
          strcpy(&s[i],NORMVIDEO); i += strlen(NORMVIDEO);
        } else {
          strcpy(&s[i],UNIXtextfgbg); i += strlen(UNIXtextfgbg);
        }
#else
        strcpy(&s[i],NORMVIDEO); i += strlen(NORMVIDEO);
#endif
        }
      }
    }
    s[i]=0; etype(s);
  }
}
#endif

void setcursortype(n) int n; {       /* do nothing for DOS386I */
#if LCCWIN32
switch(n){
case 0: _setcursortype(_NORMALCURSOR); break;
case 1: _setcursortype(_SOLIDCURSOR); break;
case 2: _setcursortype(_NOCURSOR); break;
};
#endif
}

void equipment(){            /* do nothing for DOS386I */
}

#if !DOS386I
int startof(s,k) char far *s; int k; {
  while(k){ if(s[k-1]==0) break; --k;}
return k;
}

void sdetab(s,d)
char *s; char *d;
{
register int lc,tc;
  lc=0;
  while(s[0]){
    if(s[0] != '\t') {*d++ = *s++; ++lc;}
    else {
      tc=(lc&(STOPS-1)); tc = tc ? STOPS-tc : STOPS;
      lc += tc; while(tc--) *d++ = ' '; ++s;
    }
  }
  *d=0;
}

/* Search source string s=ss+n[0] for a match to srcharg[] */
/* Returns string index of match >=1 or 0 for failure */
/* Array n[] is updated on failure to allow search of next string */
/* in the buffer (assume buffer full of 0-delimited strings). */
/* May 1995: Make search string ASCII 128 match end of line char */

int midstr(sss,n)
char far *sss;                /* source string */
int *n;                       /* offset to end of string on fail */
{                             /* offset to start of string on entry */
char far *s;
int nn,mm,xl;
extern int taberr; extern int zeroleft,zeroright;
extern char *lbuf;
  s=sss+n[0]; nn=0;
  if(taberr && dotabs(s)) {nn=strlen(s); sdetab(s,lbuf+1); s=lbuf+1;}
  if(zeroleft){ /* srcharg[] has a zero token at the start */
        xl=strc(s);  /* If xl==1, a match from beginning of line */
        mm= xl ? 0 : strlen(s); /* Offset to match or end of str */
  } else  xl=strd(s,&mm);
  n[0] += (nn ? nn+1 : mm+1);
  return (xl ? mm+1 : 0);
}

/* Return 1 if it matches, 0 if it fails to match */
int strc(d) char far *d;{
char *s; int x,z;
extern int zero,zeroleft; extern int FOLDCASE; extern char *srcharg;
s=srcharg;
if(zeroleft) ++s; /* First zero matches beginning of line */
while((z= *s++) != 0) {
  x = *d++;
  if(x==0){             /* Reached end of search string */
    if(z==zero) break;  /* Matched */
    /* Otherwise, z != 0 implies z != x and we exit below */
  }
  if(FOLDCASE && 'a'<=x && x<='z') x -= ' ';
  if(x!=z) return 0;   /* Failed to match */
}
return 1;       /* It matched */
}

/* Return 1 if it matches, 0 if it fails to match */
/* Updates n[0] to be the offset to the string match or end of string */
int strd(d,n) char far *d; int *n; {
register char far *d1; int flag;
register int x,xl,z;
 flag=0; d1=d;
 x= zeroleft ? srcharg[1] : srcharg[0]; /* x is already uppercase */
 xl= (FOLDCASE && 'A'<=x && x<='Z') ? (x+' ') : x;
  /* First zero matches beginning of line */
 while((z=d1[0])!=0){
  if((x==z || xl==z) && (flag=strc(d1)) != 0) break; ++d1;
 }
 n[0]=(int)(d1-d);
 return flag;
}

int strccmp(d,s,n) char *d,*s; int n; {
int fold();
while((n-- > 0) && (fold(*d) == *s)) {
  if(*d == '\0' || n == 0) return 0;
  ++d; ++s;
}
return (fold(*d) - *s);
}

int fold(c) int c; {
return (('a' <= c && c <= 'z') ? (c - ' ') : c);
}

char *strstart(q)
char *q;
{
  while(*--q) ;
  return (q);
}

char *endof(p)
char *p;
{
  return (p + strlen(p));
}

void strip(p) char *p; {
register
int i;
  i = strlen(p);
  while(--i >= 0) {
    if(p[i] != ' ') break;
  }
  p[i+1] = 0;
}

/*
 * DETAB
 *
 * Converts tabs on the fly, writing over the input buffer as
 * needed when the conversion is incomplete. Returns the next
 * buffer location to be processed.
 */
char *detab(source,dest)
char *source,*dest;
{
register char *p,*q;
register int lc,tc;
  p=source; q=dest; lc=0;
  while(p[0]){
    if(p[0] != '\t') {if(++lc >= MAXLN-1) break; *q++ = *p++; }
    else {
      tc=(lc&(STOPS-1)); tc = tc ? STOPS-tc : STOPS;
      if(lc+tc>=MAXLN-1) break;
      lc += tc; while(tc--) *q++ = ' '; ++p;
    }
  }
  *q=0;
  if(p[0]) return p;
  return p+1;
}

/*
 * DOTABS
 *
 * Returns 1 if the line needs detabbing, 0 otherwise.
 */
int dotabs(buf) char *buf; {
char *MYstrchr();
 if(MYstrchr(buf,'\t') != (char *)0) return 1;
 return 0;
}

/*
 * UNSETBITS
 *
 * Strips parity bit from a string.
 */
void unsetbits(s) char *s;{
register char *p;
  p=s;
  while(*p){ if((127 & p[0]) != 0) p[0] &= 127; ++p;}
}

char *toprint(p) char *p; {
 if(strlen(p)<curoff) p=""; else p += curoff;
 return p;
}

int strmov(d,s)         /* like strcpy. Copy null. ret count. */
register char *d,*s;    /* d=destination, s=source */
{
register int i;
i=0;
while((*d++ = *s++) != 0) ++i;
return i;     /* ret count of non-null chars */
}


int tailchar(s) char *s; {
int j;
  return ((j=strlen(s))==0 ? 0 : s[j-1]);
}

#endif          /* end !DOS386I */
#endif          /* end !TURBOC || (DOS386I && !IBMHARDWARE) */

#if !TURBOC
/*
 * ISUPPE
 *
 * Purpose:
 *              Test for upper case character.
 */
int MYisupper(c)
int c;
{
return ('A' <= c && c <= 'Z');
}

int MYislower(c)
int c;
{
return ('a' <= c && c <= 'z');
}

/*
 * TOUPPE
 *
 * Purpose:
 *              Convert character to upper case.
 */
int MYtoupper(z)
int z;
{
return ( MYislower(z) ? z - ' ' : z);
}

int MYtolower(z)
int z;
{
return ( MYisupper(z) ? z + ' ' : z);
}

/*
 * MAX
 *
 */
int MYmax(a,b)
int a,b;
{
return ( a < b ? b : a);
}

/*
 * MIN
 *
 */
int MYmin(a,b)
int a,b;
{
return ( a < b ? a : b);
}

/*
 * ABS
 *
 */
int MYabs(a)
int a;
{
return (a > 0 ? a : -a);
}
#endif

#if 0           /* No longer used. See ssprintf. */
/*
 * UTOA
 *
 * Purpose:
 *              Convert integer to ascii string.
 *              Standard C library function.
 */
char *utoa(n,str) LLong n; char *str;
{
LLong i;
char *p;
char q[61];
  i = n; *(p = &q[60]) = '\0';
  do { *--p = '0' + i%10; } while( (i /= 10) > 0);
  strcpy(str,p);
  return str;
}

#endif



#if !HASFILLCHR
void fillchr(d,c,n)
char *d; char c; unsigned n;
{
     while(n--) *d++ = c;
}
#endif

#if !HASCVUPPER
void cvupper(s)
char *s;
{
while(*s) { *s = MYtoupper(*s); ++s;}
}
#endif

/*
 * V14.C
 */
#include "pie.h"
#if !TURBOC
void menumouse(n) int n;{}
#endif
#if TURBOC
#ifdef MOUSEBUILTIN
#pragma inline
/*
 * MOUSE SENSITIVITY. A curious feature in mouse code is sensitivity to
 * movement: settling down on a character is subject to much error. In the
 * code below this problem is attacked.
 *
 *     Ideally, the cursor should lock onto a line and not be bumped off
 *     easily as the cursor scans left and right. Further, up and down
 *     motion should lock the cursor onto a column. I should like to scan
 *     up and down columns of numbers with the mouse exactly as I do with
 *     the cursor keys. The code below is an attempt to realize these
 *     features that falls short of perfection.
 *
 * MOUSE DRIVER DEFAULTS. The TSR resets the mouse driver and but sets no
 * defaults for the mouse controls: X,Y sensitivity, doubler.
 */

#define leftkey                 4b00h
#define rightkey                4d00h
#define upkey                   4800h
#define downkey                 5000h
#define retkey                  1c0dh
#define esckey                  011bh

#define mouse                   51         /* interrupt 33h */
#define resetmousedriver         0         /* reset mouse driver */
#define pressmouse               5         /* mouse button press status */
#define mousemotion             11         /* mouse cursor motion */
#define seteventhandler         12         /* set mask and event handler */
#define eventmask               0000000000001011b
/* evenmask bits 0,1,4: movement, left & right press */

/*
 Mouse driver resets to 8 and 16 respectively with function 00h.
 Znix and Merit both worked well with settings 8/16.
*/
extern int hsens,vsens;
static int hpos=0;      /* store mouse cursor position X */
static int vpos=0;      /* store mouse cursor position Y */

static
stuffbuffer(){
asm     cli                     /*Prevent interrupts from stuffing buffer*/
asm     push es                 /*typeahead buffer start at *[0040:0080]*/
asm     push si                 /*and end at *[0040:0082].*/
asm     push di                 /*At segment 40h and offset 01ah is*/
asm     push bx                 /**/
asm     mov di,40h              /*the offset into the typeahead buffer*/
asm     mov es,di               /*for the character at the head. At*/
asm     mov bx,es:[1ch]         /*segment 40h and offset 01ch is the*/
                                /*offset into the typeahead buffer for*/
asm     add bx,2                /*the last character.*/
asm     mov si,bx               /*save pointer to next char*/
asm     cmp bx,es:[82h]         /*if *[01ch]+2 == *[082h], then wraparound*/
asm     jnz isroom              /*Buffer has room if NZ*/
asm     mov bx,es:[80h]         /*Else wrap around to buffer start*/
asm     mov si,bx               /*because buffer is full to high end.*/
isroom:
asm     cmp bx,es:[1ah]         /*Any room to stuff the char?*/
asm     jz quit                 /*Failure, buffer full*/
asm     mov bx,es:[1ch]         /*segment 40h and offset 01ch is where*/
asm     mov es:[bx],cx          /*CX=scan code is to be stuffed*/
asm     mov bx,si               /**/
asm     mov es:[1ch],bx         /*BX=new offset to last buffer character*/
quit:
asm     pop bx                  /**/
asm     pop di                  /**/
asm     pop si                  /**/
asm     pop es                  /**/
asm     sti                     /*let other routines stuff the buffer*/
}

static
manystuffbuffer(){
/* AX=amount in mickeys mouse cursor has moved. */
/* BX=sensitivity in mickeys */
/* CX=key scan code to stuff into keyboard buffer */
manyloop:
asm     call stuffbuffer
asm     sub ax,bx
asm     cmp ax,bx
asm     jge manyloop
}

static
far mouseeventhandler() {
asm     sti
asm     push ax
asm     push bx
asm     push cx
asm     push dx
asm     push ds                 /* ds=mouse driver data segment */
asm     cld
asm     mov ax, SEG _DATA       /* Get data segment for our data */
asm     mov ds,ax               /* Set data segment */
/*
 Press Mouse supplies: BX=button status, CX=X mickeys, DX=Y mickeys
                       The signed integers CX, DX measure UP==+, RIGHT==+
                       Clears all button counts on each call.
*/
asm     mov ax,pressmouse       /* get status of button press */
asm     mov bx,0                /* for left button */
asm     int mouse
asm     and ax,1                /* Press left button? */
asm     jz check_rightbutton    /* No? Then check right button. */
asm     mov cx,retkey           /* Yes. Then plug in RETURN key. */
asm     call stuffbuffer        /* near function call */
check_rightbutton:
asm     mov ax,pressmouse       /* get status of button press */
asm     mov bx,2                /* for right button */
asm     int mouse
asm     and ax,2                /* press right button? */
asm     jz checkcursor          /* No? Then check cursor keys. */
asm     mov cx,esckey           /* Yes. Then plug in ESC key. */
asm     call stuffbuffer        /* near function call */
checkcursor:
asm     mov ax,mousemotion      /* Has the mouse cursor moved? */
asm     int mouse
asm     mov ax,ds:hpos          /* save new horizontal position */
asm     add ax,cx               /* cx=X coord supplied by mouse driver */
asm     mov ds:hpos,ax
asm     cmp ax,0                /* Has the mouse cursor moved? */
asm     je  motionvertical      /* No change, then check vertical */
asm     jg motionhorizontal     /* Make positive */
asm     not ax
motionhorizontal:
asm     mov bx,ds:hsens
asm     cmp ax,bx
asm     jl motionvertical       /* didn't move enough for a change */
asm     cmp ds:hpos,0
asm     mov cx,rightkey
asm     jg  stuffit1            /* If positive, then stuff cursor Right */
asm     mov cx,leftkey
stuffit1:
asm     jmp stuffit2
motionvertical:
asm     mov ax,ds:vpos          /* save new vertical position */
asm     add ax,dx               /* dx=Y coord supplied by mouse driver */
asm     mov ds:vpos,ax
asm     cmp ax,0
asm     je eventreturn          /* no change, all done */
asm     jg testvertsens         /* If positive, then stuff cursor Up */
asm     not ax
testvertsens:
asm     mov bx,ds:vsens
asm     cmp ax,bx
asm     jl eventreturn          /* didn't move enough for a change */
asm     cmp ds:vpos,0
asm     mov cx,downkey
asm     jg stuffit2
asm     mov cx,upkey
stuffit2:
asm     call manystuffbuffer    /* stuff key CX into keyboard buffer */
asm     mov ds:vpos,0           /* reset vertical saved position */
asm     mov ds:hpos,0           /* reset horizontal saved position */
eventreturn:
asm     pop ds
asm     pop dx
asm     pop cx
asm     pop bx
asm     pop ax
}

/* Returns 1 if it worked, 0 if it didn't */
mousereset(){
asm     mov ax,hsens            /* Don't hookup mouse if hsens==0 */
asm     cmp ax,0                /* hsens,vsens set in PI.SET */
asm     je nomouse
asm     mov ax,resetmousedriver /* reset mouse driver to defaults */
asm     int mouse
asm     cmp ax,0                /* bx=number of buttons, not used yet */
asm     mov ax,0
asm     je nomouse              /* exit if no mouse driver loaded */
asm     mov ax,1
nomouse:;
}

mousehook(){
asm     mov ax,hsens            /* Don't hookup mouse if hsens==0 */
asm     cmp ax,0                /* hsens,vsens set in PI.SET */
asm     je nomouse
asm     mov ax,cs               /* Hook mouse driver to event handler */
asm     mov es,ax               /* es=segment of event handler */
asm     mov dx,offset mouseeventhandler
asm     mov ax,seteventhandler  /* mouse driver function code */
asm     mov cx,eventmask        /* signal mask for interrupt */
asm     int mouse
nomouse:;
}

static int vsensx,hsensx;

/* Slow down the mouse in menus */
void menumouse(flag) int flag; {
  if(flag){
    vsensx=vsens; hsensx=hsens;
    vsens= 2*vsens; hsens= 8*hsens;
  } else {
    vsens=vsensx; hsens=hsensx;
  }
}

#else
void menumouse(n) int n;{}
#endif
#endif
#include "pie.h"

/*
 * V15.C
 */
/* gcc required for unix operating system */
/* TURBO-C 1.5 or 2.0 required for MSDOS operating system. */
/* This is used to reduce overhead. Might use sprintf() instead */
/* for any unix system, because generally that will not change */
/* the total overhead for the editor executable. */

#ifdef ssprintf
/* DO NOTHING */
#else
#include <stdarg.h>

int  ssprintf(char *s,char *format, ...)
{
    va_list argptr;

    va_start(argptr, format);     /* initialize argument pointer */
    _svprinter(s,format, argptr);    /* do it */
    va_end(argptr);
}

_svprinter(s,format,argptr) char *s; char *format; va_list  argptr;
{
int  c;
char  buf[64];
char  cbuf[2];
char *p;

    cbuf[1]=s[0]=0;
    while((c= *format++) !=0)    /* main loop to scan format string */
    {
        if(c=='%') goto arg_eval; /* scan format spec */
maincharcopy:
        cbuf[0]=c; p=cbuf;
arg_eval_ret: strcat(s,p);
    }
    return;

arg_eval:
    c= *format++;        /* Get next char */
    p="";
    switch(c)
    {
        case 'd':
        case 'u':                           /* unsigned */
            lltoa((LLONG) va_arg(argptr, int), p=buf);
            break;
#if LLONGislong
        case 'l':                           /* long */
            if((c=format[0])=='d'  || c== 'u') ++format;
            else goto quitit;
            lltoa((LLONG) va_arg(argptr, LLONG), p=buf);
            break;
#endif
        case 's':
            p=(char *)(va_arg(argptr, char *));
            break;
        case 'c':                               /* character */
            c=va_arg(argptr, int);
        case '%':
             goto maincharcopy;
quitit:
        default:             /* invalid type - return for literal output */
            --format; c='%'; goto maincharcopy;
    }
  goto arg_eval_ret;

}    /* end of _svprinter */

#define TESTSSPRINTF 0
#if TESTSSPRINTF
/* #define LLONG long */
char *lltoa(n,str) LLONG n; char *str;
{
LLONG i;
char *p;
char q[61];
char flag;
  i = n; *(p = &q[60]) = '\0'; flag=(n<0);
  if(flag) i = -i;
  do { *--p = '0' + i%10; } while( (i /= 10) > 0);
  if(flag) *--p = '-';
  strcpy(str,p);
  return str;
}

strcat(s,t) char *s; char *t; {
  s += strlen(s);
  while((*s++ = *t++)!=0);
}


#include <stdio.h>
main(){
char s[128];
 ssprintf(s,"%s %u\n","Test",100);
 puts(s);
 ssprintf(s,"%s %u\nOne more line\n","Test",100);
 puts(s);
 ssprintf(s,"%s %u\nTwo more lines\nAns=%d\n","Test",100,201);
 puts(s);
}
#endif
#endif

#if MATHFLOAT
int mathinteger=0;         /* 0=double, 1=decimal */
int setmathformat(){
  getquery("Math format (printf):");
  if(sysv[0]){
    freeup(MATHFMT);
    safecore(&MATHFMT,sysv);
  }
  return getnumquery("Double (0) or integer (1) data?",&mathinteger,0,1);
}

#if 1
extern double   atof();
int convert2double(dest) char *dest; {
/* Replace dest by its float computation */
char buf[512]; /* Has to be at least 310 chars + number of digits */
register char *p;
double x,y;
int i;
extern char *MATHFMT;       /* Usually, its "%.08g" */
extern int mathinteger;

   x=atof(p=sob(dest));
   while(p[0]){
     if((p[0]=='-' || p[0]=='+') &&
        (MYisdigit(p[1]) || p[1]=='.')) ++p;
     p=sob(skipdecimal(p));
     y=atof(p+1);
     switch(p[0]){
     case '+': x += y; goto addone;
     case '-': x -= y; goto addone;
     case '/': x /= y; goto addone;
     case '*': x *= y;
     addone:   p=sob(p+1); break;
     case '=': if(p[1]=='='){
                 y=atof(sob(p+2));
                 if(x == y) x=1; else x=0;
               }
     default: goto quit;
     }
   }
quit: ;
   i=x;
   if(mathinteger)
   sprintf(buf,MATHFMT,i); else sprintf(buf,MATHFMT,x);
   strnzcpy(dest,buf,MAXLN-1);
   return 0;
}

char *skipdecimal(s) char *s;{
extern char *skipdigits(),*sob(),*MYstrchr();
register char *p;
 p=sob(s);
 if(p[0]=='.') ++p; else {p=skipdigits(p); if(p[0]=='.') ++p;}
 p=skipdigits(p);
 if(MYstrchr("eEdD",p[0])!=(char *)0) ++p; else return p;
 if(p[0]=='-' || p[0]=='+') ++p;
 return skipdigits(p);
}

char *skipdigits(p) register char *p; {
while(MYisdigit(p[0])!=0) ++p;
return p;
}
#endif

/* Rest of V15.C contains calculator code from vee.c */
#if !CALCULATOR
void calculator(){ banner("No calculator!"); getbyte();}
#else
/* #include "vee.c" */ /* Mark Morley's expression evaluator 1992 */
#if 0
***************************************************************************
**                                                                       **
** VEE.C         Expression Evaluator                                    **
**                                                                       **
**                                                                       **
** AUTHOR:      Mark Morley                                              **
** COPYRIGHT:   (c) 1992 by Mark Morley                                  **
** HISTORY:     December 1991 original code.                             **
**              Jun 1992 - Updated comments and docs                     **
**              Jun 1995 - Cut out code for use in editor appl.          **
**                         Changed macros to functions.                  **
**                                                                       **
** You are free to incorporate this code into your own works, even if it **
** is a commercial application.  However, you may not charge anyone else **
** for the use of this code!  If you intend to distribute your code,     **
** I'd appreciate it if you left this message intact.  I'd like to       **
** receive credit wherever it is appropriate.  Thanks!                   **
**                                                                       **
** Please mail any bug reports/fixes/enhancments to me at:               **
**      morley@camosun.bc.ca                                             **
** or                                                                    **
**      Mark Morley                                                      **
**      3889 Mildred Street                                              **
**      Victoria, BC  Canada                                             **
**      V8Z 7G1                                                          **
**      (604) 479-7861                                                   **
**                                                                       **
**************************************************************************
#endif
#include <setjmp.h>

#define TYPE            double          /* Type of numbers to work with */

#define VARLEN          15              /* Max length of variable names */
#define MAXVARS         50              /* Max user-defined variables */
#define TOKLEN          30              /* Max token length */

#define VAR             1
#define DEL             2
#define NUM             3

typedef struct
{
   char name[VARLEN + 1];               /* Variable name */
   TYPE value;                          /* Variable value */
} VARIABLE;

typedef struct
{
   char* name;                          /* Function name */
   int   args;                          /* Number of arguments to expect */
   TYPE  (*func)();                     /* Pointer to function */
} FUNCTION;

void ERR(int);
double deg(double);
double rad(double);
double cvbase(double, int);
double oct(double);
double bin(double);
int GetSymbol(char *, TYPE *);
void ClearAllVars(void);
int ClearVar(char *);
int GetValue(char *, TYPE *);
int SetValue(char *, TYPE *);
void Parse(void);
int Level1(TYPE *);
void Level2(TYPE *);
void Level3(TYPE *);
void Level4(TYPE *);
void Level5(TYPE *);
void Level6(TYPE *);
void cvlower(char *);
int Evaluate( char* , TYPE* , int* );
int evalexpr(char *, char *, TYPE *);

#if 0
/* The following macros are ASCII dependant, no EBCDIC here! */
#define iswhite(c)  (c == ' ' || c == '\t')
#define isnumer(c)  ((c >= '0' && c <= '9') || c == '.')
#define MYisalpha(c)  ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') \
                    || c == '_')
#define isdelim(c)  (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' \
                    || c == '^' || c == '(' || c == ')' || c == ',' || c == '=')

#else
static int iswhite(c) int c; {
 return(c == ' ' || c == '\t');
}

static int isnumer(c) int c;{
  return((c >= '0' && c <= '9') || c == '.');
}


static int MYISalpha(c) int c;{
  return((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
                    || c == '_');
}

static int isdelim(c) int c;{
  return(MYstrchr("+-*/%^(),=",c)!=(char *)0);
}
#endif

/* Codes returned from the evaluator */
#define E_OK           0        /* Successful evaluation */
#define E_SYNTAX       1        /* Syntax error */
#define E_UNBALAN      2        /* Unbalanced parenthesis */
#define E_DIVZERO      3        /* Attempted division by zero */
#define E_UNKNOWN      4        /* Reference to unknown variable */
#define E_MAXVARS      5        /* Maximum variables exceeded */
#define E_BADFUNC      6        /* Unrecognized function */
#define E_NUMARGS      7        /* Wrong number of arguments to function
*/
#define E_NOARG        8        /* Missing an argument to a function */
#define E_EMPTY        9        /* Empty expression */

/* These defines only happen if the values are not already defined!  You may
   want to add more precision here, if your machine supports it. */
#ifndef M_PI
#define M_PI    3.14159265358979323846
#endif
#ifndef M_E
#define M_E     2.71828182845904523536
#endif

extern double deg();
extern double rad();
extern double oct();         /* Octal to base ten */
extern double bin();         /* Binary to base ten */

int   Error;                 /* The error number */
char  ERTOK[TOKLEN + 1];     /* The token that generated the error */
int   ERPOS;                 /* The offset from the start of the expression */
char* ERANC;                 /* Used to calculate ERPOS */

/*
   Add any "constants" here...  These are "read-only" values that are
   provided as a convienence to the user.  Their values can not be
   permanently changed.  The first field is the variable name, the second
   is its value.
*/
VARIABLE Consts[] =
{
   /* name, value */
   { "pi",      M_PI },
   { "e",       M_E },
   { 0 }
};

/*
   Add any math functions that you wish to recogniz here...  The first
   field is the name of the function as it would appear in an expression.
   The second field tells how many arguments to expect.  The third is
   a pointer to the actual function to use.
*/
extern double   acos(/* double x */);
extern double   asin(/* double x */);
extern double   atan(/* double x */);
extern double   atan2(/* double y, double x */);
extern double   ceil(/* double x */);
extern double   cos(/* double x */);
extern double   cosh(/* double x */);
extern double   exp(/* double x */);
extern double   fabs(/* double x */);
extern double   floor(/* double x */);
extern double   fmod(/* double x, double y */);
extern double   frexp(/* double value, int *exp */);
extern double   ldexp(/* double value, int exp */);
extern double   log(/* double x */);
extern double   log10(/* double x */);
extern double   modf(/* double value, double *iptr */);
extern double   pow(/* double x, double y */);
extern double   sin(/* double x */);
extern double   sinh(/* double x */);
extern double   sqrt(/* double x */);
extern double   tan(/* double x */);
extern double   tanh(/* double x */);
extern double   hypot();
extern double   acosh();
extern double   asinh();
extern double   atanh();
FUNCTION Funcs[] =
{
   /* name, function to call */
#if 1           /* These functions add 1.9k to the EXE file */
   { "sin",     1,    sin },
   { "cos",     1,    cos },
   { "tan",     1,    tan },
   { "asin",    1,    asin },
   { "acos",    1,    acos },
   { "atan",    1,    atan },
   { "sinh",    1,    sinh },
   { "cosh",    1,    cosh },
   { "tanh",    1,    tanh },
   { "exp",     1,    exp },
   { "ln",      1,    log },
   { "log",     1,    log },
   { "log10",   1,    log10 },
   { "sqrt",    1,    sqrt },
   { "floor",   1,    floor },
   { "ceil",    1,    ceil },
   { "hypot",   2,    hypot },
#endif
   { "abs",     1,    fabs },
   { "fmod",    2,    fmod },
   { "pow",     2,    pow },
   { "deg",     1,    deg },
   { "rad",     1,    rad },
   { "oct",     1,    oct },
   { "bin",     1,    bin },
   { "",        1,    fabs }
};

VARIABLE        Vars[MAXVARS];       /* Array for user-defined variables */
 char*  expression;          /* Pointer to the user's expression */
 char   token[TOKLEN + 1];   /* Holds the current token */
int             type;                /* Type of the current token */
jmp_buf         jb;                  /* jmp_buf for errors */


#if 0
#define ERR(n) {Error=n; ERPOS=expression-ERANC-1; strcpy(ERTOK,token);
longjmp(jb,1);}
#else  /* Using a function instead */
/* W A R N I N G: This function exits to processor state jb */
void ERR(n) int n; {
extern int Error,ERPOS;
extern char *ERANC;
extern char ERTOK[]; extern char token[];
extern char *expression;
extern jmp_buf jb;

  Error=n; ERPOS=(int)(expression-ERANC)-1;
  strcpy(ERTOK,token); longjmp(jb,1);
}
#endif


double
deg(x) double x;
{
   return( x * 180.0 / M_PI );
}

double
rad(x) double x;
{
   return( x * M_PI / 180.0 );
}

double
cvbase(x,b) double x; int b;  /* Base b to base ten */
{ /* x==1001101 should return (1)*b^6 + (1)*b^3 + (1)*b^2 + (1)*b^0 */
char s[256];
int i,j,k;
char flg;
  if(x<0){x = -x; flg=1;} else flg=0;
  sprintf(s,"%.0f",x);           
  k=strlen(s); x=0;
  for(i=0;i<k;++i){
   j=s[i]-'0'; if(j<0 || j>= b) ERR(E_SYNTAX);
   x=b*x+j;
  }
  return (flg? -x:x);
}

double
oct(x) double x;       /* Octal to base ten */
{ /* x==314 should return oct==(3)*8^2+(1)*8+(4)*1 */
  return cvbase(x,8);
}

double
bin(x) double x;{       /* Binary to base ten */
/* x==1001101 should return bin==(1)*2^6 + (1)*2^3 + (1)*2^2 + (1)*2^0 */
  return cvbase(x,2);
}



/*************************************************************************
**                                                                       **
** GetSymbol( char* s )                                                  **
**                                                                       **
** This routine obtains a value from the program's environment.          **
** This works for DOS and unix.                                          **
**                                                                       **
 ************************************************************************/

int GetSymbol(s,v) char* s; TYPE* v;
{
   char* e;

   e = getenv( s );
   if( !e )
      return( 0 );
   *v = atof( e );
   return( 1 );
}


/*************************************************************************
**                                                                       **
** ClearAllVars()                                                        **
**                                                                       **
** Erases all user-defined variables from memory. Note that constants    **
** can not be erased or modified in any way by the user.                 **
**                                                                       **
** Returns nothing.                                                      **
**                                                                       **
 *************************************************************************/

void ClearAllVars()
{
   int i;

   for( i = 0; i < MAXVARS; i++ )
   {
      *Vars[i].name = 0;
      Vars[i].value = 0;
   }
}


/*************************************************************************
**                                                                       **
** ClearVar( char* name )                                                **
**                                                                       **
** Erases the user-defined variable that is called NAME from memory.     **
** Note that constants are not affected.                                 **
**                                                                       **
** Returns 1 if the variable was found and erased, or 0 if it didn't     **
** exist.                                                                **
**                                                                       **
 *************************************************************************/

int ClearVar(name) char* name;
{
   int i;

   for( i = 0; i < MAXVARS; i++ )
      if( *Vars[i].name && ! MYstrcmp( name, Vars[i].name ) )
      {
         *Vars[i].name = 0;
         Vars[i].value = 0;
         return( 1 );
      }
   return( 0 );
}


/*************************************************************************
**                                                                       **
** GetValue(name,value) char* name; TYPE* value;
**                                                                       **
** Looks up the specified variable (or constant) known as NAME and       **
** returns its contents in VALUE.                                        **
**                                                                       **
** First the user-defined variables are searched, then the constants are **
** searched.                                                             **
**                                                                       **
** Returns 1 if the value was found, or 0 if it wasn't.                  **
**                                                                       **
 *************************************************************************/

int GetValue(name,value) char* name; TYPE* value;
{
   int i;

   /* First check for an environment variable reference... */
   if( *name == '_' )
      return( GetSymbol( name + 1, value ) );

   /* Now check the user-defined variables. */
   for( i = 0; i < MAXVARS; i++ )
      if( *Vars[i].name && ! MYstrcmp( name, Vars[i].name ) )
      {
         *value = Vars[i].value;
         return( 1 );
      }

   /* Now check the programmer-defined constants. */
   for( i = 0; *Consts[i].name; i++ )
      if( *Consts[i].name && ! MYstrcmp( name, Consts[i].name ) )
      {
         *value = Consts[i].value;
         return( 1 );
      }
   return( 0 );
}


/*************************************************************************
**                                                                       **
** SetValue( char* name, TYPE* value )                                   **
**                                                                       **
** First, it erases any user-defined variable that is called NAME.  Then **
** it creates a new variable called NAME and gives it the value VALUE.   **
**                                                                       **
** Returns 1 if the value was added, or 0 if there was no more room.     **
**                                                                       **
 *************************************************************************/

int SetValue(name,value) char* name; TYPE* value;
{
   char b[30];
   int  i;

   ClearVar( name );
   for( i = 0; i < MAXVARS; i++ )
      if( ! *Vars[i].name )
      {
         strcpy( Vars[i].name, name );
         Vars[i].name[VARLEN] = 0;
         Vars[i].value = *value;
         return( 1 );
      }
   return( 0 );
}


/*************************************************************************
**                                                                       **
** Parse()   Internal use only                                           **
**                                                                       **
** This function is used to grab the next token from the expression that **
** is being evaluated.                                                   **
**                                                                       **
 *************************************************************************/


void Parse()
{
   char* t;

   type = 0;
   t = token;
   while( iswhite( *expression ) )
      expression++;
   if( isdelim( *expression ) )
   {
      type = DEL;
      *t++ = *expression++;
   }
   else if( isnumer( *expression ) )
   {
      type = NUM;
      while( isnumer( *expression ) )
         *t++ = *expression++;
      if(*expression == 'e' || *expression == 'd'){ /* sci notation */
        if(*expression == 'd'){ *t++ = 'e'; ++expression;} else
        *t++ = *expression++;
        if(isnumer(*expression) || *expression == '-' || *expression ==
        '+') *t++ = *expression++;
        while( isnumer(*expression)) *t++ = *expression++;
      }
   }
   else if( MYISalpha( *expression ) )
   {
      type = VAR;
      while( MYISalpha( *expression ) )
        *t++ = *expression++;
      token[VARLEN] = 0;
   }
   else if( *expression )
   {
      *t++ = *expression++;
      *t = 0;
      ERR( E_SYNTAX );
   }
   *t = 0;
   while( iswhite( *expression ) )
      expression++;
}


/*************************************************************************
**                                                                       **
** Level1( TYPE* r )   Internal use only                                 **
**                                                                       **
** This function handles any variable assignment operations.             **
** It returns a value of 1 if it is a top-level assignment operation,    **
** otherwise it returns 0                                                **
**                                                                       **
 *************************************************************************/


extern void Level2(),Level3(),Level4(),Level5(),Level6();

int Level1(r) TYPE* r;
{
   char t[VARLEN + 1];

   if( type == VAR )
      if( *expression == '=' )
      {
         strcpy( t, token );
         Parse();
         Parse();
         if( !*token )
         {
            ClearVar( t );
            return(1);
         }
         Level2( r );
         if( ! SetValue( t, r ) )
            ERR( E_MAXVARS );
         return( 1 );
      }
   Level2( r );
   return( 0 );
}


/*************************************************************************
**                                                                       **
** Level2( TYPE* r )   Internal use only                                 **
**                                                                       **
** This function handles any addition and subtraction operations.        **
**                                                                       **
 *************************************************************************/


void Level2(r) TYPE* r;
{
   TYPE t = 0;
   char o;

   Level3( r );
   while( (o = *token) == '+' || o == '-' )
   {
      Parse();
      Level3( &t );
      if( o == '+' )
         *r = *r + t;
      else if( o == '-' )
         *r = *r - t;
   }
}


/*************************************************************************
**                                                                       **
** Level3( TYPE* r )   Internal use only                                 **
**                                                                       **
** This function handles any multiplication, division, or modulo.        **
**                                                                       **
 *************************************************************************/


void Level3(r) TYPE* r;
{
   TYPE t;
   char o;

   Level4( r );
   while( (o = *token) == '*' || o == '/' || o == '%' )
   {
      Parse();
      Level4( &t );
      if( o == '*' )
         *r = *r * t;
      else if( o == '/' )
      {
         if( t == 0 )
            ERR( E_DIVZERO );
         *r = *r / t;
      }
      else if( o == '%' )
      {
         if( t == 0 )
            ERR( E_DIVZERO );
         *r = fmod( *r, t );
      }
   }
}


/*************************************************************************
**                                                                       **
** Level4( TYPE* r )   Internal use only                                 **
**                                                                       **
** This function handles any "to the power of" operations.               **
**                                                                       **
 *************************************************************************/


void Level4(r) TYPE* r;
{
   TYPE t;

   Level5( r );
   if( *token == '^' )
   {
      Parse();
      Level5( &t );
      *r = pow( *r, t );
   }
}


/*************************************************************************
**                                                                       **
** Level5( TYPE* r )   Internal use only                                 **
**                                                                       **
** This function handles any unary + or - signs.                         **
**                                                                       **
 *************************************************************************/


void Level5(r) TYPE* r;
{
   char o = 0;

   if( *token == '+' || *token == '-' )
   {
      o = *token;
      Parse();
   }
   Level6( r );
   if( o == '-' )
      *r = -*r;
}


/*************************************************************************
**                                                                       **
** Level6( TYPE* r )   Internal use only                                 **
**                                                                       **
** This function handles any literal numbers, variables, or functions.   **
**                                                                       **
 *************************************************************************/


void Level6(r) TYPE* r;
{
   int  i;
   int  n;
   TYPE a[3];

   if( *token == '(' ) {
      Parse();
      if( *token == ')' ) ERR( E_NOARG );
      Level1( r );
      if( *token != ')' ) ERR( E_UNBALAN );
      Parse();
   }
   else
   {
      if( type == NUM ) {
         *r = (TYPE) atof( token );
         Parse();
      }
      else if( type == VAR ) {
         if( *expression == '(' ) {
            for( i = 0; *(Funcs[i].name)!=0; i++ ){
               if( ! MYstrcmp( token, Funcs[i].name ) ) {
                  Parse();
                  n = 0;
                  do {
                     Parse();
                     if( *token == ')' || *token == ',' )
                        ERR( E_NOARG );
                     a[n] = 0;
                     Level1( &a[n] );
                     n++;
                  } while( n < 4 && *token == ',' );
                  Parse();
                  if( n != Funcs[i].args ) {
                     strcpy( token, Funcs[i].name );
                     ERR( E_NUMARGS );
                  }
                  *r = Funcs[i].func( a[0], a[1], a[2] );
                  return;
               }
            }
            if( ! *Funcs[i].name ) ERR( E_BADFUNC );
         } else
         if( ! GetValue( token, r ) ) ERR( E_UNKNOWN );
         Parse();
      } else ERR( E_SYNTAX );
   }
}

void cvlower(s) char *s; {
int x;
  while((x=s[0])!=0) *s++ = MYtolower(x);
}


/*************************************************************************
**                                                                       **
** Evaluate( char* e, TYPE* result, int* a )                             **
**                                                                       **
** This function is called to evaluate the expression E and return the   **
** answer in RESULT.  If the expression was a top-level assignment, a    **
** value of 1 will be returned in A, otherwise it will contain 0.        **
**                                                                       **
** Returns E_OK if the expression is valid, or an error code.            **
**                                                                       **
 *************************************************************************/

int Evaluate(e,result,a) char* e; TYPE* result; int* a;
{
   if( setjmp( jb ) )
      return( Error );
   expression = e;
   ERANC = e;
   cvlower( expression );
   *result = 0;
   Parse();
   if( ! *token )
      ERR( E_EMPTY );
   *a = Level1( result );
   return( E_OK );
}

char* ErrMsgs[] =
{
   "Syntax error",
   "Unbalanced parenthesis",
   "Division by zero",
   "Unknown variable",
   "Maximum variables exceeded",
   "Unrecognized function",
   "Wrong number of arguments to function",
   "Missing an argument",
   "Empty expression"
};

int evalexpr(src,errmsg,ans)  char *src,*errmsg; double *ans;
{
   TYPE  result;
   int   ec;
   int   a;
   static int flag=1;
   char f;

   if(flag) {   /* First time, then setup vars */
     ClearAllVars(); flag=0;
     result=0; SetValue("ans", &result );
   }
   ec=Evaluate( src, &result, &a );
   if(ec != E_OK && ec != E_EMPTY ){
   /* Display error info.  An E_EMPTY error is ignored. */
   sprintf(errmsg,"EvalExpr: %s : %s [%s]",ErrMsgs[Error-1],ERTOK,ERANC);
   f=2; goto quit;
   }
   /* If we didn't assign a variable, then save the result. */
   if( ec == E_OK  && ! a) {
     SetValue("ans", &result );
     ans[0]=result;  f=0;
   } else {ans[0]=errmsg[0]=0; f=1;}
quit:
   return f;
}

#if 0
#undef iswhite
#undef isnumer
#undef MYISalpha
#undef isdelim
#endif

int calculator(dest) char *dest; {
/* Replace dest by its float computation x */
double x;       /* Float computation answer */
int i;
char buf[512];  /* Has to be at least 310 chars + number of digits */
extern char *MATHFMT;       /* Usually, its "%.08g" */
extern int MAXLN,mathinteger;

/* Using vee.c function to do the dirty work */
/* evalexpr(src,errmsg,ans)  char *src,*errmsg; double *ans; */
/* Returns 0==OK, 1==No ans to print, 2==Error /w error msg */
 if((i=evalexpr(dest,buf,&x))==2){
   banner(buf); while(ccBIOS(0)!=13) bell(); /* Get a char for sure */
 }
 if(i==1) dest[0]=0;
 if(i==0){
   i=x;
   if(mathinteger)
   sprintf(buf,MATHFMT,i); else sprintf(buf,MATHFMT,x);
   strnzcpy(dest,buf,MAXLN-1);
 }
 return 0;
}
#endif
/* End of vee.c */
#endif
#endif  /* end of pie2.c source code */
