[GiNaC-list] GiNaC vs readline [Was: Problems]

Sheplyakov Alexei varg at theor.jinr.ru
Wed Jan 31 14:08:26 CET 2007


Hello,

On Tue, Jan 30, 2007 at 11:41:46AM -0500, David Fang wrote:
> 	Unfortunately, the problems don't stop there:
> 
> ----------->8 snip 8<-------------
> f g++ -DHAVE_CONFIG_H -I. -I../../ginsh -I.. -I../../ginsh/../ginac
> -I../ginac -DIN_GINAC  -I/usr/local/include -I/sw/include  -g -O2 -MT
> ginsh_parser.o -MD -MP -MF ".deps/ginsh_parser.Tpo" -c -o ginsh_parser.o
> ../../ginsh/ginsh_parser.cc; \
> then mv -f ".deps/ginsh_parser.Tpo" ".deps/ginsh_parser.Po"; else rm -f
> ".deps/ginsh_parser.Tpo"; exit 1; fi
> if g++ -DHAVE_CONFIG_H -I. -I../../ginsh -I.. -I../../ginsh/../ginac
> -I../ginac -DIN_GINAC  -I/usr/local/include -I/sw/include  -g -O2 -MT
> ginsh_lexer.o -MD -MP -MF ".deps/ginsh_lexer.Tpo" -c -o ginsh_lexer.o
> ../../ginsh/ginsh_lexer.cc; \
> then mv -f ".deps/ginsh_lexer.Tpo" ".deps/ginsh_lexer.Po"; else rm -f
> ".deps/ginsh_lexer.Tpo"; exit 1; fi
> ../../ginsh/ginsh_parser.yy: In function 'char** fcn_completion(const
> char*, int, int)':
> ../../ginsh/ginsh_parser.yy:858: error: invalid conversion from 'const
> char*' to 'char*'
> ../../ginsh/ginsh_parser.yy:863: error: 'rl_filename_completion_function'
> was not declared in this scope
> ../../ginsh/ginsh_parser.yy:863: error: 'rl_completion_matches' was not
> declared in this scope
> ../../ginsh/ginsh_parser.yy:873: error: 'rl_completion_matches' was not
> declared in this scope
> make: *** [ginsh_parser.o] Error 1
>  ----------->8 snip 8<-------------
> 
> Those three rl_ symbols also happen to be renamed without their "rl_"
> prefixes in the editline wrapper header/library.  (I've found this problem
> with old versions of GNU readline too, BTW.)  The workaround I devised in
> the magic-7.1 source is the following:
> 
> #if defined(READLINE_4_DOT_2_PLUS) || (RL_VERSION_MAJOR >= 5) || (RL_VERSION_MAJOR == 4 && RL_VERSION_MINOR >= 2)
> #define filename_completion_function rl_filename_completion_function
> #define username_completion_function rl_username_completion_function
> #define completion_matches rl_completion_matches
> #endif

GiNaC already has proper hack for this (your solution is not good enough
for C++ anyway: one need also to const_cast<char *> the arguments and
convert pointers to functions so that compiler is happy about them).
Thus, ginsh can be compiled with old (GNU) libreadline (or compatible 
non-GNU libraries). The only problem is how to decide if we got such
an old  version. This is especially difficult if the library do not
provide any meaningful version information ("EditLine wrapper" is not
really helpful). OK, let's forget about library versions and check for
function/variables declarations themselves:

diff --git a/INSTALL b/INSTALL
index 8d0b703..7feed75 100644
--- a/INSTALL
+++ b/INSTALL
@@ -105,7 +105,7 @@ Problems building ginsh
 -----------------------
 
 The most common reason why this doesn't succeed is the absence of
-libreadline and/or the corresponding header files. Depending on what your
+GNU libreadline and/or the corresponding header files. Depending on what your
 system/distribution is, you will have to install a package called
 libreadline and maybe libreadline-dev. If your system's vendor doesn't
 supply such packages, go to <ftp://ftp.gnu.org/gnu/readline/> and compile
diff --git a/acinclude.m4 b/acinclude.m4
index 4ab1387..a45f957 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -4,35 +4,111 @@ dnl additions' names with AC_ but with GINAC_ in order to steer clear of
 dnl future trouble.
 dnl ===========================================================================
 
-dnl Usage: GINAC_RLVERSION
+dnl Usage: GINAC_HAVE_READLINE
+dnl Will test for GNU libreadline (or compatible) and termcap. On success
+dnl RL_LIBS is set to libraries to link with and CONFIG_READLINE is set
+dnl to `yes'. If old (pre 4.2) libreadline is detected CONFIG_OLD_READLINE
+dnl will be defined instead.
 dnl The maintainers of libreadline are complete morons: they don't care a shit
 dnl about compatiblilty (which is not so bad by itself) and at the same time 
 dnl they don't export the version to the preprocessor so we could kluge around 
 dnl incomatiblities.  The only reliable way to figure out the version is by 
 dnl checking the extern variable rl_library_version at runtime.  &#@$%*!
-AC_DEFUN([GINAC_LIB_READLINE_VERSION],
-[AC_CACHE_CHECK([for version of libreadline], ginac_cv_rlversion, [
+dnl To add even more fun some vendors supply libraries which return non-numeric
+dnl rl_library_version.
+AC_DEFUN([GINAC_HAVE_READLINE],[
+AC_REQUIRE([GINAC_TERMCAP])
+
+AC_CHECK_HEADERS(readline/readline.h readline/history.h)
+save_LIBS="$LIBS"
+AC_CHECK_LIB(readline, readline)
+LIBS="$LIBTERMCAP $LIBS"
+
+if test "x$ac_cv_header_readline_readline_h" != "xyes" -o \
+        "x$ac_cv_header_readline_history_h" != "xyes" -o \
+				"x$ac_cv_lib_readline_readline" != "xyes"; then
+	CONFIG_READLINE="no"
+fi
+
+if test "x$CONFIG_READLINE" != "xno"; then
+dnl To be damn sure we got correct prototype don't use AC_CHECK_LIB
+AC_CACHE_CHECK([for rl_filename_completion_function in libreadline],
+ginac_cv_readline_type, [
+AC_TRY_RUN([
+#include <readline/readline.h>
+static char** fcn_completion(const char* text, int start, int end)
+{
+	return rl_completion_matches(text, rl_filename_completion_function);
+}
+int main()
+{
+	rl_attempted_completion_function = fcn_completion;
+	return 0;
+}], [
+dnl OK
+ginac_cv_readline_type="new" ], [
+dnl failure -- check for older (<= 4.1) readline
 AC_TRY_RUN([
-#include <stdio.h>
-#include <sys/types.h>
 #include <readline/readline.h>
+static char** fcn_completion(const char* text, int start, int end)
+{
+	return completion_matches(text, filename_completion_function);
+}
+int main() 
+{
+	rl_attempted_completion_function = (CPPFunction *)fcn_completion;
+	return 0;
+}], [
+dnl OK
+ginac_cv_readline_type="old" ], [
+dnl failure -- niether "new" nor "old" libreadline
+ginac_cv_readline_type="none"], [])], [
+dnl cross-compiling -- assume "new" libreadline
+ginac_cv_readline_type="new"])])
+fi # CONFIG_READLINE != no
+
+if test "x$ginac_cv_readline_type" = "xnone"; then
+	CONFIG_READLINE="no"
+else
+	CONFIG_READLINE="yes"
+fi
 
+if test "x$CONFIG_READLINE" != "xno"; then
+AC_CACHE_CHECK([if rl_basic_word_break_characters is const char*],
+ginac_cv_rl_basic_word_break_const, [
+AC_TRY_RUN([
+#include <readline/readline.h>
+static char* orig_basic_word_break_characters;
 int main()
 {
-    FILE *fd;
-    fd = fopen("conftest.out", "w");
-    fprintf(fd, "%s\n", rl_library_version);
-    fclose(fd);
-    return 0;
-}], ginac_cv_rlversion=`cat 'conftest.out'`, ginac_cv_rlversion='unknown', ginac_cv_rlversion='4.2')])
-if test "x${ginac_cv_rlversion}" != "xunknown"; then
-  RL_VERSION_MAJOR=`echo ${ginac_cv_rlversion} | sed -e 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
-  AC_DEFINE_UNQUOTED(GINAC_RL_VERSION_MAJOR, $RL_VERSION_MAJOR, [Major version of installed readline library.])
-  RL_VERSION_MINOR=`echo ${ginac_cv_rlversion} | sed -e 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
-  AC_DEFINE_UNQUOTED(GINAC_RL_VERSION_MINOR, $RL_VERSION_MINOR, [Minor version of installed readline library.])
+	orig_basic_word_break_characters = rl_basic_word_break_characters;
+	return 0;
+}], [
+dnl OK -- so it is char* (pre-5.0 readline)
+ginac_cv_rl_basic_word_break_const="no"
+], [
+dnl failure -- assume it is const char*
+ginac_cv_rl_basic_word_break_const="yes" ], [
+dnl cross-compiling -- assume it is const char*
+ginac_cv_rl_basic_word_break_const="yes" ])])
+fi # CONFIG_READLINE != no
+
+if test "x$CONFIG_READLINE" = "xyes"; then
+	RL_LIBS="$LIBS"
+	AC_DEFINE(CONFIG_READLINE, ,[have GNU readline (or compatible)])
+	if test "x$ginac_cv_rl_basic_word_break_const" = "xyes"; then
+	  AC_DEFINE(CONFIG_RL_NEED_CONST_CAST, ,[Define if rl_basic_word_break_characters is const char*])
+	fi
+	if test "x$ginac_cv_readline_type" = "xold"; then
+	  AC_DEFINE(CONFIG_OLD_READLINE, , [Define if have GNU readline < 4.2 or compatible])
+	fi
 else
-  GINAC_WARNING([I could not run a test of libreadline (needed for building ginsh).])
+	GINAC_WARNING([Could not run a test of libreadline (needed for building ginsh).])
 fi
+LIBS="$save_LIBS"
+
+AC_SUBST(RL_LIBS)
+AC_SUBST(CONFIG_READLINE)
 ])
 
 dnl Usage: GINAC_TERMCAP
diff --git a/configure.ac b/configure.ac
index b022b3d..5aa3bb9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -83,22 +83,8 @@ AC_LANG([C++])
 dnl Check for stuff needed for building the GiNaC interactive shell (ginsh).
 AC_CHECK_HEADERS(unistd.h)
 GINAC_HAVE_RUSAGE
-AC_CHECK_HEADERS(readline/readline.h readline/history.h)
-if test "x${ac_cv_header_readline_readline_h}" != "xyes" -o "x${ac_cv_header_readline_history_h}" != "xyes"; then
-  GINAC_WARNING([I could not find the headers for libreadline (needed for building ginsh).])
-fi
-GINAC_TERMCAP
-save_LIBS=$LIBS
-LIBS="$LIBTERMCAP $LIBS"
-AC_CHECK_LIB(readline, readline)
-if test "x${ac_cv_lib_readline_readline}" = "xyes"; then
-  GINAC_LIB_READLINE_VERSION
-else
-  GINAC_WARNING([I could not find libreadline (needed by ginsh).])
-fi
-GINSH_LIBS=$LIBS
-LIBS=$save_LIBS
-AC_SUBST(GINSH_LIBS)
+dnl Check for libreadline 
+GINAC_HAVE_READLINE
 
 dnl Make sure all the necessary standard headers are installed on the system.
 AC_CHECK_HEADER(iosfwd, , GINAC_ERROR([The standard <iosfwd> header file could not be found.]))
diff --git a/ginsh/Makefile.am b/ginsh/Makefile.am
index 184fbbe..44376a7 100644
--- a/ginsh/Makefile.am
+++ b/ginsh/Makefile.am
@@ -2,7 +2,7 @@
 
 bin_PROGRAMS = ginsh
 ginsh_SOURCES = ginsh_parser.yy ginsh_lexer.ll ginsh.h ginsh_extensions.h
-ginsh_LDADD = ../ginac/libginac.la $(GINSH_LIBS)
+ginsh_LDADD = ../ginac/libginac.la $(RL_LIBS)
 
 INCLUDES = -I$(srcdir)/../ginac -I../ginac -DIN_GINAC
 AM_YFLAGS = -d
diff --git a/ginsh/ginsh_parser.yy b/ginsh/ginsh_parser.yy
index b252609..f82c9f3 100644
--- a/ginsh/ginsh_parser.yy
+++ b/ginsh/ginsh_parser.yy
@@ -45,19 +45,21 @@
 
 #define YYERROR_VERBOSE 1
 
+#ifdef CONFIG_READLINE
 // Original readline settings
 static int orig_completion_append_character;
-#if (GINAC_RL_VERSION_MAJOR < 4) || (GINAC_RL_VERSION_MAJOR == 4 && GINAC_RL_VERSION_MINOR < 2)
+#ifdef CONFIG_OLD_READLINE
 static char *orig_basic_word_break_characters;
 #else
 static const char *orig_basic_word_break_characters;
 #endif
 
-#if (GINAC_RL_VERSION_MAJOR >= 5)
+#ifdef CONFIG_RL_NEED_CONST_CAST
 #define GINAC_RL_COMPLETER_CAST(a) const_cast<char *>((a))
 #else
 #define GINAC_RL_COMPLETER_CAST(a) (a)
 #endif
+#endif // CONFIG_READLINE
 
 // Expression stack for %, %% and %%%
 static void push(const ex &e);
@@ -850,6 +852,7 @@ static char *fcn_generator(const char *text, int state)
 	return NULL;
 }
 
+#ifdef CONFIG_READLINE
 static char **fcn_completion(const char *text, int start, int end)
 {
 	if (rl_line_buffer[0] == '!') {
@@ -857,7 +860,7 @@ static char **fcn_completion(const char *text, int start, int end)
 		rl_completion_append_character = orig_completion_append_character;
 		rl_basic_word_break_characters = orig_basic_word_break_characters;
 		rl_completer_word_break_characters = GINAC_RL_COMPLETER_CAST(rl_basic_word_break_characters);
-#if (GINAC_RL_VERSION_MAJOR < 4) || (GINAC_RL_VERSION_MAJOR == 4 && GINAC_RL_VERSION_MINOR < 2)
+#ifdef CONFIG_OLD_READLINE
 		return completion_matches(const_cast<char *>(text), (CPFunction *)filename_completion_function);
 #else
 		return rl_completion_matches(text, rl_filename_completion_function);
@@ -867,13 +870,14 @@ static char **fcn_completion(const char *text, int start, int end)
 		rl_completion_append_character = '(';
 		rl_basic_word_break_characters = " \t\n\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~";
 		rl_completer_word_break_characters = GINAC_RL_COMPLETER_CAST(rl_basic_word_break_characters);
-#if (GINAC_RL_VERSION_MAJOR < 4) || (GINAC_RL_VERSION_MAJOR == 4 && GINAC_RL_VERSION_MINOR < 2)
+#ifdef CONFIG_OLD_READLINE
 		return completion_matches(const_cast<char *>(text), (CPFunction *)fcn_generator);
 #else
 		return rl_completion_matches(text, fcn_generator);
 #endif
 	}
 }
+#endif // CONFIG_READLINE
 
 void greeting(void)
 {
@@ -917,15 +921,17 @@ int main(int argc, char **argv)
 	insert_help("print_latex", "print_latex(expression) - prints a LaTeX representation of the given expression");
 	insert_help("print_csrc", "print_csrc(expression) - prints a C source code representation of the given expression");
 
+#ifdef CONFIG_READLINE
 	// Init readline completer
 	rl_readline_name = argv[0];
-#if (GINAC_RL_VERSION_MAJOR < 4) || (GINAC_RL_VERSION_MAJOR == 4 && GINAC_RL_VERSION_MINOR < 2)
+#ifdef CONFIG_OLD_READLINE
 	rl_attempted_completion_function = (CPPFunction *)fcn_completion;
 #else
 	rl_attempted_completion_function = fcn_completion;
 #endif
 	orig_completion_append_character = rl_completion_append_character;
 	orig_basic_word_break_characters = rl_basic_word_break_characters;
+#endif
 
 	// Init input file list, open first file
 	num_files = argc - 1;


Best regards,
 Alexei

-- 
All science is either physics or stamp collecting.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 827 bytes
Desc: Digital signature
Url : http://www.cebix.net/pipermail/ginac-list/attachments/20070131/0479f857/attachment.pgp


More information about the GiNaC-list mailing list