Annotation of embedaddon/readline/examples/rl-fgets.c, revision 1.1.1.1

1.1       misho       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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: 
                    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: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>