Getline_el.cxx

Go to the documentation of this file.
00001 /* @(#)root/clib:$Id: Getline_el.cxx 36302 2010-10-11 15:07:25Z axel $ */
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 /*
00016  *************************** Motivation **********************************
00017 
00018    Many interactive programs read input line by line, but would like to
00019    provide line editing and history functionality to the end-user that
00020    runs the program.
00021 
00022    The input-edit package provides that functionality.  As far as the
00023    programmer is concerned, the program only asks for the next line
00024    of input. However, until the user presses the RETURN key they can use
00025    emacs-style line editing commands and can traverse the history of lines
00026    previously typed.
00027 
00028    Other packages, such as GNU's readline, have greater capability but are
00029    also substantially larger.  Input-edit is small, since it uses neither
00030    stdio nor any termcap features, and is also quite portable.  It only uses
00031    \b to backspace and \007 to ring the bell on errors.  Since it cannot
00032    edit multiple lines it scrolls long lines left and right on the same line.
00033 
00034    Input edit uses classic (not ANSI) C, and should run on any Unix
00035    system (BSD or SYSV), PC's with the MSC compiler, or Vax/VMS (untested by me).
00036    Porting the package to new systems basicaly requires code to read a
00037    character when it is typed without echoing it, everything else should be OK.
00038 
00039    I have run the package on:
00040 
00041         DECstation 5000, Ultrix 4.2 with cc and gcc
00042         Sun Sparc 2, SunOS 4.1.1, with cc
00043         SGI Iris, IRIX System V.3, with cc
00044         PC, DRDOS 5.0, with MSC 6.0
00045 
00046    The description below is broken into two parts, the end-user (editing)
00047    interface and the programmer interface.  Send bug reports, fixes and
00048    enhancements to:
00049 
00050    Chris Thewalt (thewalt@ce.berkeley.edu)
00051    2/4/92
00052 
00053    PS: I don't have, and don't want to add, a vi mode, sorry.
00054 
00055  ************************** End-User Interface ***************************
00056 
00057    Entering printable keys generally inserts new text into the buffer (unless
00058    in overwrite mode, see below).  Other special keys can be used to modify
00059    the text in the buffer.  In the description of the keys below, ^n means
00060    Control-n, or holding the CONTROL key down while pressing "n". M-B means
00061    Meta-B (or Alt-B). Errors will ring the terminal bell.
00062 
00063    ^A/^E   : Move cursor to beginning/end of the line.
00064    ^F/^B   : Move cursor forward/backward one character.
00065    ^D      : Delete the character under the cursor.
00066    ^H, DEL : Delete the character to the left of the cursor.
00067    ^K      : Kill from the cursor to the end of line.
00068    ^L      : Redraw current line.
00069    ^O      : Toggle overwrite/insert mode. Initially in insert mode. Text
00070           added in overwrite mode (including yanks) overwrite
00071           existing text, while insert mode does not overwrite.
00072    ^P/^N   : Move to previous/next item on history list.
00073    ^R/^S   : Perform incremental reverse/forward search for string on
00074           the history list.  Typing normal characters adds to the current
00075           search string and searches for a match. Typing ^R/^S marks
00076           the start of a new search, and moves on to the next match.
00077           Typing ^H or DEL deletes the last character from the search
00078           string, and searches from the starting location of the last search.
00079           Therefore, repeated DEL's appear to unwind to the match nearest
00080           the point at which the last ^R or ^S was typed.  If DEL is
00081           repeated until the search string is empty the search location
00082           begins from the start of the history list.  Typing ESC or
00083           any other editing character accepts the current match and
00084           loads it into the buffer, terminating the search.
00085    ^T      : Toggle the characters under and to the left of the cursor.
00086    ^U      : Kill from beginning to the end of the line.
00087    ^Y      : Yank previously killed text back at current location.  Note that
00088           this will overwrite or insert, depending on the current mode.
00089    M-F/M-B : Move cursor forward/backward one word.
00090    M-D     : Delete the word under the cursor.
00091    ^SPC    : Set mark.
00092    ^W      : Kill from mark to point.
00093    ^X      : Exchange mark and point.
00094    TAB     : By default adds spaces to buffer to get to next TAB stop
00095           (just after every 8th column), although this may be rebound by the
00096           programmer, as described below.
00097    NL, CR  : returns current buffer to the program.
00098 
00099    DOS and ANSI terminal arrow key sequences are recognized, and act like:
00100 
00101    up    : same as ^P
00102    down  : same as ^N
00103    left  : same as ^B
00104    right : same as ^F
00105 
00106  ************************** Programmer Interface ***************************
00107 
00108    The programmer accesses input-edit through five functions, and optionally
00109    through three additional function pointer hooks.  The five functions are:
00110 
00111    char *Getline(const char *prompt)
00112 
00113         Prints the prompt and allows the user to edit the current line. A
00114         pointer to the line is returned when the user finishes by
00115         typing a newline or a return.  Unlike GNU readline, the returned
00116         pointer points to a static buffer, so it should not be free'd, and
00117         the buffer contains the newline character.  The user enters an
00118         end-of-file by typing ^D on an empty line, in which case the
00119         first character of the returned buffer is '\0'.  Getline never
00120         returns a NULL pointer.  The getline function sets terminal modes
00121         needed to make it work, and resets them before returning to the
00122         caller.  The getline function also looks for characters that would
00123         generate a signal, and resets the terminal modes before raising the
00124         signal condition.  If the signal handler returns to getline,
00125         the screen is automatically redrawn and editing can continue.
00126         Getline now requires both the input and output stream be connected
00127         to the terminal (not redirected) so the main program should check
00128         to make sure this is true.  If input or output have been redirected
00129         the main program should use buffered IO (stdio) rather than
00130         the slow 1 character read()s that getline uses (note: this limitation
00131         has been removed).
00132 
00133    char *Getlinem(int mode, const char *prompt)
00134 
00135         mode: -1 = init, 0 = line mode, 1 = one char at a time mode, 2 = cleanup
00136 
00137         More specialized version of the previous function. Depending on
00138         the mode, it behaves differently. Its main use is to allow
00139         character by character input from the input stream (useful when
00140         in an X eventloop). It will return NULL as long as no newline
00141         has been received. Its use is typically as follows:
00142         1) In the program initialization part one calls: Getlinem(-1,"prompt>")
00143         2) In the X inputhandler: if ((line = Getlinem(1,NULL))) {
00144         3) In the termination routine: Getlinem(2,NULL)
00145         With mode=0 the function behaves exactly like the previous function.
00146 
00147    void Gl_config(const char *which, int value)
00148 
00149         Set some config options. Which can be:
00150           "noecho":  do not echo characters (used for passwd input)
00151           "erase":   do erase line after return (used for text scrollers)
00152 
00153    void Gl_setwidth(int width)
00154 
00155         Set the width of the terminal to the specified width. The default
00156         width is 80 characters, so this function need only be called if the
00157         width of the terminal is not 80.  Since horizontal scrolling is
00158         controlled by this parameter it is important to get it right.
00159 
00160    void Gl_histinit(char *file)
00161 
00162         This function reads a history list from file. So lines from a
00163         previous session can be used again.
00164 
00165    void Gl_histadd(char *buf)
00166 
00167         The Gl_histadd function checks to see if the buf is not empty or
00168         whitespace, and also checks to make sure it is different than
00169         the last saved buffer to avoid repeats on the history list.
00170         If the buf is a new non-blank string a copy is made and saved on
00171         the history list, so the caller can re-use the specified buf.
00172 
00173    The main loop in testgl.c, included in this directory, shows how the
00174    input-edit package can be used:
00175 
00176    extern char *Getline();
00177    extern void  Gl_histadd();
00178    main()
00179    {
00180     char *p;
00181     Gl_histinit(".hist");
00182     do {
00183         p = Getline("PROMPT>>>> ");
00184         Gl_histadd(p);
00185         fputs(p, stdout);
00186     } while (*p != 0);
00187    }
00188 
00189    In order to allow the main program to have additional access to the buffer,
00190    to implement things such as completion or auto-indent modes, three
00191    function pointers can be bound to user functions to modify the buffer as
00192    described below.  By default Gl_in_hook and Gl_out_hook are set to NULL,
00193    and Gl_tab_hook is bound to a function that inserts spaces until the next
00194    logical tab stop is reached.  The user can reassign any of these pointers
00195    to other functions.  Each of the functions bound to these hooks receives
00196    the current buffer as the first argument, and must return the location of
00197    the leftmost change made in the buffer.  If the buffer isn't modified the
00198    functions should return -1.  When the hook function returns the screen is
00199    updated to reflect any changes made by the user function.
00200 
00201    int (*Gl_tab_hook)(char *buf, int prompt_width, int *cursor_loc)
00202 
00203         If Gl_tab_hook is non-NULL, it is called whenever a tab is typed.
00204         In addition to receiving the buffer, the current prompt width is
00205         given (needed to do tabbing right) and a pointer to the cursor
00206         offset is given, where a 0 offset means the first character in the
00207         line.  Not only does the cursor_loc tell the programmer where the
00208         TAB was received, but it can be reset so that the cursor will end
00209         up at the specified location after the screen is redrawn.
00210 
00211    int (*Gl_beep_hook)()
00212         Called if \007 (beep) is about to be printed. Return !=0 if handled.
00213  */
00214 
00215 extern "C" {
00216 /********************* exported interface ********************************/
00217 
00218 
00219 char* Getline(const char* prompt);   /* read a line of input */
00220 char* Getlinem(int mode, const char* prompt);   /* allows reading char by char */
00221 void Gl_config(const char* which, int value);    /* set some options */
00222 void Gl_setwidth(int w);             /* specify width of screen */
00223 void Gl_windowchanged();             /* call after SIGWINCH signal */
00224 int Gl_eof();
00225 void Gl_histinit(char* file);    /* read entries from old histfile */
00226 void Gl_histadd(char* buf);          /* adds entries to hist */
00227 void Gl_setColors(const char* colorTab, const char* colorTabComp, const char* colorBracket,
00228                   const char* colorBadBracket, const char* colorPrompt);    /* set the colours (replace default colours) for enhanced output */
00229 
00230 int (* Gl_tab_hook)(char* buf, int prompt_width, int* loc) = 0;
00231 int (* Gl_beep_hook)() = 0;
00232 int (* Gl_in_key)(int ch) = 0;
00233 }
00234 
00235 /******************** imported interface *********************************/
00236 
00237 #include <string.h>
00238 #include <ctype.h>
00239 #include <errno.h>
00240 #include <signal.h>
00241 #include <stdlib.h>
00242 #include <stdio.h>
00243 #include <list>
00244 #include <string>
00245 #include <fstream>
00246 
00247 /** newer imported interfaces **/
00248 #include "editline.h"
00249 
00250 const char* hist_file = 0;  // file name for the command history (read and write)
00251 
00252 /******************** internal interface *********************************/
00253 
00254 #define BUF_SIZE 1024
00255 
00256 // # History file size, once HistSize is reached remove all but HistSave entries,
00257 // # set to 0 to turn off command recording.
00258 // # Can be overridden by environment variable ROOT_HIST=size[:save],
00259 // # the ":save" part is optional.
00260 // # Rint.HistSize:         500
00261 // # Set to -1 for sensible default (80% of HistSize), set to 0 to disable history.
00262 // # Rint.HistSave:         400
00263 int size_lines = 500;
00264 int save_lines = -1;
00265 
00266 /************************ nonportable part *********************************/
00267 
00268 extern "C" {
00269 void
00270 Gl_config(const char* which, int value) {
00271    if (strcmp(which, "noecho") == 0) {
00272       setEcho(!value);
00273    } else {
00274       // unsupported directive
00275       printf("gl_config: %s ?\n", which);
00276    }
00277 }
00278 
00279 
00280 /******************** fairly portable part *********************************/
00281 
00282 void
00283 Gl_setwidth(int /*w*/) {
00284    termResize();  // no need to pass in width as new func detects term size itself
00285 }
00286 
00287 
00288 void
00289 Gl_windowchanged() {
00290 #ifdef TIOCGWINSZ
00291 
00292    if (isatty(0)) {
00293       static char lenv[32], cenv[32];
00294       struct winsize wins;
00295       ioctl(0, TIOCGWINSZ, &wins);
00296 
00297       if (wins.ws_col == 0) {
00298          wins.ws_col = 80;
00299       }
00300 
00301       if (wins.ws_row == 0) {
00302          wins.ws_row = 24;
00303       }
00304 
00305       Gl_setwidth(wins.ws_col);
00306 
00307       sprintf(lenv, "LINES=%d", wins.ws_row);
00308       putenv(lenv);
00309       sprintf(cenv, "COLUMNS=%d", wins.ws_col);
00310       putenv(cenv);
00311    }
00312 #endif
00313 } // Gl_windowchanged
00314 
00315 
00316 /* The new and hopefully improved Getlinem method!
00317  * Uses readline() from libeditline.
00318  * History_t and editing are also handled by libeditline.
00319  * Modes: -1 = init, 0 = line mode, 1 = one char at a time mode, 2 = cleanup
00320  */
00321 char*
00322 Getlinem(int mode, const char* prompt) {
00323    static char sprompt[80] = { 0 };
00324    char* input_buffer;
00325    rl_tab_hook = Gl_tab_hook;
00326    rl_in_key_hook = Gl_in_key;
00327 
00328    static int getline_initialized = 0;
00329 
00330    if (hist_file && getline_initialized == 0) {
00331       //rl_initialize();                // rl_initialize already being called by history_stifle()
00332       read_history(hist_file);
00333       getline_initialized = 1;
00334    }
00335 
00336    // mode 2 = cleanup
00337    if (mode == 2) {
00338       rl_reset_terminal();
00339    }
00340 
00341    // mode -1 = init
00342    if (mode == -1) {
00343       if (prompt) {
00344          strncpy(sprompt, prompt, sizeof(sprompt) - 1);
00345          sprompt[sizeof(sprompt) - 1] = 0; // force 0 termination
00346       }
00347       input_buffer = readline(sprompt, true /*newline*/);
00348 
00349       return input_buffer;
00350    }
00351 
00352    // mode 1 = one char at a time
00353    if (mode == 1) {
00354       if (prompt) {
00355          strncpy(sprompt, prompt, sizeof(sprompt) - 1);
00356          sprompt[sizeof(sprompt) - 1] = 0; // force 0 termination
00357       }
00358 
00359       // note: input_buffer will be null unless complete line entered
00360       input_buffer = readline(sprompt, false /*no newline*/);
00361 
00362       // if complete line is entered, add to history and return buffer, otherwise return null
00363       char* ch = input_buffer;
00364 
00365       if (input_buffer) {
00366          while (*ch != '\a') {
00367             if (*ch == '\n') {
00368                // line complete!
00369                return input_buffer;
00370             }
00371             ++ch;
00372          }
00373       }
00374    }
00375    return NULL;
00376 } // Getlinem
00377 
00378 
00379 void
00380 Gl_setColors(const char* colorTab, const char* colorTabComp, const char* colorBracket,
00381              const char* colorBadBracket, const char* colorPrompt) {
00382    // call to enhance.cxx to set colours
00383    setColors(colorTab, colorTabComp, colorBracket, colorBadBracket, colorPrompt);
00384 }
00385 
00386 
00387 char*
00388 Getline(const char* prompt) {
00389    // Get a line of user input, showing prompt.
00390    // Does not return after every character entered, but
00391    // only returns once the user has hit return.
00392    // For ROOT Getline.c backward compatibility reasons,
00393    // the returned value is volatile and will be overwritten
00394    // by the subsequent call to Getline() or Getlinem(),
00395    // so copy the string if it needs to stay around.
00396    // The returned value must not be deleted.
00397    // The returned string contains a trailing newline '\n'.
00398 
00399    Getlinem(-1, prompt); // init
00400    char* answer = 0;
00401    do {
00402       answer = Getlinem(1, prompt);
00403    } while (!answer);
00404    return answer;
00405 }
00406 
00407 
00408 /******************* History_t stuff **************************************/
00409 
00410 void
00411 Gl_histsize(int size, int save) {
00412    stifle_history(save);
00413    size_lines = size;
00414    save_lines = save;
00415 }
00416 
00417 
00418 void
00419 Gl_histinit(char* file) {
00420    if (size_lines == 0 || save_lines == 0) {
00421       // history recording disabled
00422       return;
00423    }
00424 
00425    hist_file = file;
00426    if (size_lines > 0) {
00427       int linecount = 0;
00428       std::list<std::string> lines;
00429       {
00430          std::ifstream in(file);
00431          if (!in) {
00432             return;
00433          }
00434 
00435          lines.push_back(std::string());
00436          while(in && std::getline(in, lines.back())) {
00437             lines.push_back(std::string());
00438             ++linecount;
00439          }
00440          lines.pop_back();
00441       }
00442 
00443       if (linecount > size_lines) {
00444          // we need to reduce it to 
00445          if (save_lines == -1) {
00446             // set default
00447             save_lines = size_lines * 80 / 100;
00448          }
00449          std::ofstream out(file);
00450          if (!out) {
00451             return;
00452          }
00453 
00454          int skipLines = linecount - save_lines;
00455          for (std::list<std::string>::const_iterator iS = lines.begin(),
00456                  eS = lines.end(); iS != eS; ++iS) {
00457             if (skipLines) {
00458                --skipLines;
00459             } else {
00460                out << *iS << std::endl;
00461             }
00462          }
00463 
00464       }
00465    }
00466 }
00467 
00468 
00469 void
00470 Gl_histadd(char* buf) {
00471    // Add to history; write the file out in case
00472    // the process is abort()ed by executing the line.
00473    add_history(buf);
00474    if (hist_file) {
00475       write_history(hist_file);
00476    }
00477 }
00478 
00479 
00480 int
00481 Gl_eof() {
00482    return rl_eof();
00483 }
00484 
00485 
00486 } // extern "C"

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