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"