File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / readline / examples / rl-fgets.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jul 30 08:16:46 2014 UTC (9 years, 11 months ago) by misho
Branches: readline, MAIN
CVS tags: v8_2p0, v8_1p0, v6_3p10_cross, v6_3p10, v6_3, p6, HEAD
readline 6.3

    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>