Getline.c

Go to the documentation of this file.
00001 /* @(#)root/clib:$Id: Getline.c 26346 2008-11-21 14:24:33Z rdm $ */
00002 /* Author: */
00003 
00004 /*
00005  * Copyright (C) 1991, 1992 by Chris Thewalt (thewalt@ce.berkeley.edu)
00006  *
00007  * Permission to use, copy, modify, and distribute this software
00008  * for any purpose and without fee is hereby granted, provided
00009  * that the above copyright notices appear in all copies and that both the
00010  * copyright notice and this permission notice appear in supporting
00011  * documentation.  This software is provided "as is" without express or
00012  * implied warranty.
00013  */
00014 /*
00015 *************************** Motivation **********************************
00016 
00017 Many interactive programs read input line by line, but would like to
00018 provide line editing and history functionality to the end-user that
00019 runs the program.
00020 
00021 The input-edit package provides that functionality.  As far as the
00022 programmer is concerned, the program only asks for the next line
00023 of input. However, until the user presses the RETURN key they can use
00024 emacs-style line editing commands and can traverse the history of lines
00025 previously typed.
00026 
00027 Other packages, such as GNU's readline, have greater capability but are
00028 also substantially larger.  Input-edit is small, since it uses neither
00029 stdio nor any termcap features, and is also quite portable.  It only uses
00030 \b to backspace and \007 to ring the bell on errors.  Since it cannot
00031 edit multiple lines it scrolls long lines left and right on the same line.
00032 
00033 Input edit uses classic (not ANSI) C, and should run on any Unix
00034 system (BSD or SYSV), PC's with the MSC compiler, or Vax/VMS (untested by me).
00035 Porting the package to new systems basicaly requires code to read a
00036 character when it is typed without echoing it, everything else should be OK.
00037 
00038 I have run the package on:
00039 
00040         DECstation 5000, Ultrix 4.2 with cc and gcc
00041         Sun Sparc 2, SunOS 4.1.1, with cc
00042         SGI Iris, IRIX System V.3, with cc
00043         PC, DRDOS 5.0, with MSC 6.0
00044 
00045 The description below is broken into two parts, the end-user (editing)
00046 interface and the programmer interface.  Send bug reports, fixes and
00047 enhancements to:
00048 
00049 Chris Thewalt (thewalt@ce.berkeley.edu)
00050 2/4/92
00051 
00052 PS: I don't have, and don't want to add, a vi mode, sorry.
00053 
00054 ************************** End-User Interface ***************************
00055 
00056 Entering printable keys generally inserts new text into the buffer (unless
00057 in overwrite mode, see below).  Other special keys can be used to modify
00058 the text in the buffer.  In the description of the keys below, ^n means
00059 Control-n, or holding the CONTROL key down while pressing "n". M-B means
00060 Meta-B (or Alt-B). Errors will ring the terminal bell.
00061 
00062 ^A/^E   : Move cursor to beginning/end of the line.
00063 ^F/^B   : Move cursor forward/backward one character.
00064 ^D      : Delete the character under the cursor.
00065 ^H, DEL : Delete the character to the left of the cursor.
00066 ^K      : Kill from the cursor to the end of line.
00067 ^L      : Redraw current line.
00068 ^O      : Toggle overwrite/insert mode. Initially in insert mode. Text
00069           added in overwrite mode (including yanks) overwrite
00070           existing text, while insert mode does not overwrite.
00071 ^P/^N   : Move to previous/next item on history list.
00072 ^R/^S   : Perform incremental reverse/forward search for string on
00073           the history list.  Typing normal characters adds to the current
00074           search string and searches for a match. Typing ^R/^S marks
00075           the start of a new search, and moves on to the next match.
00076           Typing ^H or DEL deletes the last character from the search
00077           string, and searches from the starting location of the last search.
00078           Therefore, repeated DEL's appear to unwind to the match nearest
00079           the point at which the last ^R or ^S was typed.  If DEL is
00080           repeated until the search string is empty the search location
00081           begins from the start of the history list.  Typing ESC or
00082           any other editing character accepts the current match and
00083           loads it into the buffer, terminating the search.
00084 ^T      : Toggle the characters under and to the left of the cursor.
00085 ^U      : Kill from beginning to the end of the line.
00086 ^Y      : Yank previously killed text back at current location.  Note that
00087           this will overwrite or insert, depending on the current mode.
00088 M-F/M-B : Move cursor forward/backward one word.
00089 M-D     : Delete the word under the cursor.
00090 ^SPC    : Set mark.
00091 ^W      : Kill from mark to point.
00092 ^X      : Exchange mark and point.
00093 TAB     : By default adds spaces to buffer to get to next TAB stop
00094           (just after every 8th column), although this may be rebound by the
00095           programmer, as described below.
00096 NL, CR  : returns current buffer to the program.
00097 
00098 DOS and ANSI terminal arrow key sequences are recognized, and act like:
00099 
00100   up    : same as ^P
00101   down  : same as ^N
00102   left  : same as ^B
00103   right : same as ^F
00104 
00105 ************************** Programmer Interface ***************************
00106 
00107 The programmer accesses input-edit through five functions, and optionally
00108 through three additional function pointer hooks.  The five functions are:
00109 
00110 char *Getline(const char *prompt)
00111 
00112         Prints the prompt and allows the user to edit the current line. A
00113         pointer to the line is returned when the user finishes by
00114         typing a newline or a return.  Unlike GNU readline, the returned
00115         pointer points to a static buffer, so it should not be free'd, and
00116         the buffer contains the newline character.  The user enters an
00117         end-of-file by typing ^D on an empty line, in which case the
00118         first character of the returned buffer is '\0'.  Getline never
00119         returns a NULL pointer.  The getline function sets terminal modes
00120         needed to make it work, and resets them before returning to the
00121         caller.  The getline function also looks for characters that would
00122         generate a signal, and resets the terminal modes before raising the
00123         signal condition.  If the signal handler returns to getline,
00124         the screen is automatically redrawn and editing can continue.
00125         Getline now requires both the input and output stream be connected
00126         to the terminal (not redirected) so the main program should check
00127         to make sure this is true.  If input or output have been redirected
00128         the main program should use buffered IO (stdio) rather than
00129         the slow 1 character read()s that getline uses (note: this limitation
00130         has been removed).
00131 
00132 char *Getlinem(int mode, const char *prompt)
00133 
00134         mode: -1 = init, 0 = line mode, 1 = one char at a time mode, 2 = cleanup
00135 
00136         More specialized version of the previous function. Depending on
00137         the mode, it behaves differently. Its main use is to allow
00138         character by character input from the input stream (useful when
00139         in an X eventloop). It will return NULL as long as no newline
00140         has been received. Its use is typically as follows:
00141         1) In the program initialization part one calls: Getlinem(-1,"prompt>")
00142         2) In the X inputhandler: if ((line = Getlinem(1,NULL))) {
00143         3) In the termination routine: Getlinem(2,NULL)
00144         With mode=0 the function behaves exactly like the previous function.
00145 
00146 void Gl_config(const char *which, int value)
00147 
00148         Set some config options. Which can be:
00149           "noecho":  do not echo characters (used for passwd input)
00150           "erase":   do erase line after return (used for text scrollers)
00151 
00152 void Gl_setwidth(int width)
00153 
00154         Set the width of the terminal to the specified width. The default
00155         width is 80 characters, so this function need only be called if the
00156         width of the terminal is not 80.  Since horizontal scrolling is
00157         controlled by this parameter it is important to get it right.
00158 
00159 void Gl_histinit(char *file)
00160 
00161         This function reads a history list from file. So lines from a
00162         previous session can be used again.
00163 
00164 void Gl_histadd(char *buf)
00165 
00166         The Gl_histadd function checks to see if the buf is not empty or
00167         whitespace, and also checks to make sure it is different than
00168         the last saved buffer to avoid repeats on the history list.
00169         If the buf is a new non-blank string a copy is made and saved on
00170         the history list, so the caller can re-use the specified buf.
00171 
00172 The main loop in testgl.c, included in this directory, shows how the
00173 input-edit package can be used:
00174 
00175 extern char *Getline();
00176 extern void  Gl_histadd();
00177 main()
00178 {
00179     char *p;
00180     Gl_histinit(".hist");
00181     do {
00182         p = Getline("PROMPT>>>> ");
00183         Gl_histadd(p);
00184         fputs(p, stdout);
00185     } while (*p != 0);
00186 }
00187 
00188 In order to allow the main program to have additional access to the buffer,
00189 to implement things such as completion or auto-indent modes, three
00190 function pointers can be bound to user functions to modify the buffer as
00191 described below.  By default Gl_in_hook and Gl_out_hook are set to NULL,
00192 and Gl_tab_hook is bound to a function that inserts spaces until the next
00193 logical tab stop is reached.  The user can reassign any of these pointers
00194 to other functions.  Each of the functions bound to these hooks receives
00195 the current buffer as the first argument, and must return the location of
00196 the leftmost change made in the buffer.  If the buffer isn't modified the
00197 functions should return -1.  When the hook function returns the screen is
00198 updated to reflect any changes made by the user function.
00199 
00200 int (*Gl_in_hook)(char *buf)
00201 
00202         If Gl_in_hook is non-NULL the function is called each time a new
00203         buffer is loaded. It is called when getline is entered, with an
00204         empty buffer, it is called each time a new buffer is loaded from
00205         the history with ^P or ^N, and it is called when an incremental
00206         search string is accepted (when the search is terminated). The
00207         buffer can be modified and will be redrawn upon return to Getline().
00208 
00209 int (*Gl_out_hook)(char *buf)
00210 
00211         If Gl_out_hook is non-NULL it is called when a line has been
00212         completed by the user entering a newline or return. The buffer
00213         handed to the hook does not yet have the newline appended. If the
00214         buffer is modified the screen is redrawn before getline returns the
00215         buffer to the caller.
00216 
00217 int (*Gl_tab_hook)(char *buf, int prompt_width, int *cursor_loc)
00218 
00219         If Gl_tab_hook is non-NULL, it is called whenever a tab is typed.
00220         In addition to receiving the buffer, the current prompt width is
00221         given (needed to do tabbing right) and a pointer to the cursor
00222         offset is given, where a 0 offset means the first character in the
00223         line.  Not only does the cursor_loc tell the programmer where the
00224         TAB was received, but it can be reset so that the cursor will end
00225         up at the specified location after the screen is redrawn.
00226 
00227 int (*Gl_beep_hook)()
00228         Called if \007 (beep) is about to be printed. Return !=0 if handled.
00229 */
00230 
00231 /* forward reference needed for Gl_tab_hook */
00232 static int gl_tab(char *buf, int offset, int *loc);
00233 
00234 /********************* exported interface ********************************/
00235 
00236 char   *Getline(const char *prompt); /* read a line of input */
00237 char   *Getlinem(int mode, const char *prompt); /* allows reading char by char */
00238 void    Gl_config(const char *which, int value); /* set some options */
00239 void    Gl_setwidth(int w);          /* specify width of screen */
00240 void    Gl_windowchanged();          /* call after SIGWINCH signal */
00241 void    Gl_histinit(char *file); /* read entries from old histfile */
00242 void    Gl_histadd(char *buf);       /* adds entries to hist */
00243 
00244 int             (*Gl_in_hook)(char *buf) = 0;
00245 int             (*Gl_out_hook)(char *buf) = 0;
00246 int             (*Gl_tab_hook)(char *buf, int prompt_width, int *loc) = gl_tab;
00247 int             (*Gl_beep_hook)() = 0;
00248 int             (*Gl_in_key)(int ch) = 0;
00249 
00250 /******************** imported interface *********************************/
00251 
00252 #include <string.h>
00253 #include <ctype.h>
00254 #include <errno.h>
00255 #include <signal.h>
00256 #include <stdlib.h>
00257 #include <stdio.h>
00258 
00259 /******************** internal interface *********************************/
00260 
00261 #define BUF_SIZE 1024
00262 
00263 static int      gl_init_done = -1;      /* terminal mode flag  */
00264 static int      gl_notty = 0;           /* 1 when not a tty */
00265 static int      gl_eof = 0;             /* 1 when not a tty and read() == -1 */
00266 static int      gl_termw = 80;          /* actual terminal width */
00267 static int      gl_scroll = 27;         /* width of EOL scrolling region */
00268 static int      gl_width = 0;           /* net size available for input */
00269 static int      gl_extent = 0;          /* how far to redraw, 0 means all */
00270 static int      gl_overwrite = 0;       /* overwrite mode */
00271 static int      gl_no_echo = 0;         /* do not echo input characters */
00272 static int      gl_passwd = 0;          /* do not echo input characters */
00273 static int      gl_erase_line = 0;      /* erase line before returning */
00274 static int      gl_pos, gl_cnt = 0;     /* position and size of input */
00275 static char     gl_buf[BUF_SIZE];       /* input buffer */
00276 static char     gl_killbuf[BUF_SIZE]=""; /* killed text */
00277 static const char *gl_prompt;           /* to save the prompt string */
00278 static char     gl_intrc = 0;           /* keyboard SIGINT char */
00279 static char     gl_quitc = 0;           /* keyboard SIGQUIT char */
00280 static char     gl_suspc = 0;           /* keyboard SIGTSTP char */
00281 static char     gl_dsuspc = 0;          /* delayed SIGTSTP char */
00282 static int      gl_search_mode = 0;     /* search mode flag */
00283 static int      gl_savehist = 0;        /* # of lines to save in hist file */
00284 static char     gl_histfile[256];       /* name of history file */
00285 
00286 static void     gl_init();              /* prepare to edit a line */
00287 static void     gl_cleanup();           /* to undo gl_init */
00288 static void     gl_char_init();         /* get ready for no echo input */
00289 static void     gl_char_cleanup();      /* undo gl_char_init */
00290 
00291 static void     gl_addchar(int c);      /* install specified char */
00292 static void     gl_del(int loc);        /* del, either left (-1) or cur (0) */
00293 static void     gl_error(char *buf);    /* write error msg and die */
00294 static void     gl_fixup(const char *p, int c, int cur); /* fixup state variables and screen */
00295 static int      gl_getc();              /* read one char from terminal */
00296 static void     gl_kill();              /* delete to EOL */
00297 static void     gl_newline();           /* handle \n or \r */
00298 static void     gl_putc(int c);         /* write one char to terminal */
00299 static void     gl_puts(const char *buf); /* write a line to terminal */
00300 static void     gl_redraw();            /* issue \n and redraw all */
00301 static void     gl_transpose();         /* transpose two chars */
00302 static void     gl_yank();              /* yank killed text */
00303 
00304 static int      is_whitespace(char c);  /* "whitespace" very loosely interpreted */
00305 static void     gl_back_1_word();       /* move cursor back one word */
00306 static void     gl_kill_1_word();       /* kill to end of word */
00307 static void     gl_kill_back_1_word();  /* kill to begin of word */
00308 static void     gl_kill_region(int i, int j); /* kills from i to j */
00309 static void     gl_fwd_1_word();        /* move cursor forward one word */
00310 static void     gl_set_mark();          /* sets mark to be at point */
00311 static void     gl_exch();              /* exchanges point and mark */
00312 static void     gl_wipe();              /* kills from mark to point */
00313 static int      gl_mark = -1;           /* position of mark. gl_mark<0 if not set */
00314 
00315 static void     hist_init();            /* initializes hist pointers */
00316 static char    *hist_next();            /* return ptr to next item */
00317 static char    *hist_prev();            /* return ptr to prev item */
00318 static char    *hist_save(char *p);     /* makes copy of a string, without NL */
00319 
00320 static void     search_addchar(int c);  /* increment search string */
00321 static void     search_term();          /* reset with current contents */
00322 static void     search_back(int s);     /* look back for current string */
00323 static void     search_forw(int s);     /* look forw for current string */
00324 
00325 /************************ nonportable part *********************************/
00326 
00327 #ifdef MSDOS
00328 #include <bios.h>
00329 #endif
00330 
00331 #ifdef WIN32
00332 #  define MSDOS
00333 #  include <io.h>
00334 #  include <windows.h>
00335 #endif /* WIN32 */
00336 
00337 #if defined(_AIX) || defined(__Lynx__) || defined(__APPLE__) || \
00338         defined(__OpenBSD__)
00339 #define unix
00340 #endif
00341 
00342 #if defined(__hpux) || defined(__osf__)       /* W.Karig@gsi.de */
00343 #ifndef unix
00344 #define unix
00345 #endif
00346 #endif
00347 
00348 #ifdef unix
00349 #include <unistd.h>
00350 #if !defined(__osf__) && !defined(_AIX)       /* W.Karig@gsi.de */
00351 #include <sys/ioctl.h>
00352 #endif
00353 
00354 #if defined(__linux__) && defined(__powerpc__)
00355 #   define R__PPCLINUX      /* = linux on PPC(64) */
00356 #endif
00357 #if defined(__linux__) && defined(__alpha__)
00358 #   define R__ALPHALINUX    /* = linux on Alpha */
00359 #endif
00360 #if defined(__linux__) && defined(__mips) /* cholm@nbi.dk */
00361 #   define R__MIPSLINUX    /* = linux on mips */
00362 #endif
00363 
00364 #if defined(TIOCGETP) && !defined(__sgi) && !defined(R__PPCLINUX) && \
00365     !defined(R__ALPHALINUX)  && !defined(R__MIPSLINUX) /* use BSD interface if possible */
00366 #include <sgtty.h>
00367 struct sgttyb   new_tty, old_tty;
00368 struct tchars   tch;
00369 struct ltchars  ltch;
00370 #else
00371 #ifdef SIGTSTP          /* need POSIX interface to handle SUSP */
00372 #include <termios.h>
00373 #if defined(__sun) || defined(__sgi) || defined(R__PPCLINUX) || \
00374     defined(R__ALPHALINUX) || defined(R__MIPSLINUX)
00375 #undef TIOCGETP         /* Solaris and SGI define TIOCGETP in <termios.h> */
00376 #undef TIOCSETP
00377 #endif
00378 struct termios  new_termios, old_termios;
00379 #else                   /* use SYSV interface */
00380 #include <termio.h>
00381 struct termio   new_termio, old_termio;
00382 #endif
00383 #endif
00384 #endif  /* unix */
00385 
00386 #ifdef VMS
00387 #include <descrip.h>
00388 #include <ttdef.h>
00389 #include <iodef.h>
00390 #include <starlet.h>
00391 #include <unistd.h>
00392 #include unixio
00393 
00394 static int   setbuff[2];             /* buffer to set terminal attributes */
00395 static short chan = -1;              /* channel to terminal */
00396 struct dsc$descriptor_s descrip;     /* VMS descriptor */
00397 #endif
00398 
00399 void
00400 Gl_config(const char *which, int value)
00401 {
00402    if (strcmp(which, "noecho") == 0)
00403       gl_no_echo = value;
00404    else if (strcmp(which, "erase") == 0)
00405       gl_erase_line = value;
00406    else
00407       printf("gl_config: %s ?\n", which);
00408 }
00409 
00410 static void
00411 gl_char_init()                  /* turn off input echo */
00412 {
00413     if (gl_notty) return;
00414 #ifdef unix
00415 #ifdef TIOCGETP                 /* BSD */
00416     ioctl(0, TIOCGETC, &tch);
00417     ioctl(0, TIOCGLTC, &ltch);
00418     gl_intrc = tch.t_intrc;
00419     gl_quitc = tch.t_quitc;
00420     gl_suspc = ltch.t_suspc;
00421     gl_dsuspc = ltch.t_dsuspc;
00422     ioctl(0, TIOCGETP, &old_tty);
00423     new_tty = old_tty;
00424     new_tty.sg_flags |= RAW;
00425     new_tty.sg_flags &= ~ECHO;
00426     ioctl(0, TIOCSETP, &new_tty);
00427 #else
00428 #ifdef SIGTSTP                  /* POSIX */
00429     tcgetattr(0, &old_termios);
00430     gl_intrc = old_termios.c_cc[VINTR];
00431     gl_quitc = old_termios.c_cc[VQUIT];
00432 #ifdef VSUSP
00433     gl_suspc = old_termios.c_cc[VSUSP];
00434 #endif
00435 #ifdef VDSUSP
00436     gl_dsuspc = old_termios.c_cc[VDSUSP];
00437 #endif
00438     new_termios = old_termios;
00439     new_termios.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
00440     new_termios.c_iflag |= (IGNBRK|IGNPAR);
00441     new_termios.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
00442     new_termios.c_cc[VMIN] = 1;
00443     new_termios.c_cc[VTIME] = 0;
00444     tcsetattr(0, TCSANOW, &new_termios);
00445 #else                           /* SYSV */
00446     ioctl(0, TCGETA, &old_termio);
00447     gl_intrc = old_termio.c_cc[VINTR];
00448     gl_quitc = old_termio.c_cc[VQUIT];
00449     new_termio = old_termio;
00450     new_termio.c_iflag &= ~(BRKINT|ISTRIP|IXON|IXOFF);
00451     new_termio.c_iflag |= (IGNBRK|IGNPAR);
00452     new_termio.c_lflag &= ~(ICANON|ISIG|ECHO);
00453     new_termio.c_cc[VMIN] = 1;
00454     new_termio.c_cc[VTIME] = 0;
00455     ioctl(0, TCSETA, &new_termio);
00456 #endif
00457 #endif
00458 #endif /* unix */
00459 
00460 #ifdef MSDOS
00461     gl_intrc = 'C' - '@';
00462     gl_quitc = 'Q' - '@';
00463 /*    gl_suspc = ltch.t_suspc; */
00464 #endif /* MSDOS */
00465 
00466 #ifdef vms
00467     descrip.dsc$w_length  = strlen("tt:");
00468     descrip.dsc$b_dtype   = DSC$K_DTYPE_T;
00469     descrip.dsc$b_class   = DSC$K_CLASS_S;
00470     descrip.dsc$a_pointer = "tt:";
00471     (void)sys$assign(&descrip,&chan,0,0);
00472     (void)sys$qiow(0,chan,IO$_SENSEMODE,0,0,0,setbuff,8,0,0,0,0);
00473     setbuff[1] |= TT$M_NOECHO;
00474     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
00475 #endif /* vms */
00476 }
00477 
00478 static void
00479 gl_char_cleanup()               /* undo effects of gl_char_init */
00480 {
00481     if (gl_notty) return;
00482 #ifdef unix
00483 #ifdef TIOCSETP         /* BSD */
00484     ioctl(0, TIOCSETP, &old_tty);
00485 #else
00486 #ifdef SIGTSTP          /* POSIX */
00487     tcsetattr(0, TCSANOW, &old_termios);
00488 #else                   /* SYSV */
00489     ioctl(0, TCSETA, &old_termio);
00490 #endif
00491 #endif
00492 #endif /* unix */
00493 
00494 #ifdef vms
00495     setbuff[1] &= ~TT$M_NOECHO;
00496     (void)sys$qiow(0,chan,IO$_SETMODE,0,0,0,setbuff,8,0,0,0,0);
00497     sys$dassgn(chan);
00498     chan = -1;
00499 #endif
00500 }
00501 
00502 #if defined(MSDOS) && !defined(WIN32)
00503 #  include <conio.h>
00504    int pause_()
00505    {
00506       int first_char;
00507         first_char = _getch();
00508         if (first_char == 0 || first_char == 0xE0) first_char = -_getch();
00509         return first_char;
00510    }
00511 #endif
00512 
00513 #if defined(MSDOS) && defined(WIN32)
00514 int pause_()
00515 {
00516  static HANDLE hConsoleInput = NULL;
00517  static iCharCount = 0;
00518  static int chLastChar = 0;
00519 
00520  DWORD cRead;
00521 
00522  INPUT_RECORD pirBuffer;
00523  KEY_EVENT_RECORD *KeyEvent= (KEY_EVENT_RECORD *)&(pirBuffer.Event);
00524 
00525  if (!hConsoleInput) hConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
00526 
00527  if (iCharCount) iCharCount--;      /* Whether several symbols had been read */
00528  else {
00529    chLastChar = 0;
00530    while (chLastChar == 0) {
00531      if (!ReadConsoleInput(hConsoleInput,       /* handle of a console input buffer    */
00532                            &pirBuffer,          /* address of the buffer for read data */
00533                            1,                   /* number of records to read           */
00534                            &cRead               /* address of number of records read   */
00535         )) return 0;
00536 
00537      if (pirBuffer.EventType == KEY_EVENT  && KeyEvent->bKeyDown == TRUE){
00538          iCharCount = KeyEvent->wRepeatCount - 1;
00539          chLastChar = ((int) (KeyEvent->uChar).AsciiChar & 0xffff);
00540          if (chLastChar)
00541               OemToCharBuff((char const *)&chLastChar,(char *)&chLastChar,1);
00542          else
00543               chLastChar = - (KeyEvent->wVirtualScanCode);
00544 /*            chLastChar = - (KeyEvent->wVirtualKeyCode); */
00545      }
00546    }
00547  }
00548  return chLastChar;
00549 
00550 }
00551 #endif
00552 
00553 
00554 static int
00555 gl_getc()
00556 /* get a character without echoing it to screen */
00557 {
00558 #ifdef MSDOS
00559 # define k_ctrl_C   3
00560 # define k_ctrl_Z  26
00561 # define k_ctrl_Q  17
00562 # define k_ctrl_K  11
00563 # define k_rt_arr -77
00564 # define k_lt_arr -75
00565 # define k_up_arr -72
00566 # define k_dn_arr -80
00567 # define k_PGUP   -73
00568 # define k_PGDW   -81
00569 # define k_HOME   -71
00570 # define k_END    -79
00571 # define k_INS    -82
00572 # define k_DEL    -83
00573 # define k_ENTER   13
00574 # define k_CR      13
00575 # define k_BS       8
00576 # define k_ESC     27
00577 # define k_alt_H  -35
00578 # define k_beep     7
00579 # ifndef WIN32
00580     int get_cursor__(int *,int *);
00581     int display_off__(int *);
00582     int display_on__();
00583     int locate_(int *,int *);
00584     int ixc, iyc;
00585 # endif
00586     int pause_();
00587 #endif
00588 
00589     int c;
00590 
00591 #if defined(unix)
00592     unsigned char ch;
00593     while ((c = (read(0, &ch, 1) > 0) ? ch : -1) == -1 && errno == EINTR)
00594        errno = 0;
00595 #endif
00596 
00597 #ifdef MSDOS
00598     c = pause_();
00599      if (c < 0) {
00600          switch (c) {
00601            case k_up_arr: c =  'P' - '@';   /* up -> ^P = 16 */
00602              break;
00603            case k_dn_arr: c =  'N' - '@';   /* down -> ^N = 14 */
00604              break;
00605            case k_lt_arr: c =  'B' - '@';   /* left -> ^B =2 */
00606              break;
00607            case k_rt_arr: c =  'F' - '@';   /* right -> ^F = 6*/
00608              break;
00609            case k_INS:    c =  'O' - '@';   /* right -> ^O  = 15*/
00610              break;
00611            case k_DEL:    c =  'D' - '@';   /* Delete character under cursor = 4*/
00612              break;
00613            case k_END:    c =  'E' - '@';   /* Moves cursor to end of line * = 5 */
00614              break;
00615            case k_HOME:   c =  'A' - '@';   /* Moves cursor to beginning of line = 1*/
00616              break;
00617            default: c = 0;    /* make it garbage */
00618          }
00619      }
00620      else {
00621        switch(c) {
00622            case k_ESC:    c =  'U' - '@'; /* Clear full line  -> ^U */
00623              break;
00624            default:
00625              break;
00626          }
00627      }
00628 #endif
00629 
00630 #ifdef vms
00631     if(chan < 0) {
00632        c='\0';
00633     }
00634     (void)sys$qiow(0,chan,IO$_TTYREADALL,0,0,0,&c,1,0,0,0,0);
00635     c &= 0177;                        /* get a char */
00636 #endif
00637     return c;
00638 }
00639 
00640 static void
00641 gl_putc(int c)
00642 {
00643     char   ch = c;
00644 
00645     if (gl_notty) return;
00646 
00647     if ( !gl_passwd || !isgraph(c))
00648     {
00649        if (c == '\007' && Gl_beep_hook && Gl_beep_hook())
00650           return;
00651 
00652 #ifdef WIN32
00653        CharToOemBuff((char const *)&c,&ch,1);
00654 #endif
00655 
00656        write(1, &ch, 1);
00657     }
00658 #if defined(unix) || defined(MSDOS) || defined(WIN32)
00659 #ifdef TIOCSETP         /* BSD in RAW mode, map NL to NL,CR */
00660     if (ch == '\n') {
00661         ch = '\r';
00662         write(1, &ch, 1);
00663     }
00664 #endif
00665 #endif
00666 }
00667 
00668 /******************** fairly portable part *********************************/
00669 
00670 static void
00671 gl_puts(const char *buf)
00672 {
00673     int len = strlen(buf);
00674 
00675     if (gl_notty) return;
00676 #ifdef WIN32
00677     {
00678      char *OemBuf = (char *)malloc(2*len);
00679      CharToOemBuff(buf,OemBuf,len);
00680      write(1, OemBuf, len);
00681      free(OemBuf);
00682     }
00683 #else
00684     write(1, buf, len);
00685 #endif
00686 }
00687 
00688 static void
00689 gl_error(char *buf)
00690 {
00691     int len = strlen(buf);
00692 
00693     gl_cleanup();
00694 #ifdef WIN32
00695     {
00696       char *OemBuf = (char *)malloc(2*len);
00697       CharToOemBuff(buf,OemBuf,len);
00698       write(2, OemBuf, len);
00699       free(OemBuf);
00700     }
00701 #else
00702     write(2, buf, len);
00703 #endif
00704     exit(1);
00705 }
00706 
00707 static void
00708 gl_init()
00709 /* set up variables and terminal */
00710 {
00711     if (gl_init_done < 0) {             /* -1 only on startup */
00712         hist_init();
00713     }
00714     if (isatty(0) == 0 || isatty(1) == 0)
00715         gl_notty = 1;
00716     gl_char_init();
00717     gl_init_done = 1;
00718 }
00719 
00720 static void
00721 gl_cleanup()
00722 /* undo effects of gl_init, as necessary */
00723 {
00724     if (gl_init_done > 0)
00725         gl_char_cleanup();
00726     gl_init_done = 0;
00727 }
00728 
00729 void
00730 Gl_setwidth(int w)
00731 {
00732     if (w > 20) {
00733         gl_termw = w;
00734         gl_scroll = w / 3;
00735     } else {
00736         gl_error("\n*** Error: minimum screen width is 21\n");
00737     }
00738 }
00739 
00740 void
00741 Gl_windowchanged()
00742 {
00743 #ifdef TIOCGWINSZ
00744    if (isatty(0)) {
00745       static char lenv[32], cenv[32];
00746       struct winsize wins;
00747       ioctl(0, TIOCGWINSZ, &wins);
00748 
00749       if (wins.ws_col == 0) wins.ws_col = 80;
00750       if (wins.ws_row == 0) wins.ws_row = 24;
00751 
00752       Gl_setwidth(wins.ws_col);
00753 
00754       sprintf(lenv, "LINES=%d", wins.ws_row);
00755       putenv(lenv);
00756       sprintf(cenv, "COLUMNS=%d", wins.ws_col);
00757       putenv(cenv);
00758    }
00759 #endif
00760 }
00761 
00762 /* -1 = init, 0 = line mode, 1 = one char at a time mode, 2 = cleanup */
00763 
00764 char *
00765 Getlinem(int mode, const char *prompt)
00766 {
00767     int             c, loc, tmp;
00768     int             sig;
00769 
00770     if (mode == 2) {
00771        gl_cleanup();
00772        return NULL;
00773     }
00774 
00775     if (mode < 1) {
00776        if (mode == -1) {
00777            /*Gl_config("noecho", 0);*/
00778            Gl_config("erase", 0);
00779        }
00780        gl_init();
00781        gl_prompt = (prompt) ? prompt : "";
00782        gl_buf[0] = 0;
00783        if (Gl_in_hook)
00784            Gl_in_hook(gl_buf);
00785        gl_fixup(gl_prompt, -2, BUF_SIZE);
00786        if (mode == -1) return NULL;
00787     }
00788     while ((c = gl_getc()) >= 0) {
00789         gl_extent = 0;          /* reset to full extent */
00790 
00791         if (Gl_in_key)
00792             Gl_in_key(c);
00793 #ifndef WIN32
00794         if (isprint(c)) {
00795 #else
00796         if (c >= ' ') {
00797 #endif
00798             if (gl_search_mode)
00799                search_addchar(c);
00800             else
00801                gl_addchar(c);
00802         } else {
00803             if (gl_search_mode) {
00804                 if (c == '\016' || c == '\020') {
00805                     search_term();
00806                     c = 0;              /* ignore the character */
00807                 } else if (c == '\010' || c == '\177') {
00808                     search_addchar(-1); /* unwind search string */
00809                     c = 0;
00810                 } else if (c != '\022' && c != '\023') {
00811                     search_term();      /* terminate and handle char */
00812                 }
00813             }
00814             /* NOTE:
00815              * sometimes M-x turns on bit 8               ( M-x --> 'x' + 128  )
00816              * sometimes M-x prepends an escape character ( M-x --> '\033','x' )
00817              * both cases are handled ...
00818              */
00819             switch (c)
00820             {
00821             case 'b'+128:                           /* M-b */
00822             case 'B'+128:                           /* M-B */
00823                  gl_back_1_word();
00824                  break;
00825             case 'd'+128:                           /* M-d */
00826             case 'D'+128:                           /* M-D */
00827                  gl_kill_1_word();
00828                  break;
00829             case 'f'+128:                           /* M-f */
00830             case 'F'+128:                           /* M-F */
00831                  gl_fwd_1_word();
00832                  break;
00833             case '\000':                           /* ^SPC */
00834                  gl_set_mark();
00835                  break;
00836             case '\027':                           /* ^W  */
00837                  gl_wipe();
00838                  break;
00839             case '\030':                           /* ^X  */
00840                  gl_exch();
00841                  break;
00842             case '\n':                             /* newline */
00843             case '\r':
00844                  gl_newline();
00845                  gl_cleanup();
00846                  return gl_buf;
00847                  /*NOTREACHED*/
00848                  break;
00849             case '\001': gl_fixup(gl_prompt, -1, 0);          /* ^A */
00850                  break;
00851             case '\002':                                      /* ^B */
00852                  gl_fixup(gl_prompt, -1, gl_pos-1);
00853                  break;
00854             case '\004':                                      /* ^D */
00855                  if (gl_cnt == 0) {
00856                       gl_buf[0] = 0;
00857                       gl_cleanup();
00858                       gl_putc('\n');
00859                       return gl_buf;
00860                  } else {
00861                       gl_del(0);
00862                  }
00863                  break;
00864             case '\005': gl_fixup(gl_prompt, -1, gl_cnt);     /* ^E */
00865                  break;
00866             case '\006':                                      /* ^F */
00867                  gl_fixup(gl_prompt, -1, gl_pos+1);
00868                  break;
00869             case '\010': case '\177': gl_del(-1);     /* ^H and DEL */
00870                  break;
00871             case '\t':                                        /* TAB */
00872                  if (Gl_tab_hook) {
00873                       tmp = gl_pos;
00874                       loc = Gl_tab_hook(gl_buf, strlen(gl_prompt), &tmp);
00875                       if (loc >= 0 || tmp != gl_pos || loc == -2)
00876                            gl_fixup(gl_prompt, loc, tmp);
00877                  }
00878                  break;
00879             case '\013': gl_kill();                           /* ^K */
00880                  break;
00881             case '\014': gl_redraw();                         /* ^L */
00882                  break;
00883             case '\016':                                      /* ^N */
00884                  strcpy(gl_buf, hist_next());
00885                  if (Gl_in_hook)
00886                       Gl_in_hook(gl_buf);
00887                  gl_fixup(gl_prompt, 0, BUF_SIZE);
00888                  break;
00889             case '\017': gl_overwrite = !gl_overwrite;        /* ^O */
00890                  break;
00891             case '\020':                                      /* ^P */
00892                  strcpy(gl_buf, hist_prev());
00893                  if (Gl_in_hook)
00894                       Gl_in_hook(gl_buf);
00895                  gl_fixup(gl_prompt, 0, BUF_SIZE);
00896                  break;
00897             case '\022': search_back(1);                      /* ^R */
00898                  break;
00899             case '\023': search_forw(1);                      /* ^S */
00900                  break;
00901             case '\024': gl_transpose();                      /* ^T */
00902                  break;
00903             case '\025': gl_fixup(gl_prompt,-1,0); gl_kill(); /* ^U */
00904                  break;
00905             case '\031': gl_yank();                           /* ^Y */
00906                  break;
00907             case '\033':
00908                  switch(c = gl_getc())
00909                  {
00910                  case 'b':                           /* M-b */
00911                  case 'B':                           /* M-B */
00912                       gl_back_1_word();
00913                       break;
00914                  case 'd':                           /* M-d */
00915                  case 'D':                           /* M-D */
00916                       gl_kill_1_word();
00917                       break;
00918                  case 'f':                           /* M-f */
00919                  case 'F':                           /* M-F */
00920                       gl_fwd_1_word();
00921                       break;
00922                  case '[':                                /* ansi arrow keys */
00923                  case 'O':                                /* xterm arrow keys */
00924                       switch(c = gl_getc())
00925                       {
00926                       case 'A':                           /* up */
00927                            strcpy(gl_buf, hist_prev());
00928                            if (Gl_in_hook)
00929                                 Gl_in_hook(gl_buf);
00930                            gl_fixup(gl_prompt, 0, BUF_SIZE);
00931                            break;
00932                       case 'B':                          /* down */
00933                            strcpy(gl_buf, hist_next());
00934                            if (Gl_in_hook)
00935                                 Gl_in_hook(gl_buf);
00936                            gl_fixup(gl_prompt, 0, BUF_SIZE);
00937                            break;
00938                       case 'C': gl_fixup(gl_prompt, -1, gl_pos+1);  /* right */
00939                            break;
00940                       case 'D': gl_fixup(gl_prompt, -1, gl_pos-1);  /* left */
00941                            break;
00942                       case 'H': gl_fixup(gl_prompt, -1, 0);         /* home */
00943                            break;
00944                       case 'F': gl_fixup(gl_prompt, -1, gl_cnt);    /* end */
00945                            break;
00946                       case '3':  /* delete */
00947                            gl_del(0);
00948                            c = gl_getc(); /* ignore ~ */
00949                            break;
00950                       default:                                 /* who knows */
00951                            gl_putc('\007');
00952                            break;
00953                       }
00954                       break;
00955                  case '\010':           /* DEL and ^H */
00956                  case '\177':
00957                       gl_kill_back_1_word();
00958                       break;
00959                  default:
00960                       gl_putc('\007');
00961                  }
00962                  break;
00963             default:          /* check for a terminal signal */
00964 
00965 #if defined(unix) || defined(WIN32)
00966                 if (c > 0) {    /* ignore 0 (reset above) */
00967                     sig = 0;
00968 #ifdef SIGINT
00969                     if (c == gl_intrc)
00970                         sig = SIGINT;
00971 #endif
00972 #ifdef SIGQUIT
00973                     if (c == gl_quitc)
00974                         sig = SIGQUIT;
00975 #endif
00976 #ifdef SIGTSTP
00977                     if (c == gl_suspc || c == gl_dsuspc)
00978                         sig = SIGTSTP;
00979 #endif
00980                     if (sig != 0) {
00981                         gl_cleanup();
00982 #if !defined(WIN32)
00983                         raise(sig);
00984 #endif
00985 #ifdef WIN32
00986                         if (sig == SIGINT) GenerateConsoleCtrlEvent(CTRL_C_EVENT,0);
00987                         else raise(sig);
00988 #endif
00989                         gl_init();
00990                         gl_redraw();
00991                         c = 0;
00992                     }
00993                 }
00994 #endif /* unix */
00995                 if (c > 0)
00996                     gl_putc('\007');
00997                 break;
00998             }
00999         }
01000         if (mode == 1) return NULL;
01001     }
01002     if (c == -1 && gl_notty)
01003        gl_eof = 1;
01004     else
01005        gl_eof = 0;
01006 
01007     gl_cleanup();
01008     gl_buf[0] = 0;
01009     return gl_buf;
01010 }
01011 
01012 int
01013 Gl_eof()
01014 {
01015    return gl_eof;
01016 }
01017 
01018 char *
01019 Getline(const char *prompt)
01020 {
01021    return Getlinem(0, prompt);
01022 }
01023 
01024 static void
01025 gl_addchar(int c)
01026 /* adds the character c to the input buffer at current location */
01027 {
01028     int  i;
01029 
01030     if (gl_cnt >= BUF_SIZE - 1)
01031         gl_error("\n*** Error: Getline(): input buffer overflow\n");
01032     if (gl_overwrite == 0 || gl_pos == gl_cnt) {
01033         for (i=gl_cnt; i >= gl_pos; i--)
01034             gl_buf[i+1] = gl_buf[i];
01035         gl_buf[gl_pos] = c;
01036         gl_fixup(gl_prompt, gl_pos, gl_pos+1);
01037     } else {
01038         gl_buf[gl_pos] = c;
01039         gl_extent = 1;
01040         gl_fixup(gl_prompt, gl_pos, gl_pos+1);
01041     }
01042 }
01043 
01044 static void
01045 gl_yank()
01046 /* adds the kill buffer to the input buffer at current location */
01047 {
01048     int  i, len;
01049 
01050     len = strlen(gl_killbuf);
01051     if (len > 0) {
01052         gl_mark = gl_pos;
01053         if (gl_overwrite == 0) {
01054             if (gl_cnt + len >= BUF_SIZE - 1)
01055                 gl_error("\n*** Error: Getline(): input buffer overflow\n");
01056             for (i=gl_cnt; i >= gl_pos; i--)
01057                 gl_buf[i+len] = gl_buf[i];
01058             for (i=0; i < len; i++)
01059                 gl_buf[gl_pos+i] = gl_killbuf[i];
01060             gl_fixup(gl_prompt, gl_pos, gl_pos+len);
01061         } else {
01062             if (gl_pos + len > gl_cnt) {
01063                 if (gl_pos + len >= BUF_SIZE - 1)
01064                     gl_error("\n*** Error: Getline(): input buffer overflow\n");
01065                 gl_buf[gl_pos + len] = 0;
01066             }
01067             for (i=0; i < len; i++)
01068                 gl_buf[gl_pos+i] = gl_killbuf[i];
01069             gl_extent = len;
01070             gl_fixup(gl_prompt, gl_pos, gl_pos+len);
01071         }
01072     } else
01073         gl_putc('\007');
01074 }
01075 
01076 static void
01077 gl_transpose()
01078 /* switch character under cursor and to left of cursor */
01079 {
01080     int    c;
01081 
01082     if (gl_pos > 0 && gl_cnt > gl_pos) {
01083         c = gl_buf[gl_pos-1];
01084         gl_buf[gl_pos-1] = gl_buf[gl_pos];
01085         gl_buf[gl_pos] = c;
01086         gl_extent = 2;
01087         gl_fixup(gl_prompt, gl_pos-1, gl_pos);
01088     } else
01089         gl_putc('\007');
01090 }
01091 
01092 static void
01093 gl_newline()
01094 /*
01095  * Cleans up entire line before returning to caller. A \n is appended.
01096  * If line longer than screen, we redraw starting at beginning
01097  */
01098 {
01099     int change = gl_cnt;
01100     int len = gl_cnt;
01101     int loc = gl_width - 5;     /* shifts line back to start position */
01102 
01103     if (gl_cnt >= BUF_SIZE - 1)
01104         gl_error("\n*** Error: Getline(): input buffer overflow\n");
01105     if (Gl_out_hook) {
01106         change = Gl_out_hook(gl_buf);
01107         len = strlen(gl_buf);
01108     }
01109     if (gl_erase_line) {
01110         char gl_buf0 = gl_buf[0];
01111         gl_buf[0] = '\0';
01112         gl_fixup("", 0, 0);
01113         gl_buf[0] = gl_buf0;
01114     }
01115     else {
01116         if (loc > len)
01117             loc = len;
01118         gl_fixup(gl_prompt, change, loc);   /* must do this before appending \n */
01119         gl_putc('\n');
01120     }
01121     gl_buf[len] = '\n';
01122     gl_buf[len+1] = '\0';
01123     gl_mark = -1;
01124 }
01125 
01126 static void
01127 gl_del(int loc)
01128 /*
01129  * Delete a character.  The loc variable can be:
01130  *    -1 : delete character to left of cursor
01131  *     0 : delete character under cursor
01132  */
01133 {
01134     int i;
01135 
01136     if ((loc == -1 && gl_pos > 0) || (loc == 0 && gl_pos < gl_cnt)) {
01137         for (i=gl_pos+loc; i < gl_cnt; i++)
01138             gl_buf[i] = gl_buf[i+1];
01139         gl_fixup(gl_prompt, gl_pos+loc, gl_pos+loc);
01140     } else
01141         gl_putc('\007');
01142 }
01143 
01144 static void
01145 gl_kill()
01146 /* delete from current position to the end of line */
01147 {
01148     if (gl_pos < gl_cnt) {
01149         strcpy(gl_killbuf, gl_buf + gl_pos);
01150         gl_buf[gl_pos] = '\0';
01151         gl_fixup(gl_prompt, gl_pos, gl_pos);
01152     } else
01153         gl_putc('\007');
01154 }
01155 
01156 static void
01157 gl_redraw()
01158 /* emit a newline, reset and redraw prompt and current input line */
01159 {
01160     if (gl_init_done > 0) {
01161         gl_putc('\n');
01162         gl_fixup(gl_prompt, -2, gl_pos);
01163     }
01164 }
01165 
01166 static void setCursorPosition(int x)
01167 {
01168    /* Set console cursor position.
01169       Restore console parameters in case of console corruption */
01170 
01171 #ifdef WIN32
01172    CONSOLE_SCREEN_BUFFER_INFO ci;
01173    HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
01174    if (out == INVALID_HANDLE_VALUE) return;
01175 
01176    if (!GetConsoleScreenBufferInfo(out, &ci)) return;
01177 
01178    ci.dwCursorPosition.X = x;
01179    SetConsoleCursorPosition(out, ci.dwCursorPosition);
01180    SleepEx(0, TRUE);
01181 #else
01182    if (x) { }
01183 #endif
01184 }
01185 
01186 
01187 static void
01188 gl_fixup(const char *prompt, int change, int cursor)
01189 /*
01190  * This function is used both for redrawing when input changes or for
01191  * moving within the input line.  The parameters are:
01192  *   prompt:  compared to last_prompt[] for changes;
01193  *   change : the index of the start of changes in the input buffer,
01194  *            with -1 indicating no changes, -2 indicating we're on
01195  *            a new line, redraw everything.
01196  *   cursor : the desired location of the cursor after the call.
01197  *            A value of BUF_SIZE can be used  to indicate the cursor should
01198  *            move just past the end of the input line.
01199  */
01200 {
01201     static int   gl_shift;      /* index of first on screen character */
01202     static int   off_right;     /* true if more text right of screen */
01203     static int   off_left;      /* true if more text left of screen */
01204     static char  last_prompt[BUF_SIZE] = "";
01205     int          left = 0, right = -1;          /* bounds for redraw */
01206     int          padl;          /* how much to erase at end of line */
01207     int          backup;        /* how far to backup before fixing */
01208     int          new_shift;     /* value of shift based on cursor */
01209     int          extra;         /* adjusts when shift (scroll) happens */
01210     int          i;
01211     int          new_right = -1; /* alternate right bound, using gl_extent */
01212     int          l1, l2;
01213 
01214     if (change == -2) {   /* reset */
01215         gl_pos = gl_cnt = gl_shift = off_right = off_left = 0;
01216         gl_passwd = 0;
01217         gl_puts(prompt);
01218         gl_passwd = gl_no_echo;
01219         if (strlen(prompt) > (BUF_SIZE-1)) {
01220             strncpy(last_prompt, prompt, BUF_SIZE-1);
01221             last_prompt[BUF_SIZE-1] = '\0';
01222         } else
01223             strcpy(last_prompt, prompt);
01224         change = 0;
01225         gl_width = gl_termw - strlen(prompt);
01226     } else if (strcmp(prompt, last_prompt) != 0) {
01227         l1 = strlen(last_prompt);
01228         l2 = strlen(prompt);
01229         gl_cnt = gl_cnt + l1 - l2;
01230         if (strlen(prompt) > (BUF_SIZE-1)) {
01231             strncpy(last_prompt, prompt, BUF_SIZE-1);
01232             last_prompt[BUF_SIZE-1] = '\0';
01233         } else
01234             strcpy(last_prompt, prompt);
01235         backup = gl_pos - gl_shift + l1;
01236         for (i=0; i < backup; i++)
01237             gl_putc('\b');
01238         gl_passwd = 0;
01239         gl_puts(prompt);
01240         gl_passwd = gl_no_echo;
01241         gl_pos = gl_shift;
01242         gl_width = gl_termw - l2;
01243         change = 0;
01244     }
01245     padl = (off_right)? gl_width - 1 : gl_cnt - gl_shift;   /* old length */
01246     backup = gl_pos - gl_shift;
01247     if (change >= 0) {
01248         gl_cnt = strlen(gl_buf);
01249         if (change > gl_cnt)
01250             change = gl_cnt;
01251     }
01252     if (cursor > gl_cnt) {
01253         if (cursor != BUF_SIZE)         /* BUF_SIZE means end of line */
01254             gl_putc('\007');
01255         cursor = gl_cnt;
01256     }
01257     if (cursor < 0) {
01258         gl_putc('\007');
01259         cursor = 0;
01260     }
01261     if (off_right || (off_left && cursor < gl_shift + gl_width - gl_scroll / 2))
01262         extra = 2;                      /* shift the scrolling boundary */
01263     else
01264         extra = 0;
01265     new_shift = cursor + extra + gl_scroll - gl_width;
01266     if (new_shift > 0) {
01267         new_shift /= gl_scroll;
01268         new_shift *= gl_scroll;
01269     } else
01270         new_shift = 0;
01271     if (new_shift != gl_shift) {        /* scroll occurs */
01272         gl_shift = new_shift;
01273         off_left = (gl_shift)? 1 : 0;
01274         off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
01275         left = gl_shift;
01276         new_right = right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
01277     } else if (change >= 0) {           /* no scroll, but text changed */
01278         if (change < gl_shift + off_left) {
01279             left = gl_shift;
01280         } else {
01281             left = change;
01282             backup = gl_pos - change;
01283         }
01284         off_right = (gl_cnt > gl_shift + gl_width - 1)? 1 : 0;
01285         right = (off_right)? gl_shift + gl_width - 2 : gl_cnt;
01286         new_right = (gl_extent && (right > left + gl_extent))?
01287                      left + gl_extent : right;
01288     }
01289     padl -= (off_right)? gl_width - 1 : gl_cnt - gl_shift;
01290     padl = (padl < 0)? 0 : padl;
01291     if (left <= right) {                /* clean up screen */
01292         for (i=0; i < backup; i++)
01293             gl_putc('\b');
01294         if (left == gl_shift && off_left) {
01295             gl_putc('$');
01296             left++;
01297         }
01298         for (i=left; i < new_right; i++)
01299             gl_putc(gl_buf[i]);
01300         gl_pos = new_right;
01301         if (off_right && new_right == right) {
01302             gl_putc('$');
01303             gl_pos++;
01304         } else {
01305             for (i=0; i < padl; i++)     /* erase remains of prev line */
01306                 gl_putc(' ');
01307             gl_pos += padl;
01308         }
01309     }
01310     i = gl_pos - cursor;                /* move to final cursor location */
01311     if (i > 0) {
01312         while (i--) {
01313            gl_putc('\b');
01314         }
01315     } else {
01316         for (i=gl_pos; i < cursor; i++) {
01317             gl_putc(gl_buf[i]);
01318         }
01319     }
01320     gl_pos = cursor;
01321     setCursorPosition(gl_pos + strlen(prompt) - gl_shift);
01322 }
01323 
01324 static int
01325 gl_tab(char *buf, int offset, int *loc)
01326 /* default tab handler, acts like tabstops every 8 cols */
01327 {
01328     int i, count, len;
01329 
01330     len = strlen(buf);
01331     count = 8 - (offset + *loc) % 8;
01332     for (i=len; i >= *loc; i--)
01333         buf[i+count] = buf[i];
01334     for (i=0; i < count; i++)
01335         buf[*loc+i] = ' ';
01336     i = *loc;
01337     *loc = i + count;
01338     return i;
01339 }
01340 
01341 /******************* History stuff **************************************/
01342 
01343 #ifndef HIST_SIZE
01344 #define HIST_SIZE 500
01345 #endif
01346 #ifndef HIST_SAVE
01347 #define HIST_SAVE 400
01348 #endif
01349 
01350 static int      hist_pos = 0, hist_last = 0;
01351 static int      num_hist_size = HIST_SIZE, num_hist_save = HIST_SAVE;
01352 static char   **hist_buf = 0; /* [HIST_SIZE] */
01353 
01354 static void
01355 hist_init()
01356 {
01357     int i;
01358 
01359     if (hist_buf != 0)
01360        return;
01361 
01362     i = num_hist_size;
01363     if (i == 0)
01364        i = 1;
01365     hist_buf = malloc(i * sizeof(char*));
01366     hist_buf[0] = "";
01367     for (i=1; i < num_hist_size; i++)
01368         hist_buf[i] = (char *)0;
01369 }
01370 
01371 void
01372 Gl_histsize(int size, int save)
01373 {
01374    num_hist_size = size;
01375    if (num_hist_size < 0)
01376       num_hist_size = 0;
01377    num_hist_save = save;
01378    if (num_hist_save > num_hist_size)
01379       num_hist_save = -1;
01380    if (num_hist_save < 0)
01381       num_hist_save = 4 * num_hist_size / 5;
01382    if (num_hist_size == 0)
01383       num_hist_save = 0;
01384 }
01385 
01386 void
01387 Gl_histinit(char *file)
01388 {
01389    char line[BUFSIZ];
01390    FILE *fp;
01391    int   nline = 1;   /* prevent from becoming 0 */
01392 
01393    gl_savehist = 0;
01394 
01395    hist_init();
01396 
01397    if (!strcmp(file, "-") || num_hist_size == 0)
01398       return;
01399 
01400    sprintf(gl_histfile, "%s", file);
01401 
01402    fp = fopen(gl_histfile, "r");
01403    if (fp)
01404       while (fgets(line, BUFSIZ, fp)) {
01405          nline++;
01406          Gl_histadd(line);
01407       }
01408    else
01409       fp = fopen(gl_histfile, "w");
01410 
01411    if (fp) fclose(fp);
01412 
01413    gl_savehist = nline;
01414 }
01415 
01416 void
01417 Gl_histadd(char *buf)
01418 {
01419     static char *prev = 0;
01420     char *p = buf;
01421     int len;
01422 
01423     if (num_hist_size == 0 || hist_buf == 0)
01424        return;
01425 
01426     while (*p == ' ' || *p == '\t' || *p == '\n')
01427         p++;
01428     if (*p) {
01429         len = strlen(buf);
01430         if (strchr(p, '\n'))    /* previously line already has NL stripped */
01431             len--;
01432         if (prev == 0 || (int)strlen(prev) != len ||
01433             strncmp(prev, buf, len) != 0) {
01434             hist_buf[hist_last] = hist_save(buf);
01435             prev = hist_buf[hist_last];
01436             hist_last = (hist_last + 1) % num_hist_size;
01437             if (hist_buf[hist_last] && *hist_buf[hist_last]) {
01438                 free(hist_buf[hist_last]);
01439             }
01440             hist_buf[hist_last] = "";
01441 
01442             /* append command to history file */
01443             if (gl_savehist) {
01444                FILE *fp;
01445                fp = fopen(gl_histfile, "a+");
01446                if (fp) {
01447                    fprintf(fp, "%s\n", prev);
01448                    gl_savehist++;
01449                    fclose(fp);
01450                }
01451 
01452                /* if more than num_hist_size lines, safe last num_hist_save commands
01453                   and delete the rest */
01454                if (gl_savehist > num_hist_size) {
01455                   FILE *ftmp;
01456                   char tname[L_tmpnam];
01457                   char line[BUFSIZ];
01458 
01459                   fp = fopen(gl_histfile, "r");
01460                   tmpnam(tname);
01461                   ftmp = fopen(tname, "w");
01462                   if (fp && ftmp) {
01463                      int nline = 0;
01464                      while (fgets(line, BUFSIZ, fp)) {
01465                         nline++;
01466                         gl_savehist = 1;  /* prevent from becoming 0 */
01467                         if (nline > num_hist_size-num_hist_save) {
01468                            gl_savehist++;
01469                            fprintf(ftmp, "%s", line);
01470                         }
01471                      }
01472                   }
01473                   if (fp)   fclose(fp);
01474                   if (ftmp) fclose(ftmp);
01475 
01476                   /* copy back to history file */
01477                   fp   = fopen(gl_histfile, "w");
01478                   ftmp = fopen(tname, "r");
01479                   if (fp && ftmp)
01480                      while (fgets(line, BUFSIZ, ftmp))
01481                         fprintf(fp, "%s", line);
01482 
01483                   if (fp)   fclose(fp);
01484                   if (ftmp) fclose(ftmp);
01485                   remove(tname);
01486                }
01487             }
01488         }
01489     }
01490     hist_pos = hist_last;
01491 }
01492 
01493 static char *
01494 hist_prev()
01495 /* loads previous hist entry into input buffer, sticks on first */
01496 {
01497     char *p = 0;
01498     int   next;
01499 
01500     if (num_hist_size > 0) {
01501        next = (hist_pos - 1 + num_hist_size) % num_hist_size;
01502 
01503        if (hist_buf[hist_pos] != 0 && next != hist_last) {
01504            hist_pos = next;
01505            p = hist_buf[hist_pos];
01506        }
01507     }
01508     if (p == 0) {
01509         p = "";
01510         gl_putc('\007');
01511     }
01512     return p;
01513 }
01514 
01515 static char *
01516 hist_next()
01517 /* loads next hist entry into input buffer, clears on last */
01518 {
01519     char *p = 0;
01520 
01521     if (num_hist_size > 0 && hist_pos != hist_last) {
01522         hist_pos = (hist_pos+1) % num_hist_size;
01523         p = hist_buf[hist_pos];
01524     }
01525     if (p == 0) {
01526         p = "";
01527         gl_putc('\007');
01528     }
01529     return p;
01530 }
01531 
01532 static char *
01533 hist_save(char *p)
01534 /* makes a copy of the string */
01535 {
01536     char *s = 0;
01537     int   len = strlen(p);
01538     char *nl = strchr(p, '\n');
01539 
01540     if (nl) {
01541         if ((s = (char *)malloc(len)) != 0) {
01542             strncpy(s, p, len-1);
01543             s[len-1] = 0;
01544         }
01545     } else {
01546         if ((s = (char *)malloc(len+1)) != 0) {
01547             strcpy(s, p);
01548         }
01549     }
01550     if (s == 0)
01551         gl_error("\n*** Error: hist_save() failed on malloc\n");
01552     return s;
01553 }
01554 
01555 /******************* Search stuff **************************************/
01556 
01557 static char  search_prompt[101];  /* prompt includes search string */
01558 static char  search_string[100];
01559 static int   search_pos = 0;      /* current location in search_string */
01560 static int   search_forw_flg = 0; /* search direction flag */
01561 static int   search_last = 0;     /* last match found */
01562 
01563 static void
01564 search_update(int c)
01565 {
01566     if (c == 0) {
01567         search_pos = 0;
01568         search_string[0] = 0;
01569         search_prompt[0] = '?';
01570         search_prompt[1] = ' ';
01571         search_prompt[2] = 0;
01572     } else if (c > 0) {
01573         search_string[search_pos] = c;
01574         search_string[search_pos+1] = 0;
01575         search_prompt[search_pos] = c;
01576         search_prompt[search_pos+1] = '?';
01577         search_prompt[search_pos+2] = ' ';
01578         search_prompt[search_pos+3] = 0;
01579         search_pos++;
01580     } else {
01581         if (search_pos > 0) {
01582             search_pos--;
01583             search_string[search_pos] = 0;
01584             search_prompt[search_pos] = '?';
01585             search_prompt[search_pos+1] = ' ';
01586             search_prompt[search_pos+2] = 0;
01587         } else {
01588             gl_putc('\007');
01589             hist_pos = hist_last;
01590         }
01591     }
01592 }
01593 
01594 static void
01595 search_addchar(int c)
01596 {
01597     char *loc;
01598 
01599     search_update(c);
01600     if (c < 0) {
01601         if (search_pos > 0) {
01602             hist_pos = search_last;
01603         } else {
01604             gl_buf[0] = 0;
01605             hist_pos = hist_last;
01606         }
01607         strcpy(gl_buf, hist_buf[hist_pos]);
01608     }
01609     if ((loc = strstr(gl_buf, search_string)) != 0) {
01610         gl_fixup(search_prompt, 0, loc - gl_buf);
01611     } else if (search_pos > 0) {
01612         if (search_forw_flg) {
01613             search_forw(0);
01614         } else {
01615             search_back(0);
01616         }
01617     } else {
01618         gl_fixup(search_prompt, 0, 0);
01619     }
01620 }
01621 
01622 static void
01623 search_term()
01624 {
01625     gl_search_mode = 0;
01626     if (gl_buf[0] == 0)         /* not found, reset hist list */
01627         hist_pos = hist_last;
01628     if (Gl_in_hook)
01629         Gl_in_hook(gl_buf);
01630     gl_fixup(gl_prompt, 0, gl_pos);
01631 }
01632 
01633 static void
01634 search_back(int new_search)
01635 {
01636     int    found = 0;
01637     char  *p, *loc;
01638 
01639     search_forw_flg = 0;
01640     if (gl_search_mode == 0) {
01641         search_last = hist_pos = hist_last;
01642         search_update(0);
01643         gl_search_mode = 1;
01644         gl_buf[0] = 0;
01645         gl_fixup(search_prompt, 0, 0);
01646     } else if (search_pos > 0) {
01647         while (!found) {
01648             p = hist_prev();
01649             if (*p == 0) {              /* not found, done looking */
01650                gl_buf[0] = 0;
01651                gl_fixup(search_prompt, 0, 0);
01652                found = 1;
01653             } else if ((loc = strstr(p, search_string)) != 0) {
01654                strcpy(gl_buf, p);
01655                gl_fixup(search_prompt, 0, loc - p);
01656                if (new_search)
01657                    search_last = hist_pos;
01658                found = 1;
01659             }
01660         }
01661     } else {
01662         gl_putc('\007');
01663     }
01664 }
01665 
01666 static void
01667 search_forw(int new_search)
01668 {
01669     int    found = 0;
01670     char  *p, *loc;
01671 
01672     search_forw_flg = 1;
01673     if (gl_search_mode == 0) {
01674         search_last = hist_pos = hist_last;
01675         search_update(0);
01676         gl_search_mode = 1;
01677         gl_buf[0] = 0;
01678         gl_fixup(search_prompt, 0, 0);
01679     } else if (search_pos > 0) {
01680         while (!found) {
01681             p = hist_next();
01682             if (*p == 0) {              /* not found, done looking */
01683                gl_buf[0] = 0;
01684                gl_fixup(search_prompt, 0, 0);
01685                found = 1;
01686             } else if ((loc = strstr(p, search_string)) != 0) {
01687                strcpy(gl_buf, p);
01688                gl_fixup(search_prompt, 0, loc - p);
01689                if (new_search)
01690                    search_last = hist_pos;
01691                found = 1;
01692             }
01693         }
01694     } else {
01695         gl_putc('\007');
01696     }
01697 }
01698 
01699 #if 0
01700 /***********************************************************************
01701  *                                                                     *
01702  *   Strip blanks from both sides of a string. Space for the new       *
01703  *   string is allocated and a pointer to it is returned.              *
01704  *                                                                     *
01705  ***********************************************************************/
01706 static char *strip(char *s)
01707 {
01708    char *r, *t1, *t2;
01709    int   l;
01710 
01711    l = strlen(s);
01712    r = (char *)calloc(l+1, 1);
01713 
01714    if (l == 0) {
01715       *r = '\0';
01716       return r;
01717    }
01718 
01719    /* get rid of leading blanks */
01720    t1 = s;
01721    while (*t1 == ' ')
01722       t1++;
01723 
01724    t2 = s + l - 1;
01725    while (*t2 == ' ' && t2 > s)
01726       t2--;
01727 
01728    if (t1 > t2) {
01729       *r = '\0';
01730       return r;
01731    }
01732    strncpy(r, t1, (size_t) (t2-t1+1));
01733 
01734    return r;
01735 }
01736 #endif
01737 
01738 /*****************************************************************************/
01739 /* Extra routine provided by Christian Lacunza <lacunza@cdfsg5.lbl.gov>      */
01740 /*****************************************************************************/
01741 
01742 /* move cursor back to beginning of _current_ word */
01743 /* unless it's already at the beginning,           */
01744 /* in which case it moves back to the beginning    */
01745 /* of the _previous_ word.                         */
01746 static void gl_back_1_word( void )
01747 {
01748      int i = gl_pos;
01749 
01750      /* if we're at the beginning of a word,     */
01751      /* slip back into the preceeding whitespace */
01752      if( i>0 && is_whitespace(gl_buf[i-1]) ) {
01753           i-=1;
01754      }
01755 
01756      /* now move back over all consecutive whitespace */
01757      while( i>0 && is_whitespace(gl_buf[i]) ) {
01758           i-=1;
01759      }
01760 
01761      /* now keep moving back over all consecutive non-whitespace */
01762      /* until we find the beginning of this word.                */
01763      /* ie. stop just before more whitespace shows up.           */
01764      while( i>0 && !is_whitespace(gl_buf[i-1]) ) {
01765           i-=1;
01766      }
01767 
01768      /* move the cursor here */
01769      gl_fixup(gl_prompt, -1, i);
01770 }
01771 
01772 /* kills from current position to begin of word */
01773 static void gl_kill_back_1_word( void )
01774 {
01775      int i = gl_pos;
01776 
01777      /* first find a word */
01778      while( i>0 && is_whitespace(gl_buf[i-1]) ) {
01779           i--;
01780      }
01781 
01782      /* next, find the begin of this word. */
01783      while( i>0 && !is_whitespace(gl_buf[i-1]) ) {
01784           i--;
01785      }
01786 
01787      /* kill */
01788      gl_mark = i;
01789      gl_wipe();
01790 }
01791 
01792 /* kills from current position to end of word */
01793 static void gl_kill_1_word( void )
01794 {
01795      int i = gl_pos;
01796      int j = gl_pos;
01797 
01798 /* delete this: */
01799 #if 0
01800      /* not sure what to do with "punctuation" */
01801      if( is_whitespace(gl_buf[j]) && gl_buf[j]!=' ' ) {
01802           return;
01803      }
01804      /* first find a word */
01805      while( j<gl_cnt && gl_buf[j]==' ' ) {
01806           j+=1;
01807      }
01808 #endif
01809 
01810      /* first find a word */
01811      while( j<gl_cnt && is_whitespace(gl_buf[j]) ) {
01812           j+=1;
01813      }
01814 
01815      /* next, find the end of this word. */
01816      while( j<gl_cnt && !is_whitespace(gl_buf[j+1]) ) {
01817           j+=1;
01818      }
01819 
01820      /* kill */
01821      gl_kill_region( i, j );
01822 
01823      /* fixup */
01824      gl_fixup(gl_prompt, gl_pos, gl_pos);
01825 }
01826 
01827 static void gl_kill_region( int i, int j )
01828 {
01829      /* copy to kill buffer */
01830      strncpy( gl_killbuf, gl_buf+i, j-i+1 );
01831      gl_killbuf[j-i+1]='\0';
01832 
01833      /* remove from gl_buf */
01834      while( j<gl_cnt ) {
01835           gl_buf[i]=gl_buf[j+1];
01836           i+=1;
01837           j+=1;
01838      }
01839      gl_buf[i]='\0';
01840 }
01841 
01842 /* move cursor forward to the beginning of the next word. */
01843 static void gl_fwd_1_word( void )
01844 {
01845      int i = gl_pos;
01846 
01847      /* move past all non-whitespace into the whitespace between words. */
01848      while( i<gl_cnt && !is_whitespace(gl_buf[i]) ) {
01849           i+=1;
01850      }
01851 
01852      /* move past this whitespace to the beginning of the next word. */
01853      while( i<gl_cnt && is_whitespace(gl_buf[i]) ) {
01854           i+=1;
01855      }
01856 
01857      /* move the cursor here. */
01858      gl_fixup(gl_prompt, -1, i);
01859 }
01860 
01861 /* NOTE: "whitespace" is very loosely defined. */
01862 static int is_whitespace( char c )
01863 {
01864      int decent_character;
01865 
01866      decent_character = isalpha(c) || isdigit(c) || c=='_';
01867 
01868      return !decent_character;
01869 }
01870 
01871 /* sets mark to be at point */
01872 static void gl_set_mark( void )
01873 {
01874      gl_mark = gl_pos;
01875 }
01876 
01877 /* kills from mark to point */
01878 static void gl_wipe( void )
01879 {
01880      int left, right;
01881 
01882      if( gl_mark  <   0    ) return;
01883      if( gl_mark == gl_pos ) return;
01884 
01885      if( gl_mark < gl_pos ) {
01886           left  = gl_mark;
01887           right = gl_pos;
01888      }
01889      else {
01890           left  = gl_pos;
01891           right = gl_mark;
01892      }
01893 
01894      gl_kill_region( left, right-1 );
01895      gl_fixup( gl_prompt, left, left );
01896 }
01897 
01898 /* echanges point and mark */
01899 static void gl_exch( void )
01900 {
01901      int tmp;
01902 
01903      /* make sure mark is set */
01904      if( gl_mark < 0 ) return;
01905 
01906      tmp = gl_pos;
01907      gl_fixup( gl_prompt, -1, gl_mark );
01908      gl_mark = tmp;
01909 }

Generated on Tue Jul 5 14:11:25 2011 for ROOT_528-00b_version by  doxygen 1.5.1