2 Date: Tue, 16 Mar 2004 19:38:40 -0800
3 From: Harold Levy <Harold.Levy@synopsys.com>
4 Subject: fgets(stdin) --> readline() redirector
9 Here is something you may find useful enough to include in the readline
10 distribution. It is a shared library that redirects calls to fgets(stdin)
11 to readline() via LD_PRELOAD, and it supports a custom prompt and list of
12 command names. Many people have asked me for this file, so I thought I'd
13 pass it your way in hope of just including it with readline to begin with.
20 /******************************************************************************
21 *******************************************************************************
23 FILE NAME: fgets.c TARGET: libfgets.so
24 AUTHOR: Harold Levy VERSION: 1.0
27 ABSTRACT: Customize fgets() behavior via LD_PRELOAD in the following ways:
29 -- If fgets(stdin) is called, redirect to GNU readline() to obtain
30 command-line editing, file-name completion, history, etc.
32 -- A list of commands for command-name completion can be configured by
33 setting the environment-variable FGETS_COMMAND_FILE to a file containing
34 the list of commands to be used.
36 -- Command-line editing with readline() works best when the prompt string
37 is known; you can set this with the FGETS_PROMPT environment variable.
39 -- There special strings that libfgets will interpret as internal commands:
41 _fgets_reset_ reset the command list
43 _fgets_dump_ dump status
45 _fgets_debug_ toggle debug messages
47 HOW TO BUILD: Here are examples of how to build libfgets.so on various
48 platforms; you will have to add -I and -L flags to configure access to
49 the readline header and library files.
51 (32-bit builds with gcc)
52 AIX: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldl -lreadline -ltermcap
53 HP-UX: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldld -lreadline
54 Linux: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldl -lreadline
55 SunOS: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldl -lgen -lreadline
57 (64-bit builds without gcc)
58 SunOS: SUNWspro/bin/cc -D_LARGEFILE64_SOURCE=1 -xtarget=ultra -xarch=v9 \
59 -KPIC fgets.c -Bdynamic -lc -ldl -lgen -ltermcap -lreadline
61 HOW TO USE: Different operating systems have different levels of support
62 for the LD_PRELOAD concept. The generic method for 32-bit platforms is to
63 put libtermcap.so, libfgets.so, and libreadline.so (with absolute paths)
64 in the LD_PRELOAD environment variable, and to put their parent directories
65 in the LD_LIBRARY_PATH environment variable. Unfortunately there is no
66 generic method for 64-bit platforms; e.g. for 64-bit SunOS, you would have
67 to build both 32-bit and 64-bit libfgets and libreadline libraries, and
68 use the LD_FLAGS_32 and LD_FLAGS_64 environment variables with preload and
69 library_path configurations (a mix of 32-bit and 64-bit calls are made under
72 EXAMPLE WRAPPER: Here is an example shell script wrapper around the
73 program "foo" that uses fgets() for command-line input:
76 #### replace this with the libtermcap.so directory:
78 #### replace this with the libfgets.so directory:
79 set dir2 = "/usr/fgets"
80 #### replace this with the libreadline.so directory:
81 set dir3 = "/usr/local/lib"
82 set lib1 = "${dir1}/libtermcap.so"
83 set lib2 = "${dir2}/libfgets.so"
84 set lib3 = "${dir3}/libreadline.so"
85 if ( "${?LD_PRELOAD}" ) then
86 setenv LD_PRELOAD "${lib1}:${lib2}:${lib3}:${LD_PRELOAD}"
88 setenv LD_PRELOAD "${lib1}:${lib2}:${lib3}"
90 if ( "${?LD_LIBRARY_PATH}" ) then
91 setenv LD_LIBRARY_PATH "${dir1}:${dir2}:${dir3}:${LD_LIBRARY_PATH}"
93 setenv LD_LIBRARY_PATH "${dir1}:${dir2}:${dir3}"
95 setenv FGETS_COMMAND_FILE "${dir2}/foo.commands"
96 setenv FGETS_PROMPT "foo> "
99 Copyright (C)©2003-2004 Harold Levy.
101 This code links to the GNU readline library, and as such is bound by the
102 terms of the GNU General Public License as published by the Free Software
103 Foundation, either version 2 or (at your option) any later version.
105 The GNU General Public License is often shipped with GNU software, and is
106 generally kept in a file called COPYING or LICENSE. If you do not have a
107 copy of the license, write to the Free Software Foundation, 59 Temple Place,
108 Suite 330, Boston, MA 02111 USA.
110 This program is distributed in the hope that it will be useful, but WITHOUT
111 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
112 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
115 *******************************************************************************
116 ******************************************************************************/
126 #include <readline/readline.h>
127 #include <readline/history.h>
131 /* for dynamically connecting to the native fgets() */
132 #if defined(RTLD_NEXT)
133 #define REAL_LIBC RTLD_NEXT
135 #define REAL_LIBC ((void *) -1L)
137 typedef char * ( * fgets_t
) ( char * s
, int n
, FILE * stream
) ;
142 /* -- writeable data is stored in the shared library's data segment
143 -- every process that uses the shared library gets a private memory copy of
144 its entire data segment
145 -- static data in the shared library is not copied to the application
146 -- only read-only (i.e. 'const') data is stored in the shared library's
149 static char ** my_fgets_names
= NULL
;
150 static int my_fgets_number_of_names
= 0 ;
151 static int my_fgets_debug_flag
= 0 ;
155 /* invoked with _fgets_reset_ */
160 if ( my_fgets_names
&& (my_fgets_number_of_names
> 0) ) {
162 if ( my_fgets_debug_flag
) {
163 printf ( "libfgets: removing command list\n" ) ;
165 for ( i
= 0 ; i
< my_fgets_number_of_names
; i
++ ) {
166 if ( my_fgets_names
[i
] ) free ( my_fgets_names
[i
] ) ;
168 free ( my_fgets_names
) ;
170 my_fgets_names
= NULL
;
171 my_fgets_number_of_names
= 0 ;
176 /* invoked with _fgets_dump_ */
183 s
= getenv ( "FGETS_PROMPT" ) ;
184 printf ( "FGETS_PROMPT = %s\n", s
? s
: "" ) ;
185 s
= getenv ( "FGETS_COMMAND_FILE" ) ;
186 printf ( "FGETS_COMMAND_FILE = %s\n", s
? s
: "" ) ;
187 printf ( "debug flag = %d\n", my_fgets_debug_flag
) ;
188 printf ( "#commands = %d\n", my_fgets_number_of_names
) ;
189 if ( my_fgets_debug_flag
) {
190 if ( my_fgets_names
&& (my_fgets_number_of_names
> 0) ) {
192 for ( i
= 0 ; i
< my_fgets_number_of_names
; i
++ ) {
193 printf ( "%s\n", my_fgets_names
[i
] ) ;
202 /* invoked with _fgets_debug_ */
204 my_fgets_debug_toggle (
207 my_fgets_debug_flag
= my_fgets_debug_flag
? 0 : 1 ;
208 if ( my_fgets_debug_flag
) {
209 printf ( "libfgets: debug flag = %d\n", my_fgets_debug_flag
) ;
215 /* read the command list if needed, return the i-th name */
220 if ( (! my_fgets_names
) || (! my_fgets_number_of_names
) ) {
225 char buf1
[256], buf2
[256] ;
226 fname
= getenv ( "FGETS_COMMAND_FILE" ) ;
228 if ( my_fgets_debug_flag
) {
229 printf ( "libfgets: empty or unset FGETS_COMMAND_FILE\n" ) ;
233 fp
= fopen ( fname
, "r" ) ;
235 if ( my_fgets_debug_flag
) {
236 printf ( "libfgets: cannot open '%s' for reading\n", fname
) ;
240 _fgets
= (fgets_t
) dlsym ( REAL_LIBC
, "fgets" ) ;
243 "libfgets: failed to dynamically link to native fgets()\n"
247 for ( i
= 0 ; _fgets(buf1
,255,fp
) ; i
++ ) ;
248 if ( ! i
) { fclose(fp
) ; return NULL
; }
249 my_fgets_names
= (char**) calloc ( i
, sizeof(char*) ) ;
252 while ( _fgets(buf1
,255,fp
) ) {
254 if ( 1 == sscanf(buf1
,"%s",buf2
) ) {
255 my_fgets_names
[i
] = strdup(buf2
) ;
260 my_fgets_number_of_names
= i
;
261 if ( my_fgets_debug_flag
) {
262 printf ( "libfgets: successfully read %d commands\n", i
) ;
265 if ( index
< my_fgets_number_of_names
) {
266 return my_fgets_names
[index
] ;
274 /* generate a list of partial name matches for readline() */
281 static int list_index
, len
;
285 len
= strlen ( text
) ;
287 while ( ( name
= my_fgets_lookup(list_index
) ) ) {
289 if ( ! strncmp ( name
, text
, len
) ) {
290 return ( strdup ( name
) ) ;
298 /* partial name completion callback for readline() */
300 my_fgets_completion (
309 matches
= rl_completion_matches ( text
, my_fgets_generator
) ;
316 /* fgets() intercept */
324 if ( ! s
) return NULL
;
325 if ( stream
== stdin
) {
327 char * my_fgets_line
;
328 rl_already_prompted
= 1 ;
329 rl_attempted_completion_function
= my_fgets_completion
;
330 rl_catch_signals
= 1 ;
331 rl_catch_sigwinch
= 1 ;
333 prompt
= getenv ( "FGETS_PROMPT" ) ;
335 my_fgets_line
= 0 ; ! my_fgets_line
; my_fgets_line
=readline(prompt
)
337 if ( ! strncmp(my_fgets_line
, "_fgets_reset_", 13) ) {
339 free ( my_fgets_line
) ;
343 if ( ! strncmp(my_fgets_line
, "_fgets_dump_", 12) ) {
345 free ( my_fgets_line
) ;
349 if ( ! strncmp(my_fgets_line
, "_fgets_debug_", 13) ) {
350 my_fgets_debug_toggle () ;
351 free ( my_fgets_line
) ;
355 (void) strncpy ( s
, my_fgets_line
, n
-1 ) ;
356 (void) strcat ( s
, "\n" ) ;
357 if ( *my_fgets_line
) add_history ( my_fgets_line
) ;
358 free ( my_fgets_line
) ;
361 static fgets_t _fgets
;
362 _fgets
= (fgets_t
) dlsym ( REAL_LIBC
, "fgets" ) ;
365 "libfgets: failed to dynamically link to native fgets()\n"
371 _fgets ( s
, n
, stream
)