OSDN Git Service

Initial revision
[pf3gnuchains/pf3gnuchains3x.git] / readline / examples / rl-fgets.c
diff --git a/readline/examples/rl-fgets.c b/readline/examples/rl-fgets.c
new file mode 100644 (file)
index 0000000..5512b94
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+Date: Tue, 16 Mar 2004 19:38:40 -0800
+From: Harold Levy <Harold.Levy@synopsys.com>
+Subject: fgets(stdin) --> readline() redirector
+To: chet@po.cwru.edu
+
+Hi Chet,
+
+Here is something you may find useful enough to include in the readline
+distribution.  It is a shared library that redirects calls to fgets(stdin)
+to readline() via LD_PRELOAD, and it supports a custom prompt and list of
+command names.  Many people have asked me for this file, so I thought I'd
+pass it your way in hope of just including it with readline to begin with.
+
+Best Regards,
+
+-Harold
+*/
+
+/******************************************************************************
+*******************************************************************************
+  
+  FILE NAME:    fgets.c                  TARGET:   libfgets.so
+  AUTHOR:       Harold Levy              VERSION:  1.0
+                hlevy@synopsys.com
+  
+  ABSTRACT:  Customize fgets() behavior via LD_PRELOAD in the following ways:
+  
+    -- If fgets(stdin) is called, redirect to GNU readline() to obtain
+       command-line editing, file-name completion, history, etc.
+  
+    -- A list of commands for command-name completion can be configured by
+       setting the environment-variable FGETS_COMMAND_FILE to a file containing
+       the list of commands to be used.
+  
+    -- Command-line editing with readline() works best when the prompt string
+       is known; you can set this with the FGETS_PROMPT environment variable.
+  
+    -- There special strings that libfgets will interpret as internal commands:
+  
+           _fgets_reset_    reset the command list
+  
+           _fgets_dump_     dump status
+  
+           _fgets_debug_    toggle debug messages
+
+  HOW TO BUILD:  Here are examples of how to build libfgets.so on various
+  platforms; you will have to add -I and -L flags to configure access to
+  the readline header and library files.
+
+  (32-bit builds with gcc)
+  AIX:   gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldl -lreadline -ltermcap
+  HP-UX: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldld -lreadline
+  Linux: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldl -lreadline
+  SunOS: gcc -fPIC fgets.c -shared -o libfgets.so -lc -ldl -lgen -lreadline
+
+  (64-bit builds without gcc)
+  SunOS: SUNWspro/bin/cc -D_LARGEFILE64_SOURCE=1 -xtarget=ultra -xarch=v9 \
+           -KPIC fgets.c -Bdynamic -lc -ldl -lgen -ltermcap -lreadline
+  
+  HOW TO USE:  Different operating systems have different levels of support
+  for the LD_PRELOAD concept.  The generic method for 32-bit platforms is to
+  put libtermcap.so, libfgets.so, and libreadline.so (with absolute paths)
+  in the LD_PRELOAD environment variable, and to put their parent directories
+  in the LD_LIBRARY_PATH environment variable.  Unfortunately there is no
+  generic method for 64-bit platforms; e.g. for 64-bit SunOS, you would have
+  to build both 32-bit and 64-bit libfgets and libreadline libraries, and
+  use the LD_FLAGS_32 and LD_FLAGS_64 environment variables with preload and
+  library_path configurations (a mix of 32-bit and 64-bit calls are made under
+  64-bit SunOS).
+  
+  EXAMPLE WRAPPER:  Here is an example shell script wrapper around the
+  program "foo" that uses fgets() for command-line input:
+
+      #!/bin/csh
+      #### replace this with the libtermcap.so directory:
+      set dir1 = "/usr/lib"
+      #### replace this with the libfgets.so directory:
+      set dir2 = "/usr/fgets"
+      #### replace this with the libreadline.so directory:
+      set dir3 = "/usr/local/lib"
+      set lib1 = "${dir1}/libtermcap.so"
+      set lib2 = "${dir2}/libfgets.so"
+      set lib3 = "${dir3}/libreadline.so"
+      if ( "${?LD_PRELOAD}" ) then
+        setenv LD_PRELOAD "${lib1}:${lib2}:${lib3}:${LD_PRELOAD}"
+      else
+        setenv LD_PRELOAD "${lib1}:${lib2}:${lib3}"
+      endif
+      if ( "${?LD_LIBRARY_PATH}" ) then
+        setenv LD_LIBRARY_PATH "${dir1}:${dir2}:${dir3}:${LD_LIBRARY_PATH}"
+      else
+        setenv LD_LIBRARY_PATH "${dir1}:${dir2}:${dir3}"
+      endif
+      setenv FGETS_COMMAND_FILE "${dir2}/foo.commands"
+      setenv FGETS_PROMPT       "foo> "
+      exec "foo" $*
+  
+  Copyright (C)©2003-2004 Harold Levy.
+  
+  This code links to the GNU readline library, and as such is bound by the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation, either version 2 or (at your option) any later version.
+  
+  The GNU General Public License is often shipped with GNU software, and is
+  generally kept in a file called COPYING or LICENSE.  If you do not have a
+  copy of the license, write to the Free Software Foundation, 59 Temple Place,
+  Suite 330, Boston, MA 02111 USA.
+  
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+  details.
+  
+*******************************************************************************
+******************************************************************************/
+
+
+\f
+#include <dlfcn.h>
+#include <stdio.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+
+\f
+/* for dynamically connecting to the native fgets() */
+#if defined(RTLD_NEXT)
+#define REAL_LIBC RTLD_NEXT
+#else
+#define REAL_LIBC ((void *) -1L)
+#endif
+typedef char * ( * fgets_t ) ( char * s, int n, FILE * stream ) ;
+
+
+\f
+/* private data */
+/* -- writeable data is stored in the shared library's data segment
+   -- every process that uses the shared library gets a private memory copy of
+      its entire data segment
+   -- static data in the shared library is not copied to the application
+   -- only read-only (i.e. 'const') data is stored in the shared library's
+      text segment
+*/
+static char ** my_fgets_names           = NULL ;
+static int     my_fgets_number_of_names = 0    ;
+static int     my_fgets_debug_flag      = 0    ;
+
+
+\f
+/* invoked with _fgets_reset_ */
+static void
+my_fgets_reset (
+  void
+) {
+  if ( my_fgets_names && (my_fgets_number_of_names > 0) ) {
+    int i ;
+    if ( my_fgets_debug_flag ) {
+      printf ( "libfgets:  removing command list\n" ) ;
+    }
+    for ( i = 0 ; i < my_fgets_number_of_names ; i ++ ) {
+      if ( my_fgets_names[i] ) free ( my_fgets_names[i] ) ;
+    }
+    free ( my_fgets_names ) ;
+  }
+  my_fgets_names = NULL ;
+  my_fgets_number_of_names = 0 ;
+}
+
+
+\f
+/* invoked with _fgets_dump_ */
+static void
+my_fgets_dump (
+  void
+) {
+  char * s ;
+  printf ( "\n" ) ;
+  s = getenv ( "FGETS_PROMPT" ) ;
+  printf ( "FGETS_PROMPT       = %s\n", s ? s : "" ) ;
+  s = getenv ( "FGETS_COMMAND_FILE" ) ;
+  printf ( "FGETS_COMMAND_FILE = %s\n", s ? s : "" ) ;
+  printf ( "debug flag         = %d\n", my_fgets_debug_flag ) ;
+  printf ( "#commands          = %d\n", my_fgets_number_of_names ) ;
+  if ( my_fgets_debug_flag ) {
+    if ( my_fgets_names && (my_fgets_number_of_names > 0) ) {
+      int i ;
+      for ( i = 0 ; i < my_fgets_number_of_names ; i ++ ) {
+        printf ( "%s\n", my_fgets_names[i] ) ;
+      }
+    }
+  }
+  printf ( "\n" ) ;
+}
+
+
+\f
+/* invoked with _fgets_debug_ */
+static void
+my_fgets_debug_toggle (
+  void
+) {
+  my_fgets_debug_flag = my_fgets_debug_flag ? 0 : 1 ;
+  if ( my_fgets_debug_flag ) {
+    printf ( "libfgets:  debug flag = %d\n", my_fgets_debug_flag ) ;
+  }
+}
+
+
+\f
+/* read the command list if needed, return the i-th name */
+static char *
+my_fgets_lookup (
+  int index
+) {
+  if ( (! my_fgets_names) || (! my_fgets_number_of_names) ) {
+    char * fname ;
+    FILE * fp ;
+    fgets_t _fgets ;
+    int i ;
+    char buf1[256], buf2[256] ;
+    fname = getenv ( "FGETS_COMMAND_FILE" ) ;
+    if ( ! fname ) {
+      if ( my_fgets_debug_flag ) {
+        printf ( "libfgets:  empty or unset FGETS_COMMAND_FILE\n" ) ;
+      }
+      return NULL ;
+    }
+    fp = fopen ( fname, "r" ) ;
+    if ( ! fp ) {
+      if ( my_fgets_debug_flag ) {
+        printf ( "libfgets:  cannot open '%s' for reading\n", fname ) ;
+      }
+      return NULL ;
+    }
+    _fgets = (fgets_t) dlsym ( REAL_LIBC, "fgets" ) ;
+    if ( ! _fgets ) {
+      fprintf ( stderr,
+        "libfgets:  failed to dynamically link to native fgets()\n"
+      ) ;
+      return NULL ;
+    }
+    for ( i = 0 ; _fgets(buf1,255,fp) ; i ++ ) ;
+    if ( ! i ) { fclose(fp) ; return NULL ; }
+    my_fgets_names = (char**) calloc ( i, sizeof(char*) ) ;
+    rewind ( fp ) ;
+    i = 0 ;
+    while ( _fgets(buf1,255,fp) ) {
+      buf1[255] = 0 ;
+      if ( 1 == sscanf(buf1,"%s",buf2) ) {
+        my_fgets_names[i] = strdup(buf2) ;
+        i ++ ;
+      }
+    }
+    fclose ( fp ) ;
+    my_fgets_number_of_names = i ;
+    if ( my_fgets_debug_flag ) {
+      printf ( "libfgets:  successfully read %d commands\n", i ) ;
+    }
+  }
+  if ( index < my_fgets_number_of_names ) {
+    return my_fgets_names[index] ;
+  } else {
+    return NULL ;
+  }
+}
+
+
+\f
+/* generate a list of partial name matches for readline() */
+static char *
+my_fgets_generator (
+  const char * text,
+  int          state
+)
+{
+  static int list_index, len ;
+  char *     name ;
+  if ( ! state ) {
+    list_index = 0 ;
+    len = strlen ( text ) ;
+  }
+  while ( ( name = my_fgets_lookup(list_index) ) ) {
+    list_index ++ ;
+    if ( ! strncmp ( name, text, len ) ) {
+      return ( strdup ( name ) ) ;
+    }
+  }
+  return ( NULL ) ;
+}
+
+
+\f
+/* partial name completion callback for readline() */
+static char **
+my_fgets_completion (
+  const char * text,
+  int          start,
+  int          end
+)
+{
+  char ** matches ;
+  matches = NULL ;
+  if ( ! start ) {
+    matches = rl_completion_matches ( text, my_fgets_generator ) ;
+  }
+  return ( matches ) ;
+}
+
+
+\f
+/* fgets() intercept */
+char *
+fgets (
+  char * s,
+  int    n,
+  FILE * stream
+)
+{
+  if ( ! s ) return NULL ;
+  if ( stream == stdin ) {
+    char * prompt ;
+    char * my_fgets_line ;
+    rl_already_prompted = 1 ;
+    rl_attempted_completion_function = my_fgets_completion ;
+    rl_catch_signals = 1 ;
+    rl_catch_sigwinch = 1 ;
+    rl_set_signals () ;
+    prompt = getenv ( "FGETS_PROMPT" ) ;
+    for (
+      my_fgets_line = 0 ; ! my_fgets_line ; my_fgets_line=readline(prompt)
+    ) ;
+    if ( ! strncmp(my_fgets_line, "_fgets_reset_", 13) ) {
+      my_fgets_reset () ;
+      free ( my_fgets_line ) ;
+      strcpy ( s, "\n" ) ;
+      return ( s ) ;
+    }
+    if ( ! strncmp(my_fgets_line, "_fgets_dump_", 12) ) {
+      my_fgets_dump () ;
+      free ( my_fgets_line ) ;
+      strcpy ( s, "\n" ) ;
+      return ( s ) ;
+    }
+    if ( ! strncmp(my_fgets_line, "_fgets_debug_", 13) ) {
+      my_fgets_debug_toggle () ;
+      free ( my_fgets_line ) ;
+      strcpy ( s, "\n" ) ;
+      return ( s ) ;
+    }
+    (void) strncpy ( s, my_fgets_line, n-1 ) ;
+    (void) strcat ( s, "\n" ) ;
+    if ( *my_fgets_line ) add_history ( my_fgets_line ) ;
+    free ( my_fgets_line ) ;
+    return ( s ) ;
+  } else {
+    static fgets_t _fgets ;
+    _fgets = (fgets_t) dlsym ( REAL_LIBC, "fgets" ) ;
+    if ( ! _fgets ) {
+      fprintf ( stderr,
+        "libfgets:  failed to dynamically link to native fgets()\n"
+      ) ;
+      strcpy ( s, "\n" ) ;
+      return ( s ) ;
+    }
+    return (
+      _fgets ( s, n, stream )
+    ) ;
+  }
+}