]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/libreadline/examples/rl-fgets.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / libreadline / examples / rl-fgets.c
1 /*
2 Date: Tue, 16 Mar 2004 19:38:40 -0800
3 From: Harold Levy <Harold.Levy@synopsys.com>
4 Subject: fgets(stdin) --> readline() redirector
5 To: chet@po.cwru.edu
6
7 Hi Chet,
8
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.
14
15 Best Regards,
16
17 -Harold
18 */
19
20 /******************************************************************************
21 *******************************************************************************
22   
23   FILE NAME:    fgets.c                  TARGET:   libfgets.so
24   AUTHOR:       Harold Levy              VERSION:  1.0
25                 hlevy@synopsys.com
26   
27   ABSTRACT:  Customize fgets() behavior via LD_PRELOAD in the following ways:
28   
29     -- If fgets(stdin) is called, redirect to GNU readline() to obtain
30        command-line editing, file-name completion, history, etc.
31   
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.
35   
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.
38   
39     -- There special strings that libfgets will interpret as internal commands:
40   
41            _fgets_reset_    reset the command list
42   
43            _fgets_dump_     dump status
44   
45            _fgets_debug_    toggle debug messages
46
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.
50
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
56
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
60   
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
70   64-bit SunOS).
71   
72   EXAMPLE WRAPPER:  Here is an example shell script wrapper around the
73   program "foo" that uses fgets() for command-line input:
74
75       #!/bin/csh
76       #### replace this with the libtermcap.so directory:
77       set dir1 = "/usr/lib"
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}"
87       else
88         setenv LD_PRELOAD "${lib1}:${lib2}:${lib3}"
89       endif
90       if ( "${?LD_LIBRARY_PATH}" ) then
91         setenv LD_LIBRARY_PATH "${dir1}:${dir2}:${dir3}:${LD_LIBRARY_PATH}"
92       else
93         setenv LD_LIBRARY_PATH "${dir1}:${dir2}:${dir3}"
94       endif
95       setenv FGETS_COMMAND_FILE "${dir2}/foo.commands"
96       setenv FGETS_PROMPT       "foo> "
97       exec "foo" $*
98   
99   Copyright (C)©2003-2004 Harold Levy.
100   
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.
104   
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.
109   
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
113   details.
114   
115 *******************************************************************************
116 ******************************************************************************/
117
118
119 \f
120 #include <dlfcn.h>
121 #include <stdio.h>
122 #include <strings.h>
123 #include <stdlib.h>
124 #include <unistd.h>
125
126 #include <readline/readline.h>
127 #include <readline/history.h>
128
129
130 \f
131 /* for dynamically connecting to the native fgets() */
132 #if defined(RTLD_NEXT)
133 #define REAL_LIBC RTLD_NEXT
134 #else
135 #define REAL_LIBC ((void *) -1L)
136 #endif
137 typedef char * ( * fgets_t ) ( char * s, int n, FILE * stream ) ;
138
139
140 \f
141 /* private data */
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
147       text segment
148 */
149 static char ** my_fgets_names           = NULL ;
150 static int     my_fgets_number_of_names = 0    ;
151 static int     my_fgets_debug_flag      = 0    ;
152
153
154 \f
155 /* invoked with _fgets_reset_ */
156 static void
157 my_fgets_reset (
158   void
159 ) {
160   if ( my_fgets_names && (my_fgets_number_of_names > 0) ) {
161     int i ;
162     if ( my_fgets_debug_flag ) {
163       printf ( "libfgets:  removing command list\n" ) ;
164     }
165     for ( i = 0 ; i < my_fgets_number_of_names ; i ++ ) {
166       if ( my_fgets_names[i] ) free ( my_fgets_names[i] ) ;
167     }
168     free ( my_fgets_names ) ;
169   }
170   my_fgets_names = NULL ;
171   my_fgets_number_of_names = 0 ;
172 }
173
174
175 \f
176 /* invoked with _fgets_dump_ */
177 static void
178 my_fgets_dump (
179   void
180 ) {
181   char * s ;
182   printf ( "\n" ) ;
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) ) {
191       int i ;
192       for ( i = 0 ; i < my_fgets_number_of_names ; i ++ ) {
193         printf ( "%s\n", my_fgets_names[i] ) ;
194       }
195     }
196   }
197   printf ( "\n" ) ;
198 }
199
200
201 \f
202 /* invoked with _fgets_debug_ */
203 static void
204 my_fgets_debug_toggle (
205   void
206 ) {
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 ) ;
210   }
211 }
212
213
214 \f
215 /* read the command list if needed, return the i-th name */
216 static char *
217 my_fgets_lookup (
218   int index
219 ) {
220   if ( (! my_fgets_names) || (! my_fgets_number_of_names) ) {
221     char * fname ;
222     FILE * fp ;
223     fgets_t _fgets ;
224     int i ;
225     char buf1[256], buf2[256] ;
226     fname = getenv ( "FGETS_COMMAND_FILE" ) ;
227     if ( ! fname ) {
228       if ( my_fgets_debug_flag ) {
229         printf ( "libfgets:  empty or unset FGETS_COMMAND_FILE\n" ) ;
230       }
231       return NULL ;
232     }
233     fp = fopen ( fname, "r" ) ;
234     if ( ! fp ) {
235       if ( my_fgets_debug_flag ) {
236         printf ( "libfgets:  cannot open '%s' for reading\n", fname ) ;
237       }
238       return NULL ;
239     }
240     _fgets = (fgets_t) dlsym ( REAL_LIBC, "fgets" ) ;
241     if ( ! _fgets ) {
242       fprintf ( stderr,
243         "libfgets:  failed to dynamically link to native fgets()\n"
244       ) ;
245       return NULL ;
246     }
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*) ) ;
250     rewind ( fp ) ;
251     i = 0 ;
252     while ( _fgets(buf1,255,fp) ) {
253       buf1[255] = 0 ;
254       if ( 1 == sscanf(buf1,"%s",buf2) ) {
255         my_fgets_names[i] = strdup(buf2) ;
256         i ++ ;
257       }
258     }
259     fclose ( fp ) ;
260     my_fgets_number_of_names = i ;
261     if ( my_fgets_debug_flag ) {
262       printf ( "libfgets:  successfully read %d commands\n", i ) ;
263     }
264   }
265   if ( index < my_fgets_number_of_names ) {
266     return my_fgets_names[index] ;
267   } else {
268     return NULL ;
269   }
270 }
271
272
273 \f
274 /* generate a list of partial name matches for readline() */
275 static char *
276 my_fgets_generator (
277   const char * text,
278   int          state
279 )
280 {
281   static int list_index, len ;
282   char *     name ;
283   if ( ! state ) {
284     list_index = 0 ;
285     len = strlen ( text ) ;
286   }
287   while ( ( name = my_fgets_lookup(list_index) ) ) {
288     list_index ++ ;
289     if ( ! strncmp ( name, text, len ) ) {
290       return ( strdup ( name ) ) ;
291     }
292   }
293   return ( NULL ) ;
294 }
295
296
297 \f
298 /* partial name completion callback for readline() */
299 static char **
300 my_fgets_completion (
301   const char * text,
302   int          start,
303   int          end
304 )
305 {
306   char ** matches ;
307   matches = NULL ;
308   if ( ! start ) {
309     matches = rl_completion_matches ( text, my_fgets_generator ) ;
310   }
311   return ( matches ) ;
312 }
313
314
315 \f
316 /* fgets() intercept */
317 char *
318 fgets (
319   char * s,
320   int    n,
321   FILE * stream
322 )
323 {
324   if ( ! s ) return NULL ;
325   if ( stream == stdin ) {
326     char * prompt ;
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 ;
332     rl_set_signals () ;
333     prompt = getenv ( "FGETS_PROMPT" ) ;
334     for (
335       my_fgets_line = 0 ; ! my_fgets_line ; my_fgets_line=readline(prompt)
336     ) ;
337     if ( ! strncmp(my_fgets_line, "_fgets_reset_", 13) ) {
338       my_fgets_reset () ;
339       free ( my_fgets_line ) ;
340       strcpy ( s, "\n" ) ;
341       return ( s ) ;
342     }
343     if ( ! strncmp(my_fgets_line, "_fgets_dump_", 12) ) {
344       my_fgets_dump () ;
345       free ( my_fgets_line ) ;
346       strcpy ( s, "\n" ) ;
347       return ( s ) ;
348     }
349     if ( ! strncmp(my_fgets_line, "_fgets_debug_", 13) ) {
350       my_fgets_debug_toggle () ;
351       free ( my_fgets_line ) ;
352       strcpy ( s, "\n" ) ;
353       return ( s ) ;
354     }
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 ) ;
359     return ( s ) ;
360   } else {
361     static fgets_t _fgets ;
362     _fgets = (fgets_t) dlsym ( REAL_LIBC, "fgets" ) ;
363     if ( ! _fgets ) {
364       fprintf ( stderr,
365         "libfgets:  failed to dynamically link to native fgets()\n"
366       ) ;
367       strcpy ( s, "\n" ) ;
368       return ( s ) ;
369     }
370     return (
371       _fgets ( s, n, stream )
372     ) ;
373   }
374 }