Annotation of embedaddon/readline/examples/rl-fgets.c, revision 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>