1136644Sache/* 2136644SacheDate: Tue, 16 Mar 2004 19:38:40 -0800 3136644SacheFrom: Harold Levy <Harold.Levy@synopsys.com> 4136644SacheSubject: fgets(stdin) --> readline() redirector 5136644SacheTo: chet@po.cwru.edu 6136644Sache 7136644SacheHi Chet, 8136644Sache 9136644SacheHere is something you may find useful enough to include in the readline 10136644Sachedistribution. It is a shared library that redirects calls to fgets(stdin) 11136644Sacheto readline() via LD_PRELOAD, and it supports a custom prompt and list of 12136644Sachecommand names. Many people have asked me for this file, so I thought I'd 13136644Sachepass it your way in hope of just including it with readline to begin with. 14136644Sache 15136644SacheBest Regards, 16136644Sache 17136644Sache-Harold 18136644Sache*/ 19136644Sache 20136644Sache/****************************************************************************** 21136644Sache******************************************************************************* 22136644Sache 23136644Sache FILE NAME: fgets.c TARGET: libfgets.so 24136644Sache AUTHOR: Harold Levy VERSION: 1.0 25136644Sache hlevy@synopsys.com 26136644Sache 27136644Sache ABSTRACT: Customize fgets() behavior via LD_PRELOAD in the following ways: 28136644Sache 29136644Sache -- If fgets(stdin) is called, redirect to GNU readline() to obtain 30136644Sache command-line editing, file-name completion, history, etc. 31136644Sache 32136644Sache -- A list of commands for command-name completion can be configured by 33136644Sache setting the environment-variable FGETS_COMMAND_FILE to a file containing 34136644Sache the list of commands to be used. 35136644Sache 36136644Sache -- Command-line editing with readline() works best when the prompt string 37136644Sache is known; you can set this with the FGETS_PROMPT environment variable. 38136644Sache 39136644Sache -- There special strings that libfgets will interpret as internal commands: 40136644Sache 41136644Sache _fgets_reset_ reset the command list 42136644Sache 43136644Sache _fgets_dump_ dump status 44136644Sache 45136644Sache _fgets_debug_ toggle debug messages 46136644Sache 47136644Sache HOW TO BUILD: Here are examples of how to build libfgets.so on various 48136644Sache platforms; you will have to add -I and -L flags to configure access to 49136644Sache the readline header and library files. 50136644Sache 51136644Sache (32-bit builds with gcc) 52136644Sache AIX: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldl -lreadline -ltermcap 53136644Sache HP-UX: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldld -lreadline 54136644Sache Linux: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldl -lreadline 55136644Sache SunOS: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldl -lgen -lreadline 56136644Sache 57136644Sache (64-bit builds without gcc) 58136644Sache SunOS: SUNWspro/bin/cc -D_LARGEFILE64_SOURCE=1 -xtarget=ultra -xarch=v9 \ 59136644Sache -KPIC fgets.c -Bdynamic -lc -ldl -lgen -ltermcap -lreadline 60136644Sache 61136644Sache HOW TO USE: Different operating systems have different levels of support 62136644Sache for the LD_PRELOAD concept. The generic method for 32-bit platforms is to 63136644Sache put libtermcap.so, libfgets.so, and libreadline.so (with absolute paths) 64136644Sache in the LD_PRELOAD environment variable, and to put their parent directories 65136644Sache in the LD_LIBRARY_PATH environment variable. Unfortunately there is no 66136644Sache generic method for 64-bit platforms; e.g. for 64-bit SunOS, you would have 67136644Sache to build both 32-bit and 64-bit libfgets and libreadline libraries, and 68136644Sache use the LD_FLAGS_32 and LD_FLAGS_64 environment variables with preload and 69136644Sache library_path configurations (a mix of 32-bit and 64-bit calls are made under 70136644Sache 64-bit SunOS). 71136644Sache 72136644Sache EXAMPLE WRAPPER: Here is an example shell script wrapper around the 73136644Sache program "foo" that uses fgets() for command-line input: 74136644Sache 75136644Sache #!/bin/csh 76136644Sache #### replace this with the libtermcap.so directory: 77136644Sache set dir1 = "/usr/lib" 78136644Sache #### replace this with the libfgets.so directory: 79136644Sache set dir2 = "/usr/fgets" 80136644Sache #### replace this with the libreadline.so directory: 81136644Sache set dir3 = "/usr/local/lib" 82136644Sache set lib1 = "${dir1}/libtermcap.so" 83136644Sache set lib2 = "${dir2}/libfgets.so" 84136644Sache set lib3 = "${dir3}/libreadline.so" 85136644Sache if ( "${?LD_PRELOAD}" ) then 86136644Sache setenv LD_PRELOAD "${lib1}:${lib2}:${lib3}:${LD_PRELOAD}" 87136644Sache else 88136644Sache setenv LD_PRELOAD "${lib1}:${lib2}:${lib3}" 89136644Sache endif 90136644Sache if ( "${?LD_LIBRARY_PATH}" ) then 91136644Sache setenv LD_LIBRARY_PATH "${dir1}:${dir2}:${dir3}:${LD_LIBRARY_PATH}" 92136644Sache else 93136644Sache setenv LD_LIBRARY_PATH "${dir1}:${dir2}:${dir3}" 94136644Sache endif 95136644Sache setenv FGETS_COMMAND_FILE "${dir2}/foo.commands" 96136644Sache setenv FGETS_PROMPT "foo> " 97136644Sache exec "foo" $* 98136644Sache 99136644Sache Copyright (C)�2003-2004 Harold Levy. 100136644Sache 101136644Sache This code links to the GNU readline library, and as such is bound by the 102136644Sache terms of the GNU General Public License as published by the Free Software 103136644Sache Foundation, either version 2 or (at your option) any later version. 104136644Sache 105136644Sache The GNU General Public License is often shipped with GNU software, and is 106136644Sache generally kept in a file called COPYING or LICENSE. If you do not have a 107136644Sache copy of the license, write to the Free Software Foundation, 59 Temple Place, 108136644Sache Suite 330, Boston, MA 02111 USA. 109136644Sache 110136644Sache This program is distributed in the hope that it will be useful, but WITHOUT 111136644Sache ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 112136644Sache FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 113136644Sache details. 114136644Sache 115136644Sache******************************************************************************* 116136644Sache******************************************************************************/ 117136644Sache 118136644Sache 119136644Sache 120136644Sache#include <dlfcn.h> 121136644Sache#include <stdio.h> 122136644Sache#include <strings.h> 123136644Sache#include <stdlib.h> 124136644Sache#include <unistd.h> 125136644Sache 126136644Sache#include <readline/readline.h> 127136644Sache#include <readline/history.h> 128136644Sache 129136644Sache 130136644Sache 131136644Sache/* for dynamically connecting to the native fgets() */ 132136644Sache#if defined(RTLD_NEXT) 133136644Sache#define REAL_LIBC RTLD_NEXT 134136644Sache#else 135136644Sache#define REAL_LIBC ((void *) -1L) 136136644Sache#endif 137136644Sachetypedef char * ( * fgets_t ) ( char * s, int n, FILE * stream ) ; 138136644Sache 139136644Sache 140136644Sache 141136644Sache/* private data */ 142136644Sache/* -- writeable data is stored in the shared library's data segment 143136644Sache -- every process that uses the shared library gets a private memory copy of 144136644Sache its entire data segment 145136644Sache -- static data in the shared library is not copied to the application 146136644Sache -- only read-only (i.e. 'const') data is stored in the shared library's 147136644Sache text segment 148136644Sache*/ 149136644Sachestatic char ** my_fgets_names = NULL ; 150136644Sachestatic int my_fgets_number_of_names = 0 ; 151136644Sachestatic int my_fgets_debug_flag = 0 ; 152136644Sache 153136644Sache 154136644Sache 155136644Sache/* invoked with _fgets_reset_ */ 156136644Sachestatic void 157136644Sachemy_fgets_reset ( 158136644Sache void 159136644Sache) { 160136644Sache if ( my_fgets_names && (my_fgets_number_of_names > 0) ) { 161136644Sache int i ; 162136644Sache if ( my_fgets_debug_flag ) { 163136644Sache printf ( "libfgets: removing command list\n" ) ; 164136644Sache } 165136644Sache for ( i = 0 ; i < my_fgets_number_of_names ; i ++ ) { 166136644Sache if ( my_fgets_names[i] ) free ( my_fgets_names[i] ) ; 167136644Sache } 168136644Sache free ( my_fgets_names ) ; 169136644Sache } 170136644Sache my_fgets_names = NULL ; 171136644Sache my_fgets_number_of_names = 0 ; 172136644Sache} 173136644Sache 174136644Sache 175136644Sache 176136644Sache/* invoked with _fgets_dump_ */ 177136644Sachestatic void 178136644Sachemy_fgets_dump ( 179136644Sache void 180136644Sache) { 181136644Sache char * s ; 182136644Sache printf ( "\n" ) ; 183136644Sache s = getenv ( "FGETS_PROMPT" ) ; 184136644Sache printf ( "FGETS_PROMPT = %s\n", s ? s : "" ) ; 185136644Sache s = getenv ( "FGETS_COMMAND_FILE" ) ; 186136644Sache printf ( "FGETS_COMMAND_FILE = %s\n", s ? s : "" ) ; 187136644Sache printf ( "debug flag = %d\n", my_fgets_debug_flag ) ; 188136644Sache printf ( "#commands = %d\n", my_fgets_number_of_names ) ; 189136644Sache if ( my_fgets_debug_flag ) { 190136644Sache if ( my_fgets_names && (my_fgets_number_of_names > 0) ) { 191136644Sache int i ; 192136644Sache for ( i = 0 ; i < my_fgets_number_of_names ; i ++ ) { 193136644Sache printf ( "%s\n", my_fgets_names[i] ) ; 194136644Sache } 195136644Sache } 196136644Sache } 197136644Sache printf ( "\n" ) ; 198136644Sache} 199136644Sache 200136644Sache 201136644Sache 202136644Sache/* invoked with _fgets_debug_ */ 203136644Sachestatic void 204136644Sachemy_fgets_debug_toggle ( 205136644Sache void 206136644Sache) { 207136644Sache my_fgets_debug_flag = my_fgets_debug_flag ? 0 : 1 ; 208136644Sache if ( my_fgets_debug_flag ) { 209136644Sache printf ( "libfgets: debug flag = %d\n", my_fgets_debug_flag ) ; 210136644Sache } 211136644Sache} 212136644Sache 213136644Sache 214136644Sache 215136644Sache/* read the command list if needed, return the i-th name */ 216136644Sachestatic char * 217136644Sachemy_fgets_lookup ( 218136644Sache int index 219136644Sache) { 220136644Sache if ( (! my_fgets_names) || (! my_fgets_number_of_names) ) { 221136644Sache char * fname ; 222136644Sache FILE * fp ; 223136644Sache fgets_t _fgets ; 224136644Sache int i ; 225136644Sache char buf1[256], buf2[256] ; 226136644Sache fname = getenv ( "FGETS_COMMAND_FILE" ) ; 227136644Sache if ( ! fname ) { 228136644Sache if ( my_fgets_debug_flag ) { 229136644Sache printf ( "libfgets: empty or unset FGETS_COMMAND_FILE\n" ) ; 230136644Sache } 231136644Sache return NULL ; 232136644Sache } 233136644Sache fp = fopen ( fname, "r" ) ; 234136644Sache if ( ! fp ) { 235136644Sache if ( my_fgets_debug_flag ) { 236136644Sache printf ( "libfgets: cannot open '%s' for reading\n", fname ) ; 237136644Sache } 238136644Sache return NULL ; 239136644Sache } 240136644Sache _fgets = (fgets_t) dlsym ( REAL_LIBC, "fgets" ) ; 241136644Sache if ( ! _fgets ) { 242136644Sache fprintf ( stderr, 243136644Sache "libfgets: failed to dynamically link to native fgets()\n" 244136644Sache ) ; 245136644Sache return NULL ; 246136644Sache } 247136644Sache for ( i = 0 ; _fgets(buf1,255,fp) ; i ++ ) ; 248136644Sache if ( ! i ) { fclose(fp) ; return NULL ; } 249136644Sache my_fgets_names = (char**) calloc ( i, sizeof(char*) ) ; 250136644Sache rewind ( fp ) ; 251136644Sache i = 0 ; 252136644Sache while ( _fgets(buf1,255,fp) ) { 253136644Sache buf1[255] = 0 ; 254136644Sache if ( 1 == sscanf(buf1,"%s",buf2) ) { 255136644Sache my_fgets_names[i] = strdup(buf2) ; 256136644Sache i ++ ; 257136644Sache } 258136644Sache } 259136644Sache fclose ( fp ) ; 260136644Sache my_fgets_number_of_names = i ; 261136644Sache if ( my_fgets_debug_flag ) { 262136644Sache printf ( "libfgets: successfully read %d commands\n", i ) ; 263136644Sache } 264136644Sache } 265136644Sache if ( index < my_fgets_number_of_names ) { 266136644Sache return my_fgets_names[index] ; 267136644Sache } else { 268136644Sache return NULL ; 269136644Sache } 270136644Sache} 271136644Sache 272136644Sache 273136644Sache 274136644Sache/* generate a list of partial name matches for readline() */ 275136644Sachestatic char * 276136644Sachemy_fgets_generator ( 277136644Sache const char * text, 278136644Sache int state 279136644Sache) 280136644Sache{ 281136644Sache static int list_index, len ; 282136644Sache char * name ; 283136644Sache if ( ! state ) { 284136644Sache list_index = 0 ; 285136644Sache len = strlen ( text ) ; 286136644Sache } 287136644Sache while ( ( name = my_fgets_lookup(list_index) ) ) { 288136644Sache list_index ++ ; 289136644Sache if ( ! strncmp ( name, text, len ) ) { 290136644Sache return ( strdup ( name ) ) ; 291136644Sache } 292136644Sache } 293136644Sache return ( NULL ) ; 294136644Sache} 295136644Sache 296136644Sache 297136644Sache 298136644Sache/* partial name completion callback for readline() */ 299136644Sachestatic char ** 300136644Sachemy_fgets_completion ( 301136644Sache const char * text, 302136644Sache int start, 303136644Sache int end 304136644Sache) 305136644Sache{ 306136644Sache char ** matches ; 307136644Sache matches = NULL ; 308136644Sache if ( ! start ) { 309136644Sache matches = rl_completion_matches ( text, my_fgets_generator ) ; 310136644Sache } 311136644Sache return ( matches ) ; 312136644Sache} 313136644Sache 314136644Sache 315136644Sache 316136644Sache/* fgets() intercept */ 317136644Sachechar * 318136644Sachefgets ( 319136644Sache char * s, 320136644Sache int n, 321136644Sache FILE * stream 322136644Sache) 323136644Sache{ 324136644Sache if ( ! s ) return NULL ; 325136644Sache if ( stream == stdin ) { 326136644Sache char * prompt ; 327136644Sache char * my_fgets_line ; 328136644Sache rl_already_prompted = 1 ; 329136644Sache rl_attempted_completion_function = my_fgets_completion ; 330136644Sache rl_catch_signals = 1 ; 331136644Sache rl_catch_sigwinch = 1 ; 332136644Sache rl_set_signals () ; 333136644Sache prompt = getenv ( "FGETS_PROMPT" ) ; 334136644Sache for ( 335136644Sache my_fgets_line = 0 ; ! my_fgets_line ; my_fgets_line=readline(prompt) 336136644Sache ) ; 337136644Sache if ( ! strncmp(my_fgets_line, "_fgets_reset_", 13) ) { 338136644Sache my_fgets_reset () ; 339136644Sache free ( my_fgets_line ) ; 340136644Sache strcpy ( s, "\n" ) ; 341136644Sache return ( s ) ; 342136644Sache } 343136644Sache if ( ! strncmp(my_fgets_line, "_fgets_dump_", 12) ) { 344136644Sache my_fgets_dump () ; 345136644Sache free ( my_fgets_line ) ; 346136644Sache strcpy ( s, "\n" ) ; 347136644Sache return ( s ) ; 348136644Sache } 349136644Sache if ( ! strncmp(my_fgets_line, "_fgets_debug_", 13) ) { 350136644Sache my_fgets_debug_toggle () ; 351136644Sache free ( my_fgets_line ) ; 352136644Sache strcpy ( s, "\n" ) ; 353136644Sache return ( s ) ; 354136644Sache } 355136644Sache (void) strncpy ( s, my_fgets_line, n-1 ) ; 356136644Sache (void) strcat ( s, "\n" ) ; 357136644Sache if ( *my_fgets_line ) add_history ( my_fgets_line ) ; 358136644Sache free ( my_fgets_line ) ; 359136644Sache return ( s ) ; 360136644Sache } else { 361136644Sache static fgets_t _fgets ; 362136644Sache _fgets = (fgets_t) dlsym ( REAL_LIBC, "fgets" ) ; 363136644Sache if ( ! _fgets ) { 364136644Sache fprintf ( stderr, 365136644Sache "libfgets: failed to dynamically link to native fgets()\n" 366136644Sache ) ; 367136644Sache strcpy ( s, "\n" ) ; 368136644Sache return ( s ) ; 369136644Sache } 370136644Sache return ( 371136644Sache _fgets ( s, n, stream ) 372136644Sache ) ; 373136644Sache } 374136644Sache} 375