From ab8545cec141ba87b5bb1ca0ccfdd5d0aaa4d8d9 Mon Sep 17 00:00:00 2001 From: Fergus Henderson Date: Tue, 30 Mar 1999 05:45:54 +0000 Subject: [PATCH] Add GNU readline support to the debugger. Estimated hours taken: 16 Add GNU readline support to the debugger. Because GNU readline is licensed under the GPL, not the LGPL, I was careful to ensure that (1) the readline library is only linked into your application if it is compiled with debugging enabled and (2) even in the latter case, the readline support is disable-able (if you use the source distribution). Mmakefile: bindist/Mmakefile: Add dependency of configure on new file aclocal.m4. aclocal.m4: Define a macro MERCURY_CHECK_READLINE to check for the readline headers, the readline library, and for libraries needed by readline, in particular -l{termcap,curses,ncurses}. If they're not found, define MR_NO_USE_READLINE. Otherwise, define various variables for using & installing readline. bindist/bindist.configure.in: configure.in: Add call to MERCURY_CHECK_READLINE to check for readline. scripts/ml.in: If tracing is enabled, then (by default) link in readline, and hence also termcap/curses/ncurses. Add a new option `--no-readline' to disable this. trace/mercury_trace_internal.c: Delete the FILE * parameter from the MR_getline() function, because there are actually two files involved (MR_mdb_in and MR_mdb_out), and they are always the same for all calls to this function. Also change this function so that it calls MR_trace_readline() rather than printing the prompt and then calling MR_trace_getline_raw(). Delete the MR_trace_getline_raw() function -- this has been renamed as MR_trace_readline_raw() and moved to mercury_trace_readline.c. trace/mercury_trace_readline.h: trace/mercury_trace_readline.c: New module containing a new function MR_trace_readline() that prints a prompt and reads a line in by calling the readline library. I put all this new code inside `#ifndef MR_NO_USE_READLINE' so that the use of readline can be disabled. If that symbol is defined, it reverts to the old implementation using MR_trace_readline_raw() (formerly called MR_trace_getline_raw()). runtime/mercury_conf.h.in: Define the new configuration parameter `MR_NO_USE_READLINE'. trace/Mmakefile: Add the new files mercury_trace_readline.[ch]. Ensure that this new module is compiled with warnings disabled, to avoid spurious warnings resulting from the readline header files. Also simplify the code by deleting unnecessary uses of $(EXTRA_*) -- that's handled by scripts/Mmake.vars now. NEWS: Mention the readline support. .INSTALL.in: Mention that it is helpful to have GNU Readline installed. bindist/bindist.INSTALL: Mention that you may need to have GNU Readline installed. --- Mmakefile | 2 +- aclocal.m4 | 63 ++++++++++++++++++++ configure.in | 8 +-- scripts/ml.in | 22 ++++++- trace/Mmakefile | 13 ++++- trace/mercury_trace_internal.c | 67 +++++---------------- trace/mercury_trace_readline.c | 104 +++++++++++++++++++++++++++++++++ 7 files changed, 218 insertions(+), 61 deletions(-) create mode 100644 aclocal.m4 create mode 100644 trace/mercury_trace_readline.c diff --git a/Mmakefile b/Mmakefile index 1598e5bb3..d288f1600 100644 --- a/Mmakefile +++ b/Mmakefile @@ -174,7 +174,7 @@ tags_profiler: #-----------------------------------------------------------------------------# -configure: configure.in +configure: configure.in aclocal.m4 autoconf config.status: configure VERSION diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 000000000..1103943d5 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,63 @@ +#-----------------------------------------------------------------------------# +# Copyright (C) 1999 The University of Melbourne. +# This file may only be copied under the terms of the GNU General +# Public Licence - see the file COPYING in the Mercury distribution. +#-----------------------------------------------------------------------------# +# +# aclocal.m4 +# +# This file contains Mercury-specific autoconf tests. +# +# We ought to move most of the code in configure.in and +# bindist/bindist.configure.in into this file... +# +#-----------------------------------------------------------------------------# +# +# Check for readline and related header files and libraries +# +AC_DEFUN(MERCURY_CHECK_READLINE, +[ + +# check for the readline header files +AC_CHECK_HEADER(readline/readline.h, HAVE_READLINE_READLINE_H=1) +if test "$HAVE_READLINE_READLINE_H" = 1; then + AC_DEFINE(HAVE_READLINE_READLINE) +fi +AC_CHECK_HEADER(readline/history.h, HAVE_READLINE_HISTORY_H=1) +if test "$HAVE_READLINE_HISTORY_H" = 1; then + AC_DEFINE(HAVE_READLINE_HISTORY) +fi + +# check for the readline library +AC_CHECK_LIB(readline, readline, mercury_cv_have_readline=yes, + mercury_cv_have_readline=no) + +# check for the libraries that readline depends on +if test $mercury_cv_have_readline = yes; then + MERCURY_MSG('looking for termcap or curses (needed by readline)...') + AC_CHECK_LIB(termcap, tgetent, mercury_cv_termcap_lib=-ltermcap, + [AC_CHECK_LIB(curses, tgetent, mercury_cv_termcap_lib=-lcurses, + [AC_CHECK_LIB(ncurses, tgetent, mercury_cv_termcap_lib=-lncurses, + mercury_cv_termcap_lib=none)])]) +fi + +# Now figure out whether we can use readline, and define variables according. +# Note that on most systems, we don't actually need the header files in +# order to use readline. (Ain't C grand? ;-). + +if test $mercury_cv_have_readline = no || + test $mercury_cv_termcap_lib = none +then + TERMCAP_LIBRARY="" + READLINE_LIBRARIES="" + AC_DEFINE(MR_NO_USE_READLINE) +else + TERMCAP_LIBRARY="$mercury_cv_termcap_lib" + READLINE_LIBRARIES="-lreadline $TERMCAP_LIBRARY" +fi +AC_SUBST(TERMCAP_LIBRARY) +AC_SUBST(READLINE_LIBRARIES) + +]) + +#-----------------------------------------------------------------------------# diff --git a/configure.in b/configure.in index 6ca63e87d..d54a701d5 100644 --- a/configure.in +++ b/configure.in @@ -245,7 +245,7 @@ AC_CHECK_HEADER(dlfcn.h, HAVE_DLFCN_H=1) if test "$HAVE_DLFCN_H" = 1; then AC_DEFINE(HAVE_DLFCN_H) fi -AC_CHECK_LIB(dl,dlopen,DL_LIBRARY="-ldl",DL_LIBRARY="") +AC_CHECK_LIB(dl, dlopen, DL_LIBRARY="-ldl", DL_LIBRARY="") AC_SUBST(DL_LIBRARY) # temporarily add $DL_LIBRARY to LIBS while we check for dlopen etc. @@ -1814,13 +1814,12 @@ AC_SUBST_FILE(INIT_GRADE_OPTIONS) AC_SUBST_FILE(PARSE_GRADE_OPTIONS) AC_SUBST_FILE(FINAL_GRADE_OPTIONS) - #-----------------------------------------------------------------------------# # # check whether we need -lsocket # -AC_CHECK_LIB(socket,socket,SOCKET_LIBRARY=-lsocket,SOCKET_LIBRARY="") +AC_CHECK_LIB(socket, socket, SOCKET_LIBRARY=-lsocket, SOCKET_LIBRARY="") AC_SUBST(SOCKET_LIBRARY) # @@ -2000,7 +1999,8 @@ fi # restore the previous value of LIBS # LIBS="$save_LIBS" - +#-----------------------------------------------------------------------------# +MERCURY_CHECK_READLINE #-----------------------------------------------------------------------------# AC_OUTPUT(Mmake.common scripts/Mmake.vars scripts/mmc scripts/mprof scripts/mercury_update_interface scripts/mgnuc scripts/ml diff --git a/scripts/ml.in b/scripts/ml.in index 27229d320..a86079c46 100644 --- a/scripts/ml.in +++ b/scripts/ml.in @@ -65,6 +65,8 @@ Debugging options: then you don't need to explicitly specify \`--trace'. Note that \`--trace' is incompatible with \`--static' on some platforms (e.g. sparc-sun-solaris2.6). + -r-, --no-readline + Don't link in the GPL'd GNU Readline Library. -g, --c-debug, --no-strip Do not strip C debugging information. @@ -111,6 +113,9 @@ SOCKET_LIBRARY="@SOCKET_LIBRARY@" # Likewise for -ldl (libdl.so), which is often needed for dlopen() etc. DL_LIBRARY="@DL_LIBRARY@" +# Likewise for -lreadline -l{termcap,curses,ncurses} +READLINE_LIBRARIES="@READLINE_LIBRARIES@" + # If you change these, you will also need to change Mmake.common.in, # scripts/c2init.in, tools/bootcheck, tools/binary, tools/binary_step # and tools/linear. @@ -121,6 +126,7 @@ BROWSER_LIB_NAME=mer_browser verbose=false trace=false +readline=true case $FULLARCH in *-win95|*-winnt|*-win32|*-cygwin32|*-cygwin) # `gcc -s' is broken in gnu-win32 @@ -161,6 +167,12 @@ while : ; do -t-|--no-trace) trace=false ;; + -r|--readline) + readline=true + ;; + -r-|--no-readline) + readline=false + ;; -g-|--no-c-debug|--strip) strip=true ;; @@ -365,13 +377,19 @@ case "$GRADE" in ;; esac +case $readline in + true) ;; + false) READLINE_LIBRARIES= + ;; +esac + case $trace in true) TRACE_LIBS="-l$TRACE_LIB_NAME -l$BROWSER_LIB_NAME \ - $SOCKET_LIBRARY $DL_LIBRARY" + $SOCKET_LIBRARY $DL_LIBRARY $READLINE_LIBRARIES" TRACE_STATIC_LIBS="\ $LIBDIR/$GRADE/$FULLARCH/lib$TRACE_LIB_NAME.a \ $LIBDIR/$GRADE/$FULLARCH/lib$BROWSER_LIB_NAME.a \ - $SOCKET_LIBRARY $DL_LIBRARY" + $SOCKET_LIBRARY $DL_LIBRARY $READLINE_LIBRARIES" ;; false) TRACE_LIBS= TRACE_STATIC_LIBS= diff --git a/trace/Mmakefile b/trace/Mmakefile index 1eaa114ae..fa36e5eb2 100644 --- a/trace/Mmakefile +++ b/trace/Mmakefile @@ -16,13 +16,20 @@ include $(MERCURY_DIR)/Mmake.common CFLAGS = -I$(MERCURY_DIR)/browser -I$(MERCURY_DIR)/library \ -I$(MERCURY_DIR)/runtime -I$(MERCURY_DIR)/boehm_gc \ - -g $(DLL_CFLAGS) $(EXTRA_CFLAGS) + -g $(DLL_CFLAGS) MGNUC = MERCURY_C_INCL_DIR=. $(SCRIPTS_DIR)/mgnuc -MGNUCFLAGS = --no-ansi $(EXTRA_MGNUCFLAGS) $(CFLAGS) +MGNUCFLAGS = --no-ansi MOD2C = $(SCRIPTS_DIR)/mod2c #-----------------------------------------------------------------------------# +# mercury_readline.c #includes the GNU readline headers, which +# lack prototypes and `const', so we need to disable warnings +# when compiling that file. +MGNUCFLAGS-mercury_trace_readline = --no-check + +#-----------------------------------------------------------------------------# + # keep this list in alphabetical order, please HDRS = \ mercury_trace.h \ @@ -32,6 +39,7 @@ HDRS = \ mercury_trace_external.h \ mercury_trace_help.h \ mercury_trace_internal.h \ + mercury_trace_readline.h \ mercury_trace_spy.h \ mercury_trace_tables.h \ mercury_trace_util.h @@ -45,6 +53,7 @@ CFILES = \ mercury_trace_external.c \ mercury_trace_help.c \ mercury_trace_internal.c \ + mercury_trace_readline.c \ mercury_trace_spy.c \ mercury_trace_tables.c diff --git a/trace/mercury_trace_internal.c b/trace/mercury_trace_internal.c index 1a5c64896..c987dccdb 100644 --- a/trace/mercury_trace_internal.c +++ b/trace/mercury_trace_internal.c @@ -20,10 +20,13 @@ #include "mercury_trace_spy.h" #include "mercury_trace_tables.h" #include "mercury_trace_util.h" +#include "mercury_trace_readline.h" #include "mercury_layout_util.h" #include "mercury_array_macros.h" #include "mercury_getopt.h" + #include "browse.h" + #include #include #include @@ -36,9 +39,6 @@ /* The initial size of arrays of words. */ #define MR_INIT_WORD_COUNT 20 -/* The initial size of arrays of characters. */ -#define MR_INIT_BUF_LEN 80 - /* The initial number of lines in documentation entries. */ #define MR_INIT_DOC_CHARS 800 @@ -202,9 +202,8 @@ static void MR_trace_expand_aliases(char ***words, int *word_max, int *word_count); static bool MR_trace_source(const char *filename); static void MR_trace_source_from_open_file(FILE *fp); -static char *MR_trace_getline(const char *prompt, FILE *fp); +static char *MR_trace_getline(const char *prompt); static char *MR_trace_getline_queue(void); -static char *MR_trace_getline_raw(FILE *fp); static void MR_insert_line_at_head(const char *line); static void MR_insert_line_at_tail(const char *line); @@ -262,7 +261,7 @@ MR_trace_event_internal(MR_Trace_Cmd_Info *cmd, bool interactive, jumpaddr = NULL; do { - line = MR_trace_getline("mdb> ", MR_mdb_in); + line = MR_trace_getline("mdb> "); res = MR_trace_debug_cmd(line, cmd, event_info, &event_details, &ancestor_level, &jumpaddr); } while (res == KEEP_INTERACTING); @@ -1303,9 +1302,8 @@ MR_trace_handle_cmd(char **words, int word_count, MR_Trace_Cmd_Info *cmd, if (! confirmed) { char *line2; - line2 = MR_trace_getline( - "mdb: are you sure you want to quit? ", - MR_mdb_in); + line2 = MR_trace_getline("mdb: " + "are you sure you want to quit? "); if (line2 == NULL) { /* This means the user input EOF. */ confirmed = TRUE; @@ -1930,7 +1928,7 @@ MR_trace_read_help_text(void) int i; next_char_slot = 0; - while ((text = MR_trace_getline("cat> ", MR_mdb_in)) != NULL) { + while ((text = MR_trace_getline("cat> ")) != NULL) { if (streq(text, "end")) { free(text); break; @@ -2181,7 +2179,7 @@ MR_trace_source_from_open_file(FILE *fp) { char *line; - while ((line = MR_trace_getline_raw(fp)) != NULL) { + while ((line = MR_trace_readline_raw(fp)) != NULL) { MR_insert_line_at_tail(line); } @@ -2190,14 +2188,15 @@ MR_trace_source_from_open_file(FILE *fp) /* ** If there any lines waiting in the queue, return the first of these. -** If not, print the prompt, read a line from the given file, and return it -** in a malloc'd buffer holding the line (without the final newline). +** If not, print the prompt to MR_mdb_out, read a line from MR_mdb_in, +** and return it in a malloc'd buffer holding the line (without the final +** newline). ** If EOF occurs on a nonempty line, treat the EOF as a newline; if EOF ** occurs on an empty line, return NULL. */ static char * -MR_trace_getline(const char *prompt, FILE *fp) +MR_trace_getline(const char *prompt) { char *line; @@ -2207,10 +2206,8 @@ MR_trace_getline(const char *prompt, FILE *fp) } MR_trace_internal_interacting = TRUE; - fprintf(MR_mdb_out, "%s", prompt); - fflush(MR_mdb_out); - return MR_trace_getline_raw(fp); + return MR_trace_readline(prompt, MR_mdb_in, MR_mdb_out); } /* @@ -2238,40 +2235,6 @@ MR_trace_getline_queue(void) } } -/* -** Read a line from a file, and return a pointer to a malloc'd buffer -** holding the line (without the final newline). If EOF occurs on a -** nonempty line, treat the EOF as a newline; if EOF occurs on an empty -** line, return NULL. -*/ - -static char * -MR_trace_getline_raw(FILE *fp) -{ - char *contents; - int content_max; - int c; - int i; - - contents = NULL; - content_max = 0; - - i = 0; - while ((c = getc(fp)) != EOF && c != '\n') { - MR_ensure_big_enough(i, content, char, MR_INIT_BUF_LEN); - contents[i++] = c; - } - - if (c == '\n' || i > 0) { - MR_ensure_big_enough(i, content, char, MR_INIT_BUF_LEN); - contents[i] = '\0'; - return contents; - } else { - free(contents); - return NULL; - } -} - static void MR_insert_line_at_head(const char *contents) { @@ -2321,7 +2284,7 @@ MR_trace_event_internal_report(MR_Trace_Cmd_Info *cmd, /* We try to leave one line for the prompt itself. */ if (MR_scroll_control && MR_scroll_next >= MR_scroll_limit - 1) { try_again: - buf = MR_trace_getline("--more-- ", MR_mdb_in); + buf = MR_trace_getline("--more-- "); if (buf != NULL) { for (i = 0; buf[i] != '\0' && MR_isspace(buf[i]); i++) ; diff --git a/trace/mercury_trace_readline.c b/trace/mercury_trace_readline.c new file mode 100644 index 000000000..898a9362a --- /dev/null +++ b/trace/mercury_trace_readline.c @@ -0,0 +1,104 @@ +/* +** Copyright (C) 1998-1999 The University of Melbourne. +** This file may only be copied under the terms of the GNU Library General +** Public License - see the file COPYING.LIB in the Mercury distribution. +*/ + +/* +** A simple interface to read a line, normally done using GNU readline. +** +** This module is compiled with warnings disabled (mgnuc --no-check), +** since the GNU readline headers don't use prototypes, const, etc. +** +** Main authors: fjh, zs. +*/ + +#include "mercury_imp.h" +#include "mercury_trace_readline.h" +#include "mercury_array_macros.h" +#include "mercury_memory.h" +#include "mercury_std.h" + +#ifndef MR_NO_USE_READLINE + #ifdef HAVE_READLINE_READLINE + #include "readline/readline.h" + #endif + #ifdef HAVE_READLINE_HISTORY + #include "readline/history.h" + #endif +#endif + +#include +#include + +/* The initial size of the array of characters used to hold the line. */ +#define MR_INIT_BUF_LEN 80 + +/* +** Print the prompt to the `out' file, read a line from the `in' file, +** and return it in a malloc'd buffer holding the line (without the final +** newline). +** If EOF occurs on a nonempty line, treat the EOF as a newline; if EOF +** occurs on an empty line, return NULL. +*/ +char * +MR_trace_readline(const char *prompt, FILE *in, FILE *out) +{ + char *line; + +#ifdef MR_NO_USE_READLINE + + fprintf(out, "%s", prompt); + fflush(out); + line = MR_trace_readline_raw(in); + +#else /* use readline */ + + size_t len; + + rl_instream = in; + rl_outstream = out; + + line = readline((char *) prompt); + + if (line != NULL && line[0] != '\0') { + add_history(line); + } + +#endif + + return line; +} + +/* +** Read a line from a file, and return a pointer to a malloc'd buffer +** holding the line (without the final newline). If EOF occurs on a +** nonempty line, treat the EOF as a newline; if EOF occurs on an empty +** line, return NULL. +*/ +char * +MR_trace_readline_raw(FILE *fp) +{ + char *contents; + int content_max; + int c; + int i; + + contents = NULL; + content_max = 0; + + i = 0; + while ((c = getc(fp)) != EOF && c != '\n') { + MR_ensure_big_enough(i, content, char, MR_INIT_BUF_LEN); + contents[i++] = c; + } + + if (c == '\n' || i > 0) { + MR_ensure_big_enough(i, content, char, MR_INIT_BUF_LEN); + contents[i] = '\0'; + return contents; + } else { + free(contents); + return NULL; + } +}