diff options
Diffstat (limited to 'Build/source/utils/texinfo/info')
52 files changed, 25381 insertions, 0 deletions
diff --git a/Build/source/utils/texinfo/info/Makefile.am b/Build/source/utils/texinfo/info/Makefile.am new file mode 100644 index 00000000000..176f5939a53 --- /dev/null +++ b/Build/source/utils/texinfo/info/Makefile.am @@ -0,0 +1,74 @@ +# $Id: Makefile.am,v 1.8 2004/10/28 14:03:27 karl Exp $ +# Makefile.am for texinfo/info. +# Run automake in .. to produce Makefile.in from this. +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# Use `ginfo' for building to avoid confusion with the standard `info' +# target. Removes the `g' in the install rule before applying any +# user-specified name transformations. +bin_PROGRAMS = ginfo infokey +transform = s/ginfo/info/; $(program_transform_name) + +localedir = $(datadir)/locale +infodir2 = $(datadir)/info + +# -I. for funs.h. +# Automake puts -I.. and -I$(srcdir) into DEFS by default, but +# we need to override it, so include them ourselves. +INCLUDES = -I. -I$(top_srcdir)/lib -I../intl -I.. -I$(srcdir) +DEFS += -DLOCALEDIR=\"$(localedir)\" -DINFODIR=\"$(infodir)\" \ + -DINFODIR2=\"$(infodir2)\" +LDADD = ../lib/libtxi.a $(TERMLIBS) $(LIBINTL) + +EXTRA_DIST = README pcterm.c + +ginfo_SOURCES = dir.c display.c display.h doc.h dribble.c dribble.h \ + echo-area.c echo-area.h \ + filesys.c filesys.h footnotes.c footnotes.h gc.c gc.h \ + indices.c indices.h info-utils.c info-utils.h info.c info.h infodoc.c \ + infomap.c infomap.h m-x.c man.c man.h nodemenu.c nodes.c nodes.h \ + search.c search.h session.c session.h signals.c signals.h \ + termdep.h terminal.c terminal.h tilde.c tilde.h \ + variables.c variables.h window.c window.h +nodist_ginfo_SOURCES = doc.c funs.h +infokey_SOURCES = infokey.c infokey.h key.h +nodist_infokey_SOURCES = key.c funs.h + +# The files `doc.c', `key.c' and `funs.h' are created by ./makedoc run over the +# source files which contain DECLARE_INFO_COMMAND. `funs.h' is a header file +# listing the functions found. `doc.c' is a structure containing pointers +# to those functions along with completable names and documentation strings. +# +generated_sources = doc.c key.c funs.h + +# We need a linear ordering to prevent race condition in parallel make. +# See the node ``(automake)Multiple Outputs'' for an explanation. +doc.c: key.c +key.c: funs.h + +# These files are not distributed. +DISTCLEANFILES = $(generated_sources) + +noinst_PROGRAMS = makedoc +makedoc_SOURCES = makedoc.c + +# Files with Info commands defined that makedoc should read. +cmd_sources = $(srcdir)/session.c $(srcdir)/echo-area.c $(srcdir)/infodoc.c \ + $(srcdir)/m-x.c $(srcdir)/indices.c $(srcdir)/nodemenu.c \ + $(srcdir)/footnotes.c $(srcdir)/variables.c + +# The $(EXEEXT) should be added by Automake, but isn't. Fine. +$(generated_sources): makedoc$(EXEEXT) $(cmd_sources) + rm -f $(generated_sources) + $(top_builddir)/$(native_tools)/info/makedoc $(cmd_sources) + +# The following hack is necessary to hint make before the automatic +# dependencies are built. +BUILT_SOURCES = funs.h diff --git a/Build/source/utils/texinfo/info/Makefile.in b/Build/source/utils/texinfo/info/Makefile.in new file mode 100644 index 00000000000..56fc64f82e5 --- /dev/null +++ b/Build/source/utils/texinfo/info/Makefile.in @@ -0,0 +1,584 @@ +# Makefile.in generated by automake 1.9.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# $Id: Makefile.am,v 1.8 2004/10/28 14:03:27 karl Exp $ +# Makefile.am for texinfo/info. +# Run automake in .. to produce Makefile.in from this. +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +SOURCES = $(ginfo_SOURCES) $(nodist_ginfo_SOURCES) $(infokey_SOURCES) $(nodist_infokey_SOURCES) $(makedoc_SOURCES) + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = ginfo$(EXEEXT) infokey$(EXEEXT) +noinst_PROGRAMS = makedoc$(EXEEXT) +subdir = info +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/codeset.m4 \ + $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/glibc21.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intdiv0.m4 \ + $(top_srcdir)/m4/intmax.m4 $(top_srcdir)/m4/inttypes-pri.m4 \ + $(top_srcdir)/m4/inttypes.m4 $(top_srcdir)/m4/inttypes_h.m4 \ + $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/longdouble.m4 $(top_srcdir)/m4/longlong.m4 \ + $(top_srcdir)/m4/mkstemp.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/onceonly_2_57.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/printf-posix.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/signed.m4 $(top_srcdir)/m4/size_max.m4 \ + $(top_srcdir)/m4/stdint_h.m4 $(top_srcdir)/m4/strcase.m4 \ + $(top_srcdir)/m4/uintmax_t.m4 $(top_srcdir)/m4/ulonglong.m4 \ + $(top_srcdir)/m4/wchar_t.m4 $(top_srcdir)/m4/wint_t.m4 \ + $(top_srcdir)/m4/xsize.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) +am_ginfo_OBJECTS = dir.$(OBJEXT) display.$(OBJEXT) dribble.$(OBJEXT) \ + echo-area.$(OBJEXT) filesys.$(OBJEXT) footnotes.$(OBJEXT) \ + gc.$(OBJEXT) indices.$(OBJEXT) info-utils.$(OBJEXT) \ + info.$(OBJEXT) infodoc.$(OBJEXT) infomap.$(OBJEXT) \ + m-x.$(OBJEXT) man.$(OBJEXT) nodemenu.$(OBJEXT) nodes.$(OBJEXT) \ + search.$(OBJEXT) session.$(OBJEXT) signals.$(OBJEXT) \ + terminal.$(OBJEXT) tilde.$(OBJEXT) variables.$(OBJEXT) \ + window.$(OBJEXT) +nodist_ginfo_OBJECTS = doc.$(OBJEXT) +ginfo_OBJECTS = $(am_ginfo_OBJECTS) $(nodist_ginfo_OBJECTS) +ginfo_LDADD = $(LDADD) +am__DEPENDENCIES_1 = +ginfo_DEPENDENCIES = ../lib/libtxi.a $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_infokey_OBJECTS = infokey.$(OBJEXT) +nodist_infokey_OBJECTS = key.$(OBJEXT) +infokey_OBJECTS = $(am_infokey_OBJECTS) $(nodist_infokey_OBJECTS) +infokey_LDADD = $(LDADD) +infokey_DEPENDENCIES = ../lib/libtxi.a $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_makedoc_OBJECTS = makedoc.$(OBJEXT) +makedoc_OBJECTS = $(am_makedoc_OBJECTS) +makedoc_LDADD = $(LDADD) +makedoc_DEPENDENCIES = ../lib/libtxi.a $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(ginfo_SOURCES) $(nodist_ginfo_SOURCES) $(infokey_SOURCES) \ + $(nodist_infokey_SOURCES) $(makedoc_SOURCES) +DIST_SOURCES = $(ginfo_SOURCES) $(infokey_SOURCES) $(makedoc_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +transform = s/ginfo/info/; $(program_transform_name) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ -DLOCALEDIR=\"$(localedir)\" -DINFODIR=\"$(infodir)\" \ + -DINFODIR2=\"$(infodir2)\" +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GENCAT = @GENCAT@ +GLIBC21 = @GLIBC21@ +GMSGFMT = @GMSGFMT@ +HAVE_ASPRINTF = @HAVE_ASPRINTF@ +HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@ +HAVE_SNPRINTF = @HAVE_SNPRINTF@ +HAVE_WPRINTF = @HAVE_WPRINTF@ +HELP2MAN = @HELP2MAN@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTALL_WARNINGS_FALSE = @INSTALL_WARNINGS_FALSE@ +INSTALL_WARNINGS_TRUE = @INSTALL_WARNINGS_TRUE@ +INSTOBJEXT = @INSTOBJEXT@ +INTLBISON = @INTLBISON@ +INTLLIBS = @INTLLIBS@ +INTLOBJS = @INTLOBJS@ +INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGMERGE = @MSGMERGE@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TERMLIBS = @TERMLIBS@ +TOOLS_ONLY_FALSE = @TOOLS_ONLY_FALSE@ +TOOLS_ONLY_TRUE = @TOOLS_ONLY_TRUE@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +native_tools = @native_tools@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +localedir = $(datadir)/locale +infodir2 = $(datadir)/info + +# -I. for funs.h. +# Automake puts -I.. and -I$(srcdir) into DEFS by default, but +# we need to override it, so include them ourselves. +INCLUDES = -I. -I$(top_srcdir)/lib -I../intl -I.. -I$(srcdir) +LDADD = ../lib/libtxi.a $(TERMLIBS) $(LIBINTL) +EXTRA_DIST = README pcterm.c +ginfo_SOURCES = dir.c display.c display.h doc.h dribble.c dribble.h \ + echo-area.c echo-area.h \ + filesys.c filesys.h footnotes.c footnotes.h gc.c gc.h \ + indices.c indices.h info-utils.c info-utils.h info.c info.h infodoc.c \ + infomap.c infomap.h m-x.c man.c man.h nodemenu.c nodes.c nodes.h \ + search.c search.h session.c session.h signals.c signals.h \ + termdep.h terminal.c terminal.h tilde.c tilde.h \ + variables.c variables.h window.c window.h + +nodist_ginfo_SOURCES = doc.c funs.h +infokey_SOURCES = infokey.c infokey.h key.h +nodist_infokey_SOURCES = key.c funs.h + +# The files `doc.c', `key.c' and `funs.h' are created by ./makedoc run over the +# source files which contain DECLARE_INFO_COMMAND. `funs.h' is a header file +# listing the functions found. `doc.c' is a structure containing pointers +# to those functions along with completable names and documentation strings. +# +generated_sources = doc.c key.c funs.h + +# These files are not distributed. +DISTCLEANFILES = $(generated_sources) +makedoc_SOURCES = makedoc.c + +# Files with Info commands defined that makedoc should read. +cmd_sources = $(srcdir)/session.c $(srcdir)/echo-area.c $(srcdir)/infodoc.c \ + $(srcdir)/m-x.c $(srcdir)/indices.c $(srcdir)/nodemenu.c \ + $(srcdir)/footnotes.c $(srcdir)/variables.c + + +# The following hack is necessary to hint make before the automatic +# dependencies are built. +BUILT_SOURCES = funs.h +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu info/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu info/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) +ginfo$(EXEEXT): $(ginfo_OBJECTS) $(ginfo_DEPENDENCIES) + @rm -f ginfo$(EXEEXT) + $(LINK) $(ginfo_LDFLAGS) $(ginfo_OBJECTS) $(ginfo_LDADD) $(LIBS) +infokey$(EXEEXT): $(infokey_OBJECTS) $(infokey_DEPENDENCIES) + @rm -f infokey$(EXEEXT) + $(LINK) $(infokey_LDFLAGS) $(infokey_OBJECTS) $(infokey_LDADD) $(LIBS) +makedoc$(EXEEXT): $(makedoc_OBJECTS) $(makedoc_DEPENDENCIES) + @rm -f makedoc$(EXEEXT) + $(LINK) $(makedoc_LDFLAGS) $(makedoc_OBJECTS) $(makedoc_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/display.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dribble.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/echo-area.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesys.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/footnotes.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indices.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/infodoc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/infokey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/infomap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m-x.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/makedoc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/man.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nodemenu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nodes.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signals.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/terminal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tilde.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/variables.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-noinstPROGRAMS ctags distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-info-am + + +# We need a linear ordering to prevent race condition in parallel make. +# See the node ``(automake)Multiple Outputs'' for an explanation. +doc.c: key.c +key.c: funs.h + +# The $(EXEEXT) should be added by Automake, but isn't. Fine. +$(generated_sources): makedoc$(EXEEXT) $(cmd_sources) + rm -f $(generated_sources) + $(top_builddir)/$(native_tools)/info/makedoc $(cmd_sources) +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Build/source/utils/texinfo/info/README b/Build/source/utils/texinfo/info/README new file mode 100644 index 00000000000..4178a0107e0 --- /dev/null +++ b/Build/source/utils/texinfo/info/README @@ -0,0 +1,30 @@ +$Id: README,v 1.3 2004/04/11 17:56:45 karl Exp $ +texinfo/info/README + + Copyright (C) 2002 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. + +Info 2.0 is a complete rewrite of the original standalone Info I wrote in +1987, the first program I wrote for rms. That program was something like +my second Unix program ever, and my die-hard machine language coding habits +tended to show through. I found the original Info hard to read and +maintain, and thus decided to write this one. + +The rewrite consists of about 12,000 lines of code written in about 12 +days. I believe this version of Info to be in much better shape than the +original Info. + +Info 2.0 is substantially different from its original standalone +predecessor. It appears almost identical to the GNU Emacs version, but has +the advantages of smaller size, ease of portability, and a built in library +which can be used in other programs (to get or display documentation from +Info files, for example). + +A full listing of the commands available in Info can be gotten by typing +`?' while within an Info window. This produces a node in a window which +can be viewed just like any Info node. + +--Brian Fox <bfox@gnu.org> diff --git a/Build/source/utils/texinfo/info/dir.c b/Build/source/utils/texinfo/info/dir.c new file mode 100644 index 00000000000..98061dfdfa6 --- /dev/null +++ b/Build/source/utils/texinfo/info/dir.c @@ -0,0 +1,290 @@ +/* dir.c -- how to build a special "dir" node from "localdir" files. + $Id: dir.c,v 1.3 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "info-utils.h" +#include "filesys.h" +#include "tilde.h" + +/* The "dir" node can be built from the contents of a file called "dir", + with the addition of the menus of every file named in the array + dirs_to_add which are found in INFOPATH. */ + +static void add_menu_to_file_buffer (char *contents, long int size, + FILE_BUFFER *fb); +static void insert_text_into_fb_at_binding (FILE_BUFFER *fb, + SEARCH_BINDING *binding, char *text, int textlen); +void maybe_build_dir_node (char *dirname); + +static char *dirs_to_add[] = { + "dir", "localdir", (char *)NULL +}; + + +/* Return zero if the file represented in the stat structure TEST has + already been seen, nonzero else. */ + +typedef struct +{ + unsigned long device; + unsigned long inode; +} dir_file_list_entry_type; + +static int +new_dir_file_p (struct stat *test) +{ + static unsigned dir_file_list_len = 0; + static dir_file_list_entry_type *dir_file_list = NULL; + unsigned i; + + for (i = 0; i < dir_file_list_len; i++) + { + dir_file_list_entry_type entry; + entry = dir_file_list[i]; + if (entry.device == test->st_dev && entry.inode == test->st_ino) + return 0; + } + + dir_file_list_len++; + dir_file_list = xrealloc (dir_file_list, + dir_file_list_len * sizeof (dir_file_list_entry_type)); + dir_file_list[dir_file_list_len - 1].device = test->st_dev; + dir_file_list[dir_file_list_len - 1].inode = test->st_ino; + return 1; +} + + +void +maybe_build_dir_node (char *dirname) +{ + int path_index, update_tags; + char *this_dir; + FILE_BUFFER *dir_buffer = info_find_file (dirname); + + /* If there is no "dir" in the current info path, we cannot build one + from nothing. */ + if (!dir_buffer) + return; + + /* If this directory has already been built, return now. */ + if (dir_buffer->flags & N_CannotGC) + return; + + /* Initialize the list we use to avoid reading the same dir file twice + with the dir file just found. */ + new_dir_file_p (&dir_buffer->finfo); + + path_index = update_tags = 0; + + /* Using each element of the path, check for one of the files in + DIRS_TO_ADD. Do not check for "localdir.info.Z" or anything else. + Only files explictly named are eligible. This is a design decision. + There can be an info file name "localdir.info" which contains + information on the setting up of "localdir" files. */ + while ((this_dir = extract_colon_unit (infopath, &path_index))) + { + register int da_index; + char *from_file; + + /* Expand a leading tilde if one is present. */ + if (*this_dir == '~') + { + char *tilde_expanded_dirname; + + tilde_expanded_dirname = tilde_expand_word (this_dir); + if (tilde_expanded_dirname != this_dir) + { + free (this_dir); + this_dir = tilde_expanded_dirname; + } + } + + /* For every different file named in DIRS_TO_ADD found in the + search path, add that file's menu to our "dir" node. */ + for (da_index = 0; (from_file = dirs_to_add[da_index]); da_index++) + { + struct stat finfo; + int statable; + int namelen = strlen (from_file); + char *fullpath = xmalloc (3 + strlen (this_dir) + namelen); + + strcpy (fullpath, this_dir); + if (!IS_SLASH (fullpath[strlen (fullpath) - 1])) + strcat (fullpath, "/"); + strcat (fullpath, from_file); + + statable = (stat (fullpath, &finfo) == 0); + + /* Only add this file if we have not seen it before. */ + if (statable && S_ISREG (finfo.st_mode) && new_dir_file_p (&finfo)) + { + long filesize; + int compressed; + char *contents = filesys_read_info_file (fullpath, &filesize, + &finfo, &compressed); + if (contents) + { + update_tags++; + add_menu_to_file_buffer (contents, filesize, dir_buffer); + free (contents); + } + } + + free (fullpath); + } + free (this_dir); + } + + if (update_tags) + build_tags_and_nodes (dir_buffer); + + /* Flag that the dir buffer has been built. */ + dir_buffer->flags |= N_CannotGC; +} + +/* Given CONTENTS and FB (a file buffer), add the menu found in CONTENTS + to the menu found in FB->contents. Second argument SIZE is the total + size of CONTENTS. */ +static void +add_menu_to_file_buffer (char *contents, long int size, FILE_BUFFER *fb) +{ + SEARCH_BINDING contents_binding, fb_binding; + long contents_offset, fb_offset; + + contents_binding.buffer = contents; + contents_binding.start = 0; + contents_binding.end = size; + contents_binding.flags = S_FoldCase | S_SkipDest; + + fb_binding.buffer = fb->contents; + fb_binding.start = 0; + fb_binding.end = fb->filesize; + fb_binding.flags = S_FoldCase | S_SkipDest; + + /* Move to the start of the menus in CONTENTS and FB. */ + contents_offset = search_forward (INFO_MENU_LABEL, &contents_binding); + fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding); + + /* If there is no menu in CONTENTS, quit now. */ + if (contents_offset == -1) + return; + + /* There is a menu in CONTENTS, and contents_offset points to the first + character following the menu starter string. Skip all whitespace + and newline characters. */ + contents_offset += skip_whitespace_and_newlines (contents + contents_offset); + + /* If there is no menu in FB, make one. */ + if (fb_offset == -1) + { + /* Find the start of the second node in this file buffer. If there + is only one node, we will be adding the contents to the end of + this node. */ + fb_offset = find_node_separator (&fb_binding); + + /* If not even a single node separator, give up. */ + if (fb_offset == -1) + return; + + fb_binding.start = fb_offset; + fb_binding.start += + skip_node_separator (fb_binding.buffer + fb_binding.start); + + /* Try to find the next node separator. */ + fb_offset = find_node_separator (&fb_binding); + + /* If found one, consider that the start of the menu. Otherwise, the + start of this menu is the end of the file buffer (i.e., fb->size). */ + if (fb_offset != -1) + fb_binding.start = fb_offset; + else + fb_binding.start = fb_binding.end; + + insert_text_into_fb_at_binding + (fb, &fb_binding, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)); + + fb_binding.buffer = fb->contents; + fb_binding.start = 0; + fb_binding.end = fb->filesize; + fb_offset = search_forward (INFO_MENU_LABEL, &fb_binding); + if (fb_offset == -1) + abort (); + } + + /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that + appear in their respective buffers. Add the remainder of CONTENTS + to the end of FB's menu. */ + fb_binding.start = fb_offset; + fb_offset = find_node_separator (&fb_binding); + if (fb_offset != -1) + fb_binding.start = fb_offset; + else + fb_binding.start = fb_binding.end; + + /* Leave exactly one blank line between directory entries. */ + { + int num_found = 0; + + while ((fb_binding.start > 0) && + (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1]))) + { + num_found++; + fb_binding.start--; + } + + /* Optimize if possible. */ + if (num_found >= 2) + { + fb_binding.buffer[fb_binding.start++] = '\n'; + fb_binding.buffer[fb_binding.start++] = '\n'; + } + else + { + /* Do it the hard way. */ + insert_text_into_fb_at_binding (fb, &fb_binding, "\n\n", 2); + fb_binding.start += 2; + } + } + + /* Insert the new menu. */ + insert_text_into_fb_at_binding + (fb, &fb_binding, contents + contents_offset, size - contents_offset); +} + +static void +insert_text_into_fb_at_binding (FILE_BUFFER *fb, + SEARCH_BINDING *binding, char *text, int textlen) +{ + char *contents; + long start, end; + + start = binding->start; + end = fb->filesize; + + contents = (char *)xmalloc (fb->filesize + textlen + 1); + memcpy (contents, fb->contents, start); + memcpy (contents + start, text, textlen); + memcpy (contents + start + textlen, fb->contents + start, end - start); + free (fb->contents); + fb->contents = contents; + fb->filesize += textlen; + fb->finfo.st_size = fb->filesize; +} diff --git a/Build/source/utils/texinfo/info/display.c b/Build/source/utils/texinfo/info/display.c new file mode 100644 index 00000000000..1a9fd0a9ae4 --- /dev/null +++ b/Build/source/utils/texinfo/info/display.c @@ -0,0 +1,646 @@ +/* display.c -- How to display Info windows. + $Id: display.c,v 1.7 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1997, 2003, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "display.h" + +extern int info_any_buffered_input_p (void); /* Found in session.c. */ + +static void free_display (DISPLAY_LINE **display); +static DISPLAY_LINE **make_display (int width, int height); + +void handle_tag (char *tag); +void handle_tag_start (char *tag); +void handle_tag_end (char *tag); + +/* An array of display lines which tell us what is currently visible on + the display. */ +DISPLAY_LINE **the_display = (DISPLAY_LINE **)NULL; + +/* Non-zero means do no output. */ +int display_inhibited = 0; + +/* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */ +void +display_initialize_display (int width, int height) +{ + free_display (the_display); + the_display = make_display (width, height); + display_clear_display (the_display); +} + +/* Clear all of the lines in DISPLAY making the screen blank. */ +void +display_clear_display (DISPLAY_LINE **display) +{ + register int i; + + for (i = 0; display[i]; i++) + { + display[i]->text[0] = '\0'; + display[i]->textlen = 0; + display[i]->inverse = 0; + } +} + +/* Non-zero if we didn't completely redisplay a window. */ +int display_was_interrupted_p = 0; + +/* Update the windows pointed to by WINDOW in the_display. This actually + writes the text on the screen. */ +void +display_update_display (WINDOW *window) +{ + register WINDOW *win; + + display_was_interrupted_p = 0; + + /* For every window in the list, check contents against the display. */ + for (win = window; win; win = win->next) + { + /* Only re-display visible windows which need updating. */ + if (((win->flags & W_WindowVisible) == 0) || + ((win->flags & W_UpdateWindow) == 0) || + (win->height == 0)) + continue; + + display_update_one_window (win); + if (display_was_interrupted_p) + break; + } + + /* Always update the echo area. */ + display_update_one_window (the_echo_area); +} + +void +handle_tag_start (char *tag) +{ + /* TODO really handle this tag. */ + return; +} + +void +handle_tag_end (char *tag) +{ + /* TODO really handle this tag. */ + return; +} + +void +handle_tag (char *tag) +{ + if (tag[0] == '/') + { + tag++; + handle_tag_end (tag); + } + else + handle_tag_start (tag); +} + +/* Display WIN on the_display. Unlike display_update_display (), this + function only does one window. */ +void +display_update_one_window (WINDOW *win) +{ + register char *nodetext; /* Current character to display. */ + register char *last_node_char; /* Position of the last character in node. */ + register int i; /* General use index. */ + char *printed_line; /* Buffer for a printed line. */ + int pl_index = 0; /* Index into PRINTED_LINE. */ + int line_index = 0; /* Number of lines done so far. */ + int pl_ignore = 0; /* How many chars use zero width on screen. */ + int allocated_win_width; + DISPLAY_LINE **display = the_display; + + /* If display is inhibited, that counts as an interrupted display. */ + if (display_inhibited) + display_was_interrupted_p = 1; + + /* If the window has no height, or display is inhibited, quit now. */ + if (!win->height || display_inhibited) + return; + + /* If the window's first row doesn't appear in the_screen, then it + cannot be displayed. This can happen when the_echo_area is the + window to be displayed, and the screen has shrunk to less than one + line. */ + if ((win->first_row < 0) || (win->first_row > the_screen->height)) + return; + + /* Print each line in the window into our local buffer, and then + check the contents of that buffer against the display. If they + differ, update the display. */ + allocated_win_width = win->width + 1; + printed_line = (char *)xmalloc (allocated_win_width); + + if (!win->node || !win->line_starts) + goto done_with_node_display; + + nodetext = win->line_starts[win->pagetop]; + last_node_char = win->node->contents + win->node->nodelen; + + for (; nodetext < last_node_char; nodetext++) + { + char *rep = NULL, *rep_carried_over, rep_temp[2]; + int replen; + + if (isprint (*nodetext)) + { + rep_temp[0] = *nodetext; + replen = 1; + rep_temp[1] = '\0'; + rep = rep_temp; + } + else + { + if (*nodetext == '\r' || *nodetext == '\n') + { + replen = win->width - pl_index + pl_ignore; + } + else if (*nodetext == '\0' + && (nodetext + 2) < last_node_char + && *(nodetext + 1) == '\b' + && *(nodetext + 2) == '[') + { + /* Found new style tag/cookie \0\b[ + Read until the closing tag \0\b] */ + int element_len = 0; + char *element; + + /* Skip the escapes. */ + nodetext += 3; + + while (!(*nodetext == '\0' + && *(nodetext + 1) == '\b' + && *(nodetext + 2) == ']')) + { + nodetext++; + element_len++; + } + + element = (char *) malloc (element_len + 1); + strncpy (element, nodetext - element_len, element_len); + + /* Skip the escapes. */ + nodetext += 2; + pl_ignore += element_len + 5; + /* Append string terminator. */ + element[element_len] = '\0'; + + handle_tag (element); + + /* Over and out */ + free (element); + + continue; + } + else + { + rep = printed_representation (*nodetext, pl_index); + replen = strlen (rep); + } + } + + /* Support ANSI escape sequences under -R. */ + if (raw_escapes_p + && *nodetext == '\033' + && nodetext[1] == '[' + && isdigit (nodetext[2])) + { + if (nodetext[3] == 'm') + pl_ignore += 4; + else if (isdigit (nodetext[3]) && nodetext[4] == 'm') + pl_ignore += 5; + } + while (pl_index + 2 >= allocated_win_width - 1) + { + allocated_win_width *= 2; + printed_line = (char *)xrealloc (printed_line, allocated_win_width); + } + + /* If this character can be printed without passing the width of + the line, then stuff it into the line. */ + if (replen + pl_index < win->width + pl_ignore) + { + /* Optimize if possible. */ + if (replen == 1) + { + printed_line[pl_index++] = *rep; + } + else + { + for (i = 0; i < replen; i++) + printed_line[pl_index++] = rep[i]; + } + } + else + { + DISPLAY_LINE *entry; + + /* If this character cannot be printed in this line, we have + found the end of this line as it would appear on the screen. + Carefully print the end of the line, and then compare. */ + if (*nodetext == '\n' || *nodetext == '\r' || *nodetext == '\t') + { + printed_line[pl_index] = '\0'; + rep_carried_over = (char *)NULL; + } + else + { + /* The printed representation of this character extends into + the next line. Remember the offset of the last character + printed out of REP so that we can carry the character over + to the next line. */ + for (i = 0; pl_index < (win->width + pl_ignore - 1);) + printed_line[pl_index++] = rep[i++]; + + rep_carried_over = rep + i; + + /* If printing the last character in this window couldn't + possibly cause the screen to scroll, place a backslash + in the rightmost column. */ + if (1 + line_index + win->first_row < the_screen->height) + { + if (win->flags & W_NoWrap) + printed_line[pl_index++] = '$'; + else + printed_line[pl_index++] = '\\'; + } + printed_line[pl_index] = '\0'; + } + + /* We have the exact line as it should appear on the screen. + Check to see if this line matches the one already appearing + on the screen. */ + entry = display[line_index + win->first_row]; + + /* If the screen line is inversed, then we have to clear + the line from the screen first. Why, I don't know. + (But don't do this if we have no visible entries, as can + happen if the window is shrunk very small.) */ + if ((entry && entry->inverse) + /* Need to erase the line if it has escape sequences. */ + || (raw_escapes_p && strchr (entry->text, '\033') != 0)) + { + terminal_goto_xy (0, line_index + win->first_row); + terminal_clear_to_eol (); + entry->inverse = 0; + entry->text[0] = '\0'; + entry->textlen = 0; + } + + /* Find the offset where these lines differ. */ + for (i = 0; i < pl_index; i++) + if (printed_line[i] != entry->text[i]) + break; + + /* If the lines are not the same length, or if they differed + at all, we must do some redrawing. */ + if ((i != pl_index) || (pl_index != entry->textlen)) + { + /* Move to the proper point on the terminal. */ + terminal_goto_xy (i, line_index + win->first_row); + + /* If there is any text to print, print it. */ + if (i != pl_index) + terminal_put_text (printed_line + i); + + /* If the printed text didn't extend all the way to the edge + of the window, and text was appearing between here and the + edge of the window, clear from here to the end of the line. */ + if ((pl_index < win->width + pl_ignore + && pl_index < entry->textlen) + || (entry->inverse)) + terminal_clear_to_eol (); + + fflush (stdout); + + /* Update the display text buffer. */ + if (strlen (printed_line) > (unsigned int) screenwidth) + /* printed_line[] can include more than screenwidth + characters if we are under -R and there are escape + sequences in it. However, entry->text was + allocated (in display_initialize_display) for + screenwidth characters only. */ + entry->text = xrealloc (entry->text, strlen (printed_line)+1); + strcpy (entry->text + i, printed_line + i); + entry->textlen = pl_index; + + /* Lines showing node text are not in inverse. Only modelines + have that distinction. */ + entry->inverse = 0; + } + + /* We have done at least one line. Increment our screen line + index, and check against the bottom of the window. */ + if (++line_index == win->height) + break; + + /* A line has been displayed, and the screen reflects that state. + If there is typeahead pending, then let that typeahead be read + now, instead of continuing with the display. */ + if (info_any_buffered_input_p ()) + { + free (printed_line); + display_was_interrupted_p = 1; + return; + } + + /* Reset PL_INDEX to the start of the line. */ + pl_index = 0; + pl_ignore = 0; /* this is computed per line */ + + /* If there are characters from REP left to print, stuff them + into the buffer now. */ + if (rep_carried_over) + for (; rep[pl_index]; pl_index++) + printed_line[pl_index] = rep[pl_index]; + + /* If this window has chosen not to wrap lines, skip to the end + of the physical line in the buffer, and start a new line here. */ + if (pl_index && (win->flags & W_NoWrap)) + { + char *begin; + + pl_index = 0; + printed_line[0] = '\0'; + + begin = nodetext; + + while ((nodetext < last_node_char) && (*nodetext != '\n')) + nodetext++; + } + } + } + + done_with_node_display: + /* We have reached the end of the node or the end of the window. If it + is the end of the node, then clear the lines of the window from here + to the end of the window. */ + for (; line_index < win->height; line_index++) + { + DISPLAY_LINE *entry = display[line_index + win->first_row]; + + /* If this line has text on it then make it go away. */ + if (entry && entry->textlen) + { + entry->textlen = 0; + entry->text[0] = '\0'; + + terminal_goto_xy (0, line_index + win->first_row); + terminal_clear_to_eol (); + } + } + + /* Finally, if this window has a modeline it might need to be redisplayed. + Check the window's modeline against the one in the display, and update + if necessary. */ + if ((win->flags & W_InhibitMode) == 0) + { + window_make_modeline (win); + line_index = win->first_row + win->height; + + /* This display line must both be in inverse, and have the same + contents. */ + if ((!display[line_index]->inverse) || + (strcmp (display[line_index]->text, win->modeline) != 0)) + { + terminal_goto_xy (0, line_index); + terminal_begin_inverse (); + terminal_put_text (win->modeline); + terminal_end_inverse (); + strcpy (display[line_index]->text, win->modeline); + display[line_index]->inverse = 1; + display[line_index]->textlen = strlen (win->modeline); + fflush (stdout); + } + } + + /* Okay, this window doesn't need updating anymore. */ + win->flags &= ~W_UpdateWindow; + free (printed_line); + fflush (stdout); +} + +/* Scroll the region of the_display starting at START, ending at END, and + moving the lines AMOUNT lines. If AMOUNT is less than zero, the lines + are moved up in the screen, otherwise down. Actually, it is possible + for no scrolling to take place in the case that the terminal doesn't + support it. This doesn't matter to us. */ +void +display_scroll_display (int start, int end, int amount) +{ + register int i, last; + DISPLAY_LINE *temp; + + /* If this terminal cannot do scrolling, give up now. */ + if (!terminal_can_scroll) + return; + + /* If there isn't anything displayed on the screen because it is too + small, quit now. */ + if (!the_display[0]) + return; + + /* If there is typeahead pending, then don't actually do any scrolling. */ + if (info_any_buffered_input_p ()) + return; + + /* Do it on the screen. */ + terminal_scroll_terminal (start, end, amount); + + /* Now do it in the display buffer so our contents match the screen. */ + if (amount > 0) + { + last = end + amount; + + /* Shift the lines to scroll right into place. */ + for (i = 0; i < (end - start); i++) + { + temp = the_display[last - i]; + the_display[last - i] = the_display[end - i]; + the_display[end - i] = temp; + } + + /* The lines have been shifted down in the buffer. Clear all of the + lines that were vacated. */ + for (i = start; i != (start + amount); i++) + { + the_display[i]->text[0] = '\0'; + the_display[i]->textlen = 0; + the_display[i]->inverse = 0; + } + } + + if (amount < 0) + { + last = start + amount; + for (i = 0; i < (end - start); i++) + { + temp = the_display[last + i]; + the_display[last + i] = the_display[start + i]; + the_display[start + i] = temp; + } + + /* The lines have been shifted up in the buffer. Clear all of the + lines that are left over. */ + for (i = end + amount; i != end; i++) + { + the_display[i]->text[0] = '\0'; + the_display[i]->textlen = 0; + the_display[i]->inverse = 0; + } + } +} + +/* Try to scroll lines in WINDOW. OLD_PAGETOP is the pagetop of WINDOW before + having had its line starts recalculated. OLD_STARTS is the list of line + starts that used to appear in this window. OLD_COUNT is the number of lines + that appear in the OLD_STARTS array. */ +void +display_scroll_line_starts (WINDOW *window, int old_pagetop, + char **old_starts, int old_count) +{ + register int i, old, new; /* Indices into the line starts arrays. */ + int last_new, last_old; /* Index of the last visible line. */ + int old_first, new_first; /* Index of the first changed line. */ + int unchanged_at_top = 0; + int already_scrolled = 0; + + /* Locate the first line which was displayed on the old window. */ + old_first = old_pagetop; + new_first = window->pagetop; + + /* Find the last line currently visible in this window. */ + last_new = window->pagetop + (window->height - 1); + if (last_new > window->line_count) + last_new = window->line_count - 1; + + /* Find the last line which used to be currently visible in this window. */ + last_old = old_pagetop + (window->height - 1); + if (last_old > old_count) + last_old = old_count - 1; + + for (old = old_first, new = new_first; + old < last_old && new < last_new; + old++, new++) + if (old_starts[old] != window->line_starts[new]) + break; + else + unchanged_at_top++; + + /* Loop through the old lines looking for a match in the new lines. */ + for (old = old_first + unchanged_at_top; old < last_old; old++) + { + for (new = new_first; new < last_new; new++) + if (old_starts[old] == window->line_starts[new]) + { + /* Find the extent of the matching lines. */ + for (i = 0; (old + i) < last_old; i++) + if (old_starts[old + i] != window->line_starts[new + i]) + break; + + /* Scroll these lines if there are enough of them. */ + { + int start, end, amount; + + start = (window->first_row + + ((old + already_scrolled) - old_pagetop)); + amount = new - (old + already_scrolled); + end = window->first_row + window->height; + + /* If we are shifting the block of lines down, then the last + AMOUNT lines will become invisible. Thus, don't bother + scrolling them. */ + if (amount > 0) + end -= amount; + + if ((end - start) > 0) + { + display_scroll_display (start, end, amount); + + /* Some lines have been scrolled. Simulate the scrolling + by offsetting the value of the old index. */ + old += i; + already_scrolled += amount; + } + } + } + } +} + +/* Move the screen cursor to directly over the current character in WINDOW. */ +void +display_cursor_at_point (WINDOW *window) +{ + int vpos, hpos; + + vpos = window_line_of_point (window) - window->pagetop + window->first_row; + hpos = window_get_cursor_column (window); + terminal_goto_xy (hpos, vpos); + fflush (stdout); +} + +/* **************************************************************** */ +/* */ +/* Functions Static to this File */ +/* */ +/* **************************************************************** */ + +/* Make a DISPLAY_LINE ** with width and height. */ +static DISPLAY_LINE ** +make_display (int width, int height) +{ + register int i; + DISPLAY_LINE **display; + + display = (DISPLAY_LINE **)xmalloc ((1 + height) * sizeof (DISPLAY_LINE *)); + + for (i = 0; i < height; i++) + { + display[i] = (DISPLAY_LINE *)xmalloc (sizeof (DISPLAY_LINE)); + display[i]->text = (char *)xmalloc (1 + width); + display[i]->textlen = 0; + display[i]->inverse = 0; + } + display[i] = (DISPLAY_LINE *)NULL; + return (display); +} + +/* Free the storage allocated to DISPLAY. */ +static void +free_display (DISPLAY_LINE **display) +{ + register int i; + register DISPLAY_LINE *display_line; + + if (!display) + return; + + for (i = 0; (display_line = display[i]); i++) + { + free (display_line->text); + free (display_line); + } + free (display); +} diff --git a/Build/source/utils/texinfo/info/display.h b/Build/source/utils/texinfo/info/display.h new file mode 100644 index 00000000000..286db293ee2 --- /dev/null +++ b/Build/source/utils/texinfo/info/display.h @@ -0,0 +1,78 @@ +/* display.h -- How the display in Info is done. + $Id: display.h,v 1.3 2004/04/11 17:56:45 karl Exp $ + + This file is part of GNU Info, a program for reading online documentation + stored in Info format. + + Copyright (C) 1993, 1997, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_DISPLAY_H +#define INFO_DISPLAY_H + +#include "info-utils.h" +#include "terminal.h" + +typedef struct { + char *text; /* Text of the line as it appears. */ + int textlen; /* Printable Length of TEXT. */ + int inverse; /* Non-zero means this line is inverse. */ +} DISPLAY_LINE; + +/* An array of display lines which tell us what is currently visible on + the display. */ +extern DISPLAY_LINE **the_display; + +/* Non-zero means do no output. */ +extern int display_inhibited; + +/* Non-zero if we didn't completely redisplay a window. */ +extern int display_was_interrupted_p; + +/* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */ +extern void display_initialize_display (int width, int height); + +/* Clear all of the lines in DISPLAY making the screen blank. */ +extern void display_clear_display (DISPLAY_LINE **display); + +/* Update the windows pointed to by WINDOWS in THE_DISPLAY. This actually + writes the text on the screen. */ +extern void display_update_display (WINDOW *window); + +/* Display WIN on THE_DISPLAY. Unlike display_update_display (), this + function only does one window. */ +extern void display_update_one_window (WINDOW *win); + +/* Move the screen cursor to directly over the current character in WINDOW. */ +extern void display_cursor_at_point (WINDOW *window); + +/* Scroll the region of the_display starting at START, ending at END, and + moving the lines AMOUNT lines. If AMOUNT is less than zero, the lines + are moved up in the screen, otherwise down. Actually, it is possible + for no scrolling to take place in the case that the terminal doesn't + support it. This doesn't matter to us. */ +extern void display_scroll_display (int start, int end, int amount); + +/* Try to scroll lines in WINDOW. OLD_PAGETOP is the pagetop of WINDOW before + having had its line starts recalculated. OLD_STARTS is the list of line + starts that used to appear in this window. OLD_COUNT is the number of lines + that appear in the OLD_STARTS array. */ +extern void display_scroll_line_starts (WINDOW *window, int old_pagetop, + char **old_starts, int old_count); + +#endif /* not INFO_DISPLAY_H */ diff --git a/Build/source/utils/texinfo/info/doc.h b/Build/source/utils/texinfo/info/doc.h new file mode 100644 index 00000000000..9315e8adc36 --- /dev/null +++ b/Build/source/utils/texinfo/info/doc.h @@ -0,0 +1,103 @@ +/* doc.h -- Structures associating function pointers with documentation. + $Id: doc.h,v 1.3 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 2001, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#if !defined (DOC_H) +#define DOC_H + +#include "info.h" /* for NAMED_FUNCTIONS, VFunction, etc. */ + +#if defined (INFOKEY) +/* For each function, we keep track of the first defined key sequence + which invokes that function, for each different map. This is so that + the dynamic documentation generation in infodoc.c (a) doesn't have to + search through copious KEYMAP_ENTRYs, and, more importantly, (b) the + user and programmer can choose the preferred key sequence that is + printed for any given function -- it's just the first one that + appears in the user's infokey file or the default keymaps in + infomap.c. + + Each FUNCTION_DOC has a linked list of FUNCTION_KEYSEQ structs + hanging off it, which are created on startup when the user and/or + default keymaps are being parsed. */ +typedef struct function_keyseq +{ + struct function_keyseq *next; + struct keymap_entry *map; + char *keyseq; +} FUNCTION_KEYSEQ; + +#endif /* INFOKEY */ + + +/* An array of FUNCTION_DOC structures is defined in doc.c, which is + automagically generated by the makedoc utility, whose job is to scan + through the source files for command function declarations and + compile a list of all the ones it finds. This saves tedious + housekeeping and avoids errors of omission. */ +typedef struct +{ + VFunction *func; +#if defined (NAMED_FUNCTIONS) + char *func_name; +#endif /* NAMED_FUNCTIONS */ +#if defined (INFOKEY) + FUNCTION_KEYSEQ *keys; +#endif /* INFOKEY */ + char *doc; +} FUNCTION_DOC; + +extern FUNCTION_DOC function_doc_array[]; + +/* Under the old key-binding system, an info command is specified by + the pointer to its function. Under the new INFOKEY binding system, + it is specified by a pointer to the command's FUNCTION_DOC structure, + defined in doc.c, from which the pointer to the function can be + easily divined using the InfoFunction() extractor. */ +#if defined(INFOKEY) +typedef FUNCTION_DOC InfoCommand; +/* The cast to VFunction * prevents pgcc from complaining about + dereferencing a void *. */ +#define InfoFunction(ic) ((ic) ? (ic)->func : (VFunction *) NULL) +#define InfoCmd(fn) (&function_doc_array[A_##fn]) +#define DocInfoCmd(fd) ((fd) && (fd)->func ? (fd) : NULL) +#else /* !INFOKEY */ +typedef VFunction InfoCommand; +#define InfoFunction(vf) ((vf)) +#define InfoCmd(fn) fn +#define DocInfoCmd(fd) ((fd)->func) +#endif /* !INFOKEY */ + +#include "infomap.h" /* for Keymap. */ + +#if defined (NAMED_FUNCTIONS) +extern char *function_name (InfoCommand *cmd); +extern InfoCommand *named_function (char *name); +#endif /* NAMED_FUNCTIONS */ + +extern char *function_documentation (InfoCommand *cmd); +extern char *key_documentation (char key, Keymap map); +extern char *pretty_keyname (unsigned char key); +extern char *pretty_keyseq (char *keyseq); +extern char *where_is (Keymap map, InfoCommand *cmd); +extern char *replace_in_documentation (char *string, int help_is_only_window_p); +extern void dump_map_to_message_buffer (char *prefix, Keymap map); + +#endif /* !DOC_H */ diff --git a/Build/source/utils/texinfo/info/dribble.c b/Build/source/utils/texinfo/info/dribble.c new file mode 100644 index 00000000000..d4563193661 --- /dev/null +++ b/Build/source/utils/texinfo/info/dribble.c @@ -0,0 +1,68 @@ +/* dribble.c -- dribble files for Info. + $Id: dribble.c,v 1.3 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1998, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "dribble.h" + +/* When non-zero, it is a stream to write all input characters to for the + duration of this info session. */ +FILE *info_dribble_file = (FILE *)NULL; + +/* Open a dribble file named NAME, perhaps closing an already open one. + This sets the global variable INFO_DRIBBLE_FILE to the open stream. */ +void +open_dribble_file (char *name) +{ + /* Perhaps close existing dribble file. */ + close_dribble_file (); + + /* Keystrokes can be non-printable characters, so we need binary I/O. */ + info_dribble_file = fopen (name, FOPEN_WBIN); + +#if defined (HAVE_SETVBUF) + if (info_dribble_file) +# if defined (SETVBUF_REVERSED) + setvbuf (info_dribble_file, _IONBF, (char *)NULL, 1); +# else + setvbuf (info_dribble_file, (char *)NULL, _IONBF, 1); +# endif /* !SETVBUF_REVERSED */ +#endif /* HAVE_SETVBUF */ +} + +/* If there is a dribble file already open, close it. */ +void +close_dribble_file (void) +{ + if (info_dribble_file) + { + fflush (info_dribble_file); + fclose (info_dribble_file); + info_dribble_file = (FILE *)NULL; + } +} + +/* Write some output to our existing dribble file. */ +void +dribble (unsigned char byte) +{ + if (info_dribble_file) + fwrite (&byte, sizeof (unsigned char), 1, info_dribble_file); +} diff --git a/Build/source/utils/texinfo/info/dribble.h b/Build/source/utils/texinfo/info/dribble.h new file mode 100644 index 00000000000..f30f2040d8e --- /dev/null +++ b/Build/source/utils/texinfo/info/dribble.h @@ -0,0 +1,41 @@ +/* dribble.h -- Functions and vars declared in dribble.c. */ + +/* This file is part of GNU Info, a program for reading online documentation + stored in Info format. + + Copyright (C) 1993, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#if !defined (_DRIBBLE_H_) +#define _DRIBBLE_H_ + +/* When non-zero, it is a stream to write all input characters to for the + duration of this info session. */ +extern FILE *info_dribble_file; + +/* Open a dribble file named NAME, perhaps closing an already open one. + This sets the global variable INFO_DRIBBLE_FILE to the open stream. */ +extern void open_dribble_file (char *name); + +/* If there is a dribble file already open, close it. */ +extern void close_dribble_file (void); + +/* Write some output to our existing dribble file. */ +extern void dribble (unsigned char byte); + +#endif /* !_DRIBBLE_H_ */ diff --git a/Build/source/utils/texinfo/info/echo-area.c b/Build/source/utils/texinfo/info/echo-area.c new file mode 100644 index 00000000000..31385c88a37 --- /dev/null +++ b/Build/source/utils/texinfo/info/echo-area.c @@ -0,0 +1,1524 @@ +/* echo-area.c -- how to read a line in the echo area. + $Id: echo-area.c,v 1.7 2004/12/14 00:15:36 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 1999, 2001, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" + +#if defined (FD_SET) +# if defined (hpux) +# define fd_set_cast(x) (int *)(x) +# else +# define fd_set_cast(x) (fd_set *)(x) +# endif /* !hpux */ +#endif /* FD_SET */ + +/* Non-zero means that C-g was used to quit reading input. */ +int info_aborted_echo_area = 0; + +/* Non-zero means that the echo area is being used to read input. */ +int echo_area_is_active = 0; + +/* The address of the last command executed in the echo area. */ +VFunction *ea_last_executed_command = (VFunction *)NULL; + +/* Non-zero means that the last command executed while reading input + killed some text. */ +int echo_area_last_command_was_kill = 0; + +/* Variables which hold on to the current state of the input line. */ +static char input_line[1 + EA_MAX_INPUT]; +static char *input_line_prompt; +static int input_line_point; +static int input_line_beg; +static int input_line_end; +static NODE input_line_node = { + (char *)NULL, (char *)NULL, (char *)NULL, input_line, + EA_MAX_INPUT, 0, N_IsInternal +}; + +static void echo_area_initialize_node (void); +static void push_echo_area (void), pop_echo_area (void); +static int echo_area_stack_contains_completions_p (void); + +static void ea_kill_text (int from, int to); + +/* Non-zero means we force the user to complete. */ +static int echo_area_must_complete_p = 0; +static int completions_window_p (WINDOW *window); + +/* If non-null, this is a window which was specifically created to display + possible completions output. We remember it so we can delete it when + appropriate. */ +static WINDOW *echo_area_completions_window = (WINDOW *)NULL; + +/* Variables which keep track of the window which was active prior to + entering the echo area. */ +static WINDOW *calling_window = (WINDOW *)NULL; +static NODE *calling_window_node = (NODE *)NULL; +static long calling_window_point = 0; +static long calling_window_pagetop = 0; + +/* Remember the node and pertinent variables of the calling window. */ +static void +remember_calling_window (WINDOW *window) +{ + /* Only do this if the calling window is not the completions window, or, + if it is the completions window and there is no other window. */ + if (!completions_window_p (window) || + ((window == windows) && !(window->next))) + { + calling_window = window; + calling_window_node = window->node; + calling_window_point = window->point; + calling_window_pagetop = window->pagetop; + } +} + +/* Restore the caller's window so that it shows the node that it was showing + on entry to info_read_xxx_echo_area (). */ +static void +restore_calling_window (void) +{ + register WINDOW *win, *compwin = (WINDOW *)NULL; + + /* If the calling window is still visible, and it is the window that + we used for completions output, then restore the calling window. */ + for (win = windows; win; win = win->next) + { + if (completions_window_p (win)) + compwin = win; + + if (win == calling_window && win == compwin) + { + window_set_node_of_window (calling_window, calling_window_node); + calling_window->point = calling_window_point; + calling_window->pagetop = calling_window_pagetop; + compwin = (WINDOW *)NULL; + break; + } + } + + /* Delete the completions window if it is still present, it isn't the + last window on the screen, and there aren't any prior echo area reads + pending which created a completions window. */ + if (compwin) + { + if ((compwin != windows || windows->next) && + !echo_area_stack_contains_completions_p ()) + { + WINDOW *next; + int pagetop = 0; + int start = 0; + int end = 0; + int amount = 0; + + next = compwin->next; + if (next) + { + start = next->first_row; + end = start + next->height; + amount = - (compwin->height + 1); + pagetop = next->pagetop; + } + + info_delete_window_internal (compwin); + + /* This is not necessary because info_delete_window_internal () + calls echo_area_inform_of_deleted_window (), which does the + right thing. */ +#if defined (UNNECESSARY) + echo_area_completions_window = (WINDOW *)NULL; +#endif /* UNNECESSARY */ + + if (next) + { + display_scroll_display (start, end, amount); + next->pagetop = pagetop; + display_update_display (windows); + } + } + } +} + +/* Set up a new input line with PROMPT. */ +static void +initialize_input_line (char *prompt) +{ + input_line_prompt = prompt; + if (prompt) + strcpy (input_line, prompt); + else + input_line[0] = '\0'; + + input_line_beg = input_line_end = input_line_point = strlen (prompt); +} + +static char * +echo_area_after_read (void) +{ + char *return_value; + + if (info_aborted_echo_area) + { + info_aborted_echo_area = 0; + return_value = (char *)NULL; + } + else + { + if (input_line_beg == input_line_end) + return_value = xstrdup (""); + else + { + int line_len = input_line_end - input_line_beg; + return_value = (char *) xmalloc (1 + line_len); + strncpy (return_value, &input_line[input_line_beg], line_len); + return_value[line_len] = '\0'; + } + } + return (return_value); +} + +/* Read a line of text in the echo area. Return a malloc ()'ed string, + or NULL if the user aborted out of this read. WINDOW is the currently + active window, so that we can restore it when we need to. PROMPT, if + non-null, is a prompt to print before reading the line. */ +char * +info_read_in_echo_area (WINDOW *window, char *prompt) +{ + char *line; + + /* If the echo area is already active, remember the current state. */ + if (echo_area_is_active) + push_echo_area (); + + /* Initialize our local variables. */ + initialize_input_line (prompt); + + /* Initialize the echo area for the first (but maybe not the last) time. */ + echo_area_initialize_node (); + + /* Save away the original node of this window, and the window itself, + so echo area commands can temporarily use this window. */ + remember_calling_window (window); + + /* Let the rest of Info know that the echo area is active. */ + echo_area_is_active++; + active_window = the_echo_area; + + /* Read characters in the echo area. */ + info_read_and_dispatch (); + + echo_area_is_active--; + + /* Restore the original active window and show point in it. */ + active_window = calling_window; + restore_calling_window (); + display_cursor_at_point (active_window); + fflush (stdout); + + /* Get the value of the line. */ + line = echo_area_after_read (); + + /* If there is a previous loop waiting for us, restore it now. */ + if (echo_area_is_active) + pop_echo_area (); + + /* Return the results to the caller. */ + return (line); +} + +/* (re) Initialize the echo area node. */ +static void +echo_area_initialize_node (void) +{ + register int i; + + for (i = input_line_end; (unsigned int) i < sizeof (input_line); i++) + input_line[i] = ' '; + + input_line[i - 1] = '\n'; + window_set_node_of_window (the_echo_area, &input_line_node); + input_line[input_line_end] = '\n'; +} + +/* Prepare to read characters in the echo area. This can initialize the + echo area node, but its primary purpose is to side effect the input + line buffer contents. */ +void +echo_area_prep_read (void) +{ + if (the_echo_area->node != &input_line_node) + echo_area_initialize_node (); + + the_echo_area->point = input_line_point; + input_line[input_line_end] = '\n'; + display_update_one_window (the_echo_area); + display_cursor_at_point (active_window); +} + + +/* **************************************************************** */ +/* */ +/* Echo Area Movement Commands */ +/* */ +/* **************************************************************** */ + +DECLARE_INFO_COMMAND (ea_forward, _("Move forward a character")) +{ + if (count < 0) + ea_backward (window, -count, key); + else + { + input_line_point += count; + if (input_line_point > input_line_end) + input_line_point = input_line_end; + } +} + +DECLARE_INFO_COMMAND (ea_backward, _("Move backward a character")) +{ + if (count < 0) + ea_forward (window, -count, key); + else + { + input_line_point -= count; + if (input_line_point < input_line_beg) + input_line_point = input_line_beg; + } +} + +DECLARE_INFO_COMMAND (ea_beg_of_line, _("Move to the start of this line")) +{ + input_line_point = input_line_beg; +} + +DECLARE_INFO_COMMAND (ea_end_of_line, _("Move to the end of this line")) +{ + input_line_point = input_line_end; +} + +#define alphabetic(c) (islower (c) || isupper (c) || isdigit (c)) + +/* Move forward a word in the input line. */ +DECLARE_INFO_COMMAND (ea_forward_word, _("Move forward a word")) +{ + int c; + + if (count < 0) + ea_backward_word (window, -count, key); + else + { + while (count--) + { + if (input_line_point == input_line_end) + return; + + /* If we are not in a word, move forward until we are in one. + Then, move forward until we hit a non-alphabetic character. */ + c = input_line[input_line_point]; + + if (!alphabetic (c)) + { + while (++input_line_point < input_line_end) + { + c = input_line[input_line_point]; + if (alphabetic (c)) + break; + } + } + + if (input_line_point == input_line_end) + return; + + while (++input_line_point < input_line_end) + { + c = input_line[input_line_point]; + if (!alphabetic (c)) + break; + } + } + } +} + +DECLARE_INFO_COMMAND (ea_backward_word, _("Move backward a word")) +{ + int c; + + if (count < 0) + ea_forward_word (window, -count, key); + else + { + while (count--) + { + if (input_line_point == input_line_beg) + return; + + /* Like ea_forward_word (), except that we look at the + characters just before point. */ + + c = input_line[input_line_point - 1]; + + if (!alphabetic (c)) + { + while ((--input_line_point) != input_line_beg) + { + c = input_line[input_line_point - 1]; + if (alphabetic (c)) + break; + } + } + + while (input_line_point != input_line_beg) + { + c = input_line[input_line_point - 1]; + if (!alphabetic (c)) + break; + else + --input_line_point; + } + } + } +} + +DECLARE_INFO_COMMAND (ea_delete, _("Delete the character under the cursor")) +{ + register int i; + + if (count < 0) + ea_rubout (window, -count, key); + else + { + if (input_line_point == input_line_end) + return; + + if (info_explicit_arg || count > 1) + { + int orig_point; + + orig_point = input_line_point; + ea_forward (window, count, key); + ea_kill_text (orig_point, input_line_point); + input_line_point = orig_point; + } + else + { + for (i = input_line_point; i < input_line_end; i++) + input_line[i] = input_line[i + 1]; + + input_line_end--; + } + } +} + +DECLARE_INFO_COMMAND (ea_rubout, _("Delete the character behind the cursor")) +{ + if (count < 0) + ea_delete (window, -count, key); + else + { + int start; + + if (input_line_point == input_line_beg) + return; + + start = input_line_point; + ea_backward (window, count, key); + + if (info_explicit_arg || count > 1) + ea_kill_text (start, input_line_point); + else + ea_delete (window, count, key); + } +} + +DECLARE_INFO_COMMAND (ea_abort, _("Cancel or quit operation")) +{ + /* If any text, just discard it, and restore the calling window's node. + If no text, quit. */ + if (input_line_end != input_line_beg) + { + terminal_ring_bell (); + input_line_end = input_line_point = input_line_beg; + if (calling_window->node != calling_window_node) + restore_calling_window (); + } + else + info_aborted_echo_area = 1; +} + +DECLARE_INFO_COMMAND (ea_newline, _("Accept (or force completion of) this line")) +{ + /* Stub does nothing. Simply here to see if it has been executed. */ +} + +DECLARE_INFO_COMMAND (ea_quoted_insert, _("Insert next character verbatim")) +{ + unsigned char character; + + character = info_get_another_input_char (); + ea_insert (window, count, character); +} + +DECLARE_INFO_COMMAND (ea_insert, _("Insert this character")) +{ + register int i; + + if ((input_line_end + 1) == EA_MAX_INPUT) + { + terminal_ring_bell (); + return; + } + + for (i = input_line_end + 1; i != input_line_point; i--) + input_line[i] = input_line[i - 1]; + + input_line[input_line_point] = key; + input_line_point++; + input_line_end++; +} + +DECLARE_INFO_COMMAND (ea_tab_insert, _("Insert a TAB character")) +{ + ea_insert (window, count, '\t'); +} + +/* Transpose the characters at point. If point is at the end of the line, + then transpose the characters before point. */ +DECLARE_INFO_COMMAND (ea_transpose_chars, _("Transpose characters at point")) +{ + /* Handle conditions that would make it impossible to transpose + characters. */ + if (!count || !input_line_point || (input_line_end - input_line_beg) < 2) + return; + + while (count) + { + int t; + if (input_line_point == input_line_end) + { + t = input_line[input_line_point - 1]; + + input_line[input_line_point - 1] = input_line[input_line_point - 2]; + input_line[input_line_point - 2] = t; + } + else + { + t = input_line[input_line_point]; + + input_line[input_line_point] = input_line[input_line_point - 1]; + input_line[input_line_point - 1] = t; + + if (count < 0 && input_line_point != input_line_beg) + input_line_point--; + else + input_line_point++; + } + + if (count < 0) + count++; + else + count--; + } +} + +/* **************************************************************** */ +/* */ +/* Echo Area Killing and Yanking */ +/* */ +/* **************************************************************** */ + +static char **kill_ring = (char **)NULL; +static int kill_ring_index = 0; /* Number of kills appearing in KILL_RING. */ +static int kill_ring_slots = 0; /* Number of slots allocated to KILL_RING. */ +static int kill_ring_loc = 0; /* Location of current yank pointer. */ + +/* The largest number of kills that we remember at one time. */ +static int max_retained_kills = 15; + +DECLARE_INFO_COMMAND (ea_yank, _("Yank back the contents of the last kill")) +{ + register int i; + register char *text; + + if (!kill_ring_index) + { + inform_in_echo_area ((char *) _("Kill ring is empty")); + return; + } + + text = kill_ring[kill_ring_loc]; + + for (i = 0; text[i]; i++) + ea_insert (window, 1, text[i]); +} + +/* If the last command was yank, or yank_pop, and the text just before + point is identical to the current kill item, then delete that text + from the line, rotate the index down, and yank back some other text. */ +DECLARE_INFO_COMMAND (ea_yank_pop, _("Yank back a previous kill")) +{ + register int len; + + if (((ea_last_executed_command != (VFunction *) ea_yank) && + (ea_last_executed_command != (VFunction *) ea_yank_pop)) || + (kill_ring_index == 0)) + return; + + len = strlen (kill_ring[kill_ring_loc]); + + /* Delete the last yanked item from the line. */ + { + register int i, counter; + + counter = input_line_end - input_line_point; + + for (i = input_line_point - len; counter; i++, counter--) + input_line[i] = input_line[i + len]; + + input_line_end -= len; + input_line_point -= len; + } + + /* Get a previous kill, and yank that. */ + kill_ring_loc--; + if (kill_ring_loc < 0) + kill_ring_loc = kill_ring_index - 1; + + ea_yank (window, count, key); +} + +/* Delete the text from point to end of line. */ +DECLARE_INFO_COMMAND (ea_kill_line, _("Kill to the end of the line")) +{ + if (count < 0) + { + ea_kill_text (input_line_point, input_line_beg); + input_line_point = input_line_beg; + } + else + ea_kill_text (input_line_point, input_line_end); +} + +/* Delete the text from point to beg of line. */ +DECLARE_INFO_COMMAND (ea_backward_kill_line, + _("Kill to the beginning of the line")) +{ + if (count < 0) + ea_kill_text (input_line_point, input_line_end); + else + { + ea_kill_text (input_line_point, input_line_beg); + input_line_point = input_line_beg; + } +} + +/* Delete from point to the end of the current word. */ +DECLARE_INFO_COMMAND (ea_kill_word, _("Kill the word following the cursor")) +{ + int orig_point = input_line_point; + + if (count < 0) + ea_backward_kill_word (window, -count, key); + else + { + ea_forward_word (window, count, key); + + if (input_line_point != orig_point) + ea_kill_text (orig_point, input_line_point); + + input_line_point = orig_point; + } +} + +/* Delete from point to the start of the current word. */ +DECLARE_INFO_COMMAND (ea_backward_kill_word, + _("Kill the word preceding the cursor")) +{ + int orig_point = input_line_point; + + if (count < 0) + ea_kill_word (window, -count, key); + else + { + ea_backward_word (window, count, key); + + if (input_line_point != orig_point) + ea_kill_text (orig_point, input_line_point); + } +} + +/* The way to kill something. This appends or prepends to the last + kill, if the last command was a kill command. If FROM is less + than TO, then the killed text is appended to the most recent kill, + otherwise it is prepended. If the last command was not a kill command, + then a new slot is made for this kill. */ +static void +ea_kill_text (int from, int to) +{ + register int i, counter, distance; + int killing_backwards, slot; + char *killed_text; + + killing_backwards = (from > to); + + /* If killing backwards, reverse the values of FROM and TO. */ + if (killing_backwards) + { + int temp = from; + from = to; + to = temp; + } + + /* Remember the text that we are about to delete. */ + distance = to - from; + killed_text = (char *)xmalloc (1 + distance); + strncpy (killed_text, &input_line[from], distance); + killed_text[distance] = '\0'; + + /* Actually delete the text from the line. */ + counter = input_line_end - to; + + for (i = from; counter; i++, counter--) + input_line[i] = input_line[i + distance]; + + input_line_end -= distance; + + /* If the last command was a kill, append or prepend the killed text to + the last command's killed text. */ + if (echo_area_last_command_was_kill) + { + char *old, *new; + + slot = kill_ring_loc; + old = kill_ring[slot]; + new = (char *)xmalloc (1 + strlen (old) + strlen (killed_text)); + + if (killing_backwards) + { + /* Prepend TEXT to current kill. */ + strcpy (new, killed_text); + strcat (new, old); + } + else + { + /* Append TEXT to current kill. */ + strcpy (new, old); + strcat (new, killed_text); + } + + free (old); + free (killed_text); + kill_ring[slot] = new; + } + else + { + /* Try to store the kill in a new slot, unless that would cause there + to be too many remembered kills. */ + slot = kill_ring_index; + + if (slot == max_retained_kills) + slot = 0; + + if (slot + 1 > kill_ring_slots) + kill_ring = (char **) xrealloc + (kill_ring, + (kill_ring_slots += max_retained_kills) * sizeof (char *)); + + if (slot != kill_ring_index) + free (kill_ring[slot]); + else + kill_ring_index++; + + kill_ring[slot] = killed_text; + + kill_ring_loc = slot; + } + + /* Notice that the last command was a kill. */ + echo_area_last_command_was_kill++; +} + +/* **************************************************************** */ +/* */ +/* Echo Area Completion */ +/* */ +/* **************************************************************** */ + +/* Pointer to an array of REFERENCE to complete over. */ +static REFERENCE **echo_area_completion_items = (REFERENCE **)NULL; + +/* Sorted array of REFERENCE * which is the possible completions found in + the variable echo_area_completion_items. If there is only one element, + it is the only possible completion. */ +static REFERENCE **completions_found = (REFERENCE **)NULL; +static int completions_found_index = 0; +static int completions_found_slots = 0; + +/* The lowest common denominator found while completing. */ +static REFERENCE *LCD_completion; + +/* Internal functions used by the user calls. */ +static void build_completions (void), completions_must_be_rebuilt (void); + +/* Variable which holds the output of completions. */ +static NODE *possible_completions_output_node = (NODE *)NULL; + +static char *compwin_name = "*Completions*"; + +/* Return non-zero if WINDOW is a window used for completions output. */ +static int +completions_window_p (WINDOW *window) +{ + int result = 0; + + if (internal_info_node_p (window->node) && + (strcmp (window->node->nodename, compwin_name) == 0)) + result = 1; + + return (result); +} + +/* Workhorse for completion readers. If FORCE is non-zero, the user cannot + exit unless the line read completes, or is empty. */ +char * +info_read_completing_internal (WINDOW *window, char *prompt, + REFERENCE **completions, int force) +{ + char *line; + + /* If the echo area is already active, remember the current state. */ + if (echo_area_is_active) + push_echo_area (); + + echo_area_must_complete_p = force; + + /* Initialize our local variables. */ + initialize_input_line (prompt); + + /* Initialize the echo area for the first (but maybe not the last) time. */ + echo_area_initialize_node (); + + /* Save away the original node of this window, and the window itself, + so echo area commands can temporarily use this window. */ + remember_calling_window (window); + + /* Save away the list of items to complete over. */ + echo_area_completion_items = completions; + completions_must_be_rebuilt (); + + active_window = the_echo_area; + echo_area_is_active++; + + /* Read characters in the echo area. */ + while (1) + { + info_read_and_dispatch (); + + line = echo_area_after_read (); + + /* Force the completion to take place if the user hasn't accepted + a default or aborted, and if FORCE is active. */ + if (force && line && *line && completions) + { + register int i; + + build_completions (); + + /* If there is only one completion, then make the line be that + completion. */ + if (completions_found_index == 1) + { + free (line); + line = xstrdup (completions_found[0]->label); + break; + } + + /* If one of the completions matches exactly, then that is okay, so + return the current line. */ + for (i = 0; i < completions_found_index; i++) + if (strcasecmp (completions_found[i]->label, line) == 0) + { + free (line); + line = xstrdup (completions_found[i]->label); + break; + } + + /* If no match, go back and try again. */ + if (i == completions_found_index) + { + if (!completions_found_index) + inform_in_echo_area ((char *) _("No completions")); + else + inform_in_echo_area ((char *) _("Not complete")); + continue; + } + } + break; + } + echo_area_is_active--; + + /* Restore the original active window and show point in it. */ + active_window = calling_window; + restore_calling_window (); + display_cursor_at_point (active_window); + fflush (stdout); + + echo_area_completion_items = (REFERENCE **)NULL; + completions_must_be_rebuilt (); + + /* If there is a previous loop waiting for us, restore it now. */ + if (echo_area_is_active) + pop_echo_area (); + + return (line); +} + +/* Read a line in the echo area with completion over COMPLETIONS. */ +char * +info_read_completing_in_echo_area (WINDOW *window, + char *prompt, REFERENCE **completions) +{ + return (info_read_completing_internal (window, prompt, completions, 1)); +} + +/* Read a line in the echo area allowing completion over COMPLETIONS, but + not requiring it. */ +char * +info_read_maybe_completing (WINDOW *window, + char *prompt, REFERENCE **completions) +{ + return (info_read_completing_internal (window, prompt, completions, 0)); +} + +DECLARE_INFO_COMMAND (ea_possible_completions, _("List possible completions")) +{ + if (!echo_area_completion_items) + { + ea_insert (window, count, key); + return; + } + + build_completions (); + + if (!completions_found_index) + { + terminal_ring_bell (); + inform_in_echo_area ((char *) _("No completions")); + } + else if ((completions_found_index == 1) && (key != '?')) + { + inform_in_echo_area ((char *) _("Sole completion")); + } + else + { + register int i, l; + int limit, iterations, max_label = 0; + + initialize_message_buffer (); + printf_to_message_buffer (completions_found_index == 1 + ? (char *) _("One completion:\n") + : (char *) _("%d completions:\n"), + (void *) (long) completions_found_index, + NULL, NULL); + + /* Find the maximum length of a label. */ + for (i = 0; i < completions_found_index; i++) + { + int len = strlen (completions_found[i]->label); + if (len > max_label) + max_label = len; + } + + max_label += 4; + + /* Find out how many columns we should print in. */ + limit = calling_window->width / max_label; + if (limit != 1 && (limit * max_label == calling_window->width)) + limit--; + + /* Avoid a possible floating exception. If max_label > width then + the limit will be 0 and a divide-by-zero fault will result. */ + if (limit == 0) + limit = 1; + + /* How many iterations of the printing loop? */ + iterations = (completions_found_index + (limit - 1)) / limit; + + /* Watch out for special case. If the number of completions is less + than LIMIT, then just do the inner printing loop. */ + if (completions_found_index < limit) + iterations = 1; + + /* Print the sorted items, up-and-down alphabetically. */ + for (i = 0; i < iterations; i++) + { + register int j; + + for (j = 0, l = i; j < limit; j++) + { + if (l >= completions_found_index) + break; + else + { + char *label; + int printed_length, k; + + label = completions_found[l]->label; + printed_length = strlen (label); + printf_to_message_buffer ("%s", label, NULL, NULL); + + if (j + 1 < limit) + { + for (k = 0; k < max_label - printed_length; k++) + printf_to_message_buffer (" ", NULL, NULL, NULL); + } + } + l += iterations; + } + printf_to_message_buffer ("\n", NULL, NULL, NULL); + } + + /* Make a new node to hold onto possible completions. Don't destroy + dangling pointers. */ + { + NODE *temp; + + temp = message_buffer_to_node (); + add_gcable_pointer (temp->contents); + name_internal_node (temp, compwin_name); + possible_completions_output_node = temp; + } + + /* Find a suitable window for displaying the completions output. + First choice is an existing window showing completions output. + If there is only one window, and it is large, make another + (smaller) window, and use that one. Otherwise, use the caller's + window. */ + { + WINDOW *compwin; + + compwin = get_internal_info_window (compwin_name); + + if (!compwin) + { + /* If we can split the window to display most of the completion + items, then do so. */ + if (calling_window->height > (iterations * 2) + && calling_window->height / 2 >= WINDOW_MIN_SIZE) + { + int start, pagetop; +#ifdef SPLIT_BEFORE_ACTIVE + int end; +#endif + + active_window = calling_window; + + /* Perhaps we can scroll this window on redisplay. */ + start = calling_window->first_row; + pagetop = calling_window->pagetop; + + compwin = + window_make_window (possible_completions_output_node); + active_window = the_echo_area; + window_change_window_height + (compwin, -(compwin->height - (iterations + 2))); + + window_adjust_pagetop (calling_window); + remember_calling_window (calling_window); + +#if defined (SPLIT_BEFORE_ACTIVE) + /* If the pagetop hasn't changed, scrolling the calling + window is a reasonable thing to do. */ + if (pagetop == calling_window->pagetop) + { + end = start + calling_window->height; + display_scroll_display + (start, end, calling_window->prev->height + 1); + } +#else /* !SPLIT_BEFORE_ACTIVE */ + /* If the pagetop has changed, set the new pagetop here. */ + if (pagetop != calling_window->pagetop) + { + int newtop = calling_window->pagetop; + calling_window->pagetop = pagetop; + set_window_pagetop (calling_window, newtop); + } +#endif /* !SPLIT_BEFORE_ACTIVE */ + + echo_area_completions_window = compwin; + remember_window_and_node (compwin, compwin->node); + } + else + compwin = calling_window; + } + + if (compwin->node != possible_completions_output_node) + { + window_set_node_of_window + (compwin, possible_completions_output_node); + remember_window_and_node (compwin, compwin->node); + } + + display_update_display (windows); + } + } +} + +DECLARE_INFO_COMMAND (ea_complete, _("Insert completion")) +{ + if (!echo_area_completion_items) + { + ea_insert (window, count, key); + return; + } + + /* If KEY is SPC, and we are not forcing completion to take place, simply + insert the key. */ + if (!echo_area_must_complete_p && key == SPC) + { + ea_insert (window, count, key); + return; + } + + if (ea_last_executed_command == (VFunction *) ea_complete) + { + /* If the keypress is a SPC character, and we have already tried + completing once, and there are several completions, then check + the batch of completions to see if any continue with a space. + If there are some, insert the space character and continue. */ + if (key == SPC && completions_found_index > 1) + { + register int i, offset; + + offset = input_line_end - input_line_beg; + + for (i = 0; i < completions_found_index; i++) + if (completions_found[i]->label[offset] == ' ') + break; + + if (completions_found[i]) + ea_insert (window, 1, ' '); + else + { + ea_possible_completions (window, count, key); + return; + } + } + else + { + ea_possible_completions (window, count, key); + return; + } + } + + input_line_point = input_line_end; + build_completions (); + + if (!completions_found_index) + terminal_ring_bell (); + else if (LCD_completion->label[0] == '\0') + ea_possible_completions (window, count, key); + else + { + register int i; + input_line_point = input_line_end = input_line_beg; + for (i = 0; LCD_completion->label[i]; i++) + ea_insert (window, 1, LCD_completion->label[i]); + } +} + +/* Utility REFERENCE used to store possible LCD. */ +static REFERENCE LCD_reference = { + (char *)NULL, (char *)NULL, (char *)NULL, 0, 0, 0 +}; + +static void remove_completion_duplicates (void); + +/* Variables which remember the state of the most recent call + to build_completions (). */ +static char *last_completion_request = (char *)NULL; +static REFERENCE **last_completion_items = (REFERENCE **)NULL; + +/* How to tell the completion builder to reset internal state. */ +static void +completions_must_be_rebuilt (void) +{ + maybe_free (last_completion_request); + last_completion_request = (char *)NULL; + last_completion_items = (REFERENCE **)NULL; +} + +/* Build a list of possible completions from echo_area_completion_items, + and the contents of input_line. */ +static void +build_completions (void) +{ + register int i, len; + register REFERENCE *entry; + char *request; + int informed_of_lengthy_job = 0; + + /* If there are no items to complete over, exit immediately. */ + if (!echo_area_completion_items) + { + completions_found_index = 0; + LCD_completion = (REFERENCE *)NULL; + return; + } + + /* Check to see if this call to build completions is the same as the last + call to build completions. */ + len = input_line_end - input_line_beg; + request = (char *)xmalloc (1 + len); + strncpy (request, &input_line[input_line_beg], len); + request[len] = '\0'; + + if (last_completion_request && last_completion_items && + last_completion_items == echo_area_completion_items && + (strcmp (last_completion_request, request) == 0)) + { + free (request); + return; + } + + maybe_free (last_completion_request); + last_completion_request = request; + last_completion_items = echo_area_completion_items; + + /* Always start at the beginning of the list. */ + completions_found_index = 0; + LCD_completion = (REFERENCE *)NULL; + + for (i = 0; (entry = echo_area_completion_items[i]); i++) + { + if (strncasecmp (request, entry->label, len) == 0) + add_pointer_to_array (entry, completions_found_index, + completions_found, completions_found_slots, + 20, REFERENCE *); + + if (!informed_of_lengthy_job && completions_found_index > 100) + { + informed_of_lengthy_job = 1; + window_message_in_echo_area ((char *) _("Building completions..."), + NULL, NULL); + } + } + + if (!completions_found_index) + return; + + /* Sort and prune duplicate entries from the completions array. */ + remove_completion_duplicates (); + + /* If there is only one completion, just return that. */ + if (completions_found_index == 1) + { + LCD_completion = completions_found[0]; + return; + } + + /* Find the least common denominator. */ + { + long shortest = 100000; + + for (i = 1; i < completions_found_index; i++) + { + register int j; + int c1, c2; + + for (j = 0; + (c1 = info_tolower (completions_found[i - 1]->label[j])) && + (c2 = info_tolower (completions_found[i]->label[j])); + j++) + if (c1 != c2) + break; + + if (shortest > j) + shortest = j; + } + + maybe_free (LCD_reference.label); + LCD_reference.label = (char *)xmalloc (1 + shortest); + /* Since both the sorting done inside remove_completion_duplicates + and all the comparisons above are case-insensitive, it's + possible that the completion we are going to return is + identical to what the user typed but for the letter-case. This + is confusing, since the user could type FOOBAR<TAB> and get her + string change letter-case for no good reason. So try to find a + possible completion whose letter-case is identical, and if so, + use that. */ + if (completions_found_index > 1) + { + int req_len = strlen (request); + + for (i = 0; i < completions_found_index; i++) + if (strncmp (request, completions_found[i]->label, req_len) == 0) + break; + /* If none of the candidates match exactly, use the first one. */ + if (i >= completions_found_index) + i = 0; + } + strncpy (LCD_reference.label, completions_found[i]->label, shortest); + LCD_reference.label[shortest] = '\0'; + LCD_completion = &LCD_reference; + } + + if (informed_of_lengthy_job) + echo_area_initialize_node (); +} + +/* Function called by qsort. */ +static int +compare_references (const void *entry1, const void *entry2) +{ + REFERENCE **e1 = (REFERENCE **) entry1; + REFERENCE **e2 = (REFERENCE **) entry2; + + return (strcasecmp ((*e1)->label, (*e2)->label)); +} + +/* Prune duplicate entries from COMPLETIONS_FOUND. */ +static void +remove_completion_duplicates (void) +{ + register int i, j; + REFERENCE **temp; + int newlen; + + if (!completions_found_index) + return; + + /* Sort the items. */ + qsort (completions_found, completions_found_index, sizeof (REFERENCE *), + compare_references); + + for (i = 0, newlen = 1; i < completions_found_index - 1; i++) + { + if (strcmp (completions_found[i]->label, + completions_found[i + 1]->label) == 0) + completions_found[i] = (REFERENCE *)NULL; + else + newlen++; + } + + /* We have marked all the dead slots. It is faster to copy the live slots + twice than to prune the dead slots one by one. */ + temp = (REFERENCE **)xmalloc ((1 + newlen) * sizeof (REFERENCE *)); + for (i = 0, j = 0; i < completions_found_index; i++) + if (completions_found[i]) + temp[j++] = completions_found[i]; + + for (i = 0; i < newlen; i++) + completions_found[i] = temp[i]; + + completions_found[i] = (REFERENCE *)NULL; + completions_found_index = newlen; + free (temp); +} + +/* Scroll the "other" window. If there is a window showing completions, scroll + that one, otherwise scroll the window which was active on entering the read + function. */ +DECLARE_INFO_COMMAND (ea_scroll_completions_window, _("Scroll the completions window")) +{ + WINDOW *compwin; + int old_pagetop; + + compwin = get_internal_info_window (compwin_name); + + if (!compwin) + compwin = calling_window; + + old_pagetop = compwin->pagetop; + + /* Let info_scroll_forward () do the work, and print any messages that + need to be displayed. */ + info_scroll_forward (compwin, count, key); +} + +/* Function which gets called when an Info window is deleted while the + echo area is active. WINDOW is the window which has just been deleted. */ +void +echo_area_inform_of_deleted_window (WINDOW *window) +{ + /* If this is the calling_window, forget what we remembered about it. */ + if (window == calling_window) + { + if (active_window != the_echo_area) + remember_calling_window (active_window); + else + remember_calling_window (windows); + } + + /* If this window was the echo_area_completions_window, then notice that + the window has been deleted. */ + if (window == echo_area_completions_window) + echo_area_completions_window = (WINDOW *)NULL; +} + +/* **************************************************************** */ +/* */ +/* Pushing and Popping the Echo Area */ +/* */ +/* **************************************************************** */ + +/* Push and Pop the echo area. */ +typedef struct { + char *line; + char *prompt; + REFERENCE **comp_items; + int point, beg, end; + int must_complete; + NODE node; + WINDOW *compwin; +} PUSHED_EA; + +static PUSHED_EA **pushed_echo_areas = (PUSHED_EA **)NULL; +static int pushed_echo_areas_index = 0; +static int pushed_echo_areas_slots = 0; + +/* Pushing the echo_area has a side effect of zeroing the completion_items. */ +static void +push_echo_area (void) +{ + PUSHED_EA *pushed; + + pushed = (PUSHED_EA *)xmalloc (sizeof (PUSHED_EA)); + pushed->line = xstrdup (input_line); + pushed->prompt = input_line_prompt; + pushed->point = input_line_point; + pushed->beg = input_line_beg; + pushed->end = input_line_end; + pushed->node = input_line_node; + pushed->comp_items = echo_area_completion_items; + pushed->must_complete = echo_area_must_complete_p; + pushed->compwin = echo_area_completions_window; + + add_pointer_to_array (pushed, pushed_echo_areas_index, pushed_echo_areas, + pushed_echo_areas_slots, 4, PUSHED_EA *); + + echo_area_completion_items = (REFERENCE **)NULL; +} + +static void +pop_echo_area (void) +{ + PUSHED_EA *popped; + + popped = pushed_echo_areas[--pushed_echo_areas_index]; + + strcpy (input_line, popped->line); + free (popped->line); + input_line_prompt = popped->prompt; + input_line_point = popped->point; + input_line_beg = popped->beg; + input_line_end = popped->end; + input_line_node = popped->node; + echo_area_completion_items = popped->comp_items; + echo_area_must_complete_p = popped->must_complete; + echo_area_completions_window = popped->compwin; + completions_must_be_rebuilt (); + + /* If the completion window no longer exists, forget about it. */ + if (echo_area_completions_window) + { + register WINDOW *win; + + for (win = windows; win; win = win->next) + if (echo_area_completions_window == win) + break; + + /* If the window wasn't found, then it has already been deleted. */ + if (!win) + echo_area_completions_window = (WINDOW *)NULL; + } + + free (popped); +} + +/* Returns non-zero if any of the prior stacked calls to read in the echo + area produced a completions window. */ +static int +echo_area_stack_contains_completions_p (void) +{ + register int i; + + for (i = 0; i < pushed_echo_areas_index; i++) + if (pushed_echo_areas[i]->compwin) + return (1); + + return (0); +} + +/* **************************************************************** */ +/* */ +/* Error Messages While Reading in Echo Area */ +/* */ +/* **************************************************************** */ + +#if defined (HAVE_SYS_TIME_H) +# include <sys/time.h> +# define HAVE_STRUCT_TIMEVAL +#endif /* HAVE_SYS_TIME_H */ + +static void +pause_or_input (void) +{ +#ifdef FD_SET + struct timeval timer; + fd_set readfds; + int ready; + + FD_ZERO (&readfds); + FD_SET (fileno (stdin), &readfds); + timer.tv_sec = 2; + timer.tv_usec = 0; + ready = select (fileno (stdin) + 1, &readfds, (fd_set *) NULL, + (fd_set *) NULL, &timer); +#endif /* FD_SET */ +} + +/* Print MESSAGE right after the end of the current line, and wait + for input or a couple of seconds, whichever comes first. Then flush the + informational message that was printed. */ +void +inform_in_echo_area (const char *message) +{ + int i; + char *text; + int avail = EA_MAX_INPUT + 1 - input_line_end; + + text = xstrdup (message); + for (i = 0; text[i] && text[i] != '\n' && i < avail; i++) + ; + text[i] = 0; + + echo_area_initialize_node (); + sprintf (&input_line[input_line_end], "%s[%s]\n", + echo_area_is_active ? " ": "", text); + free (text); + the_echo_area->point = input_line_point; + display_update_one_window (the_echo_area); + display_cursor_at_point (active_window); + fflush (stdout); + pause_or_input (); + echo_area_initialize_node (); +} diff --git a/Build/source/utils/texinfo/info/echo-area.h b/Build/source/utils/texinfo/info/echo-area.h new file mode 100644 index 00000000000..31784282485 --- /dev/null +++ b/Build/source/utils/texinfo/info/echo-area.h @@ -0,0 +1,85 @@ +/* echo-area.h -- Functions used in reading information from the echo area. + $Id: echo-area.h,v 1.4 2004/08/07 22:03:08 karl Exp $ + + This file is part of GNU Info, a program for reading online documentation + stored in Info format. + + Copyright (C) 1993, 1997, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_ECHO_AREA_H +#define INFO_ECHO_AREA_H + +#define EA_MAX_INPUT 256 + +extern int echo_area_is_active, info_aborted_echo_area; + +/* Non-zero means that the last command executed while reading input + killed some text. */ +extern int echo_area_last_command_was_kill; + +extern void inform_in_echo_area (const char *message); +extern void echo_area_inform_of_deleted_window (WINDOW *window); +extern void echo_area_prep_read (void); +extern VFunction *ea_last_executed_command; +extern char * info_read_completing_internal (WINDOW *window, char *prompt, + REFERENCE **completions, int force); + +/* Read a line of text in the echo area. Return a malloc ()'ed string, + or NULL if the user aborted out of this read. WINDOW is the currently + active window, so that we can restore it when we need to. PROMPT, if + non-null, is a prompt to print before reading the line. */ +extern char *info_read_in_echo_area (WINDOW *window, char *prompt); + +/* Read a line in the echo area with completion over COMPLETIONS. + Takes arguments of WINDOW, PROMPT, and COMPLETIONS, a REFERENCE **. */ +char *info_read_completing_in_echo_area (WINDOW *window, + char *prompt, REFERENCE **completions); + +/* Read a line in the echo area allowing completion over COMPLETIONS, but + not requiring it. Takes arguments of WINDOW, PROMPT, and COMPLETIONS, + a REFERENCE **. */ +extern char *info_read_maybe_completing (WINDOW *window, + char *prompt, REFERENCE **completions); + +extern void ea_insert (WINDOW *window, int count, unsigned char key); +extern void ea_quoted_insert (WINDOW *window, int count, unsigned char key); +extern void ea_beg_of_line (WINDOW *window, int count, unsigned char key); +extern void ea_backward (WINDOW *window, int count, unsigned char key); +extern void ea_delete (WINDOW *window, int count, unsigned char key); +extern void ea_end_of_line (WINDOW *window, int count, unsigned char key); +extern void ea_forward (WINDOW *window, int count, unsigned char key); +extern void ea_abort (WINDOW *window, int count, unsigned char key); +extern void ea_rubout (WINDOW *window, int count, unsigned char key); +extern void ea_complete (WINDOW *window, int count, unsigned char key); +extern void ea_newline (WINDOW *window, int count, unsigned char key); +extern void ea_kill_line (WINDOW *window, int count, unsigned char key); +extern void ea_backward_kill_line (WINDOW *window, int count, unsigned char key); +extern void ea_transpose_chars (WINDOW *window, int count, unsigned char key); +extern void ea_yank (WINDOW *window, int count, unsigned char key); +extern void ea_tab_insert (WINDOW *window, int count, unsigned char key); +extern void ea_possible_completions (WINDOW *window, int count, unsigned char key); +extern void ea_backward_word (WINDOW *window, int count, unsigned char key); +extern void ea_kill_word (WINDOW *window, int count, unsigned char key); +extern void ea_forward_word (WINDOW *window, int count, unsigned char key); +extern void ea_yank_pop (WINDOW *window, int count, unsigned char key); +extern void ea_backward_kill_word (WINDOW *window, int count, unsigned char key); +extern void ea_scroll_completions_window (WINDOW *window, int count, + unsigned char key); + +#endif /* not INFO_ECHO_AREA_H */ diff --git a/Build/source/utils/texinfo/info/filesys.c b/Build/source/utils/texinfo/info/filesys.c new file mode 100644 index 00000000000..81344806456 --- /dev/null +++ b/Build/source/utils/texinfo/info/filesys.c @@ -0,0 +1,711 @@ +/* filesys.c -- filesystem specific functions. + $Id: filesys.c,v 1.6 2004/07/30 17:17:40 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2000, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" + +#include "tilde.h" +#include "filesys.h" + +/* Local to this file. */ +static char *info_file_in_path (char *filename, char *path); +static char *lookup_info_filename (char *filename); +static char *info_absolute_file (char *fname); + +static void remember_info_filename (char *filename, char *expansion); +static void maybe_initialize_infopath (void); + +typedef struct +{ + char *suffix; + char *decompressor; +} COMPRESSION_ALIST; + +static char *info_suffixes[] = { + ".info", + "-info", + "/index", + ".inf", /* 8+3 file on filesystem which supports long file names */ +#ifdef __MSDOS__ + /* 8+3 file names strike again... */ + ".in", /* for .inz, .igz etc. */ + ".i", +#endif + "", + NULL +}; + +static COMPRESSION_ALIST compress_suffixes[] = { + { ".gz", "gunzip" }, + { ".bz2", "bunzip2" }, + { ".z", "gunzip" }, + { ".Z", "uncompress" }, + { ".Y", "unyabba" }, +#ifdef __MSDOS__ + { "gz", "gunzip" }, + { "z", "gunzip" }, +#endif + { (char *)NULL, (char *)NULL } +}; + +/* The path on which we look for info files. You can initialize this + from the environment variable INFOPATH if there is one, or you can + call info_add_path () to add paths to the beginning or end of it. + You can call zap_infopath () to make the path go away. */ +char *infopath = (char *)NULL; +static int infopath_size = 0; + +/* Expand the filename in PARTIAL to make a real name for this operating + system. This looks in INFO_PATHS in order to find the correct file. + If it can't find the file, it returns NULL. */ +static char *local_temp_filename = (char *)NULL; +static int local_temp_filename_size = 0; + +char * +info_find_fullpath (char *partial) +{ + int initial_character; + char *temp; + + filesys_error_number = 0; + + maybe_initialize_infopath (); + + if (partial && (initial_character = *partial)) + { + char *expansion; + + expansion = lookup_info_filename (partial); + + if (expansion) + return (expansion); + + /* If we have the full path to this file, we still may have to add + various extensions to it. I guess we have to stat this file + after all. */ + if (IS_ABSOLUTE (partial)) + temp = info_absolute_file (partial); + else if (initial_character == '~') + { + expansion = tilde_expand_word (partial); + if (IS_ABSOLUTE (expansion)) + { + temp = info_absolute_file (expansion); + free (expansion); + } + else + temp = expansion; + } + else if (initial_character == '.' && + (IS_SLASH (partial[1]) || + (partial[1] == '.' && IS_SLASH (partial[2])))) + { + if (local_temp_filename_size < 1024) + local_temp_filename = (char *)xrealloc + (local_temp_filename, (local_temp_filename_size = 1024)); +#if defined (HAVE_GETCWD) + if (!getcwd (local_temp_filename, local_temp_filename_size)) +#else /* !HAVE_GETCWD */ + if (!getwd (local_temp_filename)) +#endif /* !HAVE_GETCWD */ + { + filesys_error_number = errno; + return (partial); + } + + strcat (local_temp_filename, "/"); + strcat (local_temp_filename, partial); + temp = info_absolute_file (local_temp_filename); /* try extensions */ + if (!temp) + partial = local_temp_filename; + } + else + temp = info_file_in_path (partial, infopath); + + if (temp) + { + remember_info_filename (partial, temp); + if (strlen (temp) > (unsigned int) local_temp_filename_size) + local_temp_filename = (char *) xrealloc + (local_temp_filename, + (local_temp_filename_size = (50 + strlen (temp)))); + strcpy (local_temp_filename, temp); + free (temp); + return (local_temp_filename); + } + } + return (partial); +} + +/* Scan the list of directories in PATH looking for FILENAME. If we find + one that is a regular file, return it as a new string. Otherwise, return + a NULL pointer. */ +static char * +info_file_in_path (char *filename, char *path) +{ + struct stat finfo; + char *temp_dirname; + int statable, dirname_index; + + /* Reject ridiculous cases up front, to prevent infinite recursion + later on. E.g., someone might say "info '(.)foo'"... */ + if (!*filename || STREQ (filename, ".") || STREQ (filename, "..")) + return NULL; + + dirname_index = 0; + + while ((temp_dirname = extract_colon_unit (path, &dirname_index))) + { + register int i, pre_suffix_length; + char *temp; + + /* Expand a leading tilde if one is present. */ + if (*temp_dirname == '~') + { + char *expanded_dirname; + + expanded_dirname = tilde_expand_word (temp_dirname); + free (temp_dirname); + temp_dirname = expanded_dirname; + } + + temp = (char *)xmalloc (30 + strlen (temp_dirname) + strlen (filename)); + strcpy (temp, temp_dirname); + if (!IS_SLASH (temp[(strlen (temp)) - 1])) + strcat (temp, "/"); + strcat (temp, filename); + + pre_suffix_length = strlen (temp); + + free (temp_dirname); + + for (i = 0; info_suffixes[i]; i++) + { + strcpy (temp + pre_suffix_length, info_suffixes[i]); + + statable = (stat (temp, &finfo) == 0); + + /* If we have found a regular file, then use that. Else, if we + have found a directory, look in that directory for this file. */ + if (statable) + { + if (S_ISREG (finfo.st_mode)) + { + return (temp); + } + else if (S_ISDIR (finfo.st_mode)) + { + char *newpath, *filename_only, *newtemp; + + newpath = xstrdup (temp); + filename_only = filename_non_directory (filename); + newtemp = info_file_in_path (filename_only, newpath); + + free (newpath); + if (newtemp) + { + free (temp); + return (newtemp); + } + } + } + else + { + /* Add various compression suffixes to the name to see if + the file is present in compressed format. */ + register int j, pre_compress_suffix_length; + + pre_compress_suffix_length = strlen (temp); + + for (j = 0; compress_suffixes[j].suffix; j++) + { + strcpy (temp + pre_compress_suffix_length, + compress_suffixes[j].suffix); + + statable = (stat (temp, &finfo) == 0); + if (statable && (S_ISREG (finfo.st_mode))) + return (temp); + } + } + } + free (temp); + } + return ((char *)NULL); +} + +/* Assume FNAME is an absolute file name, and check whether it is + a regular file. If it is, return it as a new string; otherwise + return a NULL pointer. We do it by taking the file name apart + into its directory and basename parts, and calling info_file_in_path.*/ +static char * +info_absolute_file (char *fname) +{ + char *containing_dir = xstrdup (fname); + char *base = filename_non_directory (containing_dir); + + if (base > containing_dir) + base[-1] = '\0'; + + return info_file_in_path (filename_non_directory (fname), containing_dir); +} + + +/* Given a string containing units of information separated by the + PATH_SEP character, return the next one after IDX, or NULL if there + are no more. Advance IDX to the character after the colon. */ + +char * +extract_colon_unit (char *string, int *idx) +{ + unsigned int i = (unsigned int) *idx; + unsigned int start = i; + + if (!string || i >= strlen (string)) + return NULL; + + if (!string[i]) /* end of string */ + return NULL; + + /* Advance to next PATH_SEP. */ + while (string[i] && string[i] != PATH_SEP[0]) + i++; + + { + char *value = xmalloc ((i - start) + 1); + strncpy (value, &string[start], (i - start)); + value[i - start] = 0; + + i++; /* move past PATH_SEP */ + *idx = i; + return value; + } +} + +/* A structure which associates a filename with its expansion. */ +typedef struct +{ + char *filename; + char *expansion; +} FILENAME_LIST; + +/* An array of remembered arguments and results. */ +static FILENAME_LIST **names_and_files = (FILENAME_LIST **)NULL; +static int names_and_files_index = 0; +static int names_and_files_slots = 0; + +/* Find the result for having already called info_find_fullpath () with + FILENAME. */ +static char * +lookup_info_filename (char *filename) +{ + if (filename && names_and_files) + { + register int i; + for (i = 0; names_and_files[i]; i++) + { + if (FILENAME_CMP (names_and_files[i]->filename, filename) == 0) + return (names_and_files[i]->expansion); + } + } + return (char *)NULL;; +} + +/* Add a filename and its expansion to our list. */ +static void +remember_info_filename (char *filename, char *expansion) +{ + FILENAME_LIST *new; + + if (names_and_files_index + 2 > names_and_files_slots) + { + int alloc_size; + names_and_files_slots += 10; + + alloc_size = names_and_files_slots * sizeof (FILENAME_LIST *); + + names_and_files = + (FILENAME_LIST **) xrealloc (names_and_files, alloc_size); + } + + new = (FILENAME_LIST *)xmalloc (sizeof (FILENAME_LIST)); + new->filename = xstrdup (filename); + new->expansion = expansion ? xstrdup (expansion) : (char *)NULL; + + names_and_files[names_and_files_index++] = new; + names_and_files[names_and_files_index] = (FILENAME_LIST *)NULL; +} + +static void +maybe_initialize_infopath (void) +{ + if (!infopath_size) + { + infopath = (char *) + xmalloc (infopath_size = (1 + strlen (DEFAULT_INFOPATH))); + + strcpy (infopath, DEFAULT_INFOPATH); + } +} + +/* Add PATH to the list of paths found in INFOPATH. 2nd argument says + whether to put PATH at the front or end of INFOPATH. */ +void +info_add_path (char *path, int where) +{ + int len; + + if (!infopath) + { + infopath = (char *)xmalloc (infopath_size = 200 + strlen (path)); + infopath[0] = '\0'; + } + + len = strlen (path) + strlen (infopath); + + if (len + 2 >= infopath_size) + infopath = (char *)xrealloc (infopath, (infopath_size += (2 * len) + 2)); + + if (!*infopath) + strcpy (infopath, path); + else if (where == INFOPATH_APPEND) + { + strcat (infopath, PATH_SEP); + strcat (infopath, path); + } + else if (where == INFOPATH_PREPEND) + { + char *temp = xstrdup (infopath); + strcpy (infopath, path); + strcat (infopath, PATH_SEP); + strcat (infopath, temp); + free (temp); + } +} + +/* Make INFOPATH have absolutely nothing in it. */ +void +zap_infopath (void) +{ + if (infopath) + free (infopath); + + infopath = (char *)NULL; + infopath_size = 0; +} + +/* Given a chunk of text and its length, convert all CRLF pairs at every + end-of-line into a single Newline character. Return the length of + produced text. + + This is required because the rest of code is too entrenched in having + a single newline at each EOL; in particular, searching for various + Info headers and cookies can become extremely tricky if that assumption + breaks. + + FIXME: this could also support Mac-style text files with a single CR + at the EOL, but what about random CR characters in non-Mac files? Can + we afford converting them into newlines as well? Maybe implement some + heuristics here, like in Emacs 20. + + FIXME: is it a good idea to show the EOL type on the modeline? */ +long +convert_eols (char *text, long int textlen) +{ + register char *s = text; + register char *d = text; + + while (textlen--) + { + if (*s == '\r' && textlen && s[1] == '\n') + { + s++; + textlen--; + } + *d++ = *s++; + } + + return (long)(d - text); +} + +/* Read the contents of PATHNAME, returning a buffer with the contents of + that file in it, and returning the size of that buffer in FILESIZE. + FINFO is a stat struct which has already been filled in by the caller. + If the file turns out to be compressed, set IS_COMPRESSED to non-zero. + If the file cannot be read, return a NULL pointer. */ +char * +filesys_read_info_file (char *pathname, long int *filesize, + struct stat *finfo, int *is_compressed) +{ + long st_size; + + *filesize = filesys_error_number = 0; + + if (compressed_filename_p (pathname)) + { + *is_compressed = 1; + return (filesys_read_compressed (pathname, filesize)); + } + else + { + int descriptor; + char *contents; + + *is_compressed = 0; + descriptor = open (pathname, O_RDONLY | O_BINARY, 0666); + + /* If the file couldn't be opened, give up. */ + if (descriptor < 0) + { + filesys_error_number = errno; + return ((char *)NULL); + } + + /* Try to read the contents of this file. */ + st_size = (long) finfo->st_size; + contents = (char *)xmalloc (1 + st_size); + if ((read (descriptor, contents, st_size)) != st_size) + { + filesys_error_number = errno; + close (descriptor); + free (contents); + return ((char *)NULL); + } + + close (descriptor); + + /* Convert any DOS-style CRLF EOLs into Unix-style NL. + Seems like a good idea to have even on Unix, in case the Info + files are coming from some Windows system across a network. */ + *filesize = convert_eols (contents, st_size); + + /* EOL conversion can shrink the text quite a bit. We don't + want to waste storage. */ + if (*filesize < st_size) + contents = (char *)xrealloc (contents, 1 + *filesize); + contents[*filesize] = '\0'; + + return (contents); + } +} + +/* Typically, pipe buffers are 4k. */ +#define BASIC_PIPE_BUFFER (4 * 1024) + +/* We use some large multiple of that. */ +#define FILESYS_PIPE_BUFFER_SIZE (16 * BASIC_PIPE_BUFFER) + +char * +filesys_read_compressed (char *pathname, long int *filesize) +{ + FILE *stream; + char *command, *decompressor; + char *contents = (char *)NULL; + + *filesize = filesys_error_number = 0; + + decompressor = filesys_decompressor_for_file (pathname); + + if (!decompressor) + return ((char *)NULL); + + command = (char *)xmalloc (15 + strlen (pathname) + strlen (decompressor)); + /* Explicit .exe suffix makes the diagnostics of `popen' + better on systems where COMMAND.COM is the stock shell. */ + sprintf (command, "%s%s < %s", + decompressor, STRIP_DOT_EXE ? ".exe" : "", pathname); + +#if !defined (BUILDING_LIBRARY) + if (info_windows_initialized_p) + { + char *temp; + + temp = (char *)xmalloc (5 + strlen (command)); + sprintf (temp, "%s...", command); + message_in_echo_area ("%s", temp, NULL); + free (temp); + } +#endif /* !BUILDING_LIBRARY */ + + stream = popen (command, FOPEN_RBIN); + free (command); + + /* Read chunks from this file until there are none left to read. */ + if (stream) + { + long offset, size; + char *chunk; + + offset = size = 0; + chunk = (char *)xmalloc (FILESYS_PIPE_BUFFER_SIZE); + + while (1) + { + int bytes_read; + + bytes_read = fread (chunk, 1, FILESYS_PIPE_BUFFER_SIZE, stream); + + if (bytes_read + offset >= size) + contents = (char *)xrealloc + (contents, size += (2 * FILESYS_PIPE_BUFFER_SIZE)); + + memcpy (contents + offset, chunk, bytes_read); + offset += bytes_read; + if (bytes_read != FILESYS_PIPE_BUFFER_SIZE) + break; + } + + free (chunk); + if (pclose (stream) == -1) + { + if (contents) + free (contents); + contents = (char *)NULL; + filesys_error_number = errno; + } + else + { + *filesize = convert_eols (contents, offset); + contents = (char *)xrealloc (contents, 1 + *filesize); + contents[*filesize] = '\0'; + } + } + else + { + filesys_error_number = errno; + } + +#if !defined (BUILDING_LIBARARY) + if (info_windows_initialized_p) + unmessage_in_echo_area (); +#endif /* !BUILDING_LIBRARY */ + return (contents); +} + +/* Return non-zero if FILENAME belongs to a compressed file. */ +int +compressed_filename_p (char *filename) +{ + char *decompressor; + + /* Find the final extension of this filename, and see if it matches one + of our known ones. */ + decompressor = filesys_decompressor_for_file (filename); + + if (decompressor) + return (1); + else + return (0); +} + +/* Return the command string that would be used to decompress FILENAME. */ +char * +filesys_decompressor_for_file (char *filename) +{ + register int i; + char *extension = (char *)NULL; + + /* Find the final extension of FILENAME, and see if it appears in our + list of known compression extensions. */ + for (i = strlen (filename) - 1; i > 0; i--) + if (filename[i] == '.') + { + extension = filename + i; + break; + } + + if (!extension) + return ((char *)NULL); + + for (i = 0; compress_suffixes[i].suffix; i++) + if (FILENAME_CMP (extension, compress_suffixes[i].suffix) == 0) + return (compress_suffixes[i].decompressor); + +#if defined (__MSDOS__) + /* If no other suffix matched, allow any extension which ends + with `z' to be decompressed by gunzip. Due to limited 8+3 DOS + file namespace, we can expect many such cases, and supporting + every weird suffix thus produced would be a pain. */ + if (extension[strlen (extension) - 1] == 'z' || + extension[strlen (extension) - 1] == 'Z') + return "gunzip"; +#endif + + return ((char *)NULL); +} + +/* The number of the most recent file system error. */ +int filesys_error_number = 0; + +/* A function which returns a pointer to a static buffer containing + an error message for FILENAME and ERROR_NUM. */ +static char *errmsg_buf = (char *)NULL; +static int errmsg_buf_size = 0; + +char * +filesys_error_string (char *filename, int error_num) +{ + int len; + char *result; + + if (error_num == 0) + return ((char *)NULL); + + result = strerror (error_num); + + len = 4 + strlen (filename) + strlen (result); + if (len >= errmsg_buf_size) + errmsg_buf = (char *)xrealloc (errmsg_buf, (errmsg_buf_size = 2 + len)); + + sprintf (errmsg_buf, "%s: %s", filename, result); + return (errmsg_buf); +} + + +/* Check for "dir" with all the possible info and compression suffixes, + in combination. */ + +int +is_dir_name (char *filename) +{ + unsigned i; + + for (i = 0; info_suffixes[i]; i++) + { + unsigned c; + char trydir[50]; + strcpy (trydir, "dir"); + strcat (trydir, info_suffixes[i]); + + if (strcasecmp (filename, trydir) == 0) + return 1; + + for (c = 0; compress_suffixes[c].suffix; c++) + { + char dir_compressed[50]; /* can be short */ + strcpy (dir_compressed, trydir); + strcat (dir_compressed, compress_suffixes[c].suffix); + if (strcasecmp (filename, dir_compressed) == 0) + return 1; + } + } + + return 0; +} diff --git a/Build/source/utils/texinfo/info/filesys.h b/Build/source/utils/texinfo/info/filesys.h new file mode 100644 index 00000000000..89ca5430807 --- /dev/null +++ b/Build/source/utils/texinfo/info/filesys.h @@ -0,0 +1,92 @@ +/* filesys.h -- external declarations for filesys.c. + $Id: filesys.h,v 1.3 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_FILESYS_H +#define INFO_FILESYS_H + +/* The path on which we look for info files. You can initialize this + from the environment variable INFOPATH if there is one, or you can + call info_add_path () to add paths to the beginning or end of it. */ +extern char *infopath; + +/* Make INFOPATH have absolutely nothing in it. */ +extern void zap_infopath (void); + +/* Add PATH to the list of paths found in INFOPATH. 2nd argument says + whether to put PATH at the front or end of INFOPATH. */ +extern void info_add_path (char *path, int where); + +/* Defines that are passed along with the pathname to info_add_path (). */ +#define INFOPATH_PREPEND 0 +#define INFOPATH_APPEND 1 + +/* Expand the filename in PARTIAL to make a real name for this operating + system. This looks in INFO_PATHS in order to find the correct file. + If it can't find the file, it returns NULL. */ +extern char *info_find_fullpath (char *partial); + +/* Given a chunk of text and its length, convert all CRLF pairs at the + EOLs into a single Newline character. Return the length of produced + text. */ +long convert_eols (char *text, long textlen); + +/* Read the contents of PATHNAME, returning a buffer with the contents of + that file in it, and returning the size of that buffer in FILESIZE. + FINFO is a stat struct which has already been filled in by the caller. + If the file cannot be read, return a NULL pointer. */ +extern char *filesys_read_info_file (char *pathname, long int *filesize, + struct stat *finfo, int *is_compressed); + +extern char *filesys_read_compressed (char *pathname, long int *filesize); + +/* Return the command string that would be used to decompress FILENAME. */ +extern char *filesys_decompressor_for_file (char *filename); +extern int compressed_filename_p (char *filename); + +/* A function which returns a pointer to a static buffer containing + an error message for FILENAME and ERROR_NUM. */ +extern char *filesys_error_string (char *filename, int error_num); + +/* The number of the most recent file system error. */ +extern int filesys_error_number; + +/* Given a string containing units of information separated by colons, + return the next one pointed to by IDX, or NULL if there are no more. + Advance IDX to the character after the colon. */ +extern char *extract_colon_unit (char *string, int *idx); + +/* Return true if FILENAME is `dir', with a possible compression suffix. */ +extern int is_dir_name (char *filename); + +/* The default value of INFOPATH. */ +#if !defined (DEFAULT_INFOPATH) +# define DEFAULT_INFOPATH "/usr/local/info:/usr/info:/usr/local/lib/info:/usr/lib/info:/usr/local/gnu/info:/usr/local/gnu/lib/info:/usr/gnu/info:/usr/gnu/lib/info:/opt/gnu/info:/usr/share/info:/usr/share/lib/info:/usr/local/share/info:/usr/local/share/lib/info:/usr/gnu/lib/emacs/info:/usr/local/gnu/lib/emacs/info:/usr/local/lib/emacs/info:/usr/local/emacs/info:." +#endif /* !DEFAULT_INFOPATH */ + +#if !defined (S_ISREG) && defined (S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif /* !S_ISREG && S_IFREG */ + +#if !defined (S_ISDIR) && defined (S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif /* !S_ISDIR && S_IFDIR */ + +#endif /* not INFO_FILESYS_H */ diff --git a/Build/source/utils/texinfo/info/footnotes.c b/Build/source/utils/texinfo/info/footnotes.c new file mode 100644 index 00000000000..c8cd7b54a27 --- /dev/null +++ b/Build/source/utils/texinfo/info/footnotes.c @@ -0,0 +1,268 @@ +/* footnotes.c -- Some functions for manipulating footnotes. + $Id: footnotes.c,v 1.4 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 1999, 2002, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" + +/* Nonzero means attempt to show footnotes when displaying a new window. */ +int auto_footnotes_p = 0; + +static char *footnote_nodename = "*Footnotes*"; + +NODE * make_footnotes_node (NODE *node); + +#define FOOTNOTE_HEADER_FORMAT \ + "*** Footnotes appearing in the node `%s' ***\n" + +/* Find the window currently showing footnotes. */ +static WINDOW * +find_footnotes_window (void) +{ + WINDOW *win; + + /* Try to find an existing window first. */ + for (win = windows; win; win = win->next) + if (internal_info_node_p (win->node) && + (strcmp (win->node->nodename, footnote_nodename) == 0)) + break; + + return (win); +} + +/* Manufacture a node containing the footnotes of this node, and + return the manufactured node. If NODE has no footnotes, return a + NULL pointer. */ +NODE * +make_footnotes_node (NODE *node) +{ + NODE *fn_node, *result = (NODE *)NULL; + long fn_start; + + /* Make the initial assumption that the footnotes appear as simple + text within this windows node. */ + fn_node = node; + + /* See if this node contains the magic footnote label. */ + fn_start = + info_search_in_node (FOOTNOTE_LABEL, node, 0, (WINDOW *)NULL, 1, 0); + + /* If it doesn't, check to see if it has an associated footnotes node. */ + if (fn_start == -1) + { + REFERENCE **refs; + + refs = info_xrefs_of_node (node); + + if (refs) + { + register int i; + char *refname; + int reflen = strlen ("-Footnotes") + strlen (node->nodename); + + refname = (char *)xmalloc (reflen + 1); + + strcpy (refname, node->nodename); + strcat (refname, "-Footnotes"); + + for (i = 0; refs[i]; i++) + if ((refs[i]->nodename != (char *)NULL) && + /* Support both the older "foo-Footnotes" and the new + style "foo-Footnote-NN" references. */ + (strcmp (refs[i]->nodename, refname) == 0 || + (strncmp (refs[i]->nodename, refname, reflen - 1) == 0 && + refs[i]->nodename[reflen - 1] == '-' && + isdigit (refs[i]->nodename[reflen])))) + { + char *filename; + + filename = node->parent; + if (!filename) + filename = node->filename; + + fn_node = info_get_node (filename, refname); + + if (fn_node) + fn_start = 0; + + break; + } + + free (refname); + info_free_references (refs); + } + } + + /* If we never found the start of a footnotes area, quit now. */ + if (fn_start == -1) + return ((NODE *)NULL); + + /* Make the new node. */ + result = (NODE *)xmalloc (sizeof (NODE)); + result->flags = 0; + result->display_pos = 0; + + /* Get the size of the footnotes appearing within this node. */ + { + char *header; + long text_start = fn_start; + + header = (char *)xmalloc + (1 + strlen (node->nodename) + strlen (FOOTNOTE_HEADER_FORMAT)); + sprintf (header, FOOTNOTE_HEADER_FORMAT, node->nodename); + + /* Move the start of the displayed text to right after the first line. + This effectively skips either "---- footno...", or "File: foo...". */ + while (text_start < fn_node->nodelen) + if (fn_node->contents[text_start++] == '\n') + break; + + result->nodelen = strlen (header) + fn_node->nodelen - text_start; + + /* Set the contents of this node. */ + result->contents = (char *)xmalloc (1 + result->nodelen); + sprintf (result->contents, "%s", header); + memcpy (result->contents + strlen (header), + fn_node->contents + text_start, fn_node->nodelen - text_start); + + name_internal_node (result, footnote_nodename); + free (header); + } + +#if defined (NOTDEF) + /* If the footnotes were gleaned from the node that we were called with, + shorten the calling node's display length. */ + if (fn_node == node) + narrow_node (node, 0, fn_start); +#endif /* NOTDEF */ + + return (result); +} + +/* Create or delete the footnotes window depending on whether footnotes + exist in WINDOW's node or not. Returns FN_FOUND if footnotes were found + and displayed. Returns FN_UNFOUND if there were no footnotes found + in WINDOW's node. Returns FN_UNABLE if there were footnotes, but the + window to show them couldn't be made. */ +int +info_get_or_remove_footnotes (WINDOW *window) +{ + WINDOW *fn_win; + NODE *new_footnotes; + + fn_win = find_footnotes_window (); + + /* If we are in the footnotes window, change nothing. */ + if (fn_win == window) + return (FN_FOUND); + + /* Try to find footnotes for this window's node. */ + new_footnotes = make_footnotes_node (window->node); + + /* If there was a window showing footnotes, and there are no footnotes + for the current window, delete the old footnote window. */ + if (fn_win && !new_footnotes) + { + if (windows->next) + info_delete_window_internal (fn_win); + } + + /* If there are footnotes for this window's node, but no window around + showing footnotes, try to make a new window. */ + if (new_footnotes && !fn_win) + { + WINDOW *old_active; + WINDOW *last, *win; + + /* Always make this window be the last one appearing in the list. Find + the last window in the chain. */ + for (win = windows, last = windows; win; last = win, win = win->next); + + /* Try to split this window, and make the split window the one to + contain the footnotes. */ + old_active = active_window; + active_window = last; + fn_win = window_make_window (new_footnotes); + active_window = old_active; + + if (!fn_win) + { + free (new_footnotes->contents); + free (new_footnotes); + + /* If we are hacking automatic footnotes, and there are footnotes + but we couldn't display them, print a message to that effect. */ + if (auto_footnotes_p) + inform_in_echo_area ((char *) _("Footnotes could not be displayed")); + return (FN_UNABLE); + } + } + + /* If there are footnotes, and there is a window to display them, + make that window be the number of lines appearing in the footnotes. */ + if (new_footnotes && fn_win) + { + window_set_node_of_window (fn_win, new_footnotes); + + window_change_window_height + (fn_win, fn_win->line_count - fn_win->height); + + remember_window_and_node (fn_win, new_footnotes); + add_gcable_pointer (new_footnotes->contents); + } + + if (!new_footnotes) + return (FN_UNFOUND); + else + return (FN_FOUND); +} + +/* Show the footnotes associated with this node in another window. */ +DECLARE_INFO_COMMAND (info_show_footnotes, + _("Show the footnotes associated with this node in another window")) +{ + /* A negative argument means just make the window go away. */ + if (count < 0) + { + WINDOW *fn_win = find_footnotes_window (); + + /* If there is an old footnotes window, and it isn't the only window + on the screen, delete it. */ + if (fn_win && windows->next) + info_delete_window_internal (fn_win); + } + else + { + int result; + + result = info_get_or_remove_footnotes (window); + + switch (result) + { + case FN_UNFOUND: + info_error ((char *) msg_no_foot_node, NULL, NULL); + break; + + case FN_UNABLE: + info_error ((char *) msg_win_too_small, NULL, NULL); + break; + } + } +} diff --git a/Build/source/utils/texinfo/info/footnotes.h b/Build/source/utils/texinfo/info/footnotes.h new file mode 100644 index 00000000000..7f583994261 --- /dev/null +++ b/Build/source/utils/texinfo/info/footnotes.h @@ -0,0 +1,43 @@ +/* footnotes.h -- Some functions for manipulating footnotes. + $Id: footnotes.h,v 1.3 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_FOOTNOTES_H +#define INFO_FOOTNOTES_H + +/* Magic string which indicates following text is footnotes. */ +#define FOOTNOTE_LABEL N_("---------- Footnotes ----------") + +#define FN_FOUND 0 +#define FN_UNFOUND 1 +#define FN_UNABLE 2 + + +/* Create or delete the footnotes window depending on whether footnotes + exist in WINDOW's node or not. Returns FN_FOUND if footnotes were found + and displayed. Returns FN_UNFOUND if there were no footnotes found + in WINDOW's node. Returns FN_UNABLE if there were footnotes, but the + window to show them couldn't be made. */ +extern int info_get_or_remove_footnotes (WINDOW *window); + +/* Non-zero means attempt to show footnotes when displaying a new window. */ +extern int auto_footnotes_p; + +#endif /* not INFO_FOOTNOTES_H */ diff --git a/Build/source/utils/texinfo/info/gc.c b/Build/source/utils/texinfo/info/gc.c new file mode 100644 index 00000000000..9416e3bb2e8 --- /dev/null +++ b/Build/source/utils/texinfo/info/gc.c @@ -0,0 +1,92 @@ +/* gc.c -- Functions to remember and garbage collect unused node contents. + $Id: gc.c,v 1.3 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" + +/* Array of pointers to the contents of gc-able nodes. A pointer on this + list can be garbage collected when no info window contains a node whose + contents member match the pointer. */ +static char **gcable_pointers = (char **)NULL; +static int gcable_pointers_index = 0; +static int gcable_pointers_slots = 0; + +/* Add POINTER to the list of garbage collectible pointers. A pointer + is not actually garbage collected until no info window contains a node + whose contents member is equal to the pointer. */ +void +add_gcable_pointer (char *pointer) +{ + gc_pointers (); + add_pointer_to_array (pointer, gcable_pointers_index, gcable_pointers, + gcable_pointers_slots, 10, char *); +} + +/* Grovel the list of info windows and gc-able pointers finding those + node->contents which are collectible, and free them. */ +void +gc_pointers (void) +{ + register int i, j, k; + INFO_WINDOW *iw; + char **new = (char **)NULL; + int new_index = 0; + int new_slots = 0; + + if (!info_windows || !gcable_pointers_index) + return; + + for (i = 0; (iw = info_windows[i]); i++) + { + for (j = 0; j < iw->nodes_index; j++) + { + NODE *node = iw->nodes[j]; + + /* If this node->contents appears in our list of gcable_pointers, + it is not gc-able, so save it. */ + for (k = 0; k < gcable_pointers_index; k++) + if (gcable_pointers[k] == node->contents) + { + add_pointer_to_array + (node->contents, new_index, new, new_slots, 10, char *); + break; + } + } + } + + /* We have gathered all of the pointers which need to be saved. Free any + of the original pointers which do not appear in the new list. */ + for (i = 0; i < gcable_pointers_index; i++) + { + for (j = 0; j < new_index; j++) + if (gcable_pointers[i] == new[j]) + break; + + /* If we got all the way through the new list, then the old pointer + can be garbage collected. */ + if (new && !new[j]) + free (gcable_pointers[i]); + } + + free (gcable_pointers); + gcable_pointers = new; + gcable_pointers_slots = new_slots; + gcable_pointers_index = new_index; +} diff --git a/Build/source/utils/texinfo/info/gc.h b/Build/source/utils/texinfo/info/gc.h new file mode 100644 index 00000000000..67d031f9b87 --- /dev/null +++ b/Build/source/utils/texinfo/info/gc.h @@ -0,0 +1,37 @@ +/* gc.h -- Functions for garbage collecting unused node contents. + $Id: gc.h,v 1.3 2004/04/11 17:56:45 karl Exp $ + + This file is part of GNU Info, a program for reading online documentation + stored in Info format. + + Copyright (C) 1993, 1997, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_GC_H +#define INFO_GC_H + +/* Add POINTER to the list of garbage collectible pointers. A pointer + is not actually garbage collected until no info window contains a node + whose contents member is equal to the pointer. */ +extern void add_gcable_pointer (char *pointer); + +/* Grovel the list of info windows and gc-able pointers finding those + node->contents which are collectible, and free them. */ +extern void gc_pointers (void); + +#endif /* not INFO_GC_H */ diff --git a/Build/source/utils/texinfo/info/indices.c b/Build/source/utils/texinfo/info/indices.c new file mode 100644 index 00000000000..f5b0eb57633 --- /dev/null +++ b/Build/source/utils/texinfo/info/indices.c @@ -0,0 +1,735 @@ +/* indices.c -- deal with an Info file index. + $Id: indices.c,v 1.5 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 1999, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "indices.h" + +/* User-visible variable controls the output of info-index-next. */ +int show_index_match = 1; + +/* In the Info sense, an index is a menu. This variable holds the last + parsed index. */ +static REFERENCE **index_index = (REFERENCE **)NULL; + +/* The offset of the most recently selected index element. */ +static int index_offset = 0; + +/* Variable which holds the last string searched for. */ +static char *index_search = (char *)NULL; + +/* A couple of "globals" describing where the initial index was found. */ +static char *initial_index_filename = (char *)NULL; +static char *initial_index_nodename = (char *)NULL; + +/* A structure associating index names with index offset ranges. */ +typedef struct { + char *name; /* The nodename of this index. */ + int first; /* The index in our list of the first entry. */ + int last; /* The index in our list of the last entry. */ +} INDEX_NAME_ASSOC; + +/* An array associating index nodenames with index offset ranges. */ +static INDEX_NAME_ASSOC **index_nodenames = (INDEX_NAME_ASSOC **)NULL; +static int index_nodenames_index = 0; +static int index_nodenames_slots = 0; + +/* Add the name of NODE, and the range of the associated index elements + (passed in ARRAY) to index_nodenames. */ +static void +add_index_to_index_nodenames (REFERENCE **array, NODE *node) +{ + register int i, last; + INDEX_NAME_ASSOC *assoc; + + for (last = 0; array[last + 1]; last++); + assoc = (INDEX_NAME_ASSOC *)xmalloc (sizeof (INDEX_NAME_ASSOC)); + assoc->name = xstrdup (node->nodename); + + if (!index_nodenames_index) + { + assoc->first = 0; + assoc->last = last; + } + else + { + for (i = 0; index_nodenames[i + 1]; i++); + assoc->first = 1 + index_nodenames[i]->last; + assoc->last = assoc->first + last; + } + add_pointer_to_array + (assoc, index_nodenames_index, index_nodenames, index_nodenames_slots, + 10, INDEX_NAME_ASSOC *); +} + +/* Find and return the indices of WINDOW's file. The indices are defined + as the first node in the file containing the word "Index" and any + immediately following nodes whose names also contain "Index". All such + indices are concatenated and the result returned. If WINDOW's info file + doesn't have any indices, a NULL pointer is returned. */ +REFERENCE ** +info_indices_of_window (WINDOW *window) +{ + FILE_BUFFER *fb; + + fb = file_buffer_of_window (window); + + return (info_indices_of_file_buffer (fb)); +} + +REFERENCE ** +info_indices_of_file_buffer (FILE_BUFFER *file_buffer) +{ + register int i; + REFERENCE **result = (REFERENCE **)NULL; + + /* No file buffer, no indices. */ + if (!file_buffer) + return ((REFERENCE **)NULL); + + /* Reset globals describing where the index was found. */ + maybe_free (initial_index_filename); + maybe_free (initial_index_nodename); + initial_index_filename = (char *)NULL; + initial_index_nodename = (char *)NULL; + + if (index_nodenames) + { + for (i = 0; index_nodenames[i]; i++) + { + free (index_nodenames[i]->name); + free (index_nodenames[i]); + } + + index_nodenames_index = 0; + index_nodenames[0] = (INDEX_NAME_ASSOC *)NULL; + } + + /* Grovel the names of the nodes found in this file. */ + if (file_buffer->tags) + { + TAG *tag; + + for (i = 0; (tag = file_buffer->tags[i]); i++) + { + if (string_in_line ("Index", tag->nodename) != -1) + { + NODE *node; + REFERENCE **menu; + + /* Found one. Get its menu. */ + node = info_get_node (tag->filename, tag->nodename); + if (!node) + continue; + + /* Remember the filename and nodename of this index. */ + initial_index_filename = xstrdup (file_buffer->filename); + initial_index_nodename = xstrdup (tag->nodename); + + menu = info_menu_of_node (node); + + /* If we have a menu, add this index's nodename and range + to our list of index_nodenames. */ + if (menu) + { + add_index_to_index_nodenames (menu, node); + + /* Concatenate the references found so far. */ + result = info_concatenate_references (result, menu); + } + free (node); + } + } + } + + /* If there is a result, clean it up so that every entry has a filename. */ + for (i = 0; result && result[i]; i++) + if (!result[i]->filename) + result[i]->filename = xstrdup (file_buffer->filename); + + return (result); +} + +DECLARE_INFO_COMMAND (info_index_search, + _("Look up a string in the index for this file")) +{ + do_info_index_search (window, count, 0); +} + +/* Look up SEARCH_STRING in the index for this file. If SEARCH_STRING + is NULL, prompt user for input. */ +void +do_info_index_search (WINDOW *window, int count, char *search_string) +{ + FILE_BUFFER *fb; + char *line; + + /* Reset the index offset, since this is not the info-index-next command. */ + index_offset = 0; + + /* The user is selecting a new search string, so flush the old one. */ + maybe_free (index_search); + index_search = (char *)NULL; + + /* If this window's file is not the same as the one that we last built an + index for, build and remember an index now. */ + fb = file_buffer_of_window (window); + if (!initial_index_filename || + (FILENAME_CMP (initial_index_filename, fb->filename) != 0)) + { + info_free_references (index_index); + window_message_in_echo_area ((char *) _("Finding index entries..."), + NULL, NULL); + index_index = info_indices_of_file_buffer (fb); + } + + /* If there is no index, quit now. */ + if (!index_index) + { + info_error ((char *) _("No indices found."), NULL, NULL); + return; + } + + /* Okay, there is an index. Look for SEARCH_STRING, or, if it is + empty, prompt for one. */ + if (search_string && *search_string) + line = xstrdup (search_string); + else + { + line = info_read_maybe_completing (window, (char *) _("Index entry: "), + index_index); + window = active_window; + + /* User aborted? */ + if (!line) + { + info_abort_key (active_window, 1, 0); + return; + } + + /* Empty line means move to the Index node. */ + if (!*line) + { + free (line); + + if (initial_index_filename && initial_index_nodename) + { + NODE *node; + + node = info_get_node (initial_index_filename, + initial_index_nodename); + set_remembered_pagetop_and_point (window); + window_set_node_of_window (window, node); + remember_window_and_node (window, node); + window_clear_echo_area (); + return; + } + } + } + + /* The user typed either a completed index label, or a partial string. + Find an exact match, or, failing that, the first index entry containing + the partial string. So, we just call info_next_index_match () with minor + manipulation of INDEX_OFFSET. */ + { + int old_offset; + + /* Start the search right after/before this index. */ + if (count < 0) + { + register int i; + for (i = 0; index_index[i]; i++); + index_offset = i; + } + else + index_offset = -1; + + old_offset = index_offset; + + /* The "last" string searched for is this one. */ + index_search = line; + + /* Find it, or error. */ + info_next_index_match (window, count, 0); + + /* If the search failed, return the index offset to where it belongs. */ + if (index_offset == old_offset) + index_offset = 0; + } +} + +int +index_entry_exists (WINDOW *window, char *string) +{ + register int i; + FILE_BUFFER *fb; + + /* If there is no previous search string, the user hasn't built an index + yet. */ + if (!string) + return 0; + + fb = file_buffer_of_window (window); + if (!initial_index_filename + || (FILENAME_CMP (initial_index_filename, fb->filename) != 0)) + { + info_free_references (index_index); + index_index = info_indices_of_file_buffer (fb); + } + + /* If there is no index, that is an error. */ + if (!index_index) + return 0; + + for (i = 0; (i > -1) && (index_index[i]); i++) + if (strcmp (string, index_index[i]->label) == 0) + break; + + /* If that failed, look for the next substring match. */ + if ((i < 0) || (!index_index[i])) + { + for (i = 0; (i > -1) && (index_index[i]); i++) + if (string_in_line (string, index_index[i]->label) != -1) + break; + + if ((i > -1) && (index_index[i])) + string_in_line (string, index_index[i]->label); + } + + /* If that failed, return 0. */ + if ((i < 0) || (!index_index[i])) + return 0; + + return 1; +} + +DECLARE_INFO_COMMAND (info_next_index_match, + _("Go to the next matching index item from the last `\\[index-search]' command")) +{ + register int i; + int partial, dir; + NODE *node; + + /* If there is no previous search string, the user hasn't built an index + yet. */ + if (!index_search) + { + info_error ((char *) _("No previous index search string."), NULL, NULL); + return; + } + + /* If there is no index, that is an error. */ + if (!index_index) + { + info_error ((char *) _("No index entries."), NULL, NULL); + return; + } + + /* The direction of this search is controlled by the value of the + numeric argument. */ + if (count < 0) + dir = -1; + else + dir = 1; + + /* Search for the next occurence of index_search. First try to find + an exact match. */ + partial = 0; + + for (i = index_offset + dir; (i > -1) && (index_index[i]); i += dir) + if (strcmp (index_search, index_index[i]->label) == 0) + break; + + /* If that failed, look for the next substring match. */ + if ((i < 0) || (!index_index[i])) + { + for (i = index_offset + dir; (i > -1) && (index_index[i]); i += dir) + if (string_in_line (index_search, index_index[i]->label) != -1) + break; + + if ((i > -1) && (index_index[i])) + partial = string_in_line (index_search, index_index[i]->label); + } + + /* If that failed, print an error. */ + if ((i < 0) || (!index_index[i])) + { + info_error ((char *) _("No %sindex entries containing `%s'."), + index_offset > 0 ? (char *) _("more ") : "", index_search); + return; + } + + /* Okay, we found the next one. Move the offset to the current entry. */ + index_offset = i; + + /* Report to the user on what we have found. */ + { + register int j; + const char *name = _("CAN'T SEE THIS"); + char *match; + + for (j = 0; index_nodenames[j]; j++) + { + if ((i >= index_nodenames[j]->first) && + (i <= index_nodenames[j]->last)) + { + name = index_nodenames[j]->name; + break; + } + } + + /* If we had a partial match, indicate to the user which part of the + string matched. */ + match = xstrdup (index_index[i]->label); + + if (partial && show_index_match) + { + int k, ls, start, upper; + + ls = strlen (index_search); + start = partial - ls; + upper = isupper (match[start]) ? 1 : 0; + + for (k = 0; k < ls; k++) + if (upper) + match[k + start] = info_tolower (match[k + start]); + else + match[k + start] = info_toupper (match[k + start]); + } + + { + char *format; + + format = replace_in_documentation + ((char *) _("Found `%s' in %s. (`\\[next-index-match]' tries to find next.)"), + 0); + + window_message_in_echo_area (format, match, (char *) name); + } + + free (match); + } + + /* Select the node corresponding to this index entry. */ + node = info_get_node (index_index[i]->filename, index_index[i]->nodename); + + if (!node) + { + info_error ((char *) msg_cant_file_node, + index_index[i]->filename, index_index[i]->nodename); + return; + } + + info_set_node_of_window (1, window, node); + + /* Try to find an occurence of LABEL in this node. */ + { + long start, loc; + + start = window->line_starts[1] - window->node->contents; + loc = info_target_search_node (node, index_index[i]->label, start); + + if (loc != -1) + { + window->point = loc; + window_adjust_pagetop (window); + } + } +} + +/* **************************************************************** */ +/* */ +/* Info APROPOS: Search every known index. */ +/* */ +/* **************************************************************** */ + +/* For every menu item in DIR, search the indices of that file for + SEARCH_STRING. */ +REFERENCE ** +apropos_in_all_indices (char *search_string, int inform) +{ + register int i, dir_index; + REFERENCE **all_indices = (REFERENCE **)NULL; + REFERENCE **dir_menu = (REFERENCE **)NULL; + NODE *dir_node; + + dir_node = info_get_node ("dir", "Top"); + if (dir_node) + dir_menu = info_menu_of_node (dir_node); + + if (!dir_menu) + return NULL; + + /* For every menu item in DIR, get the associated node's file buffer and + read the indices of that file buffer. Gather all of the indices into + one large one. */ + for (dir_index = 0; dir_menu[dir_index]; dir_index++) + { + REFERENCE **this_index, *this_item; + NODE *this_node; + FILE_BUFFER *this_fb; + int dir_node_duplicated = 0; + + this_item = dir_menu[dir_index]; + + if (!this_item->filename) + { + dir_node_duplicated = 1; + if (dir_node->parent) + this_item->filename = xstrdup (dir_node->parent); + else + this_item->filename = xstrdup (dir_node->filename); + } + + /* Find this node. If we cannot find it, try using the label of the + entry as a file (i.e., "(LABEL)Top"). */ + this_node = info_get_node (this_item->filename, this_item->nodename); + + if (!this_node && this_item->nodename && + (strcmp (this_item->label, this_item->nodename) == 0)) + this_node = info_get_node (this_item->label, "Top"); + + if (!this_node) + { + if (dir_node_duplicated) + free (this_item->filename); + continue; + } + + /* Get the file buffer associated with this node. */ + { + char *files_name; + + files_name = this_node->parent; + if (!files_name) + files_name = this_node->filename; + + this_fb = info_find_file (files_name); + + /* If we already scanned this file, don't do that again. + In addition to being faster, this also avoids having + multiple identical entries in the *Apropos* menu. */ + for (i = 0; i < dir_index; i++) + if (FILENAME_CMP (this_fb->filename, dir_menu[i]->filename) == 0) + break; + if (i < dir_index) + { + if (dir_node_duplicated) + free (this_item->filename); + continue; + } + + if (this_fb && inform) + message_in_echo_area ((char *) _("Scanning indices of `%s'..."), + files_name, NULL); + + this_index = info_indices_of_file_buffer (this_fb); + free (this_node); + + if (this_fb && inform) + unmessage_in_echo_area (); + } + + if (this_index) + { + /* Remember the filename which contains this set of references. */ + for (i = 0; this_index && this_index[i]; i++) + if (!this_index[i]->filename) + this_index[i]->filename = xstrdup (this_fb->filename); + + /* Concatenate with the other indices. */ + all_indices = info_concatenate_references (all_indices, this_index); + } + } + + info_free_references (dir_menu); + + /* Build a list of the references which contain SEARCH_STRING. */ + if (all_indices) + { + REFERENCE *entry, **apropos_list = (REFERENCE **)NULL; + int apropos_list_index = 0; + int apropos_list_slots = 0; + + for (i = 0; (entry = all_indices[i]); i++) + { + if (string_in_line (search_string, entry->label) != -1) + { + add_pointer_to_array + (entry, apropos_list_index, apropos_list, apropos_list_slots, + 100, REFERENCE *); + } + else + { + maybe_free (entry->label); + maybe_free (entry->filename); + maybe_free (entry->nodename); + free (entry); + } + } + + free (all_indices); + all_indices = apropos_list; + } + return (all_indices); +} + +#define APROPOS_NONE \ + N_("No available info files have `%s' in their indices.") + +void +info_apropos (char *string) +{ + REFERENCE **apropos_list; + + apropos_list = apropos_in_all_indices (string, 0); + + if (!apropos_list) + info_error ((char *) _(APROPOS_NONE), string, NULL); + else + { + register int i; + REFERENCE *entry; + + for (i = 0; (entry = apropos_list[i]); i++) + fprintf (stdout, "\"(%s)%s\" -- %s\n", + entry->filename, entry->nodename, entry->label); + } + info_free_references (apropos_list); +} + +static char *apropos_list_nodename = "*Apropos*"; + +DECLARE_INFO_COMMAND (info_index_apropos, + _("Grovel all known info file's indices for a string and build a menu")) +{ + char *line; + + line = info_read_in_echo_area (window, (char *) _("Index apropos: ")); + + window = active_window; + + /* User aborted? */ + if (!line) + { + info_abort_key (window, 1, 1); + return; + } + + /* User typed something? */ + if (*line) + { + REFERENCE **apropos_list; + NODE *apropos_node; + + apropos_list = apropos_in_all_indices (line, 1); + + if (!apropos_list) + info_error ((char *) _(APROPOS_NONE), line, NULL); + else + { + register int i; + char *line_buffer; + + initialize_message_buffer (); + printf_to_message_buffer + ((char *) _("\n* Menu: Nodes whose indices contain `%s':\n"), + line, NULL, NULL); + line_buffer = (char *)xmalloc (500); + + for (i = 0; apropos_list[i]; i++) + { + int len; + /* The label might be identical to that of another index + entry in another Info file. Therefore, we make the file + name part of the menu entry, to make them all distinct. */ + sprintf (line_buffer, "* %s [%s]: ", + apropos_list[i]->label, apropos_list[i]->filename); + len = pad_to (40, line_buffer); + sprintf (line_buffer + len, "(%s)%s.", + apropos_list[i]->filename, apropos_list[i]->nodename); + printf_to_message_buffer ("%s\n", line_buffer, NULL, NULL); + } + free (line_buffer); + } + + apropos_node = message_buffer_to_node (); + add_gcable_pointer (apropos_node->contents); + name_internal_node (apropos_node, apropos_list_nodename); + + /* Even though this is an internal node, we don't want the window + system to treat it specially. So we turn off the internalness + of it here. */ + apropos_node->flags &= ~N_IsInternal; + + /* Find/Create a window to contain this node. */ + { + WINDOW *new; + NODE *node; + + set_remembered_pagetop_and_point (window); + + /* If a window is visible and showing an apropos list already, + re-use it. */ + for (new = windows; new; new = new->next) + { + node = new->node; + + if (internal_info_node_p (node) && + (strcmp (node->nodename, apropos_list_nodename) == 0)) + break; + } + + /* If we couldn't find an existing window, try to use the next window + in the chain. */ + if (!new && window->next) + new = window->next; + + /* If we still don't have a window, make a new one to contain + the list. */ + if (!new) + { + WINDOW *old_active; + + old_active = active_window; + active_window = window; + new = window_make_window ((NODE *)NULL); + active_window = old_active; + } + + /* If we couldn't make a new window, use this one. */ + if (!new) + new = window; + + /* Lines do not wrap in this window. */ + new->flags |= W_NoWrap; + + window_set_node_of_window (new, apropos_node); + remember_window_and_node (new, apropos_node); + active_window = new; + } + info_free_references (apropos_list); + } + free (line); + + if (!info_error_was_printed) + window_clear_echo_area (); +} diff --git a/Build/source/utils/texinfo/info/indices.h b/Build/source/utils/texinfo/info/indices.h new file mode 100644 index 00000000000..1ccd6763616 --- /dev/null +++ b/Build/source/utils/texinfo/info/indices.h @@ -0,0 +1,45 @@ +/* indices.h -- Functions defined in indices.c. + $Id: indices.h,v 1.3 2004/04/11 17:56:45 karl Exp $ + + This file is part of GNU Info, a program for reading online documentation + stored in Info format. + + Copyright (C) 1993, 1997, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_INDICES_H +#define INFO_INDICES_H + +/* User-visible variable controls the output of info-index-next. */ +extern int show_index_match; + +extern REFERENCE **info_indices_of_window (WINDOW *window); +extern REFERENCE **info_indices_of_file_buffer (FILE_BUFFER *file_buffer); +extern void info_apropos (char *string); + +/* For every menu item in DIR, search the indices of that file for STRING. */ +REFERENCE **apropos_in_all_indices (char *search_string, int inform); + +/* User visible functions declared in indices.c. */ +extern void info_index_search (WINDOW *window, int count, unsigned char key); +extern void info_next_index_match (WINDOW *window, int count, unsigned char key); +extern void info_index_apropos (WINDOW *window, int count, unsigned char key); +extern void do_info_index_search (WINDOW *window, int count, char *search_string); +extern int index_entry_exists (WINDOW *window, char *string); + +#endif /* not INFO_INDICES_H */ diff --git a/Build/source/utils/texinfo/info/info-utils.c b/Build/source/utils/texinfo/info/info-utils.c new file mode 100644 index 00000000000..c23bac571ae --- /dev/null +++ b/Build/source/utils/texinfo/info/info-utils.c @@ -0,0 +1,701 @@ +/* info-utils.c -- miscellanous. + $Id: info-utils.c,v 1.4 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1998, 2003, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "info-utils.h" +#if defined (HANDLE_MAN_PAGES) +# include "man.h" +#endif /* HANDLE_MAN_PAGES */ + +/* When non-zero, various display and input functions handle ISO Latin + character sets correctly. */ +int ISO_Latin_p = 1; + +/* Variable which holds the most recent filename parsed as a result of + calling info_parse_xxx (). */ +char *info_parsed_filename = (char *)NULL; + +/* Variable which holds the most recent nodename parsed as a result of + calling info_parse_xxx (). */ +char *info_parsed_nodename = (char *)NULL; + +/* Variable which holds the most recent line number parsed as a result of + calling info_parse_xxx (). */ +int info_parsed_line_number = 0; + +/* Functions to remember a filename or nodename for later return. */ +static void save_filename (char *filename); +static void saven_filename (char *filename, int len); +static void save_nodename (char *nodename); +static void saven_nodename (char *nodename, int len); + +/* How to get a reference (either menu or cross). */ +static REFERENCE **info_references_internal (char *label, + SEARCH_BINDING *binding); + +/* Parse the filename and nodename out of STRING. If STRING doesn't + contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set + INFO_PARSED_FILENAME to NULL. If second argument NEWLINES_OKAY is + non-zero, it says to allow the nodename specification to cross a + newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */ +void +info_parse_node (char *string, int newlines_okay) +{ + register int i = 0; + + /* Default the answer. */ + save_filename ((char *)NULL); + save_nodename ((char *)NULL); + + /* Special case of nothing passed. Return nothing. */ + if (!string || !*string) + return; + + string += skip_whitespace (string); + + /* Check for (FILENAME)NODENAME. */ + if (*string == '(') + { + i = 0; + /* Advance past the opening paren. */ + string++; + + /* Find the closing paren. */ + while (string[i] && string[i] != ')') + i++; + + /* Remember parsed filename. */ + saven_filename (string, i); + + /* Point directly at the nodename. */ + string += i; + + if (*string) + string++; + } + + /* Parse out nodename. */ + i = skip_node_characters (string, newlines_okay); + saven_nodename (string, i); + canonicalize_whitespace (info_parsed_nodename); + if (info_parsed_nodename && !*info_parsed_nodename) + { + free (info_parsed_nodename); + info_parsed_nodename = (char *)NULL; + } + + /* Parse ``(line ...)'' part of menus, if any. */ + { + char *rest = string + i; + + /* Advance only if it's not already at end of string. */ + if (*rest) + rest++; + + /* Skip any whitespace first, and then a newline in case the item + was so long to contain the ``(line ...)'' string in the same + physical line. */ + while (whitespace(*rest)) + rest++; + if (*rest == '\n') + { + rest++; + while (whitespace(*rest)) + rest++; + } + + /* Are we looking at an opening parenthesis? That can only mean + we have a winner. :) */ + if (strncmp (rest, "(line ", strlen ("(line ")) == 0) + { + rest += strlen ("(line "); + info_parsed_line_number = strtol (rest, NULL, 0); + } + else + info_parsed_line_number = 0; + } +} + +/* Return the node addressed by LABEL in NODE (usually one of "Prev:", + "Next:", "Up:", "File:", or "Node:". After a call to this function, + the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain + the information. */ +void +info_parse_label (char *label, NODE *node) +{ + register int i; + char *nodeline; + + /* Default answer to failure. */ + save_nodename ((char *)NULL); + save_filename ((char *)NULL); + + /* Find the label in the first line of this node. */ + nodeline = node->contents; + i = string_in_line (label, nodeline); + + if (i == -1) + return; + + nodeline += i; + nodeline += skip_whitespace (nodeline); + info_parse_node (nodeline, DONT_SKIP_NEWLINES); +} + +/* **************************************************************** */ +/* */ +/* Finding and Building Menus */ +/* */ +/* **************************************************************** */ + +/* Return a NULL terminated array of REFERENCE * which represents the menu + found in NODE. If there is no menu in NODE, just return a NULL pointer. */ +REFERENCE ** +info_menu_of_node (NODE *node) +{ + long position; + SEARCH_BINDING tmp_search; + REFERENCE **menu = (REFERENCE **)NULL; + + tmp_search.buffer = node->contents; + tmp_search.start = 0; + tmp_search.end = node->nodelen; + tmp_search.flags = S_FoldCase; + + /* Find the start of the menu. */ + position = search_forward (INFO_MENU_LABEL, &tmp_search); + + if (position == -1) + return ((REFERENCE **) NULL); + + /* We have the start of the menu now. Glean menu items from the rest + of the node. */ + tmp_search.start = position + strlen (INFO_MENU_LABEL); + tmp_search.start += skip_line (tmp_search.buffer + tmp_search.start); + tmp_search.start--; + menu = info_menu_items (&tmp_search); + return (menu); +} + +/* Return a NULL terminated array of REFERENCE * which represents the cross + refrences found in NODE. If there are no cross references in NODE, just + return a NULL pointer. */ +REFERENCE ** +info_xrefs_of_node (NODE *node) +{ + SEARCH_BINDING tmp_search; + +#if defined (HANDLE_MAN_PAGES) + if (node->flags & N_IsManPage) + return (xrefs_of_manpage (node)); +#endif + + tmp_search.buffer = node->contents; + tmp_search.start = 0; + tmp_search.end = node->nodelen; + tmp_search.flags = S_FoldCase; + + return (info_xrefs (&tmp_search)); +} + +/* Glean menu entries from BINDING->buffer + BINDING->start until we + have looked at the entire contents of BINDING. Return an array + of REFERENCE * that represents each menu item in this range. */ +REFERENCE ** +info_menu_items (SEARCH_BINDING *binding) +{ + return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding)); +} + +/* Glean cross references from BINDING->buffer + BINDING->start until + BINDING->end. Return an array of REFERENCE * that represents each + cross reference in this range. */ +REFERENCE ** +info_xrefs (SEARCH_BINDING *binding) +{ + return (info_references_internal (INFO_XREF_LABEL, binding)); +} + +/* Glean cross references or menu items from BINDING. Return an array + of REFERENCE * that represents the items found. */ +static REFERENCE ** +info_references_internal (char *label, SEARCH_BINDING *binding) +{ + SEARCH_BINDING tmp_search; + REFERENCE **refs = (REFERENCE **)NULL; + int refs_index = 0, refs_slots = 0; + int searching_for_menu_items = 0; + long position; + + tmp_search.buffer = binding->buffer; + tmp_search.start = binding->start; + tmp_search.end = binding->end; + tmp_search.flags = S_FoldCase | S_SkipDest; + + searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0); + + while ((position = search_forward (label, &tmp_search)) != -1) + { + int offset, start; + char *refdef; + REFERENCE *entry; + + tmp_search.start = position; + tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start); + start = tmp_search.start - binding->start; + refdef = tmp_search.buffer + tmp_search.start; + offset = string_in_line (":", refdef); + + /* When searching for menu items, if no colon, there is no + menu item on this line. */ + if (offset == -1) + { + if (searching_for_menu_items) + continue; + else + { + int temp; + + temp = skip_line (refdef); + offset = string_in_line (":", refdef + temp); + if (offset == -1) + continue; /* Give up? */ + else + offset += temp; + } + } + + entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + entry->filename = (char *)NULL; + entry->nodename = (char *)NULL; + entry->label = (char *)xmalloc (offset); + strncpy (entry->label, refdef, offset - 1); + entry->label[offset - 1] = '\0'; + canonicalize_whitespace (entry->label); + + refdef += offset; + entry->start = start; + entry->end = refdef - binding->buffer; + + /* If this reference entry continues with another ':' then the + nodename is the same as the label. */ + if (*refdef == ':') + { + entry->nodename = xstrdup (entry->label); + } + else + { + /* This entry continues with a specific nodename. Parse the + nodename from the specification. */ + + refdef += skip_whitespace_and_newlines (refdef); + + if (searching_for_menu_items) + info_parse_node (refdef, DONT_SKIP_NEWLINES); + else + info_parse_node (refdef, SKIP_NEWLINES); + + if (info_parsed_filename) + entry->filename = xstrdup (info_parsed_filename); + + if (info_parsed_nodename) + entry->nodename = xstrdup (info_parsed_nodename); + + entry->line_number = info_parsed_line_number; + } + + add_pointer_to_array + (entry, refs_index, refs, refs_slots, 50, REFERENCE *); + } + return (refs); +} + +/* Get the entry associated with LABEL in REFERENCES. Return a pointer + to the ENTRY if found, or NULL. */ +REFERENCE * +info_get_labeled_reference (char *label, REFERENCE **references) +{ + register int i; + REFERENCE *entry; + + for (i = 0; references && (entry = references[i]); i++) + { + if (strcmp (label, entry->label) == 0) + return (entry); + } + return ((REFERENCE *)NULL); +} + +/* A utility function for concatenating REFERENCE **. Returns a new + REFERENCE ** which is the concatenation of REF1 and REF2. The REF1 + and REF2 arrays are freed, but their contents are not. */ +REFERENCE ** +info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2) +{ + register int i, j; + REFERENCE **result; + int size; + + /* With one argument passed as NULL, simply return the other arg. */ + if (!ref1) + return (ref2); + else if (!ref2) + return (ref1); + + /* Get the total size of the slots that we will need. */ + for (i = 0; ref1[i]; i++); + size = i; + for (i = 0; ref2[i]; i++); + size += i; + + result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *)); + + /* Copy the contents over. */ + for (i = 0; ref1[i]; i++) + result[i] = ref1[i]; + + j = i; + for (i = 0; ref2[i]; i++) + result[j++] = ref2[i]; + + result[j] = (REFERENCE *)NULL; + free (ref1); + free (ref2); + return (result); +} + + + +/* Copy a reference structure. Since we tend to free everything at + every opportunity, we don't share any points, but copy everything into + new memory. */ +REFERENCE * +info_copy_reference (REFERENCE *src) +{ + REFERENCE *dest = xmalloc (sizeof (REFERENCE)); + dest->label = src->label ? xstrdup (src->label) : NULL; + dest->filename = src->filename ? xstrdup (src->filename) : NULL; + dest->nodename = src->nodename ? xstrdup (src->nodename) : NULL; + dest->start = src->start; + dest->end = src->end; + + return dest; +} + + + +/* Free the data associated with REFERENCES. */ +void +info_free_references (REFERENCE **references) +{ + register int i; + REFERENCE *entry; + + if (references) + { + for (i = 0; references && (entry = references[i]); i++) + { + maybe_free (entry->label); + maybe_free (entry->filename); + maybe_free (entry->nodename); + + free (entry); + } + + free (references); + } +} + +/* Search for sequences of whitespace or newlines in STRING, replacing + all such sequences with just a single space. Remove whitespace from + start and end of string. */ +void +canonicalize_whitespace (char *string) +{ + register int i, j; + int len, whitespace_found, whitespace_loc = 0; + char *temp; + + if (!string) + return; + + len = strlen (string); + temp = (char *)xmalloc (1 + len); + + /* Search for sequences of whitespace or newlines. Replace all such + sequences in the string with just a single space. */ + + whitespace_found = 0; + for (i = 0, j = 0; string[i]; i++) + { + if (whitespace_or_newline (string[i])) + { + whitespace_found++; + whitespace_loc = i; + continue; + } + else + { + if (whitespace_found && whitespace_loc) + { + whitespace_found = 0; + + /* Suppress whitespace at start of string. */ + if (j) + temp[j++] = ' '; + } + + temp[j++] = string[i]; + } + } + + /* Kill trailing whitespace. */ + if (j && whitespace (temp[j - 1])) + j--; + + temp[j] = '\0'; + strcpy (string, temp); + free (temp); +} + +/* String representation of a char returned by printed_representation (). */ +static char the_rep[10]; + +/* Return a pointer to a string which is the printed representation + of CHARACTER if it were printed at HPOS. */ +char * +printed_representation (unsigned char character, int hpos) +{ + register int i = 0; + int printable_limit = ISO_Latin_p ? 255 : 127; + + if (raw_escapes_p && character == '\033') + the_rep[i++] = character; + /* Show CTRL-x as ^X. */ + else if (iscntrl (character) && character < 127) + { + switch (character) + { + case '\r': + case '\n': + the_rep[i++] = character; + break; + + case '\t': + { + int tw; + + tw = ((hpos + 8) & 0xf8) - hpos; + while (i < tw) + the_rep[i++] = ' '; + } + break; + + default: + the_rep[i++] = '^'; + the_rep[i++] = (character | 0x40); + } + } + /* Show META-x as 0370. */ + else if (character > printable_limit) + { + sprintf (the_rep + i, "\\%0o", character); + i = strlen (the_rep); + } + else if (character == DEL) + { + the_rep[i++] = '^'; + the_rep[i++] = '?'; + } + else + the_rep[i++] = character; + + the_rep[i] = 0; + + return the_rep; +} + + +/* **************************************************************** */ +/* */ +/* Functions Static To This File */ +/* */ +/* **************************************************************** */ + +/* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */ +static int parsed_filename_size = 0; + +/* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */ +static int parsed_nodename_size = 0; + +static void save_string (char *string, char **string_p, int *string_size_p); +static void saven_string (char *string, int len, char **string_p, + int *string_size_p); + +/* Remember FILENAME in PARSED_FILENAME. An empty FILENAME is translated + to a NULL pointer in PARSED_FILENAME. */ +static void +save_filename (char *filename) +{ + save_string (filename, &info_parsed_filename, &parsed_filename_size); +} + +/* Just like save_filename (), but you pass the length of the string. */ +static void +saven_filename (char *filename, int len) +{ + saven_string (filename, len, + &info_parsed_filename, &parsed_filename_size); +} + +/* Remember NODENAME in PARSED_NODENAME. An empty NODENAME is translated + to a NULL pointer in PARSED_NODENAME. */ +static void +save_nodename (char *nodename) +{ + save_string (nodename, &info_parsed_nodename, &parsed_nodename_size); +} + +/* Just like save_nodename (), but you pass the length of the string. */ +static void +saven_nodename (char *nodename, int len) +{ + saven_string (nodename, len, + &info_parsed_nodename, &parsed_nodename_size); +} + +/* Remember STRING in STRING_P. STRING_P should currently have STRING_SIZE_P + bytes allocated to it. An empty STRING is translated to a NULL pointer + in STRING_P. */ +static void +save_string (char *string, char **string_p, int *string_size_p) +{ + if (!string || !*string) + { + if (*string_p) + free (*string_p); + + *string_p = (char *)NULL; + *string_size_p = 0; + } + else + { + if (strlen (string) >= (unsigned int) *string_size_p) + *string_p = (char *)xrealloc + (*string_p, (*string_size_p = 1 + strlen (string))); + + strcpy (*string_p, string); + } +} + +/* Just like save_string (), but you also pass the length of STRING. */ +static void +saven_string (char *string, int len, char **string_p, int *string_size_p) +{ + if (!string) + { + if (*string_p) + free (*string_p); + + *string_p = (char *)NULL; + *string_size_p = 0; + } + else + { + if (len >= *string_size_p) + *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len)); + + strncpy (*string_p, string, len); + (*string_p)[len] = '\0'; + } +} + +/* Return a pointer to the part of PATHNAME that simply defines the file. */ +char * +filename_non_directory (char *pathname) +{ + register char *filename = pathname + strlen (pathname); + + if (HAVE_DRIVE (pathname)) + pathname += 2; + + while (filename > pathname && !IS_SLASH (filename[-1])) + filename--; + + return (filename); +} + +/* Return non-zero if NODE is one especially created by Info. */ +int +internal_info_node_p (NODE *node) +{ +#if defined (NEVER) + if (node && + (node->filename && !*node->filename) && + !node->parent && node->nodename) + return (1); + else + return (0); +#else + return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0)); +#endif /* !NEVER */ +} + +/* Make NODE appear to be one especially created by Info. */ +void +name_internal_node (NODE *node, char *name) +{ + if (!node) + return; + + node->filename = ""; + node->parent = (char *)NULL; + node->nodename = name; + node->flags |= N_IsInternal; +} + +/* Return the window displaying NAME, the name of an internally created + Info window. */ +WINDOW * +get_internal_info_window (char *name) +{ + WINDOW *win; + + for (win = windows; win; win = win->next) + if (internal_info_node_p (win->node) && + (strcmp (win->node->nodename, name) == 0)) + break; + + return (win); +} + +/* Return a window displaying the node NODE. */ +WINDOW * +get_window_of_node (NODE *node) +{ + WINDOW *win = (WINDOW *)NULL; + + for (win = windows; win; win = win->next) + if (win->node == node) + break; + + return (win); +} diff --git a/Build/source/utils/texinfo/info/info-utils.h b/Build/source/utils/texinfo/info/info-utils.h new file mode 100644 index 00000000000..6517b152069 --- /dev/null +++ b/Build/source/utils/texinfo/info/info-utils.h @@ -0,0 +1,136 @@ +/* info-utils.h -- Exported functions and variables from info-utils.c. + $Id: info-utils.h,v 1.4 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1996, 1998, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_UTILS_H +#define INFO_UTILS_H + +#include "nodes.h" +#include "window.h" +#include "search.h" + +/* Structure which describes a node reference, such as a menu entry or + cross reference. Arrays of such references can be built by calling + info_menus_of_node () or info_xrefs_of_node (). */ +typedef struct { + char *label; /* User Label. */ + char *filename; /* File where this node can be found. */ + char *nodename; /* Name of the node. */ + int start, end; /* Offsets within the containing node of LABEL. */ + int line_number; /* Specific line number a menu item points to. */ +} REFERENCE; + +/* When non-zero, various display and input functions handle ISO Latin + character sets correctly. */ +extern int ISO_Latin_p; + +/* Variable which holds the most recent filename parsed as a result of + calling info_parse_xxx (). */ +extern char *info_parsed_filename; + +/* Variable which holds the most recent nodename parsed as a result of + calling info_parse_xxx (). */ +extern char *info_parsed_nodename; + +/* Parse the filename and nodename out of STRING. If STRING doesn't + contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set + INFO_PARSED_FILENAME to NULL. If second argument NEWLINES_OKAY is + non-zero, it says to allow the nodename specification to cross a + newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */ +void info_parse_node (char *string, int newlines_okay); + +/* Return a NULL terminated array of REFERENCE * which represents the menu + found in NODE. If there is no menu in NODE, just return a NULL pointer. */ +extern REFERENCE **info_menu_of_node (NODE *node); + +/* Return a NULL terminated array of REFERENCE * which represents the cross + refrences found in NODE. If there are no cross references in NODE, just + return a NULL pointer. */ +extern REFERENCE **info_xrefs_of_node (NODE *node); + +/* Glean cross references from BINDING->buffer + BINDING->start until + BINDING->end. Return an array of REFERENCE * that represents each + cross reference in this range. */ +extern REFERENCE **info_xrefs (SEARCH_BINDING *binding); + +/* Get the entry associated with LABEL in REFERENCES. Return a pointer to + the reference if found, or NULL. */ +extern REFERENCE *info_get_labeled_reference (char *label, + REFERENCE **references); + +/* Glean menu entries from BINDING->buffer + BINDING->start until we + have looked at the entire contents of BINDING. Return an array + of REFERENCE * that represents each menu item in this range. */ +extern REFERENCE **info_menu_items (SEARCH_BINDING *binding); + +/* A utility function for concatenating REFERENCE **. Returns a new + REFERENCE ** which is the concatenation of REF1 and REF2. The REF1 + and REF2 arrays are freed, but their contents are not. */ +REFERENCE **info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2); + +/* Copy an existing reference into new memory. */ +extern REFERENCE *info_copy_reference (REFERENCE *src); + +/* Free the data associated with REFERENCES. */ +extern void info_free_references (REFERENCE **references); + +/* Search for sequences of whitespace or newlines in STRING, replacing + all such sequences with just a single space. Remove whitespace from + start and end of string. */ +void canonicalize_whitespace (char *string); + +/* Return a pointer to a string which is the printed representation + of CHARACTER if it were printed at HPOS. */ +extern char *printed_representation (unsigned char character, int hpos); + +/* Return a pointer to the part of PATHNAME that simply defines the file. */ +extern char *filename_non_directory (char *pathname); + +/* Return non-zero if NODE is one especially created by Info. */ +extern int internal_info_node_p (NODE *node); + +/* Make NODE appear to be one especially created by Info, and give it NAME. */ +extern void name_internal_node (NODE *node, char *name); + +/* Return the window displaying NAME, the name of an internally created + Info window. */ +extern WINDOW *get_internal_info_window (char *name); + +/* Return a window displaying the node NODE. */ +extern WINDOW *get_window_of_node (NODE *node); + +/* Return the node addressed by LABEL in NODE (usually one of "Prev:", + "Next:", "Up:", "File:", or "Node:". After a call to this function, + the globals `info_parsed_nodename' and `info_parsed_filename' contain + the information. */ +extern void info_parse_label (char *label, NODE *node); + +#define info_file_label_of_node(n) info_parse_label (INFO_FILE_LABEL, n) +#define info_next_label_of_node(n) info_parse_label (INFO_NEXT_LABEL, n) +#define info_up_label_of_node(n) info_parse_label (INFO_UP_LABEL, n) +#define info_prev_label_of_node(n) \ + do { \ + info_parse_label (INFO_PREV_LABEL, n); \ + if (!info_parsed_nodename && !info_parsed_filename) \ + info_parse_label (INFO_ALTPREV_LABEL, n); \ + } while (0) + +#endif /* not INFO_UTILS_H */ diff --git a/Build/source/utils/texinfo/info/info.c b/Build/source/utils/texinfo/info/info.c new file mode 100644 index 00000000000..79dd0240c0a --- /dev/null +++ b/Build/source/utils/texinfo/info/info.c @@ -0,0 +1,685 @@ +/* info.c -- Display nodes of Info files in multiple windows. + $Id: info.c,v 1.11 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, + 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "indices.h" +#include "dribble.h" +#include "getopt.h" +#if defined (HANDLE_MAN_PAGES) +# include "man.h" +#endif /* HANDLE_MAN_PAGES */ + +static char *program_name = "info"; + +/* Non-zero means search all indices for APROPOS_SEARCH_STRING. */ +static int apropos_p = 0; + +/* Variable containing the string to search for when apropos_p is non-zero. */ +static char *apropos_search_string = (char *)NULL; + +/* Non-zero means search all indices for INDEX_SEARCH_STRING. Unlike + apropos, this puts the user at the node, running info. */ +static int index_search_p = 0; + +/* Non-zero means look for the node which describes the invocation + and command-line options of the program, and start the info + session at that node. */ +static int goto_invocation_p = 0; + +/* Variable containing the string to search for when index_search_p is + non-zero. */ +static char *index_search_string = (char *)NULL; + +/* Non-zero means print version info only. */ +static int print_version_p = 0; + +/* Non-zero means print a short description of the options. */ +static int print_help_p = 0; + +/* Array of the names of nodes that the user specified with "--node" on the + command line. */ +static char **user_nodenames = (char **)NULL; +static int user_nodenames_index = 0; +static int user_nodenames_slots = 0; + +/* String specifying the first file to load. This string can only be set + by the user specifying "--file" on the command line. */ +static char *user_filename = (char *)NULL; + +/* String specifying the name of the file to dump nodes to. This value is + filled if the user speficies "--output" on the command line. */ +static char *user_output_filename = (char *)NULL; + +/* Non-zero indicates that when "--output" is specified, all of the menu + items of the specified nodes (and their subnodes as well) should be + dumped in the order encountered. This basically can print a book. */ +int dump_subnodes = 0; + +/* Non-zero means make default keybindings be loosely modeled on vi(1). */ +int vi_keys_p = 0; + +/* Non-zero means don't remove ANSI escape sequences. */ +int raw_escapes_p = 1; + +/* Non-zero means print the absolute location of the file to be loaded. */ +static int print_where_p = 0; + +#ifdef __MSDOS__ +/* Non-zero indicates that screen output should be made 'speech-friendly'. + Since on MSDOS the usual behavior is to write directly to the video + memory, speech synthesizer software cannot grab the output. Therefore, + we provide a user option which tells us to avoid direct screen output + and use stdout instead (which loses the color output). */ +int speech_friendly = 0; +#endif + +/* Structure describing the options that Info accepts. We pass this structure + to getopt_long (). If you add or otherwise change this structure, you must + also change the string which follows it. */ +#define APROPOS_OPTION 1 +#define DRIBBLE_OPTION 2 +#define RESTORE_OPTION 3 +#define IDXSRCH_OPTION 4 +static struct option long_options[] = { + { "apropos", 1, 0, APROPOS_OPTION }, + { "directory", 1, 0, 'd' }, + { "dribble", 1, 0, DRIBBLE_OPTION }, + { "file", 1, 0, 'f' }, + { "help", 0, &print_help_p, 1 }, + { "index-search", 1, 0, IDXSRCH_OPTION }, + { "location", 0, &print_where_p, 1 }, + { "node", 1, 0, 'n' }, + { "output", 1, 0, 'o' }, + { "raw-escapes", 0, &raw_escapes_p, 1 }, + { "no-raw-escapes", 0, &raw_escapes_p, 0 }, + { "restore", 1, 0, RESTORE_OPTION }, + { "show-options", 0, 0, 'O' }, + { "subnodes", 0, &dump_subnodes, 1 }, + { "usage", 0, 0, 'O' }, + { "version", 0, &print_version_p, 1 }, + { "vi-keys", 0, &vi_keys_p, 1 }, + { "where", 0, &print_where_p, 1 }, +#ifdef __MSDOS__ + { "speech-friendly", 0, &speech_friendly, 1 }, +#endif + {NULL, 0, NULL, 0} +}; + +/* String describing the shorthand versions of the long options found above. */ +#ifdef __MSDOS__ +static char *short_options = "d:n:f:ho:ORswb"; +#else +static char *short_options = "d:n:f:ho:ORws"; +#endif + +/* When non-zero, the Info window system has been initialized. */ +int info_windows_initialized_p = 0; + +/* Some "forward" declarations. */ +static void info_short_help (void); +static void init_messages (void); + + +/* **************************************************************** */ +/* */ +/* Main Entry Point to the Info Program */ +/* */ +/* **************************************************************** */ + +int +main (int argc, char **argv) +{ + int getopt_long_index; /* Index returned by getopt_long (). */ + NODE *initial_node; /* First node loaded by Info. */ + +#ifdef HAVE_SETLOCALE + /* Set locale via LC_ALL. */ + setlocale (LC_ALL, ""); +#endif + +#ifdef ENABLE_NLS + /* Set the text message domain. */ + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); +#endif + + init_messages (); + + while (1) + { + int option_character; + + option_character = getopt_long + (argc, argv, short_options, long_options, &getopt_long_index); + + /* getopt_long returns EOF when there are no more long options. */ + if (option_character == EOF) + break; + + /* If this is a long option, then get the short version of it. */ + if (option_character == 0 && long_options[getopt_long_index].flag == 0) + option_character = long_options[getopt_long_index].val; + + /* Case on the option that we have received. */ + switch (option_character) + { + case 0: + break; + + /* User wants to add a directory. */ + case 'd': + info_add_path (optarg, INFOPATH_PREPEND); + break; + + /* User is specifying a particular node. */ + case 'n': + add_pointer_to_array (optarg, user_nodenames_index, user_nodenames, + user_nodenames_slots, 10, char *); + break; + + /* User is specifying a particular Info file. */ + case 'f': + if (user_filename) + free (user_filename); + + user_filename = xstrdup (optarg); + break; + + /* Treat -h like --help. */ + case 'h': + print_help_p = 1; + break; + + /* User is specifying the name of a file to output to. */ + case 'o': + if (user_output_filename) + free (user_output_filename); + user_output_filename = xstrdup (optarg); + break; + + /* User has specified that she wants to find the "Options" + or "Invocation" node for the program. */ + case 'O': + goto_invocation_p = 1; + break; + + /* User has specified that she wants the escape sequences + in man pages to be passed thru unaltered. */ + case 'R': + raw_escapes_p = 1; + break; + + /* User is specifying that she wishes to dump the subnodes of + the node that she is dumping. */ + case 's': + dump_subnodes = 1; + break; + + /* For compatibility with man, -w is --where. */ + case 'w': + print_where_p = 1; + break; + +#ifdef __MSDOS__ + /* User wants speech-friendly output. */ + case 'b': + speech_friendly = 1; + break; +#endif /* __MSDOS__ */ + + /* User has specified a string to search all indices for. */ + case APROPOS_OPTION: + apropos_p = 1; + maybe_free (apropos_search_string); + apropos_search_string = xstrdup (optarg); + break; + + /* User has specified a dribble file to receive keystrokes. */ + case DRIBBLE_OPTION: + close_dribble_file (); + open_dribble_file (optarg); + break; + + /* User has specified an alternate input stream. */ + case RESTORE_OPTION: + info_set_input_from_file (optarg); + break; + + /* User has specified a string to search all indices for. */ + case IDXSRCH_OPTION: + index_search_p = 1; + maybe_free (index_search_string); + index_search_string = xstrdup (optarg); + break; + + default: + fprintf (stderr, _("Try --help for more information.\n")); + xexit (1); + } + } + + /* If the output device is not a terminal, and no output filename has been + specified, make user_output_filename be "-", so that the info is written + to stdout, and turn on the dumping of subnodes. */ + if ((!isatty (fileno (stdout))) && (user_output_filename == (char *)NULL)) + { + user_output_filename = xstrdup ("-"); + dump_subnodes = 1; + } + + /* If the user specified --version, then show the version and exit. */ + if (print_version_p) + { + printf ("%s (GNU %s) %s\n", program_name, PACKAGE, VERSION); + puts (""); + puts ("Copyright (C) 2004 Free Software Foundation, Inc."); + printf (_("There is NO warranty. You may redistribute this software\n\ +under the terms of the GNU General Public License.\n\ +For more information about these matters, see the files named COPYING.\n")); + xexit (0); + } + + /* If the `--help' option was present, show the help and exit. */ + if (print_help_p) + { + info_short_help (); + xexit (0); + } + + /* If the user hasn't specified a path for Info files, default it. + Lowest priority is our messy hardwired list in filesys.h. + Then comes the user's INFODIR from the Makefile. + Highest priority is the environment variable, if set. */ + if (!infopath) + { + char *path_from_env = getenv ("INFOPATH"); + + if (path_from_env) + { + unsigned len = strlen (path_from_env); + /* Trailing : on INFOPATH means insert the default path. */ + if (len && path_from_env[len - 1] == PATH_SEP[0]) + { + path_from_env[len - 1] = 0; + info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND); + } +#ifdef INFODIR /* from the Makefile */ + info_add_path (INFODIR, INFOPATH_PREPEND); +#endif + info_add_path (path_from_env, INFOPATH_PREPEND); + } + else + { + info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND); +#ifdef INFODIR /* from the Makefile */ + info_add_path (INFODIR, INFOPATH_PREPEND); +#endif +#ifdef INFODIR2 /* from the Makefile, too */ +# ifdef INFODIR + if (!STREQ (INFODIR, INFODIR2)) +# endif + info_add_path (INFODIR2, INFOPATH_PREPEND); +#endif + } + } + + /* If the user specified a particular filename, add the path of that + file to the contents of INFOPATH. */ + if (user_filename) + add_file_directory_to_path (user_filename); + + /* If the user wants to search every known index for a given string, + do that now, and report the results. */ + if (apropos_p) + { + info_apropos (apropos_search_string); + xexit (0); + } + + /* Get the initial Info node. It is either "(dir)Top", or what the user + specifed with values in user_filename and user_nodenames. */ + initial_node = info_get_node (user_filename, + user_nodenames ? user_nodenames[0] : 0); + + /* If we couldn't get the initial node, this user is in trouble. */ + if (!initial_node) + { + if (info_recent_file_error) + info_error (info_recent_file_error, NULL, NULL); + else + info_error ((char *) msg_cant_find_node, + user_nodenames ? user_nodenames[0] : "Top", NULL); + xexit (1); + } + + /* Special cases for when the user specifies multiple nodes. If we + are dumping to an output file, dump all of the nodes specified. + Otherwise, attempt to create enough windows to handle the nodes + that this user wants displayed. */ + if (user_nodenames_index > 1) + { + free (initial_node); + + if (print_where_p) + printf ("%s\n", user_filename ? user_filename : "unknown?!"); + else if (user_output_filename) + dump_nodes_to_file + (user_filename, user_nodenames, user_output_filename, dump_subnodes); + else + begin_multiple_window_info_session (user_filename, user_nodenames); + + xexit (0); + } + + /* If there are arguments remaining, they are the names of menu items + in sequential info files starting from the first one loaded. That + file name is either "dir", or the contents of user_filename if one + was specified. */ + { + const char *errstr; + char *errarg1, *errarg2; + + NODE *new_initial_node = info_follow_menus (initial_node, argv + optind, + &errstr, &errarg1, &errarg2); + + if (new_initial_node && new_initial_node != initial_node) + initial_node = new_initial_node; + + if (print_where_p) + { + if (initial_node->parent) + printf ("%s\n", initial_node->parent); + else if (initial_node->filename + && !is_dir_name (filename_non_directory (initial_node->filename))) + printf ("%s\n", initial_node->filename); + else + xexit (1); + xexit (0); + } + + /* If the user specified that this node should be output, then do that + now. Otherwise, start the Info session with this node. Or act + accordingly if the initial node was not found. */ + if (user_output_filename && !goto_invocation_p) + { + if (!errstr) + dump_node_to_file (initial_node, user_output_filename, + dump_subnodes); + else + info_error ((char *) errstr, errarg1, errarg2); + } + else + { + + if (errstr) + begin_info_session_with_error (initial_node, (char *) errstr, + errarg1, errarg2); + /* If the user specified `--index-search=STRING' or + --show-options, start the info session in the node + corresponding to what they want. */ + else if (index_search_p || goto_invocation_p) + { + int status = 0; + + initialize_info_session (initial_node, 0); + + if (goto_invocation_p + || index_entry_exists (windows, index_search_string)) + { + terminal_prep_terminal (); + terminal_clear_screen (); + info_last_executed_command = (VFunction *)NULL; + + if (index_search_p) + do_info_index_search (windows, 0, index_search_string); + else + { + /* If they said "info --show-options foo bar baz", + the last of the arguments is the program whose + options they want to see. */ + char **p = argv + optind; + char *program; + + if (*p) + { + while (p[1]) + p++; + program = xstrdup (*p); + } + else if (user_filename) + /* If there's no command-line arguments to + supply the program name, use the Info file + name (sans extension and leading directories) + instead. */ + program = program_name_from_file_name (user_filename); + else + program = xstrdup (""); + + info_intuit_options_node (windows, initial_node, program); + free (program); + } + + if (user_output_filename) + { + dump_node_to_file (windows->node, user_output_filename, + dump_subnodes); + } + else + info_read_and_dispatch (); + + /* On program exit, leave the cursor at the bottom of the + window, and restore the terminal IO. */ + terminal_goto_xy (0, screenheight - 1); + terminal_clear_to_eol (); + fflush (stdout); + terminal_unprep_terminal (); + } + else + { + fprintf (stderr, _("no index entries found for `%s'\n"), + index_search_string); + status = 2; + } + + close_dribble_file (); + xexit (status); + } + else + begin_info_session (initial_node); + } + + xexit (0); + } + + return 0; /* Avoid bogus warnings. */ +} + +void +add_file_directory_to_path (char *filename) +{ + char *directory_name = xstrdup (filename); + char *temp = filename_non_directory (directory_name); + + if (temp != directory_name) + { + if (HAVE_DRIVE (directory_name) && temp == directory_name + 2) + { + /* The directory of "d:foo" is stored as "d:.", to avoid + mixing it with "d:/" when a slash is appended. */ + *temp = '.'; + temp += 2; + } + temp[-1] = 0; + info_add_path (directory_name, INFOPATH_PREPEND); + } + + free (directory_name); +} + + +/* Error handling. */ + +/* Non-zero if an error has been signalled. */ +int info_error_was_printed = 0; + +/* Non-zero means ring terminal bell on errors. */ +int info_error_rings_bell_p = 1; + +/* Print FORMAT with ARG1 and ARG2. If the window system was initialized, + then the message is printed in the echo area. Otherwise, a message is + output to stderr. */ +void +info_error (char *format, void *arg1, void *arg2) +{ + info_error_was_printed = 1; + + if (!info_windows_initialized_p || display_inhibited) + { + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, format, arg1, arg2); + fprintf (stderr, "\n"); + fflush (stderr); + } + else + { + if (!echo_area_is_active) + { + if (info_error_rings_bell_p) + terminal_ring_bell (); + window_message_in_echo_area (format, arg1, arg2); + } + else + { + NODE *temp; + + temp = build_message_node (format, arg1, arg2); + if (info_error_rings_bell_p) + terminal_ring_bell (); + inform_in_echo_area (temp->contents); + free (temp->contents); + free (temp); + } + } +} + + +/* Produce a scaled down description of the available options to Info. */ +static void +info_short_help (void) +{ +#ifdef __MSDOS__ + static const char speech_friendly_string[] = N_("\ + -b, --speech-friendly be friendly to speech synthesizers.\n"); +#else + static const char speech_friendly_string[] = ""; +#endif + + + printf (_("\ +Usage: %s [OPTION]... [MENU-ITEM...]\n\ +\n\ +Read documentation in Info format.\n\ +\n\ +Options:\n\ + --apropos=STRING look up STRING in all indices of all manuals.\n\ + -d, --directory=DIR add DIR to INFOPATH.\n\ + --dribble=FILENAME remember user keystrokes in FILENAME.\n\ + -f, --file=FILENAME specify Info file to visit.\n\ + -h, --help display this help and exit.\n\ + --index-search=STRING go to node pointed by index entry STRING.\n\ + -n, --node=NODENAME specify nodes in first visited Info file.\n\ + -o, --output=FILENAME output selected nodes to FILENAME.\n\ + -R, --raw-escapes output \"raw\" ANSI escapes (default).\n\ + --no-raw-escapes output escapes as literal text.\n\ + --restore=FILENAME read initial keystrokes from FILENAME.\n\ + -O, --show-options, --usage go to command-line options node.\n%s\ + --subnodes recursively output menu items.\n\ + -w, --where, --location print physical location of Info file.\n\ + --vi-keys use vi-like and less-like key bindings.\n\ + --version display version information and exit.\n\ +\n\ +The first non-option argument, if present, is the menu entry to start from;\n\ +it is searched for in all `dir' files along INFOPATH.\n\ +If it is not present, info merges all `dir' files and shows the result.\n\ +Any remaining arguments are treated as the names of menu\n\ +items relative to the initial node visited.\n\ +\n\ +Examples:\n\ + info show top-level dir menu\n\ + info emacs start at emacs node from top-level dir\n\ + info emacs buffers start at buffers node within emacs manual\n\ + info --show-options emacs start at node with emacs' command line options\n\ + info -f ./foo.info show file ./foo.info, not searching dir\n\ +"), + program_name, speech_friendly_string); + + puts (_("\n\ +Email bug reports to bug-texinfo@gnu.org,\n\ +general questions and discussion to help-texinfo@gnu.org.\n\ +Texinfo home page: http://www.gnu.org/software/texinfo/")); + + xexit (0); +} + + +/* Initialize strings for gettext. Because gettext doesn't handle N_ or + _ within macro definitions, we put shared messages into variables and + use them that way. This also has the advantage that there's only one + copy of the strings. */ + +const char *msg_cant_find_node; +const char *msg_cant_file_node; +const char *msg_cant_find_window; +const char *msg_cant_find_point; +const char *msg_cant_kill_last; +const char *msg_no_menu_node; +const char *msg_no_foot_node; +const char *msg_no_xref_node; +const char *msg_no_pointer; +const char *msg_unknown_command; +const char *msg_term_too_dumb; +const char *msg_at_node_bottom; +const char *msg_at_node_top; +const char *msg_one_window; +const char *msg_win_too_small; +const char *msg_cant_make_help; + +static void +init_messages (void) +{ + msg_cant_find_node = _("Cannot find node `%s'."); + msg_cant_file_node = _("Cannot find node `(%s)%s'."); + msg_cant_find_window = _("Cannot find a window!"); + msg_cant_find_point = _("Point doesn't appear within this window's node!"); + msg_cant_kill_last = _("Cannot delete the last window."); + msg_no_menu_node = _("No menu in this node."); + msg_no_foot_node = _("No footnotes in this node."); + msg_no_xref_node = _("No cross references in this node."); + msg_no_pointer = _("No `%s' pointer for this node."); + msg_unknown_command = _("Unknown Info command `%c'; try `?' for help."); + msg_term_too_dumb = _("Terminal type `%s' is not smart enough to run Info."); + msg_at_node_bottom = _("You are already at the last page of this node."); + msg_at_node_top = _("You are already at the first page of this node."); + msg_one_window = _("Only one window."); + msg_win_too_small = _("Resulting window would be too small."); + msg_cant_make_help = _("Not enough room for a help window, please delete a window."); +} diff --git a/Build/source/utils/texinfo/info/info.h b/Build/source/utils/texinfo/info/info.h new file mode 100644 index 00000000000..7ff8abe9b19 --- /dev/null +++ b/Build/source/utils/texinfo/info/info.h @@ -0,0 +1,160 @@ +/* info.h -- Header file which includes all of the other headers. + $Id: info.h,v 1.4 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_H +#define INFO_H + +/* We always want these, so why clutter up the compile command? */ +#define HANDLE_MAN_PAGES +#define NAMED_FUNCTIONS +#define INFOKEY + +/* System dependencies. */ +#include "system.h" + +/* Some of our other include files use these. */ +typedef int Function (); +typedef void VFunction (); +typedef char *CFunction (); + +#include "filesys.h" +#include "doc.h" +#include "display.h" +#include "session.h" +#include "echo-area.h" +#include "footnotes.h" +#include "gc.h" + +#define info_toupper(x) (islower (x) ? toupper (x) : x) +#define info_tolower(x) (isupper (x) ? tolower (x) : x) + +#if !defined (whitespace) +# define whitespace(c) ((c == ' ') || (c == '\t')) +#endif /* !whitespace */ + +#if !defined (whitespace_or_newline) +# define whitespace_or_newline(c) (whitespace (c) || (c == '\n')) +#endif /* !whitespace_or_newline */ + +/* Add POINTER to the list of pointers found in ARRAY. SLOTS is the number + of slots that have already been allocated. INDEX is the index into the + array where POINTER should be added. GROW is the number of slots to grow + ARRAY by, in the case that it needs growing. TYPE is a cast of the type + of object stored in ARRAY (e.g., NODE_ENTRY *. */ +#define add_pointer_to_array(pointer, idx, array, slots, grow, type) \ + do { \ + if (idx + 2 >= slots) \ + array = (type *)(xrealloc (array, (slots += grow) * sizeof (type))); \ + array[idx++] = (type)pointer; \ + array[idx] = (type)NULL; \ + } while (0) + +#define maybe_free(x) do { if (x) free (x); } while (0) + +#if !defined (zero_mem) && defined (HAVE_MEMSET) +# define zero_mem(mem, length) memset (mem, 0, length) +#endif /* !zero_mem && HAVE_MEMSET */ + +#if !defined (zero_mem) && defined (HAVE_BZERO) +# define zero_mem(mem, length) bzero (mem, length) +#endif /* !zero_mem && HAVE_BZERO */ + +#if !defined (zero_mem) +# define zero_mem(mem, length) \ + do { \ + register int zi; \ + register unsigned char *place; \ + \ + place = (unsigned char *)mem; \ + for (zi = 0; zi < length; zi++) \ + place[zi] = 0; \ + } while (0) +#endif /* !zero_mem */ + + +/* A structure associating the nodes visited in a particular window. */ +typedef struct { + WINDOW *window; /* The window that this list is attached to. */ + NODE **nodes; /* Array of nodes visited in this window. */ + int *pagetops; /* For each node in NODES, the pagetop. */ + long *points; /* For each node in NODES, the point. */ + int current; /* Index in NODES of the current node. */ + int nodes_index; /* Index where to add the next node. */ + int nodes_slots; /* Number of slots allocated to NODES. */ +} INFO_WINDOW; + +/* Array of structures describing for each window which nodes have been + visited in that window. */ +extern INFO_WINDOW **info_windows; + +/* For handling errors. If you initialize the window system, you should + also set info_windows_initialized_p to non-zero. It is used by the + info_error () function to determine how to format and output errors. */ +extern int info_windows_initialized_p; + +/* Non-zero if an error message has been printed. */ +extern int info_error_was_printed; + +/* Non-zero means ring terminal bell on errors. */ +extern int info_error_rings_bell_p; + +/* Non-zero means default keybindings are loosely modeled on vi(1). */ +extern int vi_keys_p; + +/* Non-zero means don't remove ANSI escape sequences from man pages. */ +extern int raw_escapes_p; + +/* Print FORMAT with ARG1 and ARG2. If the window system was initialized, + then the message is printed in the echo area. Otherwise, a message is + output to stderr. */ +extern void info_error (char *format, void *arg1, void *arg2); + +extern void add_file_directory_to_path (char *filename); + +/* Error message defines. */ +extern const char *msg_cant_find_node; +extern const char *msg_cant_file_node; +extern const char *msg_cant_find_window; +extern const char *msg_cant_find_point; +extern const char *msg_cant_kill_last; +extern const char *msg_no_menu_node; +extern const char *msg_no_foot_node; +extern const char *msg_no_xref_node; +extern const char *msg_no_pointer; +extern const char *msg_unknown_command; +extern const char *msg_term_too_dumb; +extern const char *msg_at_node_bottom; +extern const char *msg_at_node_top; +extern const char *msg_one_window; +extern const char *msg_win_too_small; +extern const char *msg_cant_make_help; + + +#if defined(INFOKEY) +/* Found in variables.c. */ +extern void set_variable_to_value (char *name, char *value); +#endif /* INFOKEY */ + +/* Found in m-x.c. */ +extern char *read_function_name (char *prompt, WINDOW *window); + +#endif /* !INFO_H */ diff --git a/Build/source/utils/texinfo/info/infodoc.c b/Build/source/utils/texinfo/info/infodoc.c new file mode 100644 index 00000000000..eeea147a967 --- /dev/null +++ b/Build/source/utils/texinfo/info/infodoc.c @@ -0,0 +1,1200 @@ +/* infodoc.c -- functions which build documentation nodes. + $Id: infodoc.c,v 1.8 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "funs.h" + +/* HELP_NODE_GETS_REGENERATED is always defined now that keys may get + rebound, or other changes in the help text may occur. */ +#define HELP_NODE_GETS_REGENERATED 1 + +/* The name of the node used in the help window. */ +static char *info_help_nodename = "*Info Help*"; + +/* A node containing printed key bindings and their documentation. */ +static NODE *internal_info_help_node = (NODE *)NULL; + +/* A pointer to the contents of the help node. */ +static char *internal_info_help_node_contents = (char *)NULL; + +/* The (more or less) static text which appears in the internal info + help node. The actual key bindings are inserted. Keep the + underlines (****, etc.) in the same N_ call as the text lines they + refer to, so translations can make the number of *'s or -'s match. */ +#if defined(INFOKEY) + +static char *info_internal_help_text[] = { + N_("Basic Commands in Info Windows\n\ +******************************\n"), + "\n", + N_("\\%-10[quit-help] Quit this help.\n"), + N_("\\%-10[quit] Quit Info altogether.\n"), + N_("\\%-10[get-info-help-node] Invoke the Info tutorial.\n"), + "\n", + N_("Selecting other nodes:\n\ +----------------------\n"), + N_("\\%-10[next-node] Move to the \"next\" node of this node.\n"), + N_("\\%-10[prev-node] Move to the \"previous\" node of this node.\n"), + N_("\\%-10[up-node] Move \"up\" from this node.\n"), + N_("\\%-10[menu-item] Pick menu item specified by name.\n\ + Picking a menu item causes another node to be selected.\n"), + N_("\\%-10[xref-item] Follow a cross reference. Reads name of reference.\n"), + N_("\\%-10[history-node] Move to the last node seen in this window.\n"), + N_("\\%-10[move-to-next-xref] Skip to next hypertext link within this node.\n"), + N_("\\%-10[move-to-prev-xref] Skip to previous hypertext link within this node.\n"), + N_("\\%-10[select-reference-this-line] Follow the hypertext link under cursor.\n"), + N_("\\%-10[dir-node] Move to the `directory' node. Equivalent to `\\[goto-node] (DIR)'.\n"), + N_("\\%-10[top-node] Move to the Top node. Equivalent to `\\[goto-node] Top'.\n"), + "\n", + N_("Moving within a node:\n\ +---------------------\n"), + N_("\\%-10[beginning-of-node] Go to the beginning of this node.\n"), + N_("\\%-10[end-of-node] Go to the end of this node.\n"), + N_("\\%-10[next-line] Scroll forward 1 line.\n"), + N_("\\%-10[prev-line] Scroll backward 1 line.\n"), + N_("\\%-10[scroll-forward] Scroll forward a page.\n"), + N_("\\%-10[scroll-backward] Scroll backward a page.\n"), + "\n", + N_("Other commands:\n\ +---------------\n"), + N_("\\%-10[menu-digit] Pick first ... ninth item in node's menu.\n"), + N_("\\%-10[last-menu-item] Pick last item in node's menu.\n"), + N_("\\%-10[index-search] Search for a specified string in the index entries of this Info\n\ + file, and select the node referenced by the first entry found.\n"), + N_("\\%-10[goto-node] Move to node specified by name.\n\ + You may include a filename as well, as in (FILENAME)NODENAME.\n"), + N_("\\%-10[search] Search forward for a specified string\n\ + and select the node in which the next occurrence is found.\n"), + N_("\\%-10[search-backward] Search backward for a specified string\n\ + and select the node in which the previous occurrence is found.\n"), + NULL +}; + +#else /* !INFOKEY */ + +static char *info_internal_help_text[] = { + N_("Basic Commands in Info Windows\n\ +******************************\n"), + "\n", + N_(" %-10s Quit this help.\n"), + N_(" %-10s Quit Info altogether.\n"), + N_(" %-10s Invoke the Info tutorial.\n"), + "\n", + N_("Selecting other nodes:\n\ +----------------------\n", + N_(" %-10s Move to the `next' node of this node.\n"), + N_(" %-10s Move to the `previous' node of this node.\n"), + N_(" %-10s Move `up' from this node.\n"), + N_(" %-10s Pick menu item specified by name.\n"), + N_(" Picking a menu item causes another node to be selected.\n"), + N_(" %-10s Follow a cross reference. Reads name of reference.\n"), + N_(" %-10s Move to the last node seen in this window.\n"), + N_(" %-10s Skip to next hypertext link within this node.\n"), + N_(" %-10s Follow the hypertext link under cursor.\n"), + N_(" %-10s Move to the `directory' node. Equivalent to `g (DIR)'.\n"), + N_(" %-10s Move to the Top node. Equivalent to `g Top'.\n"), + "\n", + N_("Moving within a node:\n\ +---------------------\n"), + N_(" %-10s Scroll forward a page.\n"), + N_(" %-10s Scroll backward a page.\n"), + N_(" %-10s Go to the beginning of this node.\n"), + N_(" %-10s Go to the end of this node.\n"), + N_(" %-10s Scroll forward 1 line.\n"), + N_(" %-10s Scroll backward 1 line.\n"), + "\n", + N_("Other commands:\n\ +---------------\n"), + N_(" %-10s Pick first ... ninth item in node's menu.\n"), + N_(" %-10s Pick last item in node's menu.\n"), + N_(" %-10s Search for a specified string in the index entries of this Info\n"), + N_(" file, and select the node referenced by the first entry found.\n"), + N_(" %-10s Move to node specified by name.\n"), + N_(" You may include a filename as well, as in (FILENAME)NODENAME.\n"), + N_(" %-10s Search forward for a specified string,\n"), + N_(" and select the node in which the next occurrence is found.\n"), + N_(" %-10s Search backward for a specified string\n"), + N_(" and select the node in which the next occurrence is found.\n"), + NULL +}; + +static char *info_help_keys_text[][2] = { + { "", "" }, + { "", "" }, + { "", "" }, + { "CTRL-x 0", "CTRL-x 0" }, + { "q", "q" }, + { "h", "ESC h" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "SPC", "SPC" }, + { "DEL", "b" }, + { "b", "ESC b" }, + { "e", "ESC e" }, + { "ESC 1 SPC", "RET" }, + { "ESC 1 DEL", "y" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "n", "CTRL-x n" }, + { "p", "CTRL-x p" }, + { "u", "CTRL-x u" }, + { "m", "ESC m" }, + { "", "" }, + { "f", "ESC f" }, + { "l", "l" }, + { "TAB", "TAB" }, + { "RET", "CTRL-x RET" }, + { "d", "ESC d" }, + { "t", "ESC t" }, + { "", "" }, + { "", "" }, + { "", "" }, + { "1-9", "ESC 1-9" }, + { "0", "ESC 0" }, + { "i", "CTRL-x i" }, + { "", "" }, + { "g", "CTRL-x g" }, + { "", "" }, + { "s", "/" }, + { "", "" }, + { "ESC - s", "?" }, + { "", "" }, + NULL +}; + +#endif /* !INFOKEY */ + +static char *where_is_internal (Keymap map, InfoCommand *cmd); + +void +dump_map_to_message_buffer (char *prefix, Keymap map) +{ + register int i; + unsigned prefix_len = strlen (prefix); + char *new_prefix = (char *)xmalloc (prefix_len + 2); + + strncpy (new_prefix, prefix, prefix_len); + new_prefix[prefix_len + 1] = '\0'; + + for (i = 0; i < 256; i++) + { + new_prefix[prefix_len] = i; + if (map[i].type == ISKMAP) + { + dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function); + } + else if (map[i].function) + { + register int last; + char *doc, *name; + + doc = function_documentation (map[i].function); + name = function_name (map[i].function); + + if (!*doc) + continue; + + /* Find out if there is a series of identical functions, as in + ea_insert (). */ + for (last = i + 1; last < 256; last++) + if ((map[last].type != ISFUNC) || + (map[last].function != map[i].function)) + break; + + if (last - 1 != i) + { + printf_to_message_buffer ("%s .. ", pretty_keyseq (new_prefix), + NULL, NULL); + new_prefix[prefix_len] = last - 1; + printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix), + NULL, NULL); + i = last - 1; + } + else + printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix), + NULL, NULL); + +#if defined (NAMED_FUNCTIONS) + /* Print the name of the function, and some padding before the + documentation string is printed. */ + { + int length_so_far; + int desired_doc_start = 40; /* Must be multiple of 8. */ + + printf_to_message_buffer ("(%s)", name, NULL, NULL); + length_so_far = message_buffer_length_this_line (); + + if ((desired_doc_start + strlen (doc)) + >= (unsigned int) the_screen->width) + printf_to_message_buffer ("\n ", NULL, NULL, NULL); + else + { + while (length_so_far < desired_doc_start) + { + printf_to_message_buffer ("\t", NULL, NULL, NULL); + length_so_far += character_width ('\t', length_so_far); + } + } + } +#endif /* NAMED_FUNCTIONS */ + printf_to_message_buffer ("%s\n", doc, NULL, NULL); + } + } + free (new_prefix); +} + +/* How to create internal_info_help_node. HELP_IS_ONLY_WINDOW_P says + whether we're going to end up in a second (or more) window of our + own, or whether there's only one window and we're going to usurp it. + This determines how to quit the help window. Maybe we should just + make q do the right thing in both cases. */ + +static void +create_internal_info_help_node (int help_is_only_window_p) +{ + register int i; + NODE *node; + char *contents = NULL; + char *exec_keys; + +#ifndef HELP_NODE_GETS_REGENERATED + if (internal_info_help_node_contents) + contents = internal_info_help_node_contents; +#endif /* !HELP_NODE_GETS_REGENERATED */ + + if (!contents) + { + int printed_one_mx = 0; + + initialize_message_buffer (); + + for (i = 0; info_internal_help_text[i]; i++) + { +#ifdef INFOKEY + printf_to_message_buffer (replace_in_documentation + ((char *) _(info_internal_help_text[i]), help_is_only_window_p), + NULL, NULL, NULL); +#else + /* Don't translate blank lines, gettext outputs the po file + header in that case. We want a blank line. */ + char *msg = *(info_internal_help_text[i]) + ? _(info_internal_help_text[i]) + : info_internal_help_text[i]; + char *key = info_help_keys_text[i][vi_keys_p]; + + /* If we have only one window (because the window size was too + small to split it), CTRL-x 0 doesn't work to `quit' help. */ + if (STREQ (key, "CTRL-x 0") && help_is_only_window_p) + key = "l"; + + printf_to_message_buffer (msg, key, NULL, NULL); +#endif /* !INFOKEY */ + } + + printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL); + printf_to_message_buffer ((char *) _("The current search path is:\n"), + NULL, NULL, NULL); + printf_to_message_buffer (" %s\n", infopath, NULL, NULL); + printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL); + printf_to_message_buffer ((char *) _("Commands available in Info windows:\n\n"), + NULL, NULL, NULL); + dump_map_to_message_buffer ("", info_keymap); + printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL); + printf_to_message_buffer ((char *) _("Commands available in the echo area:\n\n"), + NULL, NULL, NULL); + dump_map_to_message_buffer ("", echo_area_keymap); + +#if defined (NAMED_FUNCTIONS) + /* Get a list of commands which have no keystroke equivs. */ + exec_keys = where_is (info_keymap, InfoCmd(info_execute_command)); + if (exec_keys) + exec_keys = xstrdup (exec_keys); + for (i = 0; function_doc_array[i].func; i++) + { + InfoCommand *cmd = DocInfoCmd(&function_doc_array[i]); + + if (InfoFunction(cmd) != (VFunction *) info_do_lowercase_version + && !where_is_internal (info_keymap, cmd) + && !where_is_internal (echo_area_keymap, cmd)) + { + if (!printed_one_mx) + { + printf_to_message_buffer ("---------------------\n\n", + NULL, NULL, NULL); + if (exec_keys && exec_keys[0]) + printf_to_message_buffer + ((char *) _("The following commands can only be invoked via %s:\n\n"), + exec_keys, NULL, NULL); + else + printf_to_message_buffer + ((char *) _("The following commands cannot be invoked at all:\n\n"), + NULL, NULL, NULL); + printed_one_mx = 1; + } + + printf_to_message_buffer + ("%s %s\n %s\n", + exec_keys, + function_doc_array[i].func_name, + replace_in_documentation (strlen (function_doc_array[i].doc) + ? (char *) _(function_doc_array[i].doc) : "", 0) + ); + + } + } + + if (printed_one_mx) + printf_to_message_buffer ("\n", NULL, NULL, NULL); + + maybe_free (exec_keys); +#endif /* NAMED_FUNCTIONS */ + + printf_to_message_buffer + ("%s", replace_in_documentation + ((char *) _("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n"), 0), + NULL, NULL); + node = message_buffer_to_node (); + internal_info_help_node_contents = node->contents; + } + else + { + /* We already had the right contents, so simply use them. */ + node = build_message_node ("", 0, 0); + free (node->contents); + node->contents = contents; + node->nodelen = 1 + strlen (contents); + } + + internal_info_help_node = node; + + /* Do not GC this node's contents. It never changes, and we never need + to delete it once it is made. If you change some things (such as + placing information about dynamic variables in the help text) then + you will need to allow the contents to be gc'd, and you will have to + arrange to always regenerate the help node. */ +#if defined (HELP_NODE_GETS_REGENERATED) + add_gcable_pointer (internal_info_help_node->contents); +#endif + + name_internal_node (internal_info_help_node, info_help_nodename); + + /* Even though this is an internal node, we don't want the window + system to treat it specially. So we turn off the internalness + of it here. */ + internal_info_help_node->flags &= ~N_IsInternal; +} + +/* Return a window which is the window showing help in this Info. */ + +/* If the eligible window's height is >= this, split it to make the help + window. Otherwise display the help window in the current window. */ +#define HELP_SPLIT_SIZE 24 + +static WINDOW * +info_find_or_create_help_window (void) +{ + int help_is_only_window_p; + WINDOW *eligible = NULL; + WINDOW *help_window = get_window_of_node (internal_info_help_node); + + /* If we couldn't find the help window, then make it. */ + if (!help_window) + { + WINDOW *window; + int max = 0; + + for (window = windows; window; window = window->next) + { + if (window->height > max) + { + max = window->height; + eligible = window; + } + } + + if (!eligible) + return NULL; + } +#ifndef HELP_NODE_GETS_REGENERATED + else + /* help window is static, just return it. */ + return help_window; +#endif /* not HELP_NODE_GETS_REGENERATED */ + + /* Make sure that we have a node containing the help text. The + argument is false if help will be the only window (so l must be used + to quit help), true if help will be one of several visible windows + (so CTRL-x 0 must be used to quit help). */ + help_is_only_window_p = ((help_window && !windows->next) + || (!help_window && eligible->height < HELP_SPLIT_SIZE)); + create_internal_info_help_node (help_is_only_window_p); + + /* Either use the existing window to display the help node, or create + a new window if there was no existing help window. */ + if (!help_window) + { /* Split the largest window into 2 windows, and show the help text + in that window. */ + if (eligible->height >= HELP_SPLIT_SIZE) + { + active_window = eligible; + help_window = window_make_window (internal_info_help_node); + } + else + { + set_remembered_pagetop_and_point (active_window); + window_set_node_of_window (active_window, internal_info_help_node); + help_window = active_window; + } + } + else + { /* Case where help node always gets regenerated, and we have an + existing window in which to place the node. */ + if (active_window != help_window) + { + set_remembered_pagetop_and_point (active_window); + active_window = help_window; + } + window_set_node_of_window (active_window, internal_info_help_node); + } + remember_window_and_node (help_window, help_window->node); + return help_window; +} + +/* Create or move to the help window. */ +DECLARE_INFO_COMMAND (info_get_help_window, _("Display help message")) +{ + WINDOW *help_window; + + help_window = info_find_or_create_help_window (); + if (help_window) + { + active_window = help_window; + active_window->flags |= W_UpdateWindow; + } + else + { + info_error ((char *) msg_cant_make_help, NULL, NULL); + } +} + +/* Show the Info help node. This means that the "info" file is installed + where it can easily be found on your system. */ +DECLARE_INFO_COMMAND (info_get_info_help_node, _("Visit Info node `(info)Help'")) +{ + NODE *node; + char *nodename; + + /* If there is a window on the screen showing the node "(info)Help" or + the node "(info)Help-Small-Screen", simply select that window. */ + { + WINDOW *win; + + for (win = windows; win; win = win->next) + { + if (win->node && win->node->filename && + (strcasecmp + (filename_non_directory (win->node->filename), "info") == 0) && + ((strcmp (win->node->nodename, "Help") == 0) || + (strcmp (win->node->nodename, "Help-Small-Screen") == 0))) + { + active_window = win; + return; + } + } + } + + /* If the current window is small, show the small screen help. */ + if (active_window->height < 24) + nodename = "Help-Small-Screen"; + else + nodename = "Help"; + + /* Try to get the info file for Info. */ + node = info_get_node ("Info", nodename); + + if (!node) + { + if (info_recent_file_error) + info_error (info_recent_file_error, NULL, NULL); + else + info_error ((char *) msg_cant_file_node, "Info", nodename); + } + else + { + /* If the current window is very large (greater than 45 lines), + then split it and show the help node in another window. + Otherwise, use the current window. */ + + if (active_window->height > 45) + active_window = window_make_window (node); + else + { + set_remembered_pagetop_and_point (active_window); + window_set_node_of_window (active_window, node); + } + + remember_window_and_node (active_window, node); + } +} + +/* **************************************************************** */ +/* */ +/* Groveling Info Keymaps and Docs */ +/* */ +/* **************************************************************** */ + +/* Return the documentation associated with the Info command FUNCTION. */ +char * +function_documentation (InfoCommand *cmd) +{ + char *doc; + +#if defined (INFOKEY) + + doc = cmd->doc; + +#else /* !INFOKEY */ + + register int i; + + for (i = 0; function_doc_array[i].func; i++) + if (InfoFunction(cmd) == function_doc_array[i].func) + break; + + doc = function_doc_array[i].func ? function_doc_array[i].doc : ""; + +#endif /* !INFOKEY */ + + return replace_in_documentation ((strlen (doc) == 0) ? doc : (char *) _(doc), 0); +} + +#if defined (NAMED_FUNCTIONS) +/* Return the user-visible name of the function associated with the + Info command FUNCTION. */ +char * +function_name (InfoCommand *cmd) +{ +#if defined (INFOKEY) + + return cmd->func_name; + +#else /* !INFOKEY */ + + register int i; + + for (i = 0; function_doc_array[i].func; i++) + if (InfoFunction(cmd) == function_doc_array[i].func) + break; + + return (function_doc_array[i].func_name); + +#endif /* !INFOKEY */ +} + +/* Return a pointer to the info command for function NAME. */ +InfoCommand * +named_function (char *name) +{ + register int i; + + for (i = 0; function_doc_array[i].func; i++) + if (strcmp (function_doc_array[i].func_name, name) == 0) + break; + + return (DocInfoCmd(&function_doc_array[i])); +} +#endif /* NAMED_FUNCTIONS */ + +/* Return the documentation associated with KEY in MAP. */ +char * +key_documentation (char key, Keymap map) +{ + InfoCommand *function = map[key].function; + + if (function) + return (function_documentation (function)); + else + return ((char *)NULL); +} + +DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY")) +{ + char keys[50]; + unsigned char keystroke; + char *k = keys; + Keymap map; + + *k = '\0'; + map = window->keymap; + + for (;;) + { + message_in_echo_area ((char *) _("Describe key: %s"), + pretty_keyseq (keys), NULL); + keystroke = info_get_input_char (); + unmessage_in_echo_area (); + +#if !defined (INFOKEY) + if (Meta_p (keystroke)) + { + if (map[ESC].type != ISKMAP) + { + window_message_in_echo_area + (_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke))); + return; + } + + *k++ = '\e'; + keystroke = UnMeta (keystroke); + map = (Keymap)map[ESC].function; + } +#endif /* !INFOKEY */ + + /* Add the KEYSTROKE to our list. */ + *k++ = keystroke; + *k = '\0'; + + if (map[keystroke].function == (InfoCommand *)NULL) + { + message_in_echo_area ((char *) _("%s is undefined."), + pretty_keyseq (keys), NULL); + return; + } + else if (map[keystroke].type == ISKMAP) + { + map = (Keymap)map[keystroke].function; + continue; + } + else + { + char *keyname, *message, *fundoc, *funname = ""; + +#if defined (INFOKEY) + /* If the key is bound to do-lowercase-version, but its + lower-case variant is undefined, say that this key is + also undefined. This is especially important for unbound + edit keys that emit an escape sequence: it's terribly + confusing to see a message "Home (do-lowercase-version)" + or some such when Home is unbound. */ + if (InfoFunction(map[keystroke].function) + == (VFunction *) info_do_lowercase_version) + { + unsigned char lowerkey = Meta_p(keystroke) + ? Meta (tolower (UnMeta (keystroke))) + : tolower (keystroke); + + if (map[lowerkey].function == (InfoCommand *)NULL) + { + message_in_echo_area ((char *) _("%s is undefined."), + pretty_keyseq (keys), NULL); + return; + } + } +#endif + + keyname = pretty_keyseq (keys); + +#if defined (NAMED_FUNCTIONS) + funname = function_name (map[keystroke].function); +#endif /* NAMED_FUNCTIONS */ + + fundoc = function_documentation (map[keystroke].function); + + message = (char *)xmalloc + (10 + strlen (keyname) + strlen (fundoc) + strlen (funname)); + +#if defined (NAMED_FUNCTIONS) + sprintf (message, "%s (%s): %s.", keyname, funname, fundoc); +#else + sprintf (message, _("%s is defined to %s."), keyname, fundoc); +#endif /* !NAMED_FUNCTIONS */ + + window_message_in_echo_area ("%s", message, NULL); + free (message); + break; + } + } +} + +/* Return the pretty printable name of a single character. */ +char * +pretty_keyname (unsigned char key) +{ + static char rep_buffer[30]; + char *rep; + + if (Meta_p (key)) + { + char temp[20]; + + rep = pretty_keyname (UnMeta (key)); + +#if defined (INFOKEY) + sprintf (temp, "M-%s", rep); +#else /* !INFOKEY */ + sprintf (temp, "ESC %s", rep); +#endif /* !INFOKEY */ + strcpy (rep_buffer, temp); + rep = rep_buffer; + } + else if (Control_p (key)) + { + switch (key) + { + case '\n': rep = "LFD"; break; + case '\t': rep = "TAB"; break; + case '\r': rep = "RET"; break; + case ESC: rep = "ESC"; break; + + default: + sprintf (rep_buffer, "C-%c", UnControl (key)); + rep = rep_buffer; + } + } + else + { + switch (key) + { + case ' ': rep = "SPC"; break; + case DEL: rep = "DEL"; break; + default: + rep_buffer[0] = key; + rep_buffer[1] = '\0'; + rep = rep_buffer; + } + } + return (rep); +} + +/* Return the pretty printable string which represents KEYSEQ. */ + +static void pretty_keyseq_internal (char *keyseq, char *rep); + +char * +pretty_keyseq (char *keyseq) +{ + static char keyseq_rep[200]; + + keyseq_rep[0] = '\0'; + if (*keyseq) + pretty_keyseq_internal (keyseq, keyseq_rep); + return (keyseq_rep); +} + +static void +pretty_keyseq_internal (char *keyseq, char *rep) +{ + if (term_kP && strncmp(keyseq, term_kP, strlen(term_kP)) == 0) + { + strcpy(rep, "PgUp"); + keyseq += strlen(term_kP); + } + else if (term_kN && strncmp(keyseq, term_kN, strlen(term_kN)) == 0) + { + strcpy(rep, "PgDn"); + keyseq += strlen(term_kN); + } +#if defined(INFOKEY) + else if (term_kh && strncmp(keyseq, term_kh, strlen(term_kh)) == 0) + { + strcpy(rep, "Home"); + keyseq += strlen(term_kh); + } + else if (term_ke && strncmp(keyseq, term_ke, strlen(term_ke)) == 0) + { + strcpy(rep, "End"); + keyseq += strlen(term_ke); + } + else if (term_ki && strncmp(keyseq, term_ki, strlen(term_ki)) == 0) + { + strcpy(rep, "INS"); + keyseq += strlen(term_ki); + } + else if (term_kx && strncmp(keyseq, term_kx, strlen(term_kx)) == 0) + { + strcpy(rep, "DEL"); + keyseq += strlen(term_kx); + } +#endif /* INFOKEY */ + else if (term_ku && strncmp(keyseq, term_ku, strlen(term_ku)) == 0) + { + strcpy(rep, "Up"); + keyseq += strlen(term_ku); + } + else if (term_kd && strncmp(keyseq, term_kd, strlen(term_kd)) == 0) + { + strcpy(rep, "Down"); + keyseq += strlen(term_kd); + } + else if (term_kl && strncmp(keyseq, term_kl, strlen(term_kl)) == 0) + { + strcpy(rep, "Left"); + keyseq += strlen(term_kl); + } + else if (term_kr && strncmp(keyseq, term_kr, strlen(term_kr)) == 0) + { + strcpy(rep, "Right"); + keyseq += strlen(term_kr); + } + else + { + strcpy (rep, pretty_keyname (keyseq[0])); + keyseq++; + } + if (*keyseq) + { + strcat (rep, " "); + pretty_keyseq_internal (keyseq, rep + strlen(rep)); + } +} + +/* Return a pointer to the last character in s that is found in f. */ +static char * +strrpbrk (const char *s, const char *f) +{ + register const char *e = s + strlen(s); + register const char *t; + + while (e-- != s) + { + for (t = f; *t; t++) + if (*e == *t) + return (char *)e; + } + return NULL; +} + +/* Replace the names of functions with the key that invokes them. */ +char * +replace_in_documentation (char *string, int help_is_only_window_p) +{ + unsigned reslen = strlen (string); + register int i, start, next; + static char *result = (char *)NULL; + + maybe_free (result); + result = (char *)xmalloc (1 + reslen); + + i = next = start = 0; + + /* Skip to the beginning of a replaceable function. */ + for (i = start; string[i]; i++) + { + int j = i + 1; + + /* Is this the start of a replaceable function name? */ + if (string[i] == '\\') + { + char *fmt = NULL; + unsigned min = 0; + unsigned max = 0; + + if(string[j] == '%') + { + if (string[++j] == '-') + j++; + if (isdigit(string[j])) + { + min = atoi(string + j); + while (isdigit(string[j])) + j++; + if (string[j] == '.' && isdigit(string[j + 1])) + { + j += 1; + max = atoi(string + j); + while (isdigit(string[j])) + j++; + } + fmt = (char *)xmalloc (j - i + 2); + strncpy (fmt, string + i + 1, j - i); + fmt[j - i - 1] = 's'; + fmt[j - i] = '\0'; + } + else + j = i + 1; + } + if (string[j] == '[') + { + unsigned arg = 0; + char *argstr = NULL; + char *rep_name, *fun_name, *rep; + InfoCommand *command; + char *repstr = NULL; + unsigned replen; + + /* Copy in the old text. */ + strncpy (result + next, string + start, i - start); + next += (i - start); + start = j + 1; + + /* Look for an optional numeric arg. */ + i = start; + if (isdigit(string[i]) + || (string[i] == '-' && isdigit(string[i + 1])) ) + { + arg = atoi(string + i); + if (string[i] == '-') + i++; + while (isdigit(string[i])) + i++; + } + start = i; + + /* Move to the end of the function name. */ + for (i = start; string[i] && (string[i] != ']'); i++); + + rep_name = (char *)xmalloc (1 + i - start); + strncpy (rep_name, string + start, i - start); + rep_name[i - start] = '\0'; + + /* If we have only one window (because the window size was too + small to split it), we have to quit help by going back one + noew in the history list, not deleting the window. */ + if (strcmp (rep_name, "quit-help") == 0) + fun_name = help_is_only_window_p ? "history-node" + : "delete-window"; + else + fun_name = rep_name; + + /* Find a key which invokes this function in the info_keymap. */ + command = named_function (fun_name); + + free (rep_name); + + /* If the internal documentation string fails, there is a + serious problem with the associated command's documentation. + We croak so that it can be fixed immediately. */ + if (!command) + abort (); + + if (arg) + { + char *argrep, *p; + + argrep = where_is (info_keymap, InfoCmd(info_add_digit_to_numeric_arg)); + p = argrep ? strrpbrk (argrep, "0123456789-") : NULL; + if (p) + { + argstr = (char *)xmalloc (p - argrep + 21); + strncpy (argstr, argrep, p - argrep); + sprintf (argstr + (p - argrep), "%d", arg); + } + else + command = NULL; + } + rep = command ? where_is (info_keymap, command) : NULL; + if (!rep) + rep = "N/A"; + replen = (argstr ? strlen (argstr) : 0) + strlen (rep) + 1; + repstr = (char *)xmalloc (replen); + repstr[0] = '\0'; + if (argstr) + { + strcat(repstr, argstr); + strcat(repstr, " "); + free (argstr); + } + strcat(repstr, rep); + + if (fmt) + { + if (replen > max) + replen = max; + if (replen < min) + replen = min; + } + if (next + replen > reslen) + { + reslen = next + replen + 1; + result = (char *)xrealloc (result, reslen + 1); + } + + if (fmt) + sprintf (result + next, fmt, repstr); + else + strcpy (result + next, repstr); + + next = strlen (result); + free (repstr); + + start = i; + if (string[i]) + start++; + } + + maybe_free (fmt); + } + } + strcpy (result + next, string + start); + return (result); +} + +/* Return a string of characters which could be typed from the keymap + MAP to invoke FUNCTION. */ +static char *where_is_rep = (char *)NULL; +static int where_is_rep_index = 0; +static int where_is_rep_size = 0; + +char * +where_is (Keymap map, InfoCommand *cmd) +{ + char *rep; + + if (!where_is_rep_size) + where_is_rep = (char *)xmalloc (where_is_rep_size = 100); + where_is_rep_index = 0; + + rep = where_is_internal (map, cmd); + + /* If it couldn't be found, return "M-x Foo" (or equivalent). */ + if (!rep) + { + char *name; + + name = function_name (cmd); + if (!name) + return NULL; /* no such function */ + + rep = where_is_internal (map, InfoCmd(info_execute_command)); + if (!rep) + return ""; /* function exists but can't be got to by user */ + + sprintf (where_is_rep, "%s %s", rep, name); + + rep = where_is_rep; + } + return (rep); +} + +/* Return the printed rep of the keystrokes that invoke FUNCTION, + as found in MAP, or NULL. */ +static char * +where_is_internal (Keymap map, InfoCommand *cmd) +{ +#if defined(INFOKEY) + + register FUNCTION_KEYSEQ *k; + + for (k = cmd->keys; k; k = k->next) + if (k->map == map) + return pretty_keyseq (k->keyseq); + + return NULL; + +#else /* !INFOKEY */ + /* There is a bug in that create_internal_info_help_node calls + where_is_internal without setting where_is_rep_index to zero. This + was found by Mandrake and reported by Thierry Vignaud + <tvignaud@mandrakesoft.com> around April 24, 2002. + + I think the best fix is to make where_is_rep_index another + parameter to this recursively-called function, instead of a static + variable. But this [!INFOKEY] branch of the code is not enabled + any more, so let's just skip the whole thing. --karl, 28sep02. */ + register int i; + + /* If the function is directly invokable in MAP, return the representation + of that keystroke. */ + for (i = 0; i < 256; i++) + if ((map[i].type == ISFUNC) && map[i].function == cmd) + { + sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i)); + return (where_is_rep); + } + + /* Okay, search subsequent maps for this function. */ + for (i = 0; i < 256; i++) + { + if (map[i].type == ISKMAP) + { + int saved_index = where_is_rep_index; + char *rep; + + sprintf (where_is_rep + where_is_rep_index, "%s ", + pretty_keyname (i)); + + where_is_rep_index = strlen (where_is_rep); + rep = where_is_internal ((Keymap)map[i].function, cmd); + + if (rep) + return (where_is_rep); + + where_is_rep_index = saved_index; + } + } + + return NULL; + +#endif /* INFOKEY */ +} + +DECLARE_INFO_COMMAND (info_where_is, + _("Show what to type to execute a given command")) +{ + char *command_name; + + command_name = read_function_name ((char *) _("Where is command: "), window); + + if (!command_name) + { + info_abort_key (active_window, count, key); + return; + } + + if (*command_name) + { + InfoCommand *command; + + command = named_function (command_name); + + if (command) + { + char *location; + + location = where_is (active_window->keymap, command); + + if (!location || !location[0]) + { + info_error ((char *) _("`%s' is not on any keys"), + command_name, NULL); + } + else + { + if (strstr (location, function_name (command))) + window_message_in_echo_area + ((char *) _("%s can only be invoked via %s."), + command_name, location); + else + window_message_in_echo_area + ((char *) _("%s can be invoked via %s."), + command_name, location); + } + } + else + info_error ((char *) _("There is no function named `%s'"), + command_name, NULL); + } + + free (command_name); +} diff --git a/Build/source/utils/texinfo/info/infokey.c b/Build/source/utils/texinfo/info/infokey.c new file mode 100644 index 00000000000..51f23aab0c8 --- /dev/null +++ b/Build/source/utils/texinfo/info/infokey.c @@ -0,0 +1,924 @@ +/* infokey.c -- compile ~/.infokey to ~/.info. + $Id: infokey.c,v 1.9 2004/12/14 00:15:36 karl Exp $ + + Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Andrew Bettison <andrewb@zip.com.au>. */ + +#include "info.h" +#include "infomap.h" +#include "infokey.h" +#include "key.h" +#include "getopt.h" + +static char *program_name = "infokey"; + +/* Non-zero means print version info only. */ +static int print_version_p = 0; + +/* Non-zero means print a short description of the options. */ +static int print_help_p = 0; + +/* String specifying the source file. This is set by the user on the + command line, or a default is used. */ +static char *input_filename = (char *) NULL; + +/* String specifying the name of the file to output to. This is + set by the user on the command line, or a default is used. */ +static char *output_filename = (char *) NULL; + +/* Structure describing the options that Infokey accepts. We pass this + structure to getopt_long (). If you add or otherwise change this + structure, you must also change the string which follows it. */ +static struct option long_options[] = +{ + {"output", 1, 0, 'o'}, + {"help", 0, &print_help_p, 1}, + {"version", 0, &print_version_p, 1}, + {NULL, 0, NULL, 0} +}; + +/* String describing the shorthand versions of the long options found above. */ +static char *short_options = "o:"; + +/* Structure for holding the compiled sections. */ +enum sect_e + { + info = 0, + ea = 1, + var = 2 + }; +struct sect + { + unsigned int cur; + unsigned char data[INFOKEY_MAX_SECTIONLEN]; + }; + +/* Some "forward" declarations. */ +static char *mkpath (const char *dir, const char *file); +static int compile (FILE *fp, const char *filename, struct sect *sections); +static int write_infokey_file (FILE *fp, struct sect *sections); +static void syntax_error (const char *filename, + unsigned int linenum, const char *fmt, + const void *a1, const void *a2, const void *a3, const void *a4); +static void error_message (int error_code, const char *fmt, + const void *a1, const void *a2, const void *a3, const void *a4); +static void suggest_help (void); +static void short_help (void); + + +/* **************************************************************** */ +/* */ +/* Main Entry Point to the Infokey Program */ +/* */ +/* **************************************************************** */ + +int +main (int argc, char **argv) +{ + int getopt_long_index; /* Index returned by getopt_long (). */ + +#ifdef HAVE_SETLOCALE + /* Set locale via LC_ALL. */ + setlocale (LC_ALL, ""); +#endif + +#ifdef ENABLE_NLS + /* Set the text message domain. */ + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); +#endif + + while (1) + { + int option_character; + + option_character = getopt_long + (argc, argv, short_options, long_options, &getopt_long_index); + + /* getopt_long () returns EOF when there are no more long options. */ + if (option_character == EOF) + break; + + /* If this is a long option, then get the short version of it. */ + if (option_character == 0 && long_options[getopt_long_index].flag == 0) + option_character = long_options[getopt_long_index].val; + + /* Case on the option that we have received. */ + switch (option_character) + { + case 0: + break; + + /* User is specifying the name of a file to output to. */ + case 'o': + if (output_filename) + free (output_filename); + output_filename = xstrdup (optarg); + break; + + default: + suggest_help (); + xexit (1); + } + } + + /* If the user specified --version, then show the version and exit. */ + if (print_version_p) + { + printf ("%s (GNU %s) %s\n", program_name, PACKAGE, VERSION); + puts (""); + printf (_ ("Copyright (C) %s Free Software Foundation, Inc.\n\ +There is NO warranty. You may redistribute this software\n\ +under the terms of the GNU General Public License.\n\ +For more information about these matters, see the files named COPYING.\n"), + "2003"); + xexit (0); + } + + /* If the `--help' option was present, show the help and exit. */ + if (print_help_p) + { + short_help (); + xexit (0); + } + + /* If there is one argument remaining, it is the name of the input + file. */ + if (optind == argc - 1) + { + if (input_filename) + free (input_filename); + input_filename = xstrdup (argv[optind]); + } + else if (optind != argc) + { + error_message (0, _("incorrect number of arguments"), + NULL, NULL, NULL, NULL); + suggest_help (); + xexit (1); + } + + /* Use default filenames where none given. */ + { + char *homedir; + + homedir = getenv ("HOME"); +#ifdef __MSDOS__ + if (!homedir) + homedir = "."; +#endif + if (!input_filename) + input_filename = mkpath (homedir, INFOKEY_SRCFILE); + if (!output_filename) + output_filename = mkpath (homedir, INFOKEY_FILE); + } + + { + FILE *inf; + FILE *outf; + int write_error; + static struct sect sections[3]; + + /* Open the input file. */ + inf = fopen (input_filename, "r"); + if (!inf) + { + error_message (errno, _("cannot open input file `%s'"), + input_filename, NULL, NULL, NULL); + xexit (1); + } + + /* Compile the input file to its verious sections, then write the + section data to the output file. */ + + if (compile (inf, input_filename, sections)) + { + /* Open the output file. */ + outf = fopen (output_filename, FOPEN_WBIN); + if (!outf) + { + error_message (errno, _("cannot create output file `%s'"), + output_filename, NULL, NULL, NULL); + xexit (1); + } + + /* Write the contents of the output file and close it. If there is + an error writing to the file, delete it and exit with a failure + status. */ + write_error = 0; + if (!write_infokey_file (outf, sections)) + { + error_message (errno, _("error writing to `%s'"), + output_filename, NULL, NULL, NULL); + write_error = 1; + } + if (fclose (outf) == EOF) + { + error_message (errno, _("error closing output file `%s'"), + output_filename, NULL, NULL, NULL); + write_error = 1; + } + if (write_error) + { + unlink (output_filename); + xexit (1); + } + } + + /* Close the input file. */ + fclose (inf); + } + + return 0; +} + +static char * +mkpath (const char *dir, const char *file) +{ + char *p; + + p = xmalloc (strlen (dir) + 1 + strlen (file) + 2); + strcpy (p, dir); + strcat (p, "/"); + strcat (p, file); + return p; +} + + +/* Compilation - the real work. + + Source file syntax + ------------------ + The source file is a line-based text file with the following + structure: + + # comments + # more comments + + #info + u prev-line + d next-line + ^a invalid # just beep + \ku prev-line + #stop + \kd next-line + q quit # of course! + + #echo-area + ^a echo-area-beg-of-line + ^e echo-area-end-of-line + \kr echo-area-forward + \kl echo-area-backward + \kh echo-area-beg-of-line + \ke echo-area-end-of-line + + #var + scroll-step=1 + ISO-Latin=Off + + Lines starting with '#' are comments, and are ignored. Blank + lines are ignored. Each section is introduced by one of the + following lines: + + #info + #echo-area + #var + + The sections may occur in any order. Each section may be + omitted completely. If the 'info' section is the first in the + file, its '#info' line may be omitted. + + The 'info' and 'echo-area' sections + ----------------------------------- + Each line in the 'info' or 'echo-area' sections has the + following syntax: + + key-sequence SPACE action-name [ SPACE [ # comment ] ] \n + + Where SPACE is one or more white space characters excluding + newline, "action-name" is the name of a GNU Info command, + "comment" is any sequence of characters excluding newline, and + "key-sequence" is a concatenation of one or more key definitions + using the following syntax: + + 1. A carat ^ followed by one character indicates a single + control character; + + 2. A backslash \ followed by one, two, or three octal + digits indicates a single character having that ASCII + code; + + 3. \n indicates a single NEWLINE; + \e indicates a single ESC; + \r indicates a single CR; + \t indicates a single TAB; + \b indicates a single BACKSPACE; + + 4. \ku indicates the Up Arrow key; + \kd indicates the Down Arrow key; + \kl indicates the Left Arrow key; + \kr indicates the Right Arrow key; + \kP indicates the Page Up (PRIOR) key; + \kN indicates the Page Down (NEXT) key; + \kh indicates the Home key; + \ke indicates the End key; + \kx indicates the DEL key; + \k followed by any other character indicates a single + control-K, and the following character is interpreted + as in rules 1, 2, 3, 5 and 6. + + 5. \m followed by any sequence defined in rules 1, 2, 3, 4 + or 6 indicates the "Meta" modification of that key. + + 6. A backslash \ followed by any character not described + above indicates that character itself. In particular: + \\ indicates a single backslash \, + \ (backslash-space) indicates a single space, + \^ indicates a single caret ^, + + If the following line: + + #stop + + occurs anywhere in an 'info' or 'echo-area' section, that + indicates to GNU Info to suppress all of its default key + bindings in that context. + + The 'var' section + ----------------- + Each line in the 'var' section has the following syntax: + + variable-name = value \n + + Where "variable-name" is the name of a GNU Info variable and + "value" is the value that GNU Info will assign to that variable + when commencing execution. There must be no white space in the + variable name, nor between the variable name and the '='. All + characters immediately following the '=', up to but not + including the terminating newline, are considered to be the + value that will be assigned. In other words, white space + following the '=' is not ignored. + */ + +static int add_to_section (struct sect *s, const char *str, unsigned int len); +static int lookup_action (const char *actname); + +/* Compile the input file into its various sections. Return true if no + error was encountered. + */ +static int +compile (FILE *fp, const char *filename, struct sect *sections) +{ + int error = 0; + char rescan = 0; + unsigned int lnum = 0; + int c = 0; + + /* This parser is a true state machine, with no sneaky fetching + of input characters inside the main loop. In other words, all + state is fully represented by the following variables: + */ + enum + { + start_of_line, + start_of_comment, + in_line_comment, + in_trailing_comment, + get_keyseq, + got_keyseq, + get_action, + got_action, + get_varname, + got_varname, + get_equals, + got_equals, + get_value + } + state = start_of_line; + enum sect_e section = info; + enum + { + normal, + slosh, + control, + octal, + special_key + } + seqstate; /* used if state == get_keyseq */ + char meta = 0; + char ocnt = 0; /* used if state == get_keyseq && seqstate == octal */ + + /* Data is accumulated in the following variables. The code + avoids overflowing these strings, and throws an error + where appropriate if a string limit is exceeded. These string + lengths are arbitrary (and should be large enough) and their + lengths are not hard-coded anywhere else, so increasing them + here will not break anything. */ + char oval = 0; + char comment[10]; + unsigned int clen = 0; + char seq[20]; + unsigned int slen = 0; + char act[80]; + unsigned int alen = 0; + char varn[80]; + unsigned int varlen = 0; + char val[80]; + unsigned int vallen = 0; + +#define To_seq(c) \ + do { \ + if (slen < sizeof seq) \ + seq[slen++] = meta ? Meta(c) : (c); \ + else \ + { \ + syntax_error(filename, lnum, _("key sequence too long"), \ + NULL, NULL, NULL, NULL); \ + error = 1; \ + } \ + meta = 0; \ + } while (0) + + sections[info].cur = 1; + sections[info].data[0] = 0; + sections[ea].cur = 1; + sections[ea].data[0] = 0; + sections[var].cur = 0; + + while (!error && (rescan || (c = fgetc (fp)) != EOF)) + { + rescan = 0; + switch (state) + { + case start_of_line: + lnum++; + if (c == '#') + state = start_of_comment; + else if (c != '\n') + { + switch (section) + { + case info: + case ea: + state = get_keyseq; + seqstate = normal; + slen = 0; + break; + case var: + state = get_varname; + varlen = 0; + break; + } + rescan = 1; + } + break; + + case start_of_comment: + clen = 0; + state = in_line_comment; + /* fall through */ + case in_line_comment: + if (c == '\n') + { + state = start_of_line; + comment[clen] = '\0'; + if (strcmp (comment, "info") == 0) + section = info; + else if (strcmp (comment, "echo-area") == 0) + section = ea; + else if (strcmp (comment, "var") == 0) + section = var; + else if (strcmp (comment, "stop") == 0 + && (section == info || section == ea)) + sections[section].data[0] = 1; + } + else if (clen < sizeof comment - 1) + comment[clen++] = c; + break; + + case in_trailing_comment: + if (c == '\n') + state = start_of_line; + break; + + case get_keyseq: + switch (seqstate) + { + case normal: + if (c == '\n' || isspace (c)) + { + state = got_keyseq; + rescan = 1; + if (slen == 0) + { + syntax_error (filename, lnum, _("missing key sequence"), + NULL, NULL, NULL, NULL); + error = 1; + } + } + else if (c == '\\') + seqstate = slosh; + else if (c == '^') + seqstate = control; + else + To_seq (c); + break; + + case slosh: + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + seqstate = octal; + oval = c - '0'; + ocnt = 1; + break; + case 'b': + To_seq ('\b'); + seqstate = normal; + break; + case 'e': + To_seq ('\033'); + seqstate = normal; + break; + case 'n': + To_seq ('\n'); + seqstate = normal; + break; + case 'r': + To_seq ('\r'); + seqstate = normal; + break; + case 't': + To_seq ('\t'); + seqstate = normal; + break; + case 'm': + meta = 1; + seqstate = normal; + break; + case 'k': + seqstate = special_key; + break; + default: + /* Backslash followed by any other char + just means that char. */ + To_seq (c); + seqstate = normal; + break; + } + break; + + case octal: + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + if (++ocnt <= 3) + oval = oval * 8 + c - '0'; + if (ocnt == 3) + seqstate = normal; + break; + default: + ocnt = 4; + seqstate = normal; + rescan = 1; + break; + } + if (seqstate != octal) + { + if (oval) + To_seq (oval); + else + { + syntax_error (filename, lnum, + _("NUL character (\\000) not permitted"), + NULL, NULL, NULL, NULL); + error = 1; + } + } + break; + + case special_key: + To_seq (SK_ESCAPE); + switch (c) + { + case 'u': To_seq (SK_UP_ARROW); break; + case 'd': To_seq (SK_DOWN_ARROW); break; + case 'r': To_seq (SK_RIGHT_ARROW); break; + case 'l': To_seq (SK_LEFT_ARROW); break; + case 'U': To_seq (SK_PAGE_UP); break; + case 'D': To_seq (SK_PAGE_DOWN); break; + case 'h': To_seq (SK_HOME); break; + case 'e': To_seq (SK_END); break; + case 'x': To_seq (SK_DELETE); break; + default: To_seq (SK_LITERAL); rescan = 1; break; + } + seqstate = normal; + break; + + case control: + if (CONTROL (c)) + To_seq (CONTROL (c)); + else + { + syntax_error (filename, lnum, + (char *) _("NUL character (^%c) not permitted"), + (void *) (long) c, NULL, NULL, NULL); + error = 1; + } + seqstate = normal; + break; + } + break; + + case got_keyseq: + if (isspace (c) && c != '\n') + break; + state = get_action; + alen = 0; + /* fall through */ + case get_action: + if (c == '\n' || isspace (c)) + { + int a; + + state = got_action; + rescan = 1; + if (alen == 0) + { + syntax_error (filename, lnum, (char *) _("missing action name"), + (void *) (long) c, NULL, NULL, NULL); + error = 1; + } + else + { + act[alen] = '\0'; + a = lookup_action (act); + if (a != -1) + { + char av = a; + + if (!(add_to_section (§ions[section], seq, slen) + && add_to_section (§ions[section], "", 1) + && add_to_section (§ions[section], &av, 1))) + { + syntax_error (filename, lnum, _("section too long"), + NULL, NULL, NULL, NULL); + error = 1; + } + } + else + { + syntax_error (filename, lnum, _("unknown action `%s'"), + act, NULL, NULL, NULL); + error = 1; + } + } + } + else if (alen < sizeof act - 1) + act[alen++] = c; + else + { + syntax_error (filename, lnum, _("action name too long"), + NULL, NULL, NULL, NULL); + error = 1; + } + break; + + case got_action: + if (c == '#') + state = in_trailing_comment; + else if (c == '\n') + state = start_of_line; + else if (!isspace (c)) + { + syntax_error (filename, lnum, + _("extra characters following action `%s'"), + act, NULL, NULL, NULL); + error = 1; + } + break; + + case get_varname: + if (c == '=') + { + if (varlen == 0) + { + syntax_error (filename, lnum, _("missing variable name"), + NULL, NULL, NULL, NULL); + error = 1; + } + state = get_value; + vallen = 0; + } + else if (c == '\n' || isspace (c)) + { + syntax_error (filename, lnum, + _("missing `=' immediately after variable name"), + NULL, NULL, NULL, NULL); + error = 1; + } + else if (varlen < sizeof varn) + varn[varlen++] = c; + else + { + syntax_error (filename, lnum, _("variable name too long"), + NULL, NULL, NULL, NULL); + error = 1; + } + break; + + case get_value: + if (c == '\n') + { + state = start_of_line; + if (!(add_to_section (§ions[section], varn, varlen) + && add_to_section (§ions[section], "", 1) + && add_to_section (§ions[section], val, vallen) + && add_to_section (§ions[section], "", 1))) + { + syntax_error (filename, lnum, _("section too long"), + NULL, NULL, NULL, NULL); + error = 1; + } + } + else if (vallen < sizeof val) + val[vallen++] = c; + else + { + syntax_error (filename, lnum, _("value too long"), + NULL, NULL, NULL, NULL); + error = 1; + } + break; + + case get_equals: + case got_equals: + case got_varname: + break; + } + } + +#undef To_seq + + return !error; +} + +/* Add some characters to a section's data. Return true if all the + characters fit, or false if the section's size limit was exceeded. + */ +static int +add_to_section (struct sect *s, const char *str, unsigned int len) +{ + if (s->cur + len > sizeof s->data) + return 0; + strncpy ((char *) s->data + s->cur, str, len); + s->cur += len; + return 1; +} + +/* Translate from an action name to its numeric code. This uses the + auto-generated array in key.c. + */ +static int +lookup_action (const char *actname) +{ + int i; + + if (strcmp ("invalid", actname) == 0) + return A_INVALID; + for (i = 0; function_key_array[i].name != NULL; i++) + if (strcmp (function_key_array[i].name, actname) == 0) + return function_key_array[i].code; + return -1; +} + +/* Put an integer to an infokey file. + Integers are stored as two bytes, low order first, + in radix INFOKEY_RADIX. + */ +static int +putint (int i, FILE *fp) +{ + return fputc (i % INFOKEY_RADIX, fp) != EOF + && fputc ((i / INFOKEY_RADIX) % INFOKEY_RADIX, fp) != EOF; +} + +/* Write an entire section to an infokey file. If the section is + empty, simply omit it. + */ +static int +putsect (struct sect *s, int code, FILE *fp) +{ + if (s->cur == 0) + return 1; + return fputc (code, fp) != EOF + && putint (s->cur, fp) + && fwrite (s->data, s->cur, 1, fp) == 1; +} + +/* Write an entire infokey file, given an array containing its sections. + */ +static int +write_infokey_file (FILE *fp, struct sect *sections) +{ + /* Get rid of sections with no effect. */ + if (sections[info].cur == 1 && sections[info].data[0] == 0) + sections[info].cur = 0; + if (sections[ea].cur == 1 && sections[ea].data[0] == 0) + sections[ea].cur = 0; + + /* Write all parts of the file out in order (no lseeks), + checking for errors all the way. */ + return fputc (INFOKEY_MAGIC_S0, fp) != EOF + && fputc (INFOKEY_MAGIC_S1, fp) != EOF + && fputc (INFOKEY_MAGIC_S2, fp) != EOF + && fputc (INFOKEY_MAGIC_S3, fp) != EOF + && fputs (VERSION, fp) != EOF + && fputc ('\0', fp) != EOF + && putsect (§ions[info], INFOKEY_SECTION_INFO, fp) + && putsect (§ions[ea], INFOKEY_SECTION_EA, fp) + && putsect (§ions[var], INFOKEY_SECTION_VAR, fp) + && fputc (INFOKEY_MAGIC_E0, fp) != EOF + && fputc (INFOKEY_MAGIC_E1, fp) != EOF + && fputc (INFOKEY_MAGIC_E2, fp) != EOF + && fputc (INFOKEY_MAGIC_E3, fp) != EOF; +} + + +/* Error handling. */ + +/* Give the user a "syntax error" message in the form + progname: "filename", line N: message + */ +static void +error_message (int error_code, const char *fmt, + const void *a1, const void *a2, const void *a3, const void *a4) +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, fmt, a1, a2, a3, a4); + if (error_code) + fprintf (stderr, " - %s", strerror (error_code)); + fprintf (stderr, "\n"); +} + +/* Give the user a generic error message in the form + progname: message + */ +static void +syntax_error (const char *filename, + unsigned int linenum, const char *fmt, + const void *a1, const void *a2, const void *a3, const void *a4) +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, _("\"%s\", line %u: "), filename, linenum); + fprintf (stderr, fmt, a1, a2, a3, a4); + fprintf (stderr, "\n"); +} + +/* Produce a gentle rtfm. */ +static void +suggest_help (void) +{ + fprintf (stderr, _("Try --help for more information.\n")); +} + +/* Produce a scaled down description of the available options to Info. */ +static void +short_help (void) +{ + printf (_("\ +Usage: %s [OPTION]... [INPUT-FILE]\n\ +\n\ +Compile infokey source file to infokey file. Reads INPUT-FILE (default\n\ +$HOME/.infokey) and writes compiled key file to (by default) $HOME/.info.\n\ +\n\ +Options:\n\ + --output FILE output to FILE instead of $HOME/.info\n\ + --help display this help and exit.\n\ + --version display version information and exit.\n\ +"), program_name); + + puts (_("\n\ +Email bug reports to bug-texinfo@gnu.org,\n\ +general questions and discussion to help-texinfo@gnu.org.\n\ +Texinfo home page: http://www.gnu.org/software/texinfo/")); + + xexit (0); +} diff --git a/Build/source/utils/texinfo/info/infokey.h b/Build/source/utils/texinfo/info/infokey.h new file mode 100644 index 00000000000..d51211d0c3a --- /dev/null +++ b/Build/source/utils/texinfo/info/infokey.h @@ -0,0 +1,128 @@ +/* infokey.h -- Custom keystroke definition support. + $Id: infokey.h,v 1.2 2004/04/11 17:56:45 karl Exp $ + + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Andrew Bettison <andrewb@zip.com.au>. + + This design was derived from the "lesskey" system in less 3.4.0. by + Mark Nudelman. + + The following terminology is confusing: + source file = $HOME/.infokey + infokey file = $HOME/.info + Oh, well. + */ + + +/* Default source file, where user writes text definitions to be + compiled to the infokey file. MS-DOS doesn't allow leading + dots in file names. */ +#ifdef __MSDOS__ +#define INFOKEY_SRCFILE "_infokey" +#else +#define INFOKEY_SRCFILE ".infokey" +#endif + +/* Default "infokey file", where compiled user defs are kept and + read by Info. MS-DOS doesn't allow leading dots in file names. */ +#ifdef __MSDOS__ +#define INFOKEY_FILE "_info" +#else +#define INFOKEY_FILE ".info" +#endif + +/* +Format of entire infokey file: + + 4 bytes magic number S + X bytes version string + 1 byte '\0' terminator + + any number of sections: + 1 byte section id + 2 bytes section length (N) + N bytes section definitions: format depends on section + + 4 bytes magic number E + +Format of INFO and EA sections: + + 1 byte flag: 1 == suppress default key bindings + Repeat: + X bytes key sequence + 1 byte '\0' terminator + 1 byte action code (A_xxx) + +Format of VAR section: + + Repeat: + X bytes variable name + 1 byte '\0' terminator + Y bytes value + 1 byte '\0' terminator + +*/ + +#define INFOKEY_NMAGIC 8 + +#define INFOKEY_MAGIC_S0 '\001' +#define INFOKEY_MAGIC_S1 'I' +#define INFOKEY_MAGIC_S2 'n' +#define INFOKEY_MAGIC_S3 'f' + +#define INFOKEY_SECTION_INFO 'i' +#define INFOKEY_SECTION_EA 'e' +#define INFOKEY_SECTION_VAR 'v' + +#define INFOKEY_MAGIC_E0 'A' +#define INFOKEY_MAGIC_E1 'l' +#define INFOKEY_MAGIC_E2 'f' +#define INFOKEY_MAGIC_E3 'n' + +#define INFOKEY_RADIX 64 +#define INFOKEY_MAX_SECTIONLEN 500 +#define INFOKEY_MAX_DEFLEN 16 + +#define A_MAX_COMMAND 120 +#define A_INVALID 121 + +/* Character transformations (independent of info's own) */ + +#define CONTROL(c) ((c) & 0x1f) +#define ISCONTROL(c) (((c) & ~0x1f) == 0) +#define META(c) ((c) | 0x80) +#define UNMETA(c) ((c) & ~0x80) +#define ISMETA(c) (((c) & 0x80) != 0) + +/* Special keys (keys which output different strings on different terminals) */ + +#define SK_ESCAPE CONTROL('k') +#define SK_RIGHT_ARROW 1 +#define SK_LEFT_ARROW 2 +#define SK_UP_ARROW 3 +#define SK_DOWN_ARROW 4 +#define SK_PAGE_UP 5 +#define SK_PAGE_DOWN 6 +#define SK_HOME 7 +#define SK_END 8 +#define SK_DELETE 9 +#define SK_INSERT 10 +#define SK_CTL_LEFT_ARROW 11 +#define SK_CTL_RIGHT_ARROW 12 +#define SK_CTL_DELETE 13 +#define SK_LITERAL 40 diff --git a/Build/source/utils/texinfo/info/infomap.c b/Build/source/utils/texinfo/info/infomap.c new file mode 100644 index 00000000000..c7dc2f4aea9 --- /dev/null +++ b/Build/source/utils/texinfo/info/infomap.c @@ -0,0 +1,1831 @@ +/* infomap.c -- keymaps for Info. + $Id: infomap.c,v 1.10 2004/07/30 20:43:40 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "infomap.h" +#include "funs.h" +#include "terminal.h" + +#if defined(INFOKEY) +#include "infokey.h" +#include "variables.h" +#endif /* INFOKEY */ + +static int keymap_bind_keyseq (Keymap map, const char *keyseq, + KEYMAP_ENTRY *keyentry); + +/* Return a new keymap which has all the uppercase letters mapped to run + the function info_do_lowercase_version (). */ +Keymap +keymap_make_keymap (void) +{ + int i; + Keymap keymap; + + keymap = (Keymap)xmalloc (256 * sizeof (KEYMAP_ENTRY)); + + for (i = 0; i < 256; i++) + { + keymap[i].type = ISFUNC; + keymap[i].function = (InfoCommand *)NULL; + } + + for (i = 'A'; i < ('Z' + 1); i++) + { + keymap[i].type = ISFUNC; +#if defined(INFOKEY) + keymap[Meta(i)].type = ISFUNC; + keymap[Meta(i)].function = +#endif /* INFOKEY */ + keymap[i].function = InfoCmd(info_do_lowercase_version); + } + + return (keymap); +} + +#if defined(INFOKEY) +static FUNCTION_KEYSEQ * +find_function_keyseq (Keymap map, int c, Keymap rootmap) +{ + FUNCTION_KEYSEQ *k; + + if (map[c].type != ISFUNC) + abort(); + if (map[c].function == NULL) + return NULL; + for (k = map[c].function->keys; k; k = k->next) + { + const unsigned char *p; + Keymap m = rootmap; + if (k->map != rootmap) + continue; + for (p = (unsigned char *) k->keyseq; *p && m[*p].type == ISKMAP; p++) + m = (Keymap)m[*p].function; + if (*p != c || p[1]) + continue; + if (m[*p].type != ISFUNC) + abort (); + break; + } + return k; +} + +static void +add_function_keyseq (InfoCommand *function, + const char *keyseq, Keymap rootmap) +{ + FUNCTION_KEYSEQ *ks; + + if (function == NULL || + function == InfoCmd(info_do_lowercase_version) || + function == InfoCmd(ea_insert)) + return; + ks = (FUNCTION_KEYSEQ *)xmalloc (sizeof(FUNCTION_KEYSEQ)); + ks->next = function->keys; + ks->map = rootmap; + ks->keyseq = xstrdup(keyseq); + function->keys = ks; +} + +static void +remove_function_keyseq (InfoCommand *function, + const char *keyseq, Keymap rootmap) +{ + + FUNCTION_KEYSEQ *k, *kp; + + if (function == NULL || + function == InfoCmd(info_do_lowercase_version) || + function == InfoCmd(ea_insert)) + return; + for (kp = NULL, k = function->keys; k; kp = k, k = k->next) + if (k->map == rootmap && strcmp(k->keyseq, keyseq) == 0) + break; + if (!k) + abort (); + if (kp) + kp->next = k->next; + else + function->keys = k->next; +} +#endif /* INFOKEY */ + +/* Return a new keymap which is a copy of MAP. */ +Keymap +keymap_copy_keymap (Keymap map, Keymap rootmap, Keymap newroot) +{ + int i; + Keymap keymap; +#if defined(INFOKEY) + FUNCTION_KEYSEQ *ks; +#endif /* INFOKEY */ + + keymap = keymap_make_keymap (); + if (!newroot) + newroot = keymap; + + for (i = 0; i < 256; i++) + { + keymap[i].type = map[i].type; + switch (map[i].type) + { + case ISFUNC: + keymap[i].function = map[i].function; +#if defined(INFOKEY) + ks = find_function_keyseq (map, i, rootmap); + if (ks) + add_function_keyseq(map[i].function, ks->keyseq, newroot); +#endif /* INFOKEY */ + break; + case ISKMAP: + keymap[i].function = (InfoCommand *)keymap_copy_keymap + ((Keymap)map[i].function, rootmap, NULL); + break; + } + } + return (keymap); +} + +/* Free the keymap and its descendants. */ +void +keymap_discard_keymap (Keymap map, Keymap rootmap) +{ + int i; + + if (!map) + return; + if (!rootmap) + rootmap = map; + + for (i = 0; i < 256; i++) + { +#if defined(INFOKEY) + FUNCTION_KEYSEQ *ks; +#endif /* INFOKEY */ + switch (map[i].type) + { + case ISFUNC: +#if defined(INFOKEY) + ks = find_function_keyseq(map, i, rootmap); + if (ks) + remove_function_keyseq (map[i].function, ks->keyseq, rootmap); +#endif /* INFOKEY */ + break; + + case ISKMAP: + keymap_discard_keymap ((Keymap)map[i].function, rootmap); + break; + + } + } + free(map); +} + +/* Conditionally bind key sequence. */ +static int +keymap_bind_keyseq (Keymap map, + const char *keyseq, KEYMAP_ENTRY *keyentry) +{ + Keymap m = map; + const unsigned char *s = (unsigned char *) keyseq; + int c; + + if (s == NULL || *s == '\0') return 0; + + while ((c = *s++) != '\0') + { +#if defined(INFOKEY) + FUNCTION_KEYSEQ *ks; +#endif /* INFOKEY */ + switch (m[c].type) + { + case ISFUNC: +#if defined(INFOKEY) + ks = find_function_keyseq(m, c, map); + if (ks) + remove_function_keyseq (m[c].function, ks->keyseq, map); +#else /* !INFOKEY */ + if (!(m[c].function == NULL || ( + m != map && + m[c].function == InfoCmd(info_do_lowercase_version)) + )) + return 0; +#endif /* !INFOKEY */ + + if (*s != '\0') + { + m[c].type = ISKMAP; + /* Here we are casting the Keymap pointer returned from + keymap_make_keymap to an InfoCommand pointer. Ugh. + This makes the `function' structure garbage + if it's actually interpreted as an InfoCommand. + Should really be using a union, and taking steps to + avoid the possible error. */ + m[c].function = (InfoCommand *)keymap_make_keymap (); + } + break; + + case ISKMAP: +#if defined(INFOKEY) + if (*s == '\0') + keymap_discard_keymap ((Keymap)m[c].function, map); +#else /* !INFOKEY */ + if (*s == '\0') + return 0; +#endif + break; + } + if (*s != '\0') + { + m = (Keymap)m[c].function; + } + else + { +#if defined(INFOKEY) + add_function_keyseq (keyentry->function, keyseq, map); +#endif /* INFOKEY */ + m[c] = *keyentry; + } + } + + return 1; +} + +/* Initialize the standard info keymaps. */ + +Keymap info_keymap = NULL; +Keymap echo_area_keymap = NULL; + +#if !defined(INFOKEY) + +static void +initialize_emacs_like_keymaps () +{ + int i; + Keymap map; + + if (!info_keymap) + { + info_keymap = keymap_make_keymap (); + echo_area_keymap = keymap_make_keymap (); + } + + info_keymap[ESC].type = ISKMAP; + info_keymap[ESC].function = (InfoCommand *)keymap_make_keymap (); + info_keymap[Control ('x')].type = ISKMAP; + info_keymap[Control ('x')].function = (InfoCommand *)keymap_make_keymap (); + + /* Bind the echo area insert routines. Let's make all characters + insertable by default, regardless of which character set we might + be using. */ + for (i = 0; i < 256; i++) + echo_area_keymap[i].function = ea_insert; + + echo_area_keymap[ESC].type = ISKMAP; + echo_area_keymap[ESC].function = (InfoCommand *) keymap_make_keymap (); + echo_area_keymap[Control ('x')].type = ISKMAP; + echo_area_keymap[Control ('x')].function + = (InfoCommand *) keymap_make_keymap (); + + /* Bind numeric arg functions for both echo area and info window maps. */ + for (i = '0'; i < '9' + 1; i++) + { + ((Keymap) info_keymap[ESC].function)[i].function + = ((Keymap) echo_area_keymap[ESC].function)[i].function + = info_add_digit_to_numeric_arg; + } + ((Keymap) info_keymap[ESC].function)['-'].function = + ((Keymap) echo_area_keymap[ESC].function)['-'].function = + info_add_digit_to_numeric_arg; + + info_keymap['-'].function = info_add_digit_to_numeric_arg; + + /* Bind the echo area routines. */ + map = echo_area_keymap; + + map[Control ('a')].function = ea_beg_of_line; + map[Control ('b')].function = ea_backward; + map[Control ('d')].function = ea_delete; + map[Control ('e')].function = ea_end_of_line; + map[Control ('f')].function = ea_forward; + map[Control ('g')].function = ea_abort; + map[Control ('h')].function = ea_rubout; + map[Control ('k')].function = ea_kill_line; + map[Control ('l')].function = info_redraw_display; + map[Control ('q')].function = ea_quoted_insert; + map[Control ('t')].function = ea_transpose_chars; + map[Control ('u')].function = info_universal_argument; + map[Control ('y')].function = ea_yank; + + map[LFD].function = ea_newline; + map[RET].function = ea_newline; + map[SPC].function = ea_complete; + map[TAB].function = ea_complete; + map['?'].function = ea_possible_completions; +#ifdef __MSDOS__ + /* PC users will lynch me if I don't give them their usual DEL effect... */ + map[DEL].function = ea_delete; +#else + map[DEL].function = ea_rubout; +#endif + + /* Bind the echo area ESC keymap. */ + map = (Keymap)echo_area_keymap[ESC].function; + + map[Control ('g')].function = ea_abort; + map[Control ('v')].function = ea_scroll_completions_window; + map['b'].function = ea_backward_word; + map['d'].function = ea_kill_word; + map['f'].function = ea_forward_word; +#if defined (NAMED_FUNCTIONS) + /* map['x'].function = info_execute_command; */ +#endif /* NAMED_FUNCTIONS */ + map['y'].function = ea_yank_pop; + map['?'].function = ea_possible_completions; + map[TAB].function = ea_tab_insert; + map[DEL].function = ea_backward_kill_word; + + /* Bind the echo area Control-x keymap. */ + map = (Keymap)echo_area_keymap[Control ('x')].function; + + map['o'].function = info_next_window; + map[DEL].function = ea_backward_kill_line; + + /* Arrow key bindings for echo area keymaps. It seems that some + terminals do not match their termcap entries, so it's best to just + define everything with both of the usual prefixes. */ + map = echo_area_keymap; + keymap_bind_keyseq (map, term_ku, &map[Control ('p')]); /* up */ + keymap_bind_keyseq (map, "\033OA", &map[Control ('p')]); + keymap_bind_keyseq (map, "\033[A", &map[Control ('p')]); + keymap_bind_keyseq (map, term_kd, &map[Control ('n')]); /* down */ + keymap_bind_keyseq (map, "\033OB", &map[Control ('n')]); + keymap_bind_keyseq (map, "\033[B", &map[Control ('n')]); + keymap_bind_keyseq (map, term_kr, &map[Control ('f')]); /* right */ + keymap_bind_keyseq (map, "\033OC", &map[Control ('f')]); + keymap_bind_keyseq (map, "\033[C", &map[Control ('f')]); + keymap_bind_keyseq (map, term_kl, &map[Control ('b')]); /* left */ + keymap_bind_keyseq (map, "\033OD", &map[Control ('b')]); + keymap_bind_keyseq (map, "\033[D", &map[Control ('b')]); + keymap_bind_keyseq (map, term_kD, &map[DEL]); /* delete */ + keymap_bind_keyseq (map, term_kh, &map[Control ('a')]); /* home */ + keymap_bind_keyseq (map, term_ke, &map[Control ('e')]); /* end */ + + map = (Keymap)echo_area_keymap[ESC].function; + keymap_bind_keyseq (map, term_kl, &map['b']); /* left */ + keymap_bind_keyseq (map, "\033OA", &map['b']); + keymap_bind_keyseq (map, "\033[A", &map['b']); + keymap_bind_keyseq (map, term_kr, &map['f']); /* right */ + keymap_bind_keyseq (map, "\033OB", &map['f']); + keymap_bind_keyseq (map, "\033[B", &map['f']); + keymap_bind_keyseq (map, term_kD, &map[DEL]); /* delete */ + + map = (Keymap)echo_area_keymap[Control ('x')].function; + keymap_bind_keyseq (map, term_kD, &map[DEL]); /* delete */ + + /* Bind commands for Info window keymaps. */ + map = info_keymap; + map[TAB].function = info_move_to_next_xref; + map[LFD].function = info_select_reference_this_line; + map[RET].function = info_select_reference_this_line; + map[SPC].function = info_scroll_forward; + map[Control ('a')].function = info_beginning_of_line; + map[Control ('b')].function = info_backward_char; + map[Control ('e')].function = info_end_of_line; + map[Control ('f')].function = info_forward_char; + map[Control ('g')].function = info_abort_key; + map[Control ('h')].function = info_get_help_window; + map[Control ('l')].function = info_redraw_display; + map[Control ('n')].function = info_next_line; + map[Control ('p')].function = info_prev_line; + map[Control ('r')].function = isearch_backward; + map[Control ('s')].function = isearch_forward; + map[Control ('u')].function = info_universal_argument; + map[Control ('v')].function = info_scroll_forward_page_only; + map[','].function = info_next_index_match; + map['/'].function = info_search; + + for (i = '1'; i < '9' + 1; i++) + map[i].function = info_menu_digit; + map['0'].function = info_last_menu_item; + + map['<'].function = info_first_node; + map['>'].function = info_last_node; + map['?'].function = info_get_help_window; + map['['].function = info_global_prev_node; + map[']'].function = info_global_next_node; + + map['b'].function = info_beginning_of_node; + map['d'].function = info_dir_node; + map['e'].function = info_end_of_node; + map['f'].function = info_xref_item; + map['g'].function = info_goto_node; + map['G'].function = info_menu_sequence; + map['h'].function = info_get_info_help_node; + map['i'].function = info_index_search; + map['I'].function = info_goto_invocation_node; + map['l'].function = info_history_node; + map['m'].function = info_menu_item; + map['n'].function = info_next_node; + map['O'].function = info_goto_invocation_node; + map['p'].function = info_prev_node; + map['q'].function = info_quit; + map['r'].function = info_xref_item; + map['s'].function = info_search; + map['S'].function = info_search_case_sensitively; + map['t'].function = info_top_node; + map['u'].function = info_up_node; + map[DEL].function = info_scroll_backward; + + /* Bind members in the ESC map for Info windows. */ + map = (Keymap)info_keymap[ESC].function; + map[Control ('f')].function = info_show_footnotes; + map[Control ('g')].function = info_abort_key; + map[TAB].function = info_move_to_prev_xref; + map[Control ('v')].function = info_scroll_other_window; + map['<'].function = info_beginning_of_node; + map['>'].function = info_end_of_node; + map['b'].function = info_backward_word; + map['f'].function = info_forward_word; + map['r'].function = info_move_to_window_line; + map['v'].function = info_scroll_backward_page_only; +#if defined (NAMED_FUNCTIONS) + map['x'].function = info_execute_command; +#endif /* NAMED_FUNCTIONS */ + map[DEL].function = info_scroll_other_window_backward; + + /* Bind members in the Control-X map for Info windows. */ + map = (Keymap)info_keymap[Control ('x')].function; + + map[Control ('b')].function = list_visited_nodes; + map[Control ('c')].function = info_quit; + map[Control ('f')].function = info_view_file; + map[Control ('g')].function = info_abort_key; + map[Control ('v')].function = info_view_file; + map['0'].function = info_delete_window; + map['1'].function = info_keep_one_window; + map['2'].function = info_split_window; + map['^'].function = info_grow_window; + map['b'].function = select_visited_node; + map['k'].function = info_kill_node; + map['n'].function = info_search_next; + map['N'].function = info_search_previous; + map['o'].function = info_next_window; + map['t'].function = info_tile_windows; + map['w'].function = info_toggle_wrap; + + /* Arrow key bindings for Info windows keymap. */ + map = info_keymap; + keymap_bind_keyseq (map, term_kN, &map[Control ('v')]); /* pagedown */ + keymap_bind_keyseq (map, term_ku, &map[Control ('p')]); /* up */ + keymap_bind_keyseq (map, "\033OA", &map[Control ('p')]); + keymap_bind_keyseq (map, "\033[A", &map[Control ('p')]); + keymap_bind_keyseq (map, term_kd, &map[Control ('n')]); /* down */ + keymap_bind_keyseq (map, "\033OB", &map[Control ('n')]); + keymap_bind_keyseq (map, "\033[B", &map[Control ('n')]); + keymap_bind_keyseq (map, term_kr, &map[Control ('f')]); /* right */ + keymap_bind_keyseq (map, "\033OC", &map[Control ('f')]); + keymap_bind_keyseq (map, "\033[C", &map[Control ('f')]); + keymap_bind_keyseq (map, term_kl, &map[Control ('b')]); /* left */ + keymap_bind_keyseq (map, "\033OD", &map[Control ('b')]); + keymap_bind_keyseq (map, "\033[D", &map[Control ('b')]); + keymap_bind_keyseq (map, term_kh, &map['b']); /* home */ + keymap_bind_keyseq (map, term_ke, &map['e']); /* end */ + keymap_bind_keyseq (map, term_kD, &map[DEL]); /* delete */ + + map = (Keymap)info_keymap[ESC].function; + keymap_bind_keyseq (map, term_kl, &map['b']); /* left */ + keymap_bind_keyseq (map, "\033OA", &map['b']); + keymap_bind_keyseq (map, "\033[A", &map['b']); + keymap_bind_keyseq (map, term_kr, &map['f']); /* right */ + keymap_bind_keyseq (map, "\033OB", &map['f']); + keymap_bind_keyseq (map, "\033[B", &map['f']); + keymap_bind_keyseq (map, term_kN, &map[Control ('v')]); /* pagedown */ + keymap_bind_keyseq (map, term_kP, &map[DEL]); /* pageup */ + keymap_bind_keyseq (map, term_kD, &map[DEL]); /* delete */ + + /* The alternative to this definition of a `main map' key in the + `ESC map' section, is something like: + keymap_bind_keyseq (map, term_kP, &((KeyMap)map[ESC].function).map['v']); + */ + keymap_bind_keyseq (info_keymap/*sic*/, term_kP, &map['v']); /* pageup */ +} + +static void +initialize_vi_like_keymaps () +{ + int i; + Keymap map; + + if (!info_keymap) + { + info_keymap = keymap_make_keymap (); + echo_area_keymap = keymap_make_keymap (); + } + + info_keymap[ESC].type = ISKMAP; + info_keymap[ESC].function = (InfoCommand *)keymap_make_keymap (); + info_keymap[Control ('x')].type = ISKMAP; + info_keymap[Control ('x')].function = (InfoCommand *)keymap_make_keymap (); + + /* Bind the echo area insert routines. */ + for (i = 0; i < 256; i++) + echo_area_keymap[i].function = ea_insert; + + echo_area_keymap[ESC].type = ISKMAP; + echo_area_keymap[ESC].function = (InfoCommand *)keymap_make_keymap (); + echo_area_keymap[Control ('x')].type = ISKMAP; + echo_area_keymap[Control ('x')].function = + (InfoCommand *)keymap_make_keymap (); + + /* Bind numeric arg functions for both echo area and info window maps. */ + for (i = '0'; i < '9' + 1; i++) + { + info_keymap[i].function = + ((Keymap) echo_area_keymap[ESC].function)[i].function = + info_add_digit_to_numeric_arg; + } + info_keymap['-'].function = + ((Keymap) echo_area_keymap[ESC].function)['-'].function = + info_add_digit_to_numeric_arg; + + /* Bind the echo area routines. */ + map = echo_area_keymap; + + map[Control ('a')].function = ea_beg_of_line; + map[Control ('b')].function = ea_backward; + map[Control ('d')].function = ea_delete; + map[Control ('e')].function = ea_end_of_line; + map[Control ('f')].function = ea_forward; + map[Control ('g')].function = ea_abort; + map[Control ('h')].function = ea_rubout; + map[Control ('k')].function = ea_kill_line; + map[Control ('l')].function = info_redraw_display; + map[Control ('q')].function = ea_quoted_insert; + map[Control ('t')].function = ea_transpose_chars; + map[Control ('u')].function = ea_abort; + map[Control ('v')].function = ea_quoted_insert; + map[Control ('y')].function = ea_yank; + + map[LFD].function = ea_newline; + map[RET].function = ea_newline; + map[SPC].function = ea_complete; + map[TAB].function = ea_complete; + map['?'].function = ea_possible_completions; +#ifdef __MSDOS__ + /* PC users will lynch me if I don't give them their usual DEL effect... */ + map[DEL].function = ea_delete; +#else + map[DEL].function = ea_rubout; +#endif + + /* Bind the echo area ESC keymap. */ + map = (Keymap)echo_area_keymap[ESC].function; + + map[Control ('g')].function = ea_abort; + map[Control ('h')].function = ea_backward_kill_word; + map[Control ('v')].function = ea_scroll_completions_window; + map['0'].function = ea_beg_of_line; + map['$'].function = ea_end_of_line; + map['b'].function = ea_backward_word; + map['d'].function = ea_kill_word; + map['f'].function = ea_forward_word; + map['h'].function = ea_backward; + map['l'].function = ea_forward; + map['w'].function = ea_forward_word; + map['x'].function = ea_delete; + map['X'].function = ea_kill_word; + map['y'].function = ea_yank_pop; + map['?'].function = ea_possible_completions; + map[TAB].function = ea_tab_insert; + map[DEL].function = ea_kill_word; + + /* Bind the echo area Control-x keymap. */ + map = (Keymap)echo_area_keymap[Control ('x')].function; + + map['o'].function = info_next_window; + map[DEL].function = ea_backward_kill_line; + + /* Arrow key bindings for echo area keymaps. It seems that some + terminals do not match their termcap entries, so it's best to just + define everything with both of the usual prefixes. */ + map = echo_area_keymap; + keymap_bind_keyseq (map, term_ku, &map[Control ('p')]); /* up */ + keymap_bind_keyseq (map, "\033OA", &map[Control ('p')]); + keymap_bind_keyseq (map, "\033[A", &map[Control ('p')]); + keymap_bind_keyseq (map, term_kd, &map[Control ('n')]); /* down */ + keymap_bind_keyseq (map, "\033OB", &map[Control ('n')]); + keymap_bind_keyseq (map, "\033[B", &map[Control ('n')]); + keymap_bind_keyseq (map, term_kr, &map[Control ('f')]); /* right */ + keymap_bind_keyseq (map, "\033OC", &map[Control ('f')]); + keymap_bind_keyseq (map, "\033[C", &map[Control ('f')]); + keymap_bind_keyseq (map, term_kl, &map[Control ('b')]); /* left */ + keymap_bind_keyseq (map, "\033OD", &map[Control ('b')]); + keymap_bind_keyseq (map, "\033[D", &map[Control ('b')]); + keymap_bind_keyseq (map, term_kh, &map[Control ('a')]); /* home */ + keymap_bind_keyseq (map, term_ke, &map[Control ('e')]); /* end */ + keymap_bind_keyseq (map, term_kD, &map[DEL]); /* delete */ + + map = (Keymap)echo_area_keymap[ESC].function; + keymap_bind_keyseq (map, term_kl, &map['b']); /* left */ + keymap_bind_keyseq (map, "\033OA", &map['b']); + keymap_bind_keyseq (map, "\033[A", &map['b']); + keymap_bind_keyseq (map, term_kr, &map['f']); /* right */ + keymap_bind_keyseq (map, "\033OB", &map['f']); + keymap_bind_keyseq (map, "\033[B", &map['f']); + keymap_bind_keyseq (map, term_kD, &map[DEL]); /* delete */ + + map = (Keymap)echo_area_keymap[Control ('x')].function; + keymap_bind_keyseq (map, term_kD, &map[DEL]); + + /* Bind commands for Info window keymaps. */ + map = info_keymap; + map[TAB].function = info_move_to_next_xref; + map[LFD].function = info_down_line; + map[RET].function = info_down_line; + map[SPC].function = info_scroll_forward; + map[Control ('a')].function = info_beginning_of_line; + map[Control ('b')].function = info_scroll_backward_page_only; + map[Control ('d')].function = info_scroll_half_screen_down; + map[Control ('e')].function = info_down_line; + map[Control ('f')].function = info_scroll_forward_page_only; + map[Control ('g')].function = info_abort_key; + map[Control ('k')].function = info_up_line; + map[Control ('l')].function = info_redraw_display; + map[Control ('n')].function = info_down_line; + map[Control ('p')].function = info_up_line; + map[Control ('r')].function = info_redraw_display; + map[Control ('s')].function = isearch_forward; + map[Control ('u')].function = info_scroll_half_screen_up; + map[Control ('v')].function = info_scroll_forward_page_only; + map[Control ('y')].function = info_up_line; + map[','].function = info_next_index_match; + map['/'].function = info_search; + + for (i = '1'; i < '9' + 1; i++) + ((Keymap) info_keymap[ESC].function)[i].function = info_menu_digit; + ((Keymap) info_keymap[ESC].function)['0'].function = info_last_menu_item; + + map['<'].function = info_first_node; + map['>'].function = info_last_node; + map['?'].function = info_search_backward; + map['['].function = info_global_prev_node; + map[']'].function = info_global_next_node; + map['\''].function = info_history_node; + + map['b'].function = info_scroll_backward; + map['d'].function = info_scroll_half_screen_down; + map['e'].function = info_down_line; + map['E'].function = info_view_file; + map['f'].function = info_scroll_forward_page_only; + map['F'].function = info_scroll_forward_page_only; + map['g'].function = info_first_node; + map['G'].function = info_last_node; + map['h'].function = info_get_help_window; + map['H'].function = info_get_help_window; + map['i'].function = info_index_search; + map['I'].function = info_goto_invocation_node; + map['j'].function = info_next_line; + map['k'].function = info_prev_line; + map['l'].function = info_history_node; + map['m'].function = info_menu_item; + map['n'].function = info_search_next; + map['N'].function = info_search_previous; + map['O'].function = info_goto_invocation_node; + map['p'].function = info_prev_node; + map['q'].function = info_quit; + map['Q'].function = info_quit; + map['r'].function = info_redraw_display; + map['R'].function = info_redraw_display; + map['s'].function = info_search; + map['S'].function = info_search_case_sensitively; + map['t'].function = info_top_node; + map['u'].function = info_scroll_half_screen_up; + map['w'].function = info_scroll_backward_page_only_set_window; + map['y'].function = info_up_line; + map['z'].function = info_scroll_forward_page_only_set_window; + map['Z'].function = NULL; /* unbind, so it works to bind "ZZ" below */ + map[DEL].function = info_scroll_backward; + keymap_bind_keyseq (map, term_kD, &map[DEL]); + keymap_bind_keyseq (map, ":q", &map['q']); + keymap_bind_keyseq (map, ":Q", &map['q']); + keymap_bind_keyseq (map, "ZZ", &map['q']); + + /* Bind members in the ESC map for Info windows. */ + map = (Keymap)info_keymap[ESC].function; + map[Control ('f')].function = info_show_footnotes; + map[Control ('g')].function = info_abort_key; + map[TAB].function = info_move_to_prev_xref; + map[SPC].function = info_scroll_forward_page_only; + map[Control ('v')].function = info_scroll_other_window; + map['<'].function = info_beginning_of_node; + map['>'].function = info_end_of_node; + map['/'].function = info_search; + map['?'].function = info_search_backward; + map['b'].function = info_beginning_of_node; + map['d'].function = info_dir_node; + map['e'].function = info_end_of_node; + map['f'].function = info_xref_item; + map['g'].function = info_select_reference_this_line; + map['h'].function = info_get_info_help_node; + map['m'].function = info_menu_item; + map['n'].function = info_search; + map['N'].function = info_search_backward; + map['r'].function = isearch_backward; + map['s'].function = isearch_forward; + map['t'].function = info_top_node; + map['v'].function = info_scroll_backward_page_only; +#if defined (NAMED_FUNCTIONS) + map['x'].function = info_execute_command; +#endif /* NAMED_FUNCTIONS */ + map[DEL].function = info_scroll_other_window_backward; + + /* Bind members in the Control-X map for Info windows. */ + map = (Keymap)info_keymap[Control ('x')].function; + + map[Control ('b')].function = list_visited_nodes; + map[Control ('c')].function = info_quit; + map[Control ('f')].function = info_view_file; + map[Control ('g')].function = info_abort_key; + map[Control ('v')].function = info_view_file; + map[LFD].function = info_select_reference_this_line; + map[RET].function = info_select_reference_this_line; + map['0'].function = info_delete_window; + map['1'].function = info_keep_one_window; + map['2'].function = info_split_window; + map['^'].function = info_grow_window; + map['b'].function = select_visited_node; + map['g'].function = info_goto_node; + map['i'].function = info_index_search; + map['I'].function = info_goto_invocation_node; + map['k'].function = info_kill_node; + map['n'].function = info_next_node; + map['o'].function = info_next_window; + map['O'].function = info_goto_invocation_node; + map['p'].function = info_prev_node; + map['r'].function = info_xref_item; + map['t'].function = info_tile_windows; + map['u'].function = info_up_node; + map['w'].function = info_toggle_wrap; + map[','].function = info_next_index_match; + keymap_bind_keyseq (info_keymap, ":e", &map[Control ('v')]); + + /* Arrow key bindings for Info windows keymap. */ + map = info_keymap; + keymap_bind_keyseq (map, term_kN, &map[Control ('v')]); /* pagedown */ + keymap_bind_keyseq (map, term_ku, &map[Control ('p')]); /* up */ + keymap_bind_keyseq (map, "\033OA", &map[Control ('p')]); + keymap_bind_keyseq (map, "\033[A", &map[Control ('p')]); + keymap_bind_keyseq (map, term_kd, &map[Control ('n')]); /* down */ + keymap_bind_keyseq (map, "\033OB", &map[Control ('n')]); + keymap_bind_keyseq (map, "\033[B", &map[Control ('n')]); + keymap_bind_keyseq (map, term_kr, &map[Control ('f')]); /* right */ + keymap_bind_keyseq (map, "\033OC", &map[Control ('f')]); + keymap_bind_keyseq (map, "\033[C", &map[Control ('f')]); + keymap_bind_keyseq (map, term_kl, &map[Control ('b')]); /* left */ + keymap_bind_keyseq (map, "\033OD", &map[Control ('b')]); + keymap_bind_keyseq (map, "\033[D", &map[Control ('b')]); + keymap_bind_keyseq (map, term_kh, &map['b']); /* home */ + keymap_bind_keyseq (map, term_ke, &map['e']); /* end */ + + map = (Keymap)info_keymap[ESC].function; + keymap_bind_keyseq (map, term_kl, &map['b']); /* left */ + keymap_bind_keyseq (map, "\033OA", &map['b']); + keymap_bind_keyseq (map, "\033[A", &map['b']); + keymap_bind_keyseq (map, term_kr, &map['f']); /* right */ + keymap_bind_keyseq (map, "\033OB", &map['f']); + keymap_bind_keyseq (map, "\033[B", &map['f']); + keymap_bind_keyseq (map, term_kN, &map[Control ('v')]); /* pagedown */ + keymap_bind_keyseq (map, term_kP, &map[DEL]); /* pageup */ + keymap_bind_keyseq (map, term_kD, &map[DEL]); /* delete */ + + /* The alternative to this definition of a `main map' key in the + `ESC map' section, is something like: + keymap_bind_keyseq (map, term_kP, &((KeyMap)map[ESC].function).map['v']); + */ + keymap_bind_keyseq (info_keymap/*sic*/, term_kP, &map['v']); /* pageup */ +} + +void +initialize_info_keymaps () +{ + if (vi_keys_p) + initialize_vi_like_keymaps (); + else + initialize_emacs_like_keymaps (); +} + +#else /* defined(INFOKEY) */ + +/* Make sure that we don't have too many command codes defined. */ + +#if A_NCOMMANDS > A_MAX_COMMAND + 1 +#error "too many commands defined" +#endif + +/* Initialize the keymaps from the .info keymap file. */ + +#define NUL '\0' + +static unsigned char default_emacs_like_info_keys[] = +{ + 0, /* suppress-default-keybindings flag */ + TAB, NUL, A_info_move_to_next_xref, + LFD, NUL, A_info_select_reference_this_line, + RET, NUL, A_info_select_reference_this_line, + SPC, NUL, A_info_scroll_forward, + CONTROL('a'), NUL, A_info_beginning_of_line, + CONTROL('b'), NUL, A_info_backward_char, + CONTROL('e'), NUL, A_info_end_of_line, + CONTROL('f'), NUL, A_info_forward_char, + CONTROL('g'), NUL, A_info_abort_key, + CONTROL('h'), NUL, A_info_get_help_window, + CONTROL('l'), NUL, A_info_redraw_display, + CONTROL('n'), NUL, A_info_next_line, + CONTROL('p'), NUL, A_info_prev_line, + CONTROL('r'), NUL, A_isearch_backward, + CONTROL('s'), NUL, A_isearch_forward, + CONTROL('u'), NUL, A_info_universal_argument, + CONTROL('v'), NUL, A_info_scroll_forward_page_only, + ',', NUL, A_info_next_index_match, + '/', NUL, A_info_search, + '0', NUL, A_info_last_menu_item, + '1', NUL, A_info_menu_digit, + '2', NUL, A_info_menu_digit, + '3', NUL, A_info_menu_digit, + '4', NUL, A_info_menu_digit, + '5', NUL, A_info_menu_digit, + '6', NUL, A_info_menu_digit, + '7', NUL, A_info_menu_digit, + '8', NUL, A_info_menu_digit, + '9', NUL, A_info_menu_digit, + '<', NUL, A_info_first_node, + '>', NUL, A_info_last_node, + '?', NUL, A_info_get_help_window, + '[', NUL, A_info_global_prev_node, + ']', NUL, A_info_global_next_node, + 'b', NUL, A_info_beginning_of_node, + 'd', NUL, A_info_dir_node, + 'e', NUL, A_info_end_of_node, + 'f', NUL, A_info_xref_item, + 'g', NUL, A_info_goto_node, + 'G', NUL, A_info_menu_sequence, + 'h', NUL, A_info_get_info_help_node, + 'i', NUL, A_info_index_search, + 'l', NUL, A_info_history_node, + 'm', NUL, A_info_menu_item, + 'n', NUL, A_info_next_node, + 'O', NUL, A_info_goto_invocation_node, + 'p', NUL, A_info_prev_node, + 'q', NUL, A_info_quit, + 'r', NUL, A_info_xref_item, + 's', NUL, A_info_search, + 'S', NUL, A_info_search_case_sensitively, + 't', NUL, A_info_top_node, + 'u', NUL, A_info_up_node, + DEL, NUL, A_info_scroll_backward, + ESC, '0', NUL, A_info_add_digit_to_numeric_arg, + ESC, '1', NUL, A_info_add_digit_to_numeric_arg, + ESC, '2', NUL, A_info_add_digit_to_numeric_arg, + ESC, '3', NUL, A_info_add_digit_to_numeric_arg, + ESC, '4', NUL, A_info_add_digit_to_numeric_arg, + ESC, '5', NUL, A_info_add_digit_to_numeric_arg, + ESC, '6', NUL, A_info_add_digit_to_numeric_arg, + ESC, '7', NUL, A_info_add_digit_to_numeric_arg, + ESC, '8', NUL, A_info_add_digit_to_numeric_arg, + ESC, '9', NUL, A_info_add_digit_to_numeric_arg, + ESC, '-', NUL, A_info_add_digit_to_numeric_arg, + ESC, CONTROL('f'), NUL, A_info_show_footnotes, + ESC, CONTROL('g'), NUL, A_info_abort_key, + ESC, TAB, NUL, A_info_move_to_prev_xref, + ESC, CONTROL('v'), NUL, A_info_scroll_other_window, + ESC, '<', NUL, A_info_beginning_of_node, + ESC, '>', NUL, A_info_end_of_node, + ESC, 'b', NUL, A_info_backward_word, + ESC, 'f', NUL, A_info_forward_word, + ESC, 'r', NUL, A_info_move_to_window_line, + ESC, 'v', NUL, A_info_scroll_backward_page_only, + Meta('0'), NUL, A_info_add_digit_to_numeric_arg, + Meta('1'), NUL, A_info_add_digit_to_numeric_arg, + Meta('2'), NUL, A_info_add_digit_to_numeric_arg, + Meta('3'), NUL, A_info_add_digit_to_numeric_arg, + Meta('4'), NUL, A_info_add_digit_to_numeric_arg, + Meta('5'), NUL, A_info_add_digit_to_numeric_arg, + Meta('6'), NUL, A_info_add_digit_to_numeric_arg, + Meta('7'), NUL, A_info_add_digit_to_numeric_arg, + Meta('8'), NUL, A_info_add_digit_to_numeric_arg, + Meta('9'), NUL, A_info_add_digit_to_numeric_arg, + Meta('-'), NUL, A_info_add_digit_to_numeric_arg, + Meta(CONTROL('f')), NUL, A_info_show_footnotes, + Meta(CONTROL('g')), NUL, A_info_abort_key, + Meta(TAB), NUL, A_info_move_to_prev_xref, + Meta(CONTROL('v')), NUL, A_info_scroll_other_window, + Meta('<'), NUL, A_info_beginning_of_node, + Meta('>'), NUL, A_info_end_of_node, + Meta('b'), NUL, A_info_backward_word, + Meta('f'), NUL, A_info_forward_word, + Meta('r'), NUL, A_info_move_to_window_line, + Meta('v'), NUL, A_info_scroll_backward_page_only, +#if defined (NAMED_FUNCTIONS) + ESC, 'x', NUL, A_info_execute_command, + Meta('x'), NUL, A_info_execute_command, +#endif /* NAMED_FUNCTIONS */ + + CONTROL('x'), CONTROL('b'), NUL, A_list_visited_nodes, + CONTROL('x'), CONTROL('c'), NUL, A_info_quit, + CONTROL('x'), CONTROL('f'), NUL, A_info_view_file, + CONTROL('x'), CONTROL('g'), NUL, A_info_abort_key, + CONTROL('x'), CONTROL('v'), NUL, A_info_view_file, + CONTROL('x'), '0', NUL, A_info_delete_window, + CONTROL('x'), '1', NUL, A_info_keep_one_window, + CONTROL('x'), '2', NUL, A_info_split_window, + CONTROL('x'), '^', NUL, A_info_grow_window, + CONTROL('x'), 'b', NUL, A_select_visited_node, + CONTROL('x'), 'k', NUL, A_info_kill_node, + CONTROL('x'), 'n', NUL, A_info_search_next, + CONTROL('x'), 'N', NUL, A_info_search_previous, + CONTROL('x'), 'o', NUL, A_info_next_window, + CONTROL('x'), 't', NUL, A_info_tile_windows, + CONTROL('x'), 'w', NUL, A_info_toggle_wrap, + +/* Arrow key bindings for info keymaps. It seems that some + terminals do not match their termcap entries, so it's best to just + define everything with both of the usual prefixes. */ + + SK_ESCAPE, SK_PAGE_UP, NUL, A_info_scroll_backward_page_only, + SK_ESCAPE, SK_PAGE_DOWN, NUL, A_info_scroll_forward_page_only, + SK_ESCAPE, SK_UP_ARROW, NUL, A_info_prev_line, + '\033', 'O', 'A', NUL, A_info_prev_line, + '\033', '[', 'A', NUL, A_info_prev_line, + SK_ESCAPE, SK_DOWN_ARROW, NUL, A_info_next_line, + '\033', 'O', 'B', NUL, A_info_next_line, + '\033', '[', 'B', NUL, A_info_next_line, + SK_ESCAPE, SK_RIGHT_ARROW, NUL, A_info_forward_char, + '\033', 'O', 'C', NUL, A_info_forward_char, + '\033', '[', 'C', NUL, A_info_forward_char, + SK_ESCAPE, SK_LEFT_ARROW, NUL, A_info_backward_char, + '\033', 'O', 'D', NUL, A_info_backward_char, + '\033', '[', 'D', NUL, A_info_backward_char, + SK_ESCAPE, SK_HOME, NUL, A_info_beginning_of_node, + SK_ESCAPE, SK_END, NUL, A_info_end_of_node, + SK_ESCAPE, SK_DELETE, NUL, A_info_scroll_backward, + + ESC, SK_ESCAPE, SK_PAGE_UP, NUL, A_info_scroll_other_window_backward, + ESC, SK_ESCAPE, SK_PAGE_DOWN, NUL, A_info_scroll_other_window, + ESC, SK_ESCAPE, SK_UP_ARROW, NUL, A_info_prev_line, + ESC, '\033', 'O', 'A', NUL, A_info_prev_line, + ESC, '\033', '[', 'A', NUL, A_info_prev_line, + ESC, SK_ESCAPE, SK_DOWN_ARROW, NUL, A_info_next_line, + ESC, '\033', 'O', 'B', NUL, A_info_next_line, + ESC, '\033', '[', 'B', NUL, A_info_next_line, + ESC, SK_ESCAPE, SK_RIGHT_ARROW, NUL, A_info_forward_word, + ESC, '\033', 'O', 'C', NUL, A_info_forward_word, + ESC, '\033', '[', 'C', NUL, A_info_forward_word, + ESC, SK_ESCAPE, SK_LEFT_ARROW, NUL, A_info_backward_word, + ESC, '\033', 'O', 'D', NUL, A_info_backward_word, + ESC, '\033', '[', 'D', NUL, A_info_backward_word, +}; + +static unsigned char default_emacs_like_ea_keys[] = +{ + 0, /* suppress-default-keybindings flag */ + ESC, '0', NUL, A_info_add_digit_to_numeric_arg, + ESC, '1', NUL, A_info_add_digit_to_numeric_arg, + ESC, '2', NUL, A_info_add_digit_to_numeric_arg, + ESC, '3', NUL, A_info_add_digit_to_numeric_arg, + ESC, '4', NUL, A_info_add_digit_to_numeric_arg, + ESC, '5', NUL, A_info_add_digit_to_numeric_arg, + ESC, '6', NUL, A_info_add_digit_to_numeric_arg, + ESC, '7', NUL, A_info_add_digit_to_numeric_arg, + ESC, '8', NUL, A_info_add_digit_to_numeric_arg, + ESC, '9', NUL, A_info_add_digit_to_numeric_arg, + ESC, '-', NUL, A_info_add_digit_to_numeric_arg, + Meta('0'), NUL, A_info_add_digit_to_numeric_arg, + Meta('1'), NUL, A_info_add_digit_to_numeric_arg, + Meta('2'), NUL, A_info_add_digit_to_numeric_arg, + Meta('3'), NUL, A_info_add_digit_to_numeric_arg, + Meta('4'), NUL, A_info_add_digit_to_numeric_arg, + Meta('5'), NUL, A_info_add_digit_to_numeric_arg, + Meta('6'), NUL, A_info_add_digit_to_numeric_arg, + Meta('7'), NUL, A_info_add_digit_to_numeric_arg, + Meta('8'), NUL, A_info_add_digit_to_numeric_arg, + Meta('9'), NUL, A_info_add_digit_to_numeric_arg, + Meta('-'), NUL, A_info_add_digit_to_numeric_arg, + ESC, CONTROL('g'), NUL, A_ea_abort, + ESC, CONTROL('v'), NUL, A_ea_scroll_completions_window, + ESC, 'b', NUL, A_ea_backward_word, + ESC, 'd', NUL, A_ea_kill_word, + ESC, 'f', NUL, A_ea_forward_word, + ESC, 'y', NUL, A_ea_yank_pop, + ESC, '?', NUL, A_ea_possible_completions, + ESC, TAB, NUL, A_ea_tab_insert, + ESC, DEL, NUL, A_ea_backward_kill_word, + Meta(CONTROL('g')), NUL, A_ea_abort, + Meta(CONTROL('v')), NUL, A_ea_scroll_completions_window, + Meta('b'), NUL, A_ea_backward_word, + Meta('d'), NUL, A_ea_kill_word, + Meta('f'), NUL, A_ea_forward_word, + Meta('y'), NUL, A_ea_yank_pop, + Meta('?'), NUL, A_ea_possible_completions, + Meta(TAB), NUL, A_ea_tab_insert, + Meta(DEL), NUL, A_ea_backward_kill_word, + CONTROL('a'), NUL, A_ea_beg_of_line, + CONTROL('b'), NUL, A_ea_backward, + CONTROL('d'), NUL, A_ea_delete, + CONTROL('e'), NUL, A_ea_end_of_line, + CONTROL('f'), NUL, A_ea_forward, + CONTROL('g'), NUL, A_ea_abort, + CONTROL('h'), NUL, A_ea_rubout, +/* CONTROL('k') */ + SK_ESCAPE, SK_LITERAL, NUL, A_ea_kill_line, + CONTROL('l'), NUL, A_info_redraw_display, + CONTROL('q'), NUL, A_ea_quoted_insert, + CONTROL('t'), NUL, A_ea_transpose_chars, + CONTROL('u'), NUL, A_info_universal_argument, + CONTROL('y'), NUL, A_ea_yank, + LFD, NUL, A_ea_newline, + RET, NUL, A_ea_newline, + SPC, NUL, A_ea_complete, + TAB, NUL, A_ea_complete, + '?', NUL, A_ea_possible_completions, +#ifdef __MSDOS__ + /* PC users will lynch me if I don't give them their usual DEL + effect... */ + DEL, NUL, A_ea_delete, +#else + DEL, NUL, A_ea_rubout, +#endif +#if defined (NAMED_FUNCTIONS) + /* ESC, 'x', NUL, A_info_execute_command, */ + /* Meta('x'), NUL, A_info_execute_command, */ +#endif /* NAMED_FUNCTIONS */ + CONTROL('x'), 'o', NUL, A_info_next_window, + CONTROL('x'), DEL, NUL, A_ea_backward_kill_line, + +/* Arrow key bindings for echo area keymaps. It seems that some + terminals do not match their termcap entries, so it's best to just + define everything with both of the usual prefixes. */ + + SK_ESCAPE, SK_RIGHT_ARROW, NUL, A_ea_forward, + '\033', 'O', 'C', NUL, A_ea_forward, + '\033', '[', 'C', NUL, A_ea_forward, + SK_ESCAPE, SK_LEFT_ARROW, NUL, A_ea_backward, + '\033', 'O', 'D', NUL, A_ea_backward, + '\033', '[', 'D', NUL, A_ea_backward, + ESC, SK_ESCAPE, SK_RIGHT_ARROW, NUL, A_ea_forward_word, + ESC, '\033', 'O', 'C', NUL, A_ea_forward_word, + ESC, '\033', '[', 'C', NUL, A_ea_forward_word, + ESC, SK_ESCAPE, SK_LEFT_ARROW, NUL, A_ea_backward_word, + ESC, '\033', 'O', 'D', NUL, A_ea_backward_word, + ESC, '\033', '[', 'D', NUL, A_ea_backward_word, +#ifdef __MSDOS__ + SK_ESCAPE, SK_DELETE, NUL, A_ea_delete, +#else + SK_ESCAPE, SK_DELETE, NUL, A_ea_rubout, +#endif + SK_ESCAPE, SK_HOME, NUL, A_ea_beg_of_line, + SK_ESCAPE, SK_END, NUL, A_ea_end_of_line, + ESC, SK_ESCAPE, SK_DELETE, NUL, A_ea_backward_kill_word, + CONTROL('x'), SK_ESCAPE, SK_DELETE, NUL,A_ea_backward_kill_line, +}; + +static unsigned char default_vi_like_info_keys[] = +{ + 0, /* suppress-default-keybindings flag */ + '0', NUL, A_info_add_digit_to_numeric_arg, + '1', NUL, A_info_add_digit_to_numeric_arg, + '2', NUL, A_info_add_digit_to_numeric_arg, + '3', NUL, A_info_add_digit_to_numeric_arg, + '4', NUL, A_info_add_digit_to_numeric_arg, + '5', NUL, A_info_add_digit_to_numeric_arg, + '6', NUL, A_info_add_digit_to_numeric_arg, + '7', NUL, A_info_add_digit_to_numeric_arg, + '8', NUL, A_info_add_digit_to_numeric_arg, + '9', NUL, A_info_add_digit_to_numeric_arg, + '-', NUL, A_info_add_digit_to_numeric_arg, + TAB, NUL, A_info_move_to_next_xref, + LFD, NUL, A_info_down_line, + RET, NUL, A_info_down_line, + SPC, NUL, A_info_scroll_forward, + CONTROL('a'), NUL, A_info_beginning_of_line, + CONTROL('b'), NUL, A_info_scroll_backward_page_only, + CONTROL('d'), NUL, A_info_scroll_half_screen_down, + CONTROL('e'), NUL, A_info_down_line, + CONTROL('f'), NUL, A_info_scroll_forward_page_only, + CONTROL('g'), NUL, A_info_abort_key, + CONTROL('k'), NUL, A_info_up_line, + CONTROL('l'), NUL, A_info_redraw_display, + CONTROL('n'), NUL, A_info_down_line, + CONTROL('p'), NUL, A_info_up_line, + CONTROL('r'), NUL, A_info_redraw_display, + CONTROL('s'), NUL, A_isearch_forward, + CONTROL('u'), NUL, A_info_scroll_half_screen_up, + CONTROL('v'), NUL, A_info_scroll_forward_page_only, + CONTROL('y'), NUL, A_info_up_line, + ',', NUL, A_info_next_index_match, + '/', NUL, A_info_search, + ESC, '0', NUL, A_info_last_menu_item, + ESC, '1', NUL, A_info_menu_digit, + ESC, '2', NUL, A_info_menu_digit, + ESC, '3', NUL, A_info_menu_digit, + ESC, '4', NUL, A_info_menu_digit, + ESC, '5', NUL, A_info_menu_digit, + ESC, '6', NUL, A_info_menu_digit, + ESC, '7', NUL, A_info_menu_digit, + ESC, '8', NUL, A_info_menu_digit, + ESC, '9', NUL, A_info_menu_digit, + Meta('0'), NUL, A_info_last_menu_item, + Meta('1'), NUL, A_info_menu_digit, + Meta('2'), NUL, A_info_menu_digit, + Meta('3'), NUL, A_info_menu_digit, + Meta('4'), NUL, A_info_menu_digit, + Meta('5'), NUL, A_info_menu_digit, + Meta('6'), NUL, A_info_menu_digit, + Meta('7'), NUL, A_info_menu_digit, + Meta('8'), NUL, A_info_menu_digit, + Meta('9'), NUL, A_info_menu_digit, + '<', NUL, A_info_first_node, + '>', NUL, A_info_last_node, + '?', NUL, A_info_search_backward, + '[', NUL, A_info_global_prev_node, + ']', NUL, A_info_global_next_node, + '\'', NUL, A_info_history_node, + 'b', NUL, A_info_scroll_backward, + 'd', NUL, A_info_scroll_half_screen_down, + 'e', NUL, A_info_down_line, + 'E', NUL, A_info_view_file, + ':', 'e', NUL, A_info_view_file, + 'f', NUL, A_info_scroll_forward_page_only, + 'F', NUL, A_info_scroll_forward_page_only, + 'g', NUL, A_info_first_node, + 'G', NUL, A_info_last_node, + 'h', NUL, A_info_get_help_window, + 'H', NUL, A_info_get_help_window, + 'i', NUL, A_info_index_search, + 'I', NUL, A_info_goto_invocation_node, + 'j', NUL, A_info_next_line, + 'k', NUL, A_info_prev_line, + 'l', NUL, A_info_history_node, + 'm', NUL, A_info_menu_item, + 'n', NUL, A_info_search_next, + 'N', NUL, A_info_search_previous, + 'O', NUL, A_info_goto_invocation_node, + 'p', NUL, A_info_prev_node, + 'q', NUL, A_info_quit, + 'Q', NUL, A_info_quit, + ':', 'q', NUL, A_info_quit, + ':', 'Q', NUL, A_info_quit, + 'Z', 'Z', NUL, A_info_quit, + 'r', NUL, A_info_redraw_display, + 'R', NUL, A_info_redraw_display, + 's', NUL, A_info_search, + 'S', NUL, A_info_search_case_sensitively, + 't', NUL, A_info_top_node, + 'u', NUL, A_info_scroll_half_screen_up, + 'w', NUL, A_info_scroll_backward_page_only_set_window, + 'y', NUL, A_info_up_line, + 'z', NUL, A_info_scroll_forward_page_only_set_window, + DEL, NUL, A_info_scroll_backward, + ESC, CONTROL('f'), NUL, A_info_show_footnotes, + ESC, CONTROL('g'), NUL, A_info_abort_key, + ESC, TAB, NUL, A_info_move_to_prev_xref, + ESC, SPC, NUL, A_info_scroll_forward_page_only, + ESC, CONTROL('v'), NUL, A_info_scroll_other_window, + ESC, '<', NUL, A_info_beginning_of_node, + ESC, '>', NUL, A_info_end_of_node, + ESC, '/', NUL, A_info_search, + ESC, '?', NUL, A_info_search_backward, + ESC, 'b', NUL, A_info_beginning_of_node, + ESC, 'd', NUL, A_info_dir_node, + ESC, 'e', NUL, A_info_end_of_node, + ESC, 'f', NUL, A_info_xref_item, + ESC, 'g', NUL, A_info_select_reference_this_line, + ESC, 'h', NUL, A_info_get_info_help_node, + ESC, 'm', NUL, A_info_menu_item, + ESC, 'n', NUL, A_info_search, + ESC, 'N', NUL, A_info_search_backward, + ESC, 'r', NUL, A_isearch_backward, + ESC, 's', NUL, A_isearch_forward, + ESC, 't', NUL, A_info_top_node, + ESC, 'v', NUL, A_info_scroll_backward_page_only, +#if defined (NAMED_FUNCTIONS) + ESC, 'x', NUL, A_info_execute_command, + Meta('x'), NUL, A_info_execute_command, +#endif /* NAMED_FUNCTIONS */ + ESC, DEL, NUL, A_info_scroll_other_window_backward, + CONTROL('x'), CONTROL('b'), NUL, A_list_visited_nodes, + CONTROL('x'), CONTROL('c'), NUL, A_info_quit, + CONTROL('x'), CONTROL('f'), NUL, A_info_view_file, + CONTROL('x'), CONTROL('g'), NUL, A_info_abort_key, + CONTROL('x'), CONTROL('v'), NUL, A_info_view_file, + CONTROL('x'), LFD, NUL, A_info_select_reference_this_line, + CONTROL('x'), RET, NUL, A_info_select_reference_this_line, + CONTROL('x'), '0', NUL, A_info_delete_window, + CONTROL('x'), '1', NUL, A_info_keep_one_window, + CONTROL('x'), '2', NUL, A_info_split_window, + CONTROL('x'), '^', NUL, A_info_grow_window, + CONTROL('x'), 'b', NUL, A_select_visited_node, + CONTROL('x'), 'g', NUL, A_info_goto_node, + CONTROL('x'), 'i', NUL, A_info_index_search, + CONTROL('x'), 'I', NUL, A_info_goto_invocation_node, + CONTROL('x'), 'k', NUL, A_info_kill_node, + CONTROL('x'), 'n', NUL, A_info_next_node, + CONTROL('x'), 'o', NUL, A_info_next_window, + CONTROL('x'), 'O', NUL, A_info_goto_invocation_node, + CONTROL('x'), 'p', NUL, A_info_prev_node, + CONTROL('x'), 'r', NUL, A_info_xref_item, + CONTROL('x'), 't', NUL, A_info_tile_windows, + CONTROL('x'), 'u', NUL, A_info_up_node, + CONTROL('x'), 'w', NUL, A_info_toggle_wrap, + CONTROL('x'), ',', NUL, A_info_next_index_match, + +/* Arrow key bindings for info keymaps. It seems that some + terminals do not match their termcap entries, so it's best to just + define everything with both of the usual prefixes. */ + + SK_ESCAPE, SK_PAGE_UP, NUL, A_info_scroll_backward_page_only, + SK_ESCAPE, SK_PAGE_DOWN, NUL, A_info_scroll_forward_page_only, + SK_ESCAPE, SK_UP_ARROW, NUL, A_info_up_line, + '\033', 'O', 'A', NUL, A_info_up_line, + '\033', '[', 'A', NUL, A_info_up_line, + SK_ESCAPE, SK_DOWN_ARROW, NUL, A_info_down_line, + '\033', 'O', 'B', NUL, A_info_down_line, + '\033', '[', 'B', NUL, A_info_down_line, + SK_ESCAPE, SK_RIGHT_ARROW, NUL, A_info_scroll_forward_page_only, + '\033', 'O', 'C', NUL, A_info_scroll_forward_page_only, + '\033', '[', 'C', NUL, A_info_scroll_forward_page_only, + SK_ESCAPE, SK_LEFT_ARROW, NUL, A_info_scroll_backward_page_only, + '\033', 'O', 'D', NUL, A_info_scroll_backward_page_only, + '\033', '[', 'D', NUL, A_info_scroll_backward_page_only, + SK_ESCAPE, SK_HOME, NUL, A_info_beginning_of_node, + SK_ESCAPE, SK_END, NUL, A_info_end_of_node, + ESC, SK_ESCAPE, SK_PAGE_DOWN, NUL, A_info_scroll_other_window, + ESC, SK_ESCAPE, SK_PAGE_UP, NUL, A_info_scroll_other_window_backward, + ESC, SK_ESCAPE, SK_DELETE, NUL, A_info_scroll_other_window_backward, + ESC, SK_ESCAPE, SK_UP_ARROW, NUL, A_info_prev_node, + ESC, '\033', 'O', 'A', NUL, A_info_prev_node, + ESC, '\033', '[', 'A', NUL, A_info_prev_node, + ESC, SK_ESCAPE, SK_DOWN_ARROW, NUL, A_info_next_node, + ESC, '\033', 'O', 'B', NUL, A_info_next_node, + ESC, '\033', '[', 'B', NUL, A_info_next_node, + ESC, SK_ESCAPE, SK_RIGHT_ARROW, NUL, A_info_xref_item, + ESC, '\033', 'O', 'C', NUL, A_info_xref_item, + ESC, '\033', '[', 'C', NUL, A_info_xref_item, + ESC, SK_ESCAPE, SK_LEFT_ARROW, NUL, A_info_beginning_of_node, + ESC, '\033', 'O', 'D', NUL, A_info_beginning_of_node, + ESC, '\033', '[', 'D', NUL, A_info_beginning_of_node, + CONTROL('x'), SK_ESCAPE, SK_DELETE, NUL,A_ea_backward_kill_line, +}; + +static unsigned char default_vi_like_ea_keys[] = +{ + 0, /* suppress-default-keybindings flag */ + ESC, '1', NUL, A_info_add_digit_to_numeric_arg, + ESC, '2', NUL, A_info_add_digit_to_numeric_arg, + ESC, '3', NUL, A_info_add_digit_to_numeric_arg, + ESC, '4', NUL, A_info_add_digit_to_numeric_arg, + ESC, '5', NUL, A_info_add_digit_to_numeric_arg, + ESC, '6', NUL, A_info_add_digit_to_numeric_arg, + ESC, '7', NUL, A_info_add_digit_to_numeric_arg, + ESC, '8', NUL, A_info_add_digit_to_numeric_arg, + ESC, '9', NUL, A_info_add_digit_to_numeric_arg, + ESC, '-', NUL, A_info_add_digit_to_numeric_arg, + Meta('1'), NUL, A_info_add_digit_to_numeric_arg, + Meta('2'), NUL, A_info_add_digit_to_numeric_arg, + Meta('3'), NUL, A_info_add_digit_to_numeric_arg, + Meta('4'), NUL, A_info_add_digit_to_numeric_arg, + Meta('5'), NUL, A_info_add_digit_to_numeric_arg, + Meta('6'), NUL, A_info_add_digit_to_numeric_arg, + Meta('7'), NUL, A_info_add_digit_to_numeric_arg, + Meta('8'), NUL, A_info_add_digit_to_numeric_arg, + Meta('9'), NUL, A_info_add_digit_to_numeric_arg, + Meta('-'), NUL, A_info_add_digit_to_numeric_arg, + ESC, CONTROL('g'), NUL, A_ea_abort, + ESC, CONTROL('h'), NUL, A_ea_backward_kill_word, + ESC, CONTROL('v'), NUL, A_ea_scroll_completions_window, + ESC, '0', NUL, A_ea_beg_of_line, + ESC, '$', NUL, A_ea_end_of_line, + ESC, 'b', NUL, A_ea_backward_word, + ESC, 'd', NUL, A_ea_kill_word, + ESC, 'f', NUL, A_ea_forward_word, + ESC, 'h', NUL, A_ea_forward, + ESC, 'l', NUL, A_ea_backward, + ESC, 'w', NUL, A_ea_forward_word, + ESC, 'x', NUL, A_ea_delete, + ESC, 'X', NUL, A_ea_kill_word, + ESC, 'y', NUL, A_ea_yank_pop, + ESC, '?', NUL, A_ea_possible_completions, + ESC, TAB, NUL, A_ea_tab_insert, + ESC, DEL, NUL, A_ea_kill_word, + Meta(CONTROL('g')), NUL, A_ea_abort, + Meta(CONTROL('h')), NUL, A_ea_backward_kill_word, + Meta(CONTROL('v')), NUL, A_ea_scroll_completions_window, + Meta('0'), NUL, A_ea_beg_of_line, + Meta('$'), NUL, A_ea_end_of_line, + Meta('b'), NUL, A_ea_backward_word, + Meta('d'), NUL, A_ea_kill_word, + Meta('f'), NUL, A_ea_forward_word, + Meta('h'), NUL, A_ea_forward, + Meta('l'), NUL, A_ea_backward, + Meta('w'), NUL, A_ea_forward_word, + Meta('x'), NUL, A_ea_delete, + Meta('X'), NUL, A_ea_kill_word, + Meta('y'), NUL, A_ea_yank_pop, + Meta('?'), NUL, A_ea_possible_completions, + Meta(TAB), NUL, A_ea_tab_insert, + Meta(DEL), NUL, A_ea_kill_word, + CONTROL('a'), NUL, A_ea_beg_of_line, + CONTROL('b'), NUL, A_ea_backward, + CONTROL('d'), NUL, A_ea_delete, + CONTROL('e'), NUL, A_ea_end_of_line, + CONTROL('f'), NUL, A_ea_forward, + CONTROL('g'), NUL, A_ea_abort, + CONTROL('h'), NUL, A_ea_rubout, +/* CONTROL('k') */ + SK_ESCAPE, SK_LITERAL, NUL, A_ea_kill_line, + CONTROL('l'), NUL, A_info_redraw_display, + CONTROL('q'), NUL, A_ea_quoted_insert, + CONTROL('t'), NUL, A_ea_transpose_chars, + CONTROL('u'), NUL, A_ea_abort, + CONTROL('v'), NUL, A_ea_quoted_insert, + CONTROL('y'), NUL, A_ea_yank, + LFD, NUL, A_ea_newline, + RET, NUL, A_ea_newline, + SPC, NUL, A_ea_complete, + TAB, NUL, A_ea_complete, + '?', NUL, A_ea_possible_completions, +#ifdef __MSDOS__ + /* PC users will lynch me if I don't give them their usual DEL + effect... */ + DEL, NUL, A_ea_delete, +#else + DEL, NUL, A_ea_rubout, +#endif + CONTROL('x'), 'o', NUL, A_info_next_window, + CONTROL('x'), DEL, NUL, A_ea_backward_kill_line, + + /* Arrow key bindings for echo area keymaps. It seems that some + terminals do not match their termcap entries, so it's best to just + define everything with both of the usual prefixes. */ + + SK_ESCAPE, SK_RIGHT_ARROW, NUL, A_ea_forward, + '\033', 'O', 'C', NUL, A_ea_forward, + '\033', '[', 'C', NUL, A_ea_forward, + SK_ESCAPE, SK_LEFT_ARROW, NUL, A_ea_backward, + '\033', 'O', 'D', NUL, A_ea_backward, + '\033', '[', 'D', NUL, A_ea_backward, + SK_ESCAPE, SK_HOME, NUL, A_ea_beg_of_line, + SK_ESCAPE, SK_END, NUL, A_ea_end_of_line, +#ifdef __MSDOS__ + SK_ESCAPE, SK_DELETE, NUL, A_ea_delete, +#else + SK_DELETE, SK_DELETE, NUL, A_ea_rubout, +#endif + ESC, SK_ESCAPE, SK_RIGHT_ARROW, NUL, A_ea_forward_word, + ESC, '\033', 'O', 'C', NUL, A_ea_forward_word, + ESC, '\033', '[', 'C', NUL, A_ea_forward_word, + ESC, SK_ESCAPE, SK_LEFT_ARROW, NUL, A_ea_backward_word, + ESC, '\033', 'O', 'D', NUL, A_ea_backward_word, + ESC, '\033', '[', 'D', NUL, A_ea_backward_word, + ESC, SK_ESCAPE, SK_DELETE, NUL, A_ea_kill_word, + CONTROL('x'), SK_ESCAPE, SK_DELETE, NUL,A_ea_backward_kill_line, +}; + +static unsigned char *user_info_keys; +static unsigned int user_info_keys_len; +static unsigned char *user_ea_keys; +static unsigned int user_ea_keys_len; +static unsigned char *user_vars; +static unsigned int user_vars_len; + +/* + * Return the size of a file, or 0 if the size can't be determined. + */ +static unsigned long +filesize(int f) +{ + long pos = lseek(f, 0L, SEEK_CUR); + long sz = -1L; + if (pos != -1L) + { + sz = lseek(f, 0L, SEEK_END); + lseek(f, pos, SEEK_SET); + } + return sz == -1L ? 0L : sz; +} + +/* Get an integer from a infokey file. + Integers are stored as two bytes, low order first, in radix INFOKEY_RADIX. + */ +static int +getint(unsigned char **sp) +{ + int n; + + if ( !((*sp)[0] < INFOKEY_RADIX && (*sp)[1] < INFOKEY_RADIX) ) + return -1; + n = (*sp)[0] + (*sp)[1] * INFOKEY_RADIX; + *sp += 2; + return n; +} + + +/* Fetch the contents of the standard infokey file "$HOME/.info". Return + true if ok, false if not. */ +static int +fetch_user_maps(void) +{ + char *filename = NULL; + char *homedir; + int f; + unsigned char *buf; + unsigned long len; + long nread; + unsigned char *p; + int n; + + /* Find and open file. */ + if ((filename = getenv("INFOKEY")) != NULL) + filename = xstrdup(filename); + else if ((homedir = getenv("HOME")) != NULL) + { + filename = xmalloc(strlen(homedir) + 2 + strlen(INFOKEY_FILE)); + strcpy(filename, homedir); + strcat(filename, "/"); + strcat(filename, INFOKEY_FILE); + } +#ifdef __MSDOS__ + /* Poor baby, she doesn't have a HOME... */ + else + filename = xstrdup(INFOKEY_FILE); /* try current directory */ +#endif + if (filename == NULL || (f = open(filename, O_RDONLY)) == (-1)) + { + if (filename && errno != ENOENT) + { + info_error(filesys_error_string(filename, errno), + NULL, NULL); + free(filename); + } + return 0; + } + SET_BINARY (f); + + /* Ensure that the file is a reasonable size. */ + len = filesize(f); + if (len < INFOKEY_NMAGIC + 2 || len > 100 * 1024) + { + /* Bad file (a valid file must have at least 9 chars, and + more than 100 KB is a problem). */ + if (len < INFOKEY_NMAGIC + 2) + info_error((char *) _("Ignoring invalid infokey file `%s' - too small"), + filename, NULL); + else + info_error((char *) _("Ignoring invalid infokey file `%s' - too big"), + filename, NULL); + close(f); + free(filename); + return 0; + } + + /* Read the file into a buffer. */ + buf = (unsigned char *)xmalloc((int)len); + nread = read(f, buf, (unsigned int) len); + close(f); + if ((unsigned int) nread != len) + { + info_error((char *) _("Error reading infokey file `%s' - short read"), + filename, NULL); + free(buf); + free(filename); + return 0; + } + + /* Check the header, trailer, and version of the file to increase + our confidence that the contents are valid. */ + if ( buf[0] != INFOKEY_MAGIC_S0 + || buf[1] != INFOKEY_MAGIC_S1 + || buf[2] != INFOKEY_MAGIC_S2 + || buf[3] != INFOKEY_MAGIC_S3 + || buf[len - 4] != INFOKEY_MAGIC_E0 + || buf[len - 3] != INFOKEY_MAGIC_E1 + || buf[len - 2] != INFOKEY_MAGIC_E2 + || buf[len - 1] != INFOKEY_MAGIC_E3 + ) + { + info_error((char *) _("Invalid infokey file `%s' (bad magic numbers) -- run infokey to update it"), + filename, NULL); + free(filename); + return 0; + } + if (len < INFOKEY_NMAGIC + strlen(VERSION) + 1 + || strcmp(VERSION, (char *) (buf + 4)) != 0) + { + info_error + ((char *) _("Your infokey file `%s' is out of date -- run infokey to update it"), + filename, NULL); + free(filename); + return 0; + } + + /* Extract the pieces. */ + for (p = buf + 4 + strlen(VERSION) + 1; + (unsigned int) (p - buf) < len - 4; + p += n) + { + int s = *p++; + + n = getint(&p); + if (n < 0 || (unsigned int) n > len - 4 - (p - buf)) + { + info_error((char *) _("Invalid infokey file `%s' (bad section length) -- run infokey to update it"), + filename, NULL); + free(filename); + return 0; + } + + switch (s) + { + case INFOKEY_SECTION_INFO: + user_info_keys = p; + user_info_keys_len = n; + break; + case INFOKEY_SECTION_EA: + user_ea_keys = p; + user_ea_keys_len = n; + break; + case INFOKEY_SECTION_VAR: + user_vars = p; + user_vars_len = n; + break; + default: + info_error((char *) _("Invalid infokey file `%s' (bad section code) -- run infokey to update it"), + filename, NULL); + free(filename); + return 0; + } + } + + free(filename); + return 1; +} + +/* Decode special key sequences from the infokey file. Return zero + if the key sequence includes special keys which the terminal + doesn't define. + */ +static int +decode_keys(unsigned char *src, unsigned int slen, + unsigned char *dst, unsigned int dlen) +{ + unsigned char *s = src; + unsigned char *d = dst; + +#define To_dst(c) do { \ + if ((unsigned int) (d - dst) < dlen) *d++ = (c); \ +} while (0) + + while ((unsigned int) (s - src) < slen) + { + unsigned char c = ISMETA(*s) ? UNMETA(*s) : *s; + + if (c == SK_ESCAPE) + { + char *t; + static char lit[] = { SK_ESCAPE, NUL }; + + switch ((unsigned int) (s + 1 - src) < slen ? s[1] : '\0') + { + case SK_RIGHT_ARROW: t = term_kr; break; + case SK_LEFT_ARROW: t = term_kl; break; + case SK_UP_ARROW: t = term_ku; break; + case SK_DOWN_ARROW: t = term_kd; break; + case SK_PAGE_UP: t = term_kP; break; + case SK_PAGE_DOWN: t = term_kN; break; + case SK_HOME: t = term_kh; break; + case SK_END: t = term_ke; break; + case SK_DELETE: t = term_kx; break; + case SK_INSERT: t = term_ki; break; + case SK_LITERAL: + default: t = lit; break; + } + if (t == NULL) + return 0; + while (*t) + To_dst(ISMETA(*s) ? Meta(*t++) : *t++); + s += 2; + } + else + { + if (ISMETA(*s)) + To_dst(Meta(*s++)); + else + To_dst(*s++); + } + } + + To_dst('\0'); + + return 1; + +#undef To_dst + +} + +/* Convert an infokey file section to keymap bindings. Return false if + the default bindings are to be suppressed. */ +static int +section_to_keymaps(Keymap map, unsigned char *table, unsigned int len) +{ + int stop; + unsigned char *p; + unsigned char *seq = NULL; + unsigned int seqlen = 0; + enum { getseq, gotseq, getaction } state = getseq; + + stop = len > 0 ? table[0] : 0; + + for (p = table + 1; (unsigned int) (p - table) < len; p++) + { + switch (state) + { + case getseq: + if (*p) + { + seq = p; + state = gotseq; + } + break; + + case gotseq: + if (!*p) + { + seqlen = p - seq; + state = getaction; + } + break; + + case getaction: + { + unsigned int action = *p; + unsigned char keyseq[256]; + KEYMAP_ENTRY ke; + + state = getseq; + /* If decode_keys returns zero, it + means that seq includes keys which + the terminal doesn't support, like + PageDown. In that case, don't bind + the key sequence. */ + if (decode_keys(seq, seqlen, keyseq, + sizeof keyseq)) + { + keyseq[sizeof keyseq - 1] = '\0'; + ke.type = ISFUNC; + ke.function = + action < A_NCOMMANDS + ? &function_doc_array[action] + : NULL; + keymap_bind_keyseq(map, + (const char *) keyseq, &ke); + } + } + break; + } + } + if (state != getseq) + info_error((char *) _("Bad data in infokey file -- some key bindings ignored"), + NULL, NULL); + return !stop; +} + +/* Convert an infokey file section to variable settings. + */ +static void +section_to_vars(unsigned char *table, unsigned int len) +{ + enum { getvar, gotvar, getval, gotval } state = getvar; + unsigned char *var = NULL; + unsigned char *val = NULL; + unsigned char *p; + + for (p = table; (unsigned int) (p - table) < len; p++) + { + switch (state) + { + case getvar: + if (*p) + { + var = p; + state = gotvar; + } + break; + + case gotvar: + if (!*p) + state = getval; + break; + + case getval: + if (*p) + { + val = p; + state = gotval; + } + break; + + case gotval: + if (!*p) + { + set_variable_to_value((char *) var, (char *) val); + state = getvar; + } + break; + } + } + if (state != getvar) + info_error((char *) _("Bad data in infokey file -- some var settings ignored"), + NULL, NULL); +} + +void +initialize_info_keymaps (void) +{ + int i; + int suppress_info_default_bindings = 0; + int suppress_ea_default_bindings = 0; + + if (!info_keymap) + { + info_keymap = keymap_make_keymap (); + echo_area_keymap = keymap_make_keymap (); + } + + /* Bind the echo area insert routines. */ + for (i = 0; i < 256; i++) + if (isprint (i)) + echo_area_keymap[i].function = InfoCmd(ea_insert); + + /* Get user-defined keys and variables. */ + if (fetch_user_maps()) + { + if (user_info_keys_len && user_info_keys[0]) + suppress_info_default_bindings = 1; + if (user_ea_keys_len && user_ea_keys[0]) + suppress_ea_default_bindings = 1; + } + + /* Apply the default bindings, unless the user says to suppress + them. */ + if (vi_keys_p) + { + if (!suppress_info_default_bindings) + section_to_keymaps(info_keymap, default_vi_like_info_keys, + sizeof(default_vi_like_info_keys)); + if (!suppress_ea_default_bindings) + section_to_keymaps(echo_area_keymap, default_vi_like_ea_keys, + sizeof(default_vi_like_ea_keys)); + } + else + { + if (!suppress_info_default_bindings) + section_to_keymaps(info_keymap, default_emacs_like_info_keys, + sizeof(default_emacs_like_info_keys)); + if (!suppress_ea_default_bindings) + section_to_keymaps(echo_area_keymap, default_emacs_like_ea_keys, + sizeof(default_emacs_like_ea_keys)); + } + + /* If the user specified custom bindings, apply them on top of the + default ones. */ + if (user_info_keys_len) + section_to_keymaps(info_keymap, user_info_keys, user_info_keys_len); + + if (user_ea_keys_len) + section_to_keymaps(echo_area_keymap, user_ea_keys, user_ea_keys_len); + + if (user_vars_len) + section_to_vars(user_vars, user_vars_len); +} + +#endif /* defined(INFOKEY) */ +/* vim: set sw=2 cino={1s>2sn-s^-se-s: */ diff --git a/Build/source/utils/texinfo/info/infomap.h b/Build/source/utils/texinfo/info/infomap.h new file mode 100644 index 00000000000..31e4d8275f0 --- /dev/null +++ b/Build/source/utils/texinfo/info/infomap.h @@ -0,0 +1,82 @@ +/* infomap.h -- description of a keymap in Info and related functions. + $Id: infomap.h,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 2001, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFOMAP_H +#define INFOMAP_H + +#include "info.h" + +#define ESC '\033' +#define DEL '\177' +#define TAB '\011' +#define RET '\r' +#define LFD '\n' +#define SPC ' ' + +#define meta_character_threshold (DEL + 1) +#define control_character_threshold (SPC) + +#define meta_character_bit 0x80 +#define control_character_bit 0x40 + +#define Meta_p(c) (((c) > meta_character_threshold)) +#define Control_p(c) ((c) < control_character_threshold) + +#define Meta(c) ((c) | (meta_character_bit)) +#define UnMeta(c) ((c) & (~meta_character_bit)) +#define Control(c) ((toupper (c)) & (~control_character_bit)) +#define UnControl(c) (tolower ((c) | control_character_bit)) + +/* A keymap contains one entry for each key in the ASCII set. + Each entry consists of a type and a pointer. + FUNCTION is the address of a function to run, or the + address of a keymap to indirect through. + TYPE says which kind of thing FUNCTION is. */ +typedef struct keymap_entry +{ + char type; + InfoCommand *function; +} KEYMAP_ENTRY; + +typedef KEYMAP_ENTRY *Keymap; + +/* The values that TYPE can have in a keymap entry. */ +#define ISFUNC 0 +#define ISKMAP 1 + +extern Keymap info_keymap; +extern Keymap echo_area_keymap; + +/* Return a new keymap which has all the uppercase letters mapped to run + the function info_do_lowercase_version (). */ +extern Keymap keymap_make_keymap (void); + +/* Return a new keymap which is a copy of MAP. */ +extern Keymap keymap_copy_keymap (Keymap map, Keymap rootmap, + Keymap newroot); + +/* Free MAP and it's descendents. */ +extern void keymap_discard_keymap (Keymap map, Keymap rootmap); + +/* Initialize the info keymaps. */ +extern void initialize_info_keymaps (void); + +#endif /* not INFOMAP_H */ diff --git a/Build/source/utils/texinfo/info/key.h b/Build/source/utils/texinfo/info/key.h new file mode 100644 index 00000000000..35d2762211f --- /dev/null +++ b/Build/source/utils/texinfo/info/key.h @@ -0,0 +1,35 @@ +/* key.h -- Structure associating function names with numeric codes. */ + +/* This file is part of GNU Info, a program for reading online documentation + stored in Info format. + + Copyright (C) 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Andrew Bettison <andrewb@zip.com.au> */ + +#if !defined (KEY_H) +#define KEY_H + +typedef struct { + char *name; + unsigned char code; +} + FUNCTION_KEY; + +extern FUNCTION_KEY function_key_array[]; + +#endif /* !KEY_H */ diff --git a/Build/source/utils/texinfo/info/m-x.c b/Build/source/utils/texinfo/info/m-x.c new file mode 100644 index 00000000000..52365d1843e --- /dev/null +++ b/Build/source/utils/texinfo/info/m-x.c @@ -0,0 +1,214 @@ +/* m-x.c -- Meta-x minibuffer reader. + $Id: m-x.c,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2001, 2002, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "funs.h" + +/* **************************************************************** */ +/* */ +/* Reading Named Commands */ +/* */ +/* **************************************************************** */ + +/* Read the name of an Info function in the echo area and return the + name. A return value of NULL indicates that no function name could + be read. */ +char * +read_function_name (char *prompt, WINDOW *window) +{ + register int i; + char *line; + REFERENCE **array = (REFERENCE **)NULL; + int array_index = 0, array_slots = 0; + + /* Make an array of REFERENCE which actually contains the names of + the functions available in Info. */ + for (i = 0; function_doc_array[i].func; i++) + { + REFERENCE *entry; + + entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + entry->label = xstrdup (function_doc_array[i].func_name); + entry->nodename = (char *)NULL; + entry->filename = (char *)NULL; + + add_pointer_to_array + (entry, array_index, array, array_slots, 200, REFERENCE *); + } + + line = info_read_completing_in_echo_area (window, prompt, array); + + info_free_references (array); + + if (!echo_area_is_active) + window_clear_echo_area (); + + return (line); +} + +DECLARE_INFO_COMMAND (describe_command, + _("Read the name of an Info command and describe it")) +{ + char *line; + + line = read_function_name ((char *) _("Describe command: "), window); + + if (!line) + { + info_abort_key (active_window, count, key); + return; + } + + /* Describe the function named in "LINE". */ + if (*line) + { + InfoCommand *cmd = named_function (line); + + if (!cmd) + return; + + window_message_in_echo_area ("%s: %s.", + line, function_documentation (cmd)); + } + free (line); +} + +DECLARE_INFO_COMMAND (info_execute_command, + _("Read a command name in the echo area and execute it")) +{ + char *line; + char *keys; + char *prompt; + + prompt = (char *)xmalloc (20); + + keys = where_is (info_keymap, InfoCmd(info_execute_command)); + /* If the where_is () function thinks that this command doesn't exist, + there's something very wrong! */ + if (!keys) + abort(); + + if (info_explicit_arg || count != 1) + sprintf (prompt, "%d %s ", count, keys); + else + sprintf (prompt, "%s ", keys); + + /* Ask the completer to read a reference for us. */ + line = read_function_name (prompt, window); + + /* User aborted? */ + if (!line) + { + info_abort_key (active_window, count, key); + return; + } + + /* User accepted "default"? (There is none.) */ + if (!*line) + { + free (line); + return; + } + + /* User wants to execute a named command. Do it. */ + { + InfoCommand *command; + + if ((active_window != the_echo_area) && + (strncmp (line, "echo-area-", 10) == 0)) + { + free (line); + info_error ((char *) _("Cannot execute an `echo-area' command here."), + NULL, NULL); + return; + } + + command = named_function (line); + free (line); + + if (!command) + return; + + if (InfoFunction(command)) + (*InfoFunction(command)) (active_window, count, 0); + else + info_error ((char *) _("Undefined command: %s"), line, NULL); + } +} + +/* Okay, now that we have M-x, let the user set the screen height. */ +DECLARE_INFO_COMMAND (set_screen_height, + _("Set the height of the displayed window")) +{ + int new_height, old_height = screenheight; + + if (info_explicit_arg || count != 1) + new_height = count; + else + { + char prompt[80]; + char *line; + + new_height = screenheight; + + sprintf (prompt, _("Set screen height to (%d): "), new_height); + + line = info_read_in_echo_area (window, prompt); + + /* If the user aborted, do that now. */ + if (!line) + { + info_abort_key (active_window, count, 0); + return; + } + + /* Find out what the new height is supposed to be. */ + if (*line) + new_height = atoi (line); + + /* Clear the echo area if it isn't active. */ + if (!echo_area_is_active) + window_clear_echo_area (); + + free (line); + } + + terminal_clear_screen (); + display_clear_display (the_display); + screenheight = new_height; +#ifdef SET_SCREEN_SIZE_HELPER + SET_SCREEN_SIZE_HELPER; +#endif + if (screenheight == old_height) + { + /* Display dimensions didn't actually change, so + window_new_screen_size won't do anything, but we've + already cleared the display above. Undo the damage. */ + window_mark_chain (windows, W_UpdateWindow); + display_update_display (windows); + } + else + { + display_initialize_display (screenwidth, screenheight); + window_new_screen_size (screenwidth, screenheight); + } +} diff --git a/Build/source/utils/texinfo/info/makedoc.c b/Build/source/utils/texinfo/info/makedoc.c new file mode 100644 index 00000000000..41b1beb9679 --- /dev/null +++ b/Build/source/utils/texinfo/info/makedoc.c @@ -0,0 +1,578 @@ +/* makedoc.c -- make doc.c and funs.h from input files. + $Id: makedoc.c,v 1.4 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +/* This program grovels the contents of the source files passed as arguments + and writes out a file of function pointers and documentation strings, and + a header file which describes the contents. This only does the functions + declared with DECLARE_INFO_COMMAND. */ + +#include "info.h" +#include "infokey.h" + +static void fatal_file_error (char *filename); + +/* Name of the header file which receives the declarations of functions. */ +static char *funs_filename = "funs.h"; + +/* Name of the documentation to function pointer file. */ +static char *doc_filename = "doc.c"; +static char *key_filename = "key.c"; + +static char *doc_header[] = { + "/* doc.c -- Generated structure containing function names and doc strings.", + "", + " This file was automatically made from various source files with the", + " command `%s'. DO NOT EDIT THIS FILE, only `%s.c'.", + (char *)NULL +}; + +static char *doc_header_1[] = { + " An entry in the array FUNCTION_DOC_ARRAY is made for each command", + " found in the above files; each entry consists of a function pointer,", +#if defined (NAMED_FUNCTIONS) + " a string which is the user-visible name of the function,", +#endif /* NAMED_FUNCTIONS */ + " and a string which documents its purpose. */", + "", + "#include \"info.h\"", + "#include \"funs.h\"", + "", + "FUNCTION_DOC function_doc_array[] = {", + "", + (char *)NULL +}; + +static char *key_header[] = { + "/* key.c -- Generated array containing function names.", + "", + " This file was automatically made from various source files with the", + " command \"%s\". DO NOT EDIT THIS FILE, only \"%s.c\".", + "", + (char *)NULL +}; + +static char *key_header_1[] = { + " An entry in the array FUNCTION_KEY_ARRAY is made for each command", + " found in the above files; each entry consists of", + " a string which is the user-visible name of the function. */", + "", + "#include \"key.h\"", + "#include \"funs.h\"", + "", + "FUNCTION_KEY function_key_array[] = {", + "", + (char *)NULL +}; + +/* How to remember the locations of the functions found so that Emacs + can use the information in a tag table. */ +typedef struct { + char *name; /* Name of the tag. */ + int line; /* Line number at which it appears. */ + long char_offset; /* Character offset at which it appears. */ +} EMACS_TAG; + +typedef struct { + char *filename; /* Name of the file containing entries. */ + long entrylen; /* Total number of characters in tag block. */ + EMACS_TAG **entries; /* Entries found in FILENAME. */ + int entries_index; + int entries_slots; +} EMACS_TAG_BLOCK; + +EMACS_TAG_BLOCK **emacs_tags = (EMACS_TAG_BLOCK **)NULL; +int emacs_tags_index = 0; +int emacs_tags_slots = 0; + +#define DECLARATION_STRING "\nDECLARE_INFO_COMMAND" + +static void process_one_file (char *filename, FILE *doc_stream, + FILE *key_stream, FILE *funs_stream); +static void maybe_dump_tags (FILE *stream); +static FILE *must_fopen (char *filename, char *mode); +static void init_func_key (unsigned int val); +static unsigned int next_func_key (void); + +int +main (int argc, char **argv) +{ + register int i; + int tags_only = 0; + FILE *funs_stream, *doc_stream; + FILE *key_stream; + +#if STRIP_DOT_EXE + { + char *dot = strrchr (argv[0], '.'); + + if (dot && FILENAME_CMP (dot, ".exe") == 0) + *dot = 0; + } +#endif + + for (i = 1; i < argc; i++) + if (strcmp (argv[i], "-tags") == 0) + { + tags_only++; + break; + } + + if (tags_only) + { + funs_filename = NULL_DEVICE; + doc_filename = NULL_DEVICE; + key_filename = NULL_DEVICE; + } + + funs_stream = must_fopen (funs_filename, "w"); + doc_stream = must_fopen (doc_filename, "w"); + key_stream = must_fopen (key_filename, "w"); + + fprintf (funs_stream, + "/* %s -- Generated declarations for Info commands. */\n\n\ +#include \"info.h\"\n", + funs_filename); + + for (i = 0; doc_header[i]; i++) + { + fprintf (doc_stream, doc_header[i], argv[0], argv[0]); + fprintf (doc_stream, "\n"); + } + + fprintf (doc_stream, + _(" Source files groveled to make this file include:\n\n")); + + for (i = 0; key_header[i]; i++) + { + fprintf (key_stream, key_header[i], argv[0], argv[0]); + fprintf (key_stream, "\n"); + } + fprintf (key_stream, + _(" Source files groveled to make this file include:\n\n")); + + for (i = 1; i < argc; i++) + { + fprintf (doc_stream, "\t%s\n", argv[i]); + fprintf (key_stream, "\t%s\n", argv[i]); + } + + fprintf (doc_stream, "\n"); + for (i = 0; doc_header_1[i]; i++) + fprintf (doc_stream, "%s\n", doc_header_1[i]); + + fprintf (key_stream, "\n"); + for (i = 0; key_header_1[i]; i++) + fprintf (key_stream, "%s\n", key_header_1[i]); + + init_func_key(0); + + for (i = 1; i < argc; i++) + { + char *curfile; + curfile = argv[i]; + + if (*curfile == '-') + continue; + + fprintf (doc_stream, "/* Commands found in \"%s\". */\n", curfile); + fprintf (key_stream, "/* Commands found in \"%s\". */\n", curfile); + fprintf (funs_stream, "\n/* Functions declared in \"%s\". */\n", + curfile); + + process_one_file (curfile, doc_stream, key_stream, funs_stream); + } + +#if defined (INFOKEY) + +#if defined (NAMED_FUNCTIONS) + fprintf (doc_stream, + " { (VFunction *)NULL, (char *)NULL, (FUNCTION_KEYSEQ *)NULL, (char *)NULL }\n};\n"); +#else /* !NAMED_FUNCTIONS */ + fprintf (doc_stream, " { (VFunction *)NULL, (FUNCTION_KEYSEQ *)NULL, (char *)NULL }\n};\n"); +#endif /* !NAMED_FUNCTIONS */ + +#else /* !INFOKEY */ + +#if defined (NAMED_FUNCTIONS) + fprintf (doc_stream, + " { (VFunction *)NULL, (char *)NULL, (char *)NULL }\n};\n"); +#else /* !NAMED_FUNCTIONS */ + fprintf (doc_stream, " { (VFunction *)NULL, (char *)NULL }\n};\n"); +#endif /* !NAMED_FUNCTIONS */ + +#endif /* !INFOKEY */ + + fprintf (key_stream, " { (char *)NULL, 0 }\n};\n"); + fprintf (funs_stream, "\n#define A_NCOMMANDS %u\n", next_func_key()); + + fclose (funs_stream); + fclose (doc_stream); + fclose (key_stream); + + if (tags_only) + maybe_dump_tags (stdout); + return 0; +} + +/* Dumping out the contents of an Emacs tags table. */ +static void +maybe_dump_tags (FILE *stream) +{ + register int i; + + /* Emacs needs its TAGS file to be in Unix text format (i.e., only + newline at end of every line, no CR), so when we generate a + TAGS table, we must switch the output stream to binary mode. + (If the table is written to a terminal, this is obviously not needed.) */ + SET_BINARY (fileno (stream)); + + /* Print out the information for each block. */ + for (i = 0; i < emacs_tags_index; i++) + { + register int j; + register EMACS_TAG_BLOCK *block; + register EMACS_TAG *etag; + long block_len; + + block_len = 0; + block = emacs_tags[i]; + + /* Calculate the length of the dumped block first. */ + for (j = 0; j < block->entries_index; j++) + { + char digits[30]; + etag = block->entries[j]; + block_len += 3 + strlen (etag->name); + sprintf (digits, "%d,%ld", etag->line, etag->char_offset); + block_len += strlen (digits); + } + + /* Print out the defining line. */ + fprintf (stream, "\f\n%s,%ld\n", block->filename, block_len); + + /* Print out the individual tags. */ + for (j = 0; j < block->entries_index; j++) + { + etag = block->entries[j]; + + fprintf (stream, "%s,\177%d,%ld\n", + etag->name, etag->line, etag->char_offset); + } + } +} + +/* Keeping track of names, line numbers and character offsets of functions + found in source files. */ +static EMACS_TAG_BLOCK * +make_emacs_tag_block (char *filename) +{ + EMACS_TAG_BLOCK *block; + + block = (EMACS_TAG_BLOCK *)xmalloc (sizeof (EMACS_TAG_BLOCK)); + block->filename = xstrdup (filename); + block->entrylen = 0; + block->entries = (EMACS_TAG **)NULL; + block->entries_index = 0; + block->entries_slots = 0; + return (block); +} + +static void +add_tag_to_block (EMACS_TAG_BLOCK *block, + char *name, int line, long int char_offset) +{ + EMACS_TAG *tag; + + tag = (EMACS_TAG *)xmalloc (sizeof (EMACS_TAG)); + tag->name = name; + tag->line = line; + tag->char_offset = char_offset; + add_pointer_to_array (tag, block->entries_index, block->entries, + block->entries_slots, 50, EMACS_TAG *); +} + +/* Read the file represented by FILENAME into core, and search it for Info + function declarations. Output the declarations in various forms to the + DOC_STREAM, KEY_STREAM, and FUNS_STREAM. */ +static void +process_one_file (char *filename, FILE *doc_stream, + FILE *key_stream, FILE *funs_stream) +{ + int descriptor, decl_len; + char *buffer, *decl_str; + struct stat finfo; + long offset; + long file_size; + EMACS_TAG_BLOCK *block; + + if (stat (filename, &finfo) == -1) + fatal_file_error (filename); + + descriptor = open (filename, O_RDONLY, 0666); + + if (descriptor == -1) + fatal_file_error (filename); + + file_size = (long) finfo.st_size; + buffer = (char *)xmalloc (1 + file_size); + /* On some systems, the buffer will actually contain + less characters than the full file's size, because + the CR characters are removed from line endings. */ + file_size = read (descriptor, buffer, file_size); + close (descriptor); + + offset = 0; + decl_str = DECLARATION_STRING; + decl_len = strlen (decl_str); + + block = make_emacs_tag_block (filename); + + while (1) + { + long point = 0; + long line_start = 0; + int line_number = 0; + + char *func, *doc; +#if defined (INFOKEY) || defined (NAMED_FUNCTIONS) + char *func_name; +#endif /* INFOKEY || NAMED_FUNCTIONS */ + + for (; offset < (file_size - decl_len); offset++) + { + if (buffer[offset] == '\n') + { + line_number++; + line_start = offset + 1; + } + + if (strncmp (buffer + offset, decl_str, decl_len) == 0) + { + offset += decl_len; + point = offset; + break; + } + } + + if (!point) + break; + + /* Skip forward until we find the open paren. */ + while (point < file_size) + { + if (buffer[point] == '\n') + { + line_number++; + line_start = point + 1; + } + else if (buffer[point] == '(') + break; + + point++; + } + + while (point++ < file_size) + { + if (!whitespace_or_newline (buffer[point])) + break; + else if (buffer[point] == '\n') + { + line_number++; + line_start = point + 1; + } + } + + if (point >= file_size) + break; + + /* Now looking at name of function. Get it. */ + for (offset = point; buffer[offset] != ','; offset++); + func = (char *)xmalloc (1 + (offset - point)); + strncpy (func, buffer + point, offset - point); + func[offset - point] = '\0'; + + /* Remember this tag in the current block. */ + { + char *tag_name; + + tag_name = (char *)xmalloc (1 + (offset - line_start)); + strncpy (tag_name, buffer + line_start, offset - line_start); + tag_name[offset - line_start] = '\0'; + add_tag_to_block (block, tag_name, line_number, point); + } + +#if defined (INFOKEY) || defined (NAMED_FUNCTIONS) + /* Generate the user-visible function name from the function's name. */ + { + register int i; + char *name_start; + + name_start = func; + + if (strncmp (name_start, "info_", 5) == 0) + name_start += 5; + + func_name = xstrdup (name_start); + + /* Fix up "ea" commands. */ + if (strncmp (func_name, "ea_", 3) == 0) + { + char *temp_func_name; + + temp_func_name = (char *)xmalloc (10 + strlen (func_name)); + strcpy (temp_func_name, "echo_area_"); + strcat (temp_func_name, func_name + 3); + free (func_name); + func_name = temp_func_name; + } + + for (i = 0; func_name[i]; i++) + if (func_name[i] == '_') + func_name[i] = '-'; + } +#endif /* INFOKEY || NAMED_FUNCTIONS */ + + /* Find doc string. */ + point = offset + 1; + + while (point < file_size) + { + if (buffer[point] == '\n') + { + line_number++; + line_start = point + 1; + } + + if (buffer[point] == '"') + break; + else + point++; + } + + offset = point + 1; + + while (offset < file_size) + { + if (buffer[offset] == '\n') + { + line_number++; + line_start = offset + 1; + } + + if (buffer[offset] == '\\') + offset += 2; + else if (buffer[offset] == '"') + break; + else + offset++; + } + + offset++; + if (offset >= file_size) + break; + + doc = (char *)xmalloc (1 + (offset - point)); + strncpy (doc, buffer + point, offset - point); + doc[offset - point] = '\0'; + +#if defined (INFOKEY) + +#if defined (NAMED_FUNCTIONS) + fprintf (doc_stream, + " { (VFunction *)%s, \"%s\", (FUNCTION_KEYSEQ *)0, %s },\n", + func, func_name, doc); +#else /* !NAMED_FUNCTIONS */ + fprintf (doc_stream, + " { (VFunction *) %s, (FUNCTION_KEYSEQ *)0, %s },\n", func, doc); +#endif /* !NAMED_FUNCTIONS */ + + fprintf (key_stream, " { \"%s\", A_%s },\n", func_name, func); + +#else /* !INFOKEY */ + +#if defined (NAMED_FUNCTIONS) + fprintf (doc_stream, " { %s, \"%s\", %s },\n", func, func_name, doc); +#else /* !NAMED_FUNCTIONS */ + fprintf (doc_stream, " { %s, %s },\n", func, doc); +#endif /* !NAMED_FUNCTIONS */ + +#endif /* !INFOKEY */ + +#if defined (INFOKEY) || defined (NAMED_FUNCTIONS) + free (func_name); +#endif /* INFOKEY || NAMED_FUNCTIONS */ + +#if defined (INFOKEY) + fprintf (funs_stream, "#define A_%s %u\n", func, next_func_key()); +#endif /* INFOKEY */ + fprintf (funs_stream, + "extern void %s (WINDOW *window, int count, unsigned char key);\n", + func); + free (func); + free (doc); + } + free (buffer); + + /* If we created any tags, remember this file on our global list. Otherwise, + free the memory already allocated to it. */ + if (block->entries) + add_pointer_to_array (block, emacs_tags_index, emacs_tags, + emacs_tags_slots, 10, EMACS_TAG_BLOCK *); + else + { + free (block->filename); + free (block); + } +} + +static void +fatal_file_error (char *filename) +{ + fprintf (stderr, _("Couldn't manipulate the file %s.\n"), filename); + xexit (2); +} + +static FILE * +must_fopen (char *filename, char *mode) +{ + FILE *stream; + + stream = fopen (filename, mode); + if (!stream) + fatal_file_error (filename); + + return (stream); +} + +static unsigned int func_key; + +static void +init_func_key(unsigned int val) +{ + func_key = val; +} + +static unsigned int +next_func_key(void) +{ + return func_key++; +} diff --git a/Build/source/utils/texinfo/info/man.c b/Build/source/utils/texinfo/info/man.c new file mode 100644 index 00000000000..33fd6d4be0b --- /dev/null +++ b/Build/source/utils/texinfo/info/man.c @@ -0,0 +1,734 @@ +/* man.c: How to read and format man files. + $Id: man.c,v 1.4 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox Thu May 4 09:17:52 1995 (bfox@ai.mit.edu). */ + +#include "info.h" +#include <sys/ioctl.h> +#include "signals.h" +#if defined (HAVE_SYS_TIME_H) +#include <sys/time.h> +#endif +#if defined (HAVE_SYS_WAIT_H) +#include <sys/wait.h> +#endif + +#include "tilde.h" +#include "man.h" + +#if !defined (_POSIX_VERSION) +#define pid_t int +#endif + +#if defined (FD_SET) +# if defined (hpux) +# define fd_set_cast(x) (int *)(x) +# else +# define fd_set_cast(x) (fd_set *)(x) +# endif /* !hpux */ +#endif /* FD_SET */ + +#if STRIP_DOT_EXE +static char const * const exec_extensions[] = { + ".exe", ".com", ".bat", ".btm", ".sh", ".ksh", ".pl", ".sed", "", NULL +}; +#else +static char const * const exec_extensions[] = { "", NULL }; +#endif + +static char *read_from_fd (int fd); +static void clean_manpage (char *manpage); +static NODE *manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, + char *pagename); +static char *get_manpage_contents (char *pagename); + +NODE * +make_manpage_node (char *pagename) +{ + return (info_get_node (MANPAGE_FILE_BUFFER_NAME, pagename)); +} + +NODE * +get_manpage_node (FILE_BUFFER *file_buffer, char *pagename) +{ + NODE *node; + + node = manpage_node_of_file_buffer (file_buffer, pagename); + + if (!node) + { + char *page; + + page = get_manpage_contents (pagename); + + if (page) + { + char header[1024]; + long oldsize, newsize; + int hlen, plen; + char *old_contents = file_buffer->contents; + + sprintf (header, "\n\n%c\n%s %s, %s %s, %s (dir)\n\n", + INFO_COOKIE, + INFO_FILE_LABEL, file_buffer->filename, + INFO_NODE_LABEL, pagename, + INFO_UP_LABEL); + oldsize = file_buffer->filesize; + hlen = strlen (header); + plen = strlen (page); + newsize = (oldsize + hlen + plen); + file_buffer->contents = + (char *)xrealloc (file_buffer->contents, 1 + newsize); + memcpy (file_buffer->contents + oldsize, header, hlen); + memcpy (file_buffer->contents + oldsize + hlen, page, plen); + file_buffer->contents[newsize] = '\0'; + file_buffer->filesize = newsize; + file_buffer->finfo.st_size = newsize; + build_tags_and_nodes (file_buffer); + free (page); + /* We have just relocated file_buffer->contents from under + the feet of info_windows[] array. Therefore, all the + nodes on that list which are showing man pages have their + contents member pointing into the blue. Undo that harm. */ + if (old_contents && oldsize && old_contents != file_buffer->contents) + { + int iw; + INFO_WINDOW *info_win; + char *old_contents_end = old_contents + oldsize; + + for (iw = 0; (info_win = info_windows[iw]); iw++) + { + int in; + + for (in = 0; in < info_win->nodes_index; in++) + { + NODE *tmp_node = info_win->nodes[in]; + + /* It really only suffices to see that node->filename + is "*manpages*". But after several hours of + debugging this, would you blame me for being a bit + paranoid? */ + if (tmp_node && tmp_node->filename + && tmp_node->contents + && strcmp (tmp_node->filename, + MANPAGE_FILE_BUFFER_NAME) == 0 + && tmp_node->contents >= old_contents + && tmp_node->contents + tmp_node->nodelen + <= old_contents_end) + { + info_win->nodes[in] = + manpage_node_of_file_buffer (file_buffer, + tmp_node->nodename); + free (tmp_node->nodename); + free (tmp_node); + } + } + } + } + } + + node = manpage_node_of_file_buffer (file_buffer, pagename); + } + + return (node); +} + +FILE_BUFFER * +create_manpage_file_buffer (void) +{ + FILE_BUFFER *file_buffer = make_file_buffer (); + file_buffer->filename = xstrdup (MANPAGE_FILE_BUFFER_NAME); + file_buffer->fullpath = xstrdup (MANPAGE_FILE_BUFFER_NAME); + file_buffer->finfo.st_size = 0; + file_buffer->filesize = 0; + file_buffer->contents = (char *)NULL; + file_buffer->flags = (N_IsInternal | N_CannotGC | N_IsManPage); + + return (file_buffer); +} + +/* Scan the list of directories in PATH looking for FILENAME. If we find + one that is an executable file, return it as a new string. Otherwise, + return a NULL pointer. */ +static char * +executable_file_in_path (char *filename, char *path) +{ + struct stat finfo; + char *temp_dirname; + int statable, dirname_index; + + dirname_index = 0; + + while ((temp_dirname = extract_colon_unit (path, &dirname_index))) + { + char *temp; + char *temp_end; + int i; + + /* Expand a leading tilde if one is present. */ + if (*temp_dirname == '~') + { + char *expanded_dirname; + + expanded_dirname = tilde_expand_word (temp_dirname); + free (temp_dirname); + temp_dirname = expanded_dirname; + } + + temp = (char *)xmalloc (34 + strlen (temp_dirname) + strlen (filename)); + strcpy (temp, temp_dirname); + if (!IS_SLASH (temp[(strlen (temp)) - 1])) + strcat (temp, "/"); + strcat (temp, filename); + temp_end = temp + strlen (temp); + + free (temp_dirname); + + /* Look for FILENAME, possibly with any of the extensions + in EXEC_EXTENSIONS[]. */ + for (i = 0; exec_extensions[i]; i++) + { + if (exec_extensions[i][0]) + strcpy (temp_end, exec_extensions[i]); + statable = (stat (temp, &finfo) == 0); + + /* If we have found a regular executable file, then use it. */ + if ((statable) && (S_ISREG (finfo.st_mode)) && + (access (temp, X_OK) == 0)) + return (temp); + } + + free (temp); + } + return ((char *)NULL); +} + +/* Return the full pathname of the system man page formatter. */ +static char * +find_man_formatter (void) +{ + return (executable_file_in_path ("man", (char *)getenv ("PATH"))); +} + +static char *manpage_pagename = (char *)NULL; +static char *manpage_section = (char *)NULL; + +static void +get_page_and_section (char *pagename) +{ + register int i; + + if (manpage_pagename) + free (manpage_pagename); + + if (manpage_section) + free (manpage_section); + + manpage_pagename = (char *)NULL; + manpage_section = (char *)NULL; + + for (i = 0; pagename[i] != '\0' && pagename[i] != '('; i++); + + manpage_pagename = (char *)xmalloc (1 + i); + strncpy (manpage_pagename, pagename, i); + manpage_pagename[i] = '\0'; + + if (pagename[i] == '(') + { + int start; + + start = i + 1; + + for (i = start; pagename[i] != '\0' && pagename[i] != ')'; i++); + + manpage_section = (char *)xmalloc (1 + (i - start)); + strncpy (manpage_section, pagename + start, (i - start)); + manpage_section[i - start] = '\0'; + } +} + +#if PIPE_USE_FORK +static void +reap_children (int sig) +{ + wait (NULL); +} +#endif + +static char * +get_manpage_contents (char *pagename) +{ + static char *formatter_args[4] = { (char *)NULL }; + int pipes[2]; + pid_t child; + RETSIGTYPE (*sigsave) (int signum); + char *formatted_page = NULL; + int arg_index = 1; + + if (formatter_args[0] == (char *)NULL) + formatter_args[0] = find_man_formatter (); + + if (formatter_args[0] == (char *)NULL) + return ((char *)NULL); + + get_page_and_section (pagename); + + if (manpage_section != (char *)NULL) + formatter_args[arg_index++] = manpage_section; + + formatter_args[arg_index++] = manpage_pagename; + formatter_args[arg_index] = (char *)NULL; + + /* Open a pipe to this program, read the output, and save it away + in FORMATTED_PAGE. The reader end of the pipe is pipes[0]; the + writer end is pipes[1]. */ +#if PIPE_USE_FORK + pipe (pipes); + + sigsave = signal (SIGCHLD, reap_children); + + child = fork (); + if (child == -1) + return ((char *)NULL); + + if (child != 0) + { + /* In the parent, close the writing end of the pipe, and read from + the exec'd child. */ + close (pipes[1]); + formatted_page = read_from_fd (pipes[0]); + close (pipes[0]); + signal (SIGCHLD, sigsave); + } + else + { /* In the child, close the read end of the pipe, make the write end + of the pipe be stdout, and execute the man page formatter. */ + close (pipes[0]); + freopen (NULL_DEVICE, "w", stderr); + freopen (NULL_DEVICE, "r", stdin); + dup2 (pipes[1], fileno (stdout)); + + execv (formatter_args[0], formatter_args); + + /* If we get here, we couldn't exec, so close out the pipe and + exit. */ + close (pipes[1]); + xexit (0); + } +#else /* !PIPE_USE_FORK */ + /* Cannot fork/exec, but can popen/pclose. */ + { + FILE *fpipe; + char *cmdline = xmalloc (strlen (formatter_args[0]) + + strlen (manpage_pagename) + + (arg_index > 2 ? strlen (manpage_section) : 0) + + 3); + int save_stderr = dup (fileno (stderr)); + int fd_err = open (NULL_DEVICE, O_WRONLY, 0666); + + if (fd_err > 2) + dup2 (fd_err, fileno (stderr)); /* Don't print errors. */ + sprintf (cmdline, "%s %s %s", formatter_args[0], manpage_pagename, + arg_index > 2 ? manpage_section : ""); + fpipe = popen (cmdline, "r"); + free (cmdline); + if (fd_err > 2) + close (fd_err); + dup2 (save_stderr, fileno (stderr)); + if (fpipe == 0) + return ((char *)NULL); + formatted_page = read_from_fd (fileno (fpipe)); + if (pclose (fpipe) == -1) + { + if (formatted_page) + free (formatted_page); + return ((char *)NULL); + } + } +#endif /* !PIPE_USE_FORK */ + + /* If we have the page, then clean it up. */ + if (formatted_page) + clean_manpage (formatted_page); + + return (formatted_page); +} + +static void +clean_manpage (char *manpage) +{ + register int i, j; + int newline_count = 0; + char *newpage; + + newpage = (char *)xmalloc (1 + strlen (manpage)); + + for (i = 0, j = 0; (newpage[j] = manpage[i]); i++, j++) + { + if (manpage[i] == '\n') + newline_count++; + else + newline_count = 0; + + if (newline_count == 3) + { + j--; + newline_count--; + } + + /* A malformed man page could have a \b as its first character, + in which case decrementing j by 2 will cause us to write into + newpage[-1], smashing the hidden info stored there by malloc. */ + if (manpage[i] == '\b' || (manpage[i] == '\f' && j > 0)) + j -= 2; + else if (!raw_escapes_p) + { + /* Remove the ANSI escape sequences for color, boldface, + underlining, and italics, generated by some versions of + Groff. */ + if (manpage[i] == '\033' && manpage[i + 1] == '[' + && isdigit (manpage[i + 2])) + { + if (isdigit (manpage[i + 3]) && manpage[i + 4] == 'm') + { + i += 4; + j--; + } + else if (manpage[i + 3] == 'm') + { + i += 3; + j--; + } + /* Else do nothing: it's some unknown escape sequence, + so let's leave it alone. */ + } + } + } + + newpage[j++] = 0; + + strcpy (manpage, newpage); + free (newpage); +} + +static NODE * +manpage_node_of_file_buffer (FILE_BUFFER *file_buffer, char *pagename) +{ + NODE *node = (NODE *)NULL; + TAG *tag = (TAG *)NULL; + + if (file_buffer->contents) + { + register int i; + + for (i = 0; (tag = file_buffer->tags[i]); i++) + { + if (strcasecmp (pagename, tag->nodename) == 0) + break; + } + } + + if (tag) + { + node = (NODE *)xmalloc (sizeof (NODE)); + node->filename = file_buffer->filename; + node->nodename = xstrdup (tag->nodename); + node->contents = file_buffer->contents + tag->nodestart; + node->nodelen = tag->nodelen; + node->flags = 0; + node->display_pos = 0; + node->parent = (char *)NULL; + node->flags = (N_HasTagsTable | N_IsManPage); + node->contents += skip_node_separator (node->contents); + } + + return (node); +} + +static char * +read_from_fd (int fd) +{ + struct timeval timeout; + char *buffer = (char *)NULL; + int bsize = 0; + int bindex = 0; + int select_result; +#if defined (FD_SET) + fd_set read_fds; + + timeout.tv_sec = 15; + timeout.tv_usec = 0; + + FD_ZERO (&read_fds); + FD_SET (fd, &read_fds); + + select_result = select (fd + 1, fd_set_cast (&read_fds), 0, 0, &timeout); +#else /* !FD_SET */ + select_result = 1; +#endif /* !FD_SET */ + + switch (select_result) + { + case 0: + case -1: + break; + + default: + { + int amount_read; + int done = 0; + + while (!done) + { + while ((bindex + 1024) > (bsize)) + buffer = (char *)xrealloc (buffer, (bsize += 1024)); + buffer[bindex] = '\0'; + + amount_read = read (fd, buffer + bindex, 1023); + + if (amount_read < 0) + { + done = 1; + } + else + { + bindex += amount_read; + buffer[bindex] = '\0'; + if (amount_read == 0) + done = 1; + } + } + } + } + + if ((buffer != (char *)NULL) && (*buffer == '\0')) + { + free (buffer); + buffer = (char *)NULL; + } + + return (buffer); +} + +static char *reference_section_starters[] = +{ + "\nRELATED INFORMATION", + "\nRELATED\tINFORMATION", + "RELATED INFORMATION\n", + "RELATED\tINFORMATION\n", + "\nSEE ALSO", + "\nSEE\tALSO", + "SEE ALSO\n", + "SEE\tALSO\n", + (char *)NULL +}; + +static SEARCH_BINDING frs_binding; + +static SEARCH_BINDING * +find_reference_section (NODE *node) +{ + register int i; + long position = -1; + + frs_binding.buffer = node->contents; + frs_binding.start = 0; + frs_binding.end = node->nodelen; + frs_binding.flags = S_SkipDest; + + for (i = 0; reference_section_starters[i] != (char *)NULL; i++) + { + position = search_forward (reference_section_starters[i], &frs_binding); + if (position != -1) + break; + } + + if (position == -1) + return ((SEARCH_BINDING *)NULL); + + /* We found the start of the reference section, and point is right after + the string which starts it. The text from here to the next header + (or end of buffer) contains the only references in this manpage. */ + frs_binding.start = position; + + for (i = frs_binding.start; i < frs_binding.end - 2; i++) + { + if ((frs_binding.buffer[i] == '\n') && + (!whitespace (frs_binding.buffer[i + 1]))) + { + frs_binding.end = i; + break; + } + } + + return (&frs_binding); +} + +REFERENCE ** +xrefs_of_manpage (NODE *node) +{ + SEARCH_BINDING *reference_section; + REFERENCE **refs = (REFERENCE **)NULL; + int refs_index = 0; + int refs_slots = 0; + long position; + + reference_section = find_reference_section (node); + + if (reference_section == (SEARCH_BINDING *)NULL) + return ((REFERENCE **)NULL); + + /* Grovel the reference section building a list of references found there. + A reference is alphabetic characters followed by non-whitespace text + within parenthesis. */ + reference_section->flags = 0; + + while ((position = search_forward ("(", reference_section)) != -1) + { + register int start, end; + + for (start = position; start > reference_section->start; start--) + if (whitespace (reference_section->buffer[start])) + break; + + start++; + + for (end = position; end < reference_section->end; end++) + { + if (whitespace (reference_section->buffer[end])) + { + end = start; + break; + } + + if (reference_section->buffer[end] == ')') + { + end++; + break; + } + } + + if (end != start) + { + REFERENCE *entry; + int len = end - start; + + entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + entry->label = (char *)xmalloc (1 + len); + strncpy (entry->label, (reference_section->buffer) + start, len); + entry->label[len] = '\0'; + entry->filename = xstrdup (node->filename); + entry->nodename = xstrdup (entry->label); + entry->start = start; + entry->end = end; + + add_pointer_to_array + (entry, refs_index, refs, refs_slots, 10, REFERENCE *); + } + + reference_section->start = position + 1; + } + + return (refs); +} + +long +locate_manpage_xref (NODE *node, long int start, int dir) +{ + REFERENCE **refs; + long position = -1; + + refs = xrefs_of_manpage (node); + + if (refs) + { + register int i, count; + REFERENCE *entry; + + for (i = 0; refs[i]; i++); + count = i; + + if (dir > 0) + { + for (i = 0; (entry = refs[i]); i++) + if (entry->start > start) + { + position = entry->start; + break; + } + } + else + { + for (i = count - 1; i > -1; i--) + { + entry = refs[i]; + + if (entry->start < start) + { + position = entry->start; + break; + } + } + } + + info_free_references (refs); + } + return (position); +} + +/* This one was a little tricky. The binding buffer that is passed in has + a START and END value of 0 -- strlen (window-line-containing-point). + The BUFFER is a pointer to the start of that line. */ +REFERENCE ** +manpage_xrefs_in_binding (NODE *node, SEARCH_BINDING *binding) +{ + register int i; + REFERENCE **all_refs = xrefs_of_manpage (node); + REFERENCE **brefs = (REFERENCE **)NULL; + REFERENCE *entry; + int brefs_index = 0; + int brefs_slots = 0; + int start, end; + + if (!all_refs) + return ((REFERENCE **)NULL); + + start = binding->start + (binding->buffer - node->contents); + end = binding->end + (binding->buffer - node->contents); + + for (i = 0; (entry = all_refs[i]); i++) + { + if ((entry->start > start) && (entry->end < end)) + { + add_pointer_to_array + (entry, brefs_index, brefs, brefs_slots, 10, REFERENCE *); + } + else + { + maybe_free (entry->label); + maybe_free (entry->filename); + maybe_free (entry->nodename); + free (entry); + } + } + + free (all_refs); + return (brefs); +} diff --git a/Build/source/utils/texinfo/info/man.h b/Build/source/utils/texinfo/info/man.h new file mode 100644 index 00000000000..73ef1b6274a --- /dev/null +++ b/Build/source/utils/texinfo/info/man.h @@ -0,0 +1,39 @@ +/* man.h: Defines and external function declarations for man.c. + $Id: man.h,v 1.3 2004/04/11 17:56:46 karl Exp $ + + This file is part of GNU Info, a program for reading online documentation + stored in Info format. + + Copyright (C) 1993, 1997, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Author: Brian J. Fox (bfox@ai.mit.edu) Sat May 6 16:19:13 1995. */ + +#ifndef INFO_MAN_H +#define INFO_MAN_H + +#define MANPAGE_FILE_BUFFER_NAME "*manpages*" + +extern NODE *make_manpage_node (char *pagename); +extern NODE *get_manpage_node (FILE_BUFFER *file_buffer, + char *pagename); +extern FILE_BUFFER *create_manpage_file_buffer (void); +extern long locate_manpage_xref (NODE *node, long int start, int dir); +extern REFERENCE **xrefs_of_manpage (NODE *node); +extern REFERENCE **manpage_xrefs_in_binding (NODE *node, + SEARCH_BINDING *binding); + +#endif /* INFO_MAN_H */ diff --git a/Build/source/utils/texinfo/info/nodemenu.c b/Build/source/utils/texinfo/info/nodemenu.c new file mode 100644 index 00000000000..1090d08db02 --- /dev/null +++ b/Build/source/utils/texinfo/info/nodemenu.c @@ -0,0 +1,347 @@ +/* nodemenu.c -- produce a menu of all visited nodes. + $Id: nodemenu.c,v 1.5 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" + +NODE * get_visited_nodes (Function *filter_func); + +/* Return a line describing the format of a node information line. */ +static const char * +nodemenu_format_info (void) +{ + return (_("\n\ +* Menu:\n\ + (File)Node Lines Size Containing File\n\ + ---------- ----- ---- ---------------")); +} + +/* Produce a formatted line of information about NODE. Here is what we want + the output listing to look like: + +* Menu: + (File)Node Lines Size Containing File + ---------- ----- ---- --------------- +* (emacs)Buffers:: 48 2230 /usr/gnu/info/emacs/emacs-1 +* (autoconf)Writing configure.in:: 123 58789 /usr/gnu/info/autoconf/autoconf-1 +* (dir)Top:: 40 589 /usr/gnu/info/dir +*/ +static char * +format_node_info (NODE *node) +{ + register int i, len; + char *parent, *containing_file; + static char *line_buffer = (char *)NULL; + + if (!line_buffer) + line_buffer = (char *)xmalloc (1000); + + if (node->parent) + { + parent = filename_non_directory (node->parent); + if (!parent) + parent = node->parent; + } + else + parent = (char *)NULL; + + containing_file = node->filename; + + if (!parent && !*containing_file) + sprintf (line_buffer, "* %s::", node->nodename); + else + { + char *file = (char *)NULL; + + if (parent) + file = parent; + else + file = filename_non_directory (containing_file); + + if (!file) + file = containing_file; + + if (!*file) + file = "dir"; + + sprintf (line_buffer, "* (%s)%s::", file, node->nodename); + } + + len = pad_to (36, line_buffer); + + { + int lines = 1; + + for (i = 0; i < node->nodelen; i++) + if (node->contents[i] == '\n') + lines++; + + sprintf (line_buffer + len, "%d", lines); + } + + len = pad_to (44, line_buffer); + sprintf (line_buffer + len, "%ld", node->nodelen); + + if (node->filename && *(node->filename)) + { + len = pad_to (51, line_buffer); + strcpy (line_buffer + len, node->filename); + } + + return xstrdup (line_buffer); +} + +/* Little string comparison routine for qsort (). */ +static int +compare_strings (const void *entry1, const void *entry2) +{ + char **e1 = (char **) entry1; + char **e2 = (char **) entry2; + + return (strcasecmp (*e1, *e2)); +} + +/* The name of the nodemenu node. */ +static char *nodemenu_nodename = "*Node Menu*"; + +/* Produce an informative listing of all the visited nodes, and return it + in a node. If FILTER_FUNC is non-null, it is a function which filters + which nodes will appear in the listing. FILTER_FUNC takes an argument + of NODE, and returns non-zero if the node should appear in the listing. */ +NODE * +get_visited_nodes (Function *filter_func) +{ + register int i, iw_index; + INFO_WINDOW *info_win; + NODE *node; + char **lines = (char **)NULL; + int lines_index = 0, lines_slots = 0; + + if (!info_windows) + return ((NODE *)NULL); + + for (iw_index = 0; (info_win = info_windows[iw_index]); iw_index++) + { + for (i = 0; i < info_win->nodes_index; i++) + { + node = info_win->nodes[i]; + + /* We skip mentioning "*Node Menu*" nodes. */ + if (internal_info_node_p (node) && + (strcmp (node->nodename, nodemenu_nodename) == 0)) + continue; + + if (node && (!filter_func || (*filter_func) (node))) + { + char *line; + + line = format_node_info (node); + add_pointer_to_array + (line, lines_index, lines, lines_slots, 20, char *); + } + } + } + + /* Sort the array of information lines, if there are any. */ + if (lines) + { + register int j, newlen; + char **temp; + + qsort (lines, lines_index, sizeof (char *), compare_strings); + + /* Delete duplicates. */ + for (i = 0, newlen = 1; i < lines_index - 1; i++) + { + /* Use FILENAME_CMP here, since the most important piece + of info in each line is the file name of the node. */ + if (FILENAME_CMP (lines[i], lines[i + 1]) == 0) + { + free (lines[i]); + lines[i] = (char *)NULL; + } + else + newlen++; + } + + /* We have free ()'d and marked all of the duplicate slots. + Copy the live slots rather than pruning the dead slots. */ + temp = (char **)xmalloc ((1 + newlen) * sizeof (char *)); + for (i = 0, j = 0; i < lines_index; i++) + if (lines[i]) + temp[j++] = lines[i]; + + temp[j] = (char *)NULL; + free (lines); + lines = temp; + lines_index = newlen; + } + + initialize_message_buffer (); + + printf_to_message_buffer + ("%s", replace_in_documentation + ((char *) _("Here is the menu of nodes you have recently visited.\n\ +Select one from this menu, or use `\\[history-node]' in another window.\n"), 0), + NULL, NULL); + + printf_to_message_buffer ("%s\n", (char *) nodemenu_format_info (), + NULL, NULL); + + for (i = 0; (lines != (char **)NULL) && (i < lines_index); i++) + { + printf_to_message_buffer ("%s\n", lines[i], NULL, NULL); + free (lines[i]); + } + + if (lines) + free (lines); + + node = message_buffer_to_node (); + add_gcable_pointer (node->contents); + return (node); +} + +DECLARE_INFO_COMMAND (list_visited_nodes, + _("Make a window containing a menu of all of the currently visited nodes")) +{ + WINDOW *new; + NODE *node; + + set_remembered_pagetop_and_point (window); + + /* If a window is visible and showing the buffer list already, re-use it. */ + for (new = windows; new; new = new->next) + { + node = new->node; + + if (internal_info_node_p (node) && + (strcmp (node->nodename, nodemenu_nodename) == 0)) + break; + } + + /* If we couldn't find an existing window, try to use the next window + in the chain. */ + if (!new) + { + if (window->next) + new = window->next; + /* If there is more than one window, wrap around. */ + else if (window != windows) + new = windows; + } + + /* If we still don't have a window, make a new one to contain the list. */ + if (!new) + { + WINDOW *old_active; + + old_active = active_window; + active_window = window; + new = window_make_window ((NODE *)NULL); + active_window = old_active; + } + + /* If we couldn't make a new window, use this one. */ + if (!new) + new = window; + + /* Lines do not wrap in this window. */ + new->flags |= W_NoWrap; + node = get_visited_nodes ((Function *)NULL); + name_internal_node (node, nodemenu_nodename); + +#if 0 + /* Even if this is an internal node, we don't want the window + system to treat it specially. So we turn off the internalness + of it here. */ + /* Why? We depend on internal_info_node_p returning true, so we must + not remove the flag. Otherwise, the *Node Menu* nodes themselves + appear in the node menu. --Andreas Schwab + <schwab@issan.informatik.uni-dortmund.de>. */ + node->flags &= ~N_IsInternal; +#endif + + /* If this window is already showing a node menu, reuse the existing node + slot. */ + { + int remember_me = 1; + +#if defined (NOTDEF) + if (internal_info_node_p (new->node) && + (strcmp (new->node->nodename, nodemenu_nodename) == 0)) + remember_me = 0; +#endif /* NOTDEF */ + + window_set_node_of_window (new, node); + + if (remember_me) + remember_window_and_node (new, node); + } + + active_window = new; +} + +DECLARE_INFO_COMMAND (select_visited_node, + _("Select a node which has been previously visited in a visible window")) +{ + char *line; + NODE *node; + REFERENCE **menu; + + node = get_visited_nodes ((Function *)NULL); + + menu = info_menu_of_node (node); + free (node); + + line = + info_read_completing_in_echo_area (window, + (char *) _("Select visited node: "), menu); + + window = active_window; + + /* User aborts, just quit. */ + if (!line) + { + info_abort_key (window, 0, 0); + info_free_references (menu); + return; + } + + if (*line) + { + REFERENCE *entry; + + /* Find the selected label in the references. */ + entry = info_get_labeled_reference (line, menu); + + if (!entry) + info_error ((char *) _("The reference disappeared! (%s)."), line, NULL); + else + info_select_reference (window, entry); + } + + free (line); + info_free_references (menu); + + if (!info_error_was_printed) + window_clear_echo_area (); +} diff --git a/Build/source/utils/texinfo/info/nodes.c b/Build/source/utils/texinfo/info/nodes.c new file mode 100644 index 00000000000..a3381a83145 --- /dev/null +++ b/Build/source/utils/texinfo/info/nodes.c @@ -0,0 +1,1263 @@ +/* nodes.c -- how to get an Info file and node. + $Id: nodes.c,v 1.4 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" + +#include "nodes.h" +#include "search.h" +#include "filesys.h" +#include "info-utils.h" + +#if defined (HANDLE_MAN_PAGES) +# include "man.h" +#endif /* HANDLE_MAN_PAGES */ + +static void forget_info_file (char *filename); +static void remember_info_file (FILE_BUFFER *file_buffer); +static void free_file_buffer_tags (FILE_BUFFER *file_buffer); +static void free_info_tag (TAG *tag); +static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer, + SEARCH_BINDING *buffer_binding); +static void get_nodes_of_info_file (FILE_BUFFER *file_buffer); +static void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer, + SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding); +static void info_reload_file_buffer_contents (FILE_BUFFER *fb); +static char *adjust_nodestart (NODE *node, int min, int max); +static FILE_BUFFER *info_load_file_internal (char *filename, int get_tags); +static FILE_BUFFER *info_find_file_internal (char *filename, int get_tags); +static NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, + char *nodename); + +static long get_node_length (SEARCH_BINDING *binding); + +/* Magic number that RMS used to decide how much a tags table pointer could + be off by. I feel that it should be much smaller, like 4. */ +#define DEFAULT_INFO_FUDGE 1000 + +/* Passed to *_internal functions. INFO_GET_TAGS says to do what is + neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */ +#define INFO_NO_TAGS 0 +#define INFO_GET_TAGS 1 + +/* Global variables. */ + +/* When non-zero, this is a string describing the recent file error. */ +char *info_recent_file_error = NULL; + +/* The list of already loaded nodes. */ +FILE_BUFFER **info_loaded_files = NULL; + +/* The number of slots currently allocated to LOADED_FILES. */ +int info_loaded_files_slots = 0; + +/* Public functions for node manipulation. */ + +/* Used to build `dir' menu from `localdir' files found in INFOPATH. */ +extern void maybe_build_dir_node (char *dirname); + +/* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME. + If FILENAME is NULL, `dir' is used. + IF NODENAME is NULL, `Top' is used. + If the node cannot be found, return NULL. */ +NODE * +info_get_node (char *filename, char *nodename) +{ + NODE *node; + FILE_BUFFER *file_buffer = NULL; + + info_recent_file_error = NULL; + info_parse_node (nodename, DONT_SKIP_NEWLINES); + nodename = NULL; + + if (info_parsed_filename) + filename = info_parsed_filename; + + if (info_parsed_nodename) + nodename = info_parsed_nodename; + + /* If FILENAME is not specified, it defaults to "dir". */ + if (!filename) + filename = "dir"; + + /* If the file to be looked up is "dir", build the contents from all of + the "dir"s and "localdir"s found in INFOPATH. */ + if (is_dir_name (filename)) + maybe_build_dir_node (filename); + + /* Find the correct info file, or give up. */ + file_buffer = info_find_file (filename); + if (!file_buffer) + { + if (filesys_error_number) + info_recent_file_error = + filesys_error_string (filename, filesys_error_number); + return NULL; + } + + /* Look for the node. */ + node = info_get_node_of_file_buffer (nodename, file_buffer); + + /* If the node not found was "Top", try again with different case. */ + if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0)) + { + node = info_get_node_of_file_buffer ("Top", file_buffer); + if (!node) + node = info_get_node_of_file_buffer ("top", file_buffer); + if (!node) + node = info_get_node_of_file_buffer ("TOP", file_buffer); + } + + return node; +} + +/* Return a pointer to a NODE structure for the Info node NODENAME in + FILE_BUFFER. NODENAME can be passed as NULL, in which case the + nodename of "Top" is used. If the node cannot be found, return a + NULL pointer. */ +NODE * +info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer) +{ + NODE *node = NULL; + + /* If we are unable to find the file, we have to give up. There isn't + anything else we can do. */ + if (!file_buffer) + return NULL; + + /* If the file buffer was gc'ed, reload the contents now. */ + if (!file_buffer->contents) + info_reload_file_buffer_contents (file_buffer); + + /* If NODENAME is not specified, it defaults to "Top". */ + if (!nodename) + nodename = "Top"; + + /* If the name of the node that we wish to find is exactly "*", then the + node body is the contents of the entire file. Create and return such + a node. */ + if (strcmp (nodename, "*") == 0) + { + node = (NODE *)xmalloc (sizeof (NODE)); + node->filename = file_buffer->fullpath; + node->parent = NULL; + node->nodename = xstrdup ("*"); + node->contents = file_buffer->contents; + node->nodelen = file_buffer->filesize; + node->flags = 0; + node->display_pos = 0; + } +#if defined (HANDLE_MAN_PAGES) + /* If the file buffer is the magic one associated with manpages, call + the manpage node finding function instead. */ + else if (file_buffer->flags & N_IsManPage) + { + node = get_manpage_node (file_buffer, nodename); + } +#endif /* HANDLE_MAN_PAGES */ + /* If this is the "main" info file, it might contain a tags table. Search + the tags table for an entry which matches the node that we want. If + there is a tags table, get the file which contains this node, but don't + bother building a node list for it. */ + else if (file_buffer->tags) + { + node = info_node_of_file_buffer_tags (file_buffer, nodename); + } + + /* Return the results of our node search. */ + return node; +} + +/* Locate the file named by FILENAME, and return the information structure + describing this file. The file may appear in our list of loaded files + already, or it may not. If it does not already appear, find the file, + and add it to the list of loaded files. If the file cannot be found, + return a NULL FILE_BUFFER *. */ +FILE_BUFFER * +info_find_file (char *filename) +{ + return info_find_file_internal (filename, INFO_GET_TAGS); +} + +/* Load the info file FILENAME, remembering information about it in a + file buffer. */ +FILE_BUFFER * +info_load_file (char *filename) +{ + return info_load_file_internal (filename, INFO_GET_TAGS); +} + + +/* Private functions implementation. */ + +/* The workhorse for info_find_file (). Non-zero 2nd argument says to + try to build a tags table (or otherwise glean the nodes) for this + file once found. By default, we build the tags table, but when this + function is called by info_get_node () when we already have a valid + tags table describing the nodes, it is unnecessary. */ +static FILE_BUFFER * +info_find_file_internal (char *filename, int get_tags) +{ + int i; + FILE_BUFFER *file_buffer; + + /* First try to find the file in our list of already loaded files. */ + if (info_loaded_files) + { + for (i = 0; (file_buffer = info_loaded_files[i]); i++) + if ((FILENAME_CMP (filename, file_buffer->filename) == 0) + || (FILENAME_CMP (filename, file_buffer->fullpath) == 0) + || (!IS_ABSOLUTE (filename) + && FILENAME_CMP (filename, + filename_non_directory (file_buffer->fullpath)) + == 0)) + { + struct stat new_info, *old_info; + + /* This file is loaded. If the filename that we want is + specifically "dir", then simply return the file buffer. */ + if (is_dir_name (filename_non_directory (filename))) + return file_buffer; + +#if defined (HANDLE_MAN_PAGES) + /* Do the same for the magic MANPAGE file. */ + if (file_buffer->flags & N_IsManPage) + return file_buffer; +#endif /* HANDLE_MAN_PAGES */ + + /* The file appears to be already loaded, and is not "dir". Check + to see if it's changed since the last time it was loaded. */ + if (stat (file_buffer->fullpath, &new_info) == -1) + { + filesys_error_number = errno; + return NULL; + } + + old_info = &file_buffer->finfo; + + if (new_info.st_size != old_info->st_size + || new_info.st_mtime != old_info->st_mtime) + { + /* The file has changed. Forget that we ever had loaded it + in the first place. */ + forget_info_file (filename); + break; + } + else + { + /* The info file exists, and has not changed since the last + time it was loaded. If the caller requested a nodes list + for this file, and there isn't one here, build the nodes + for this file_buffer. In any case, return the file_buffer + object. */ + if (!file_buffer->contents) + { + /* The file's contents have been gc'ed. Reload it. */ + info_reload_file_buffer_contents (file_buffer); + if (!file_buffer->contents) + return NULL; + } + + if (get_tags && !file_buffer->tags) + build_tags_and_nodes (file_buffer); + + return file_buffer; + } + } + } + + /* The file wasn't loaded. Try to load it now. */ +#if defined (HANDLE_MAN_PAGES) + /* If the name of the file that we want is our special file buffer for + Unix manual pages, then create the file buffer, and return it now. */ + if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0) + file_buffer = create_manpage_file_buffer (); + else +#endif /* HANDLE_MAN_PAGES */ + file_buffer = info_load_file_internal (filename, get_tags); + + /* If the file was loaded, remember the name under which it was found. */ + if (file_buffer) + remember_info_file (file_buffer); + + return file_buffer; +} + +/* The workhorse function for info_load_file (). Non-zero second argument + says to build a list of tags (or nodes) for this file. This is the + default behaviour when info_load_file () is called, but it is not + necessary when loading a subfile for which we already have tags. */ +static FILE_BUFFER * +info_load_file_internal (char *filename, int get_tags) +{ + char *fullpath, *contents; + long filesize; + struct stat finfo; + int retcode, compressed; + FILE_BUFFER *file_buffer = NULL; + + /* Get the full pathname of this file, as known by the info system. + That is to say, search along INFOPATH and expand tildes, etc. */ + fullpath = info_find_fullpath (filename); + + /* Did we actually find the file? */ + retcode = stat (fullpath, &finfo); + + /* If the file referenced by the name returned from info_find_fullpath () + doesn't exist, then try again with the last part of the filename + appearing in lowercase. */ + /* This is probably not needed at all on those systems which define + FILENAME_CMP to be strcasecmp. But let's do it anyway, lest some + network redirector supports case sensitivity. */ + if (retcode < 0) + { + char *lowered_name; + char *tmp_basename; + + lowered_name = xstrdup (filename); + tmp_basename = filename_non_directory (lowered_name); + + while (*tmp_basename) + { + if (isupper (*tmp_basename)) + *tmp_basename = tolower (*tmp_basename); + + tmp_basename++; + } + + fullpath = info_find_fullpath (lowered_name); + + retcode = stat (fullpath, &finfo); + free (lowered_name); + } + + /* If the file wasn't found, give up, returning a NULL pointer. */ + if (retcode < 0) + { + filesys_error_number = errno; + return NULL; + } + + /* Otherwise, try to load the file. */ + contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed); + + if (!contents) + return NULL; + + /* The file was found, and can be read. Allocate FILE_BUFFER and fill + in the various members. */ + file_buffer = make_file_buffer (); + file_buffer->filename = xstrdup (filename); + file_buffer->fullpath = xstrdup (fullpath); + file_buffer->finfo = finfo; + file_buffer->filesize = filesize; + file_buffer->contents = contents; + if (compressed) + file_buffer->flags |= N_IsCompressed; + + /* If requested, build the tags and nodes for this file buffer. */ + if (get_tags) + build_tags_and_nodes (file_buffer); + + return file_buffer; +} + +/* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the + various slots. This can also be used to rebuild a tag or node table. */ +void +build_tags_and_nodes (FILE_BUFFER *file_buffer) +{ + SEARCH_BINDING binding; + long position; + + free_file_buffer_tags (file_buffer); + file_buffer->flags &= ~N_HasTagsTable; + + /* See if there is a tags table in this info file. */ + binding.buffer = file_buffer->contents; + binding.start = file_buffer->filesize; + binding.end = binding.start - 1000; + if (binding.end < 0) + binding.end = 0; + binding.flags = S_FoldCase; + + position = search_backward (TAGS_TABLE_END_LABEL, &binding); + + /* If there is a tag table, find the start of it, and grovel over it + extracting tag information. */ + if (position != -1) + while (1) + { + long tags_table_begin, tags_table_end; + + binding.end = position; + binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL); + if (binding.start < 0) + binding.start = 0; + + position = find_node_separator (&binding); + + /* For this test, (and all others here) failure indicates a bogus + tags table. Grovel the file. */ + if (position == -1) + break; + + /* Remember the end of the tags table. */ + binding.start = position; + tags_table_end = binding.start; + binding.end = 0; + + /* Locate the start of the tags table. */ + position = search_backward (TAGS_TABLE_BEG_LABEL, &binding); + + if (position == -1) + break; + + binding.end = position; + binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL); + position = find_node_separator (&binding); + + if (position == -1) + break; + + /* The file contains a valid tags table. Fill the FILE_BUFFER's + tags member. */ + file_buffer->flags |= N_HasTagsTable; + tags_table_begin = position; + + /* If this isn't an indirect tags table, just remember the nodes + described locally in this tags table. Note that binding.end + is pointing to just after the beginning label. */ + binding.start = binding.end; + binding.end = file_buffer->filesize; + + if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding)) + { + binding.start = tags_table_begin; + binding.end = tags_table_end; + get_nodes_of_tags_table (file_buffer, &binding); + return; + } + else + { + /* This is an indirect tags table. Build TAGS member. */ + SEARCH_BINDING indirect; + + indirect.start = tags_table_begin; + indirect.end = 0; + indirect.buffer = binding.buffer; + indirect.flags = S_FoldCase; + + position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect); + + if (position == -1) + { + /* This file is malformed. Give up. */ + return; + } + + indirect.start = position; + indirect.end = tags_table_begin; + binding.start = tags_table_begin; + binding.end = tags_table_end; + get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding); + return; + } + } + + /* This file doesn't contain any kind of tags table. Grovel the + file and build node entries for it. */ + get_nodes_of_info_file (file_buffer); +} + +/* Search through FILE_BUFFER->contents building an array of TAG *, + one entry per each node present in the file. Store the tags in + FILE_BUFFER->tags, and the number of allocated slots in + FILE_BUFFER->tags_slots. */ +static void +get_nodes_of_info_file (FILE_BUFFER *file_buffer) +{ + long nodestart; + int tags_index = 0; + SEARCH_BINDING binding; + + binding.buffer = file_buffer->contents; + binding.start = 0; + binding.end = file_buffer->filesize; + binding.flags = S_FoldCase; + + while ((nodestart = find_node_separator (&binding)) != -1) + { + int start, end; + char *nodeline; + TAG *entry; + int anchor = 0; + + /* Skip past the characters just found. */ + binding.start = nodestart; + binding.start += skip_node_separator (binding.buffer + binding.start); + + /* Move to the start of the line defining the node. */ + nodeline = binding.buffer + binding.start; + + /* Find "Node:" */ + start = string_in_line (INFO_NODE_LABEL, nodeline); + /* No Node:. Maybe it's a Ref:. */ + if (start == -1) + { + start = string_in_line (INFO_REF_LABEL, nodeline); + if (start != -1) + anchor = 1; + } + + /* If not there, this is not the start of a node. */ + if (start == -1) + continue; + + /* Find the start of the nodename. */ + start += skip_whitespace (nodeline + start); + + /* Find the end of the nodename. */ + end = start + + skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES); + + /* Okay, we have isolated the node name, and we know where the + node starts. Remember this information. */ + entry = xmalloc (sizeof (TAG)); + entry->nodename = xmalloc (1 + (end - start)); + strncpy (entry->nodename, nodeline + start, end - start); + entry->nodename[end - start] = 0; + entry->nodestart = nodestart; + if (anchor) + entry->nodelen = 0; + else + { + SEARCH_BINDING node_body; + + node_body.buffer = binding.buffer + binding.start; + node_body.start = 0; + node_body.end = binding.end - binding.start; + node_body.flags = S_FoldCase; + entry->nodelen = get_node_length (&node_body); + } + + entry->filename = file_buffer->fullpath; + + /* Add this tag to the array of tag structures in this FILE_BUFFER. */ + add_pointer_to_array (entry, tags_index, file_buffer->tags, + file_buffer->tags_slots, 100, TAG *); + } +} + +/* Return the length of the node which starts at BINDING. */ +static long +get_node_length (SEARCH_BINDING *binding) +{ + int i; + char *body; + + /* [A node] ends with either a ^_, a ^L, or end of file. */ + for (i = binding->start, body = binding->buffer; i < binding->end; i++) + { + if (body[i] == INFO_FF || body[i] == INFO_COOKIE) + break; + } + return i - binding->start; +} + +/* Build and save the array of nodes in FILE_BUFFER by searching through the + contents of BUFFER_BINDING for a tags table, and groveling the contents. */ +static void +get_nodes_of_tags_table (FILE_BUFFER *file_buffer, + SEARCH_BINDING *buffer_binding) +{ + int name_offset; + SEARCH_BINDING *tmp_search; + long position; + int tags_index = 0; + + tmp_search = copy_binding (buffer_binding); + + /* Find the start of the tags table. */ + position = find_tags_table (tmp_search); + + /* If none, we're all done. */ + if (position == -1) + return; + + /* Move to one character before the start of the actual table. */ + tmp_search->start = position; + tmp_search->start += skip_node_separator + (tmp_search->buffer + tmp_search->start); + tmp_search->start += strlen (TAGS_TABLE_BEG_LABEL); + tmp_search->start--; + + /* The tag table consists of lines containing node names and positions. + Do each line until we find one that doesn't contain a node name. */ + while ((position = search_forward ("\n", tmp_search)) != -1) + { + TAG *entry; + char *nodedef; + unsigned p; + int anchor = 0; + + /* Prepare to skip this line. */ + tmp_search->start = position; + tmp_search->start++; + + /* Skip past informative "(Indirect)" tags table line. */ + if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search)) + continue; + + /* Find the label preceding the node name. */ + name_offset = + string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start); + + /* If no node label, maybe it's an anchor. */ + if (name_offset == -1) + { + name_offset = string_in_line (INFO_REF_LABEL, + tmp_search->buffer + tmp_search->start); + if (name_offset != -1) + anchor = 1; + } + + /* If not there, not a defining line, so we must be out of the + tags table. */ + if (name_offset == -1) + break; + + entry = xmalloc (sizeof (TAG)); + + /* Find the beginning of the node definition. */ + tmp_search->start += name_offset; + nodedef = tmp_search->buffer + tmp_search->start; + nodedef += skip_whitespace (nodedef); + + /* Move past the node's name in this tag to the TAGSEP character. */ + for (p = 0; nodedef[p] && nodedef[p] != INFO_TAGSEP; p++) + ; + if (nodedef[p] != INFO_TAGSEP) + continue; + + entry->nodename = xmalloc (p + 1); + strncpy (entry->nodename, nodedef, p); + entry->nodename[p] = 0; + p++; + entry->nodestart = atol (nodedef + p); + + /* If a node, we don't know the length yet, but if it's an + anchor, the length is 0. */ + entry->nodelen = anchor ? 0 : -1; + + /* The filename of this node is currently known as the same as the + name of this file. */ + entry->filename = file_buffer->fullpath; + + /* Add this node structure to the array of node structures in this + FILE_BUFFER. */ + add_pointer_to_array (entry, tags_index, file_buffer->tags, + file_buffer->tags_slots, 100, TAG *); + } + free (tmp_search); +} + +/* A structure used only in `get_tags_of_indirect_tags_table' to hold onto + an intermediate value. */ +typedef struct { + char *filename; + long first_byte; +} SUBFILE; + +/* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the + subfiles of every node which appears in TAGS_BINDING. The 2nd argument is + a binding surrounding the indirect files list. */ +static void +get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer, + SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding) +{ + int i; + SUBFILE **subfiles = NULL; + int subfiles_index = 0, subfiles_slots = 0; + TAG *entry; + + /* First get the list of tags from the tags table. Then lookup the + associated file in the indirect list for each tag, and update it. */ + get_nodes_of_tags_table (file_buffer, tags_binding); + + /* We have the list of tags in file_buffer->tags. Get the list of + subfiles from the indirect table. */ + { + char *start, *end, *line; + SUBFILE *subfile; + + start = indirect_binding->buffer + indirect_binding->start; + end = indirect_binding->buffer + indirect_binding->end; + line = start; + + while (line < end) + { + int colon; + + colon = string_in_line (":", line); + + if (colon == -1) + break; + + subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE)); + subfile->filename = (char *)xmalloc (colon); + strncpy (subfile->filename, line, colon - 1); + subfile->filename[colon - 1] = 0; + subfile->first_byte = (long) atol (line + colon); + + add_pointer_to_array + (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *); + + while (*line++ != '\n'); + } + } + + /* If we have successfully built the indirect files table, then + merge the information in the two tables. */ + if (!subfiles) + { + free_file_buffer_tags (file_buffer); + return; + } + else + { + int tags_index; + long header_length; + SEARCH_BINDING binding; + + /* Find the length of the header of the file containing the indirect + tags table. This header appears at the start of every file. We + want the absolute position of each node within each subfile, so + we subtract the start of the containing subfile from the logical + position of the node, and then add the length of the header in. */ + binding.buffer = file_buffer->contents; + binding.start = 0; + binding.end = file_buffer->filesize; + binding.flags = S_FoldCase; + + header_length = find_node_separator (&binding); + if (header_length == -1) + header_length = 0; + + /* Build the file buffer's list of subfiles. */ + { + char *containing_dir = xstrdup (file_buffer->fullpath); + char *temp = filename_non_directory (containing_dir); + int len_containing_dir; + + if (temp > containing_dir) + { + if (HAVE_DRIVE (file_buffer->fullpath) && + temp == containing_dir + 2) + { + /* Avoid converting "d:foo" into "d:/foo" below. */ + *temp = '.'; + temp += 2; + } + temp[-1] = 0; + } + + len_containing_dir = strlen (containing_dir); + + for (i = 0; subfiles[i]; i++); + + file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *)); + + for (i = 0; subfiles[i]; i++) + { + char *fullpath; + + fullpath = (char *) xmalloc + (2 + strlen (subfiles[i]->filename) + len_containing_dir); + + sprintf (fullpath, "%s/%s", + containing_dir, subfiles[i]->filename); + + file_buffer->subfiles[i] = fullpath; + } + file_buffer->subfiles[i] = NULL; + free (containing_dir); + } + + /* For each node in the file's tags table, remember the starting + position. */ + for (tags_index = 0; (entry = file_buffer->tags[tags_index]); + tags_index++) + { + for (i = 0; + subfiles[i] && entry->nodestart >= subfiles[i]->first_byte; + i++); + + /* If the Info file containing the indirect tags table is + malformed, then give up. */ + if (!i) + { + /* The Info file containing the indirect tags table is + malformed. Give up. */ + for (i = 0; subfiles[i]; i++) + { + free (subfiles[i]->filename); + free (subfiles[i]); + free (file_buffer->subfiles[i]); + } + file_buffer->subfiles = NULL; + free_file_buffer_tags (file_buffer); + return; + } + + /* SUBFILES[i] is the index of the first subfile whose logical + first byte is greater than the logical offset of this node's + starting position. This means that the subfile directly + preceding this one is the one containing the node. */ + + entry->filename = file_buffer->subfiles[i - 1]; + entry->nodestart -= subfiles[i - 1]->first_byte; + entry->nodestart += header_length; + } + + /* We have successfully built the tags table. Remember that it + was indirect. */ + file_buffer->flags |= N_TagsIndirect; + } + + /* Free the structures assigned to SUBFILES. Free the names as well + as the structures themselves, then finally, the array. */ + for (i = 0; subfiles[i]; i++) + { + free (subfiles[i]->filename); + free (subfiles[i]); + } + free (subfiles); +} + + +/* Return the node that contains TAG in FILE_BUFFER, else + (pathologically) NULL. Called from info_node_of_file_buffer_tags. */ +static NODE * +find_node_of_anchor (FILE_BUFFER *file_buffer, TAG *tag) +{ + int anchor_pos, node_pos; + TAG *node_tag; + NODE *node; + + /* Look through the tag list for the anchor. */ + for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++) + { + TAG *t = file_buffer->tags[anchor_pos]; + if (t->nodestart == tag->nodestart) + break; + } + + /* Should not happen, because we should always find the anchor. */ + if (!file_buffer->tags[anchor_pos]) + return NULL; + + /* We've found the anchor. Look backwards in the tag table for the + preceding node (we're assuming the tags are given in order), + skipping over any preceding anchors. */ + for (node_pos = anchor_pos - 1; + node_pos >= 0 && file_buffer->tags[node_pos]->nodelen == 0; + node_pos--) + ; + + /* An info file with an anchor before any nodes is pathological, but + it's possible, so don't crash. */ + if (node_pos < 0) + return NULL; + + /* We have the tag for the node that contained the anchor tag. */ + node_tag = file_buffer->tags[node_pos]; + + /* Look up the node name in the tag table to get the actual node. + This is a recursive call, but it can't recurse again, because we + call it with a real node. */ + node = info_node_of_file_buffer_tags (file_buffer, node_tag->nodename); + + /* Start displaying the node at the anchor position. */ + if (node) + { /* The nodestart for real nodes is three characters before the `F' + in the `File:' line (a newline, the CTRL-_, and another + newline). The nodestart for anchors is the actual position. + But we offset by only 2, rather than 3, because if an anchor is + at the beginning of a paragraph, it's nicer for it to end up on + the beginning of the first line of the paragraph rather than + the blank line before it. (makeinfo has no way of knowing that + a paragraph is going to start, so we can't fix it there.) */ + node->display_pos = file_buffer->tags[anchor_pos]->nodestart + - (node_tag->nodestart + 2); + + /* Otherwise an anchor at the end of a node ends up displaying at + the end of the last line of the node (way over on the right of + the screen), which looks wrong. */ + if (node->display_pos >= (unsigned long) node->nodelen) + node->display_pos = node->nodelen - 1; + + /* Don't search in the node for the xref text, it's not there. */ + node->flags |= N_FromAnchor; + } + + return node; +} + + +/* Return the node from FILE_BUFFER which matches NODENAME by searching + the tags table in FILE_BUFFER, or NULL. */ +static NODE * +info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename) +{ + TAG *tag; + int i; + + /* If no tags at all (possibly a misformatted info file), quit. */ + if (!file_buffer->tags) { + return NULL; + } + + for (i = 0; (tag = file_buffer->tags[i]); i++) + if (strcmp (nodename, tag->nodename) == 0) + { + FILE_BUFFER *subfile = info_find_file_internal (tag->filename, + INFO_NO_TAGS); + if (!subfile) + return NULL; + + if (!subfile->contents) + { + info_reload_file_buffer_contents (subfile); + if (!subfile->contents) + return NULL; + } + + /* If we were able to find this file and load it, then return + the node within it. */ + { + NODE *node = xmalloc (sizeof (NODE)); + node->filename = subfile->fullpath; + node->parent = NULL; + node->nodename = tag->nodename; + node->contents = subfile->contents + tag->nodestart; + node->display_pos = 0; + node->flags = 0; + + if (file_buffer->flags & N_HasTagsTable) + { + node->flags |= N_HasTagsTable; + + if (file_buffer->flags & N_TagsIndirect) + { + node->flags |= N_TagsIndirect; + node->parent = file_buffer->fullpath; + } + } + + if (subfile->flags & N_IsCompressed) + node->flags |= N_IsCompressed; + + /* If TAG->nodelen hasn't been calculated yet, then we aren't + in a position to trust the entry pointer. Adjust things so + that ENTRY->nodestart gets the exact address of the start of + the node separator which starts this node, and NODE->contents + gets the address of the line defining this node. If we cannot + do that, the node isn't really here. */ + if (tag->nodelen == -1) + { + int min, max; + char *node_sep; + SEARCH_BINDING node_body; + char *buff_end; + + min = max = DEFAULT_INFO_FUDGE; + + if (tag->nodestart < DEFAULT_INFO_FUDGE) + min = tag->nodestart; + + if (DEFAULT_INFO_FUDGE > + (subfile->filesize - tag->nodestart)) + max = subfile->filesize - tag->nodestart; + + /* NODE_SEP gets the address of the separator which defines + this node, or NULL if the node wasn't found. + NODE->contents is side-effected to point to right after + the separator. */ + node_sep = adjust_nodestart (node, min, max); + if (node_sep == NULL) + { + free (node); + return NULL; + } + /* Readjust tag->nodestart. */ + tag->nodestart = node_sep - subfile->contents; + + /* Calculate the length of the current node. */ + buff_end = subfile->contents + subfile->filesize; + + node_body.buffer = node->contents; + node_body.start = 0; + node_body.end = buff_end - node_body.buffer; + node_body.flags = 0; + tag->nodelen = get_node_length (&node_body); + node->nodelen = tag->nodelen; + } + + else if (tag->nodelen == 0) /* anchor, return containing node */ + { + free (node); + node = find_node_of_anchor (file_buffer, tag); + } + + else + { + /* Since we know the length of this node, we have already + adjusted tag->nodestart to point to the exact start of + it. Simply skip the node separator. */ + node->contents += skip_node_separator (node->contents); + node->nodelen = tag->nodelen; + } + + return node; + } + } + + /* There was a tag table for this file, and the node wasn't found. + Return NULL, since this file doesn't contain the desired node. */ + return NULL; +} + +/* Managing file_buffers, nodes, and tags. */ + +/* Create a new, empty file buffer. */ +FILE_BUFFER * +make_file_buffer (void) +{ + FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER)); + + file_buffer->filename = file_buffer->fullpath = NULL; + file_buffer->contents = NULL; + file_buffer->tags = NULL; + file_buffer->subfiles = NULL; + file_buffer->tags_slots = 0; + file_buffer->flags = 0; + + return file_buffer; +} + +/* Add FILE_BUFFER to our list of already loaded info files. */ +static void +remember_info_file (FILE_BUFFER *file_buffer) +{ + int i; + + for (i = 0; info_loaded_files && info_loaded_files[i]; i++) + ; + + add_pointer_to_array (file_buffer, i, info_loaded_files, + info_loaded_files_slots, 10, FILE_BUFFER *); +} + +/* Forget the contents, tags table, nodes list, and names of FILENAME. */ +static void +forget_info_file (char *filename) +{ + int i; + FILE_BUFFER *file_buffer; + + if (!info_loaded_files) + return; + + for (i = 0; (file_buffer = info_loaded_files[i]); i++) + if (FILENAME_CMP (filename, file_buffer->filename) == 0 + || FILENAME_CMP (filename, file_buffer->fullpath) == 0) + { + free (file_buffer->filename); + free (file_buffer->fullpath); + + if (file_buffer->contents) + free (file_buffer->contents); + + /* free_file_buffer_tags () also kills the subfiles list, since + the subfiles list is only of use in conjunction with tags. */ + free_file_buffer_tags (file_buffer); + + /* Move rest of list down. */ + while (info_loaded_files[i + 1]) + { + info_loaded_files[i] = info_loaded_files[i + 1]; + i++; + } + info_loaded_files[i] = 0; + + break; + } +} + +/* Free the tags (if any) associated with FILE_BUFFER. */ +static void +free_file_buffer_tags (FILE_BUFFER *file_buffer) +{ + int i; + + if (file_buffer->tags) + { + TAG *tag; + + for (i = 0; (tag = file_buffer->tags[i]); i++) + free_info_tag (tag); + + free (file_buffer->tags); + file_buffer->tags = NULL; + file_buffer->tags_slots = 0; + } + + if (file_buffer->subfiles) + { + for (i = 0; file_buffer->subfiles[i]; i++) + free (file_buffer->subfiles[i]); + + free (file_buffer->subfiles); + file_buffer->subfiles = NULL; + } +} + +/* Free the data associated with TAG, as well as TAG itself. */ +static void +free_info_tag (TAG *tag) +{ + free (tag->nodename); + + /* We don't free tag->filename, because that filename is part of the + subfiles list for the containing FILE_BUFFER. free_info_tags () + will free the subfiles when it is appropriate. */ + + free (tag); +} + +/* Load the contents of FILE_BUFFER->contents. This function is called + when a file buffer was loaded, and then in order to conserve memory, the + file buffer's contents were freed and the pointer was zero'ed. Note that + the file was already loaded at least once successfully, so the tags and/or + nodes members are still correctly filled. */ +static void +info_reload_file_buffer_contents (FILE_BUFFER *fb) +{ + int is_compressed; + +#if defined (HANDLE_MAN_PAGES) + /* If this is the magic manpage node, don't try to reload, just give up. */ + if (fb->flags & N_IsManPage) + return; +#endif + + fb->flags &= ~N_IsCompressed; + + /* Let the filesystem do all the work for us. */ + fb->contents = + filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo), + &is_compressed); + if (is_compressed) + fb->flags |= N_IsCompressed; +} + +/* Return the actual starting memory location of NODE, side-effecting + NODE->contents. MIN and MAX are bounds for a search if one is necessary. + Because of the way that tags are implemented, the physical nodestart may + not actually be where the tag says it is. If that is the case, but the + node was found anyway, set N_UpdateTags in NODE->flags. If the node is + found, return non-zero. NODE->contents is returned positioned right after + the node separator that precedes this node, while the return value is + position directly on the separator that precedes this node. If the node + could not be found, return a NULL pointer. */ +static char * +adjust_nodestart (NODE *node, int min, int max) +{ + long position; + SEARCH_BINDING node_body; + + /* Define the node body. */ + node_body.buffer = node->contents; + node_body.start = 0; + node_body.end = max; + node_body.flags = 0; + + /* Try the optimal case first. Who knows? This file may actually be + formatted (mostly) correctly. */ + if (node_body.buffer[0] != INFO_COOKIE && min > 2) + node_body.buffer -= 3; + + position = find_node_separator (&node_body); + + /* If we found a node start, then check it out. */ + if (position != -1) + { + int sep_len; + + sep_len = skip_node_separator (node->contents); + + /* If we managed to skip a node separator, then check for this node + being the right one. */ + if (sep_len != 0) + { + char *nodedef, *nodestart; + int offset; + + nodestart = node_body.buffer + position + sep_len; + nodedef = nodestart; + offset = string_in_line (INFO_NODE_LABEL, nodedef); + + if (offset != -1) + { + nodedef += offset; + nodedef += skip_whitespace (nodedef); + offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES); + if (((unsigned int) offset == strlen (node->nodename)) && + (strncmp (node->nodename, nodedef, offset) == 0)) + { + node->contents = nodestart; + return node_body.buffer + position; + } + } + } + } + + /* Oh well, I guess we have to try to find it in a larger area. */ + node_body.buffer = node->contents - min; + node_body.start = 0; + node_body.end = min + max; + node_body.flags = 0; + + position = find_node_in_binding (node->nodename, &node_body); + + /* If the node couldn't be found, we lose big. */ + if (position == -1) + return NULL; + + /* Otherwise, the node was found, but the tags table could need updating + (if we used a tag to get here, that is). Set the flag in NODE->flags. */ + node->contents = node_body.buffer + position; + node->contents += skip_node_separator (node->contents); + if (node->flags & N_HasTagsTable) + node->flags |= N_UpdateTags; + return node_body.buffer + position; +} diff --git a/Build/source/utils/texinfo/info/nodes.h b/Build/source/utils/texinfo/info/nodes.h new file mode 100644 index 00000000000..e2fbabea172 --- /dev/null +++ b/Build/source/utils/texinfo/info/nodes.h @@ -0,0 +1,156 @@ +/* nodes.h -- How we represent nodes internally. + $Id: nodes.h,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef NODES_H +#define NODES_H + +#include "info.h" + +/* User code interface. */ + +/* Callers generally only want the node itself. This structure is used + to pass node information around. None of the information in this + structure should ever be directly freed. The structure itself can + be passed to free (). Note that NODE->parent is non-null if this + node's file is a subfile. In that case, NODE->parent is the logical + name of the file containing this node. Both names are given as full + paths, so you might have: node->filename = "/usr/gnu/info/emacs-1", + with node->parent = "/usr/gnu/info/emacs". */ +typedef struct { + char *filename; /* The physical file containing this node. */ + char *parent; /* Non-null is the logical file name. */ + char *nodename; /* The name of this node. */ + char *contents; /* Characters appearing in this node. */ + long nodelen; /* The length of the CONTENTS member. */ + unsigned long display_pos; /* Where to display at, if nonzero. */ + int flags; /* See immediately below. */ +} NODE; + +/* Defines that can appear in NODE->flags. All informative. */ +#define N_HasTagsTable 0x01 /* This node was found through a tags table. */ +#define N_TagsIndirect 0x02 /* The tags table was an indirect one. */ +#define N_UpdateTags 0x04 /* The tags table is out of date. */ +#define N_IsCompressed 0x08 /* The file is compressed on disk. */ +#define N_IsInternal 0x10 /* This node was made by Info. */ +#define N_CannotGC 0x20 /* File buffer cannot be gc'ed. */ +#define N_IsManPage 0x40 /* This node is a manpage. */ +#define N_FromAnchor 0x80 /* Synthesized for an anchor reference. */ + +/* Internal data structures. */ + +/* String constants. */ +#define INFO_FILE_LABEL "File:" +#define INFO_REF_LABEL "Ref:" +#define INFO_NODE_LABEL "Node:" +#define INFO_PREV_LABEL "Prev:" +#define INFO_ALTPREV_LABEL "Previous:" +#define INFO_NEXT_LABEL "Next:" +#define INFO_UP_LABEL "Up:" +#define INFO_MENU_LABEL "\n* Menu:" +#define INFO_MENU_ENTRY_LABEL "\n* " +#define INFO_XREF_LABEL "*Note" +#define TAGS_TABLE_END_LABEL "\nEnd Tag Table" +#define TAGS_TABLE_BEG_LABEL "Tag Table:\n" +#define INDIRECT_TAGS_TABLE_LABEL "Indirect:\n" +#define TAGS_TABLE_IS_INDIRECT_LABEL "(Indirect)" + +/* Character constants. */ +#define INFO_COOKIE '\037' +#define INFO_FF '\014' +#define INFO_TAGSEP '\177' + +/* For each logical file that we have loaded, we keep a list of the names + of the nodes that are found in that file. A pointer to a node in an + info file is called a "tag". For split files, the tag pointer is + "indirect"; that is, the pointer also contains the name of the split + file where the node can be found. For non-split files, the filename + member in the structure below simply contains the name of the current + file. The following structure describes a single node within a file. */ +typedef struct { + char *filename; /* The file where this node can be found. */ + char *nodename; /* The node pointed to by this tag. */ + long nodestart; /* The offset of the start of this node. */ + long nodelen; /* The length of this node. */ +} TAG; + +/* The following structure is used to remember information about the contents + of Info files that we have loaded at least once before. The FINFO member + is present so that we can reload the file if it has been modified since + last being loaded. All of the arrays appearing within this structure + are NULL terminated, and each array which can change size has a + corresponding SLOTS member which says how many slots have been allocated + (with malloc ()) for this array. */ +typedef struct { + char *filename; /* The filename used to find this file. */ + char *fullpath; /* The full pathname of this info file. */ + struct stat finfo; /* Information about this file. */ + char *contents; /* The contents of this particular file. */ + long filesize; /* The number of bytes this file expands to. */ + char **subfiles; /* If non-null, the list of subfiles. */ + TAG **tags; /* If non-null, the indirect tags table. */ + int tags_slots; /* Number of slots allocated for TAGS. */ + int flags; /* Various flags. Mimics of N_* flags. */ +} FILE_BUFFER; + +/* Externally visible functions. */ + +/* Array of FILE_BUFFER * which represents the currently loaded info files. */ +extern FILE_BUFFER **info_loaded_files; + +/* The number of slots currently allocated to INFO_LOADED_FILES. */ +extern int info_loaded_files_slots; + +/* Locate the file named by FILENAME, and return the information structure + describing this file. The file may appear in our list of loaded files + already, or it may not. If it does not already appear, find the file, + and add it to the list of loaded files. If the file cannot be found, + return a NULL FILE_BUFFER *. */ +extern FILE_BUFFER *info_find_file (char *filename); + +/* Force load the file named FILENAME, and return the information structure + describing this file. Even if the file was already loaded, this loads + a new buffer, rebuilds tags and nodes, and returns a new FILE_BUFFER *. */ +extern FILE_BUFFER *info_load_file (char *filename); + +/* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME. + FILENAME can be passed as NULL, in which case the filename of "dir" is used. + NODENAME can be passed as NULL, in which case the nodename of "Top" is used. + If the node cannot be found, return a NULL pointer. */ +extern NODE *info_get_node (char *filename, char *nodename); + +/* Return a pointer to a NODE structure for the Info node NODENAME in + FILE_BUFFER. NODENAME can be passed as NULL, in which case the + nodename of "Top" is used. If the node cannot be found, return a + NULL pointer. */ +extern NODE *info_get_node_of_file_buffer (char *nodename, + FILE_BUFFER *file_buffer); + +/* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the + various slots. This can also be used to rebuild a tag or node table. */ +extern void build_tags_and_nodes (FILE_BUFFER *file_buffer); + +/* When non-zero, this is a string describing the most recent file error. */ +extern char *info_recent_file_error; + +/* Create a new, empty file buffer. */ +extern FILE_BUFFER *make_file_buffer (void); + +#endif /* not NODES_H */ diff --git a/Build/source/utils/texinfo/info/pcterm.c b/Build/source/utils/texinfo/info/pcterm.c new file mode 100644 index 00000000000..ed348dc54d1 --- /dev/null +++ b/Build/source/utils/texinfo/info/pcterm.c @@ -0,0 +1,759 @@ +/* pcterm.c -- How to handle the PC terminal for Info under MS-DOS/MS-Windows. + $Id: pcterm.c,v 1.4 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1998, 1999, 2003, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +/* WARNING WARNING WARNING!!! + This probably won't work as is with anything but DJGPP! However, Borland + should come close, and other PC compilers will need minor modifications. */ + +/* intl/libintl.h defines a macro `gettext' which + conflicts with conio.h header. */ +#ifdef gettext +# undef gettext +# define gettext _gettext +#endif + +#include <pc.h> +#include <keys.h> +#include <conio.h> + +#include "variables.h" + +extern int speech_friendly; /* defined in info.c */ + +/* **************************************************************** */ +/* */ +/* PC Terminal Output Functions */ +/* */ +/* **************************************************************** */ + +static struct text_info outside_info; /* holds screen params outside Info */ +static unsigned char norm_attr, inv_attr; + +static unsigned const char * find_sequence (int); + +/* Turn on reverse video. */ +static void +pc_begin_inverse (void) +{ + textattr (inv_attr); +} + +/* Turn off reverse video. */ +static void +pc_end_inverse (void) +{ + textattr (norm_attr); +} + +/* Move the cursor up one line. */ +static void +pc_up_line (void) +{ + int x, y; + ScreenGetCursor (&y, &x); + ScreenSetCursor (MAX (y-1, 0), x); +} + +/* Move the cursor down one line. */ +static void +pc_down_line (void) +{ + int x, y; + ScreenGetCursor (&y, &x); + ScreenSetCursor (MIN (screenheight-1, y+1), x); +} + +/* Clear the entire terminal screen. */ +static void +pc_clear_screen (void) +{ + ScreenClear (); +} + +/* Clear from the current position of the cursor to the end of the line. */ +static void +pc_clear_to_eol (void) +{ + clreol (); /* perhaps to be replaced by a loop */ +} + +/* Set the global variables SCREENWIDTH and SCREENHEIGHT. */ +static void +pc_get_screen_size(void) +{ + /* Current screen dimensions are the default. */ + if (!outside_info.screenheight) /* paranoia */ + gettextinfo (&outside_info); + screenwidth = outside_info.screenwidth; + screenheight = outside_info.screenheight; + + /* Environment variable "LINES" overrides the default. */ + if (getenv ("LINES") != NULL) + screenheight = atoi (getenv ("LINES")); + + /* Environment variable "INFO_LINES" overrides "LINES". */ + if (getenv ("INFO_LINES") != NULL) + screenheight = atoi (getenv ("INFO_LINES")); +} + +/* Move the cursor to the terminal location of X and Y. */ +static void +pc_goto_xy (x, y) + int x, y; +{ + ScreenSetCursor (y, x); /* yes, pc.h says ScreenSetCursor (row, column) !! */ +} + +/* Print STRING to the terminal at the current position. */ +static void +pc_put_text (string) + char *string; +{ + if (speech_friendly) + fputs (string, stdout); + else + cputs (string); +} + +/* Ring the terminal bell. The bell is rung visibly if the terminal is + capable of doing that, and if terminal_use_visible_bell_p is non-zero. */ +static void +pc_ring_bell(void) +{ + if (terminal_has_visible_bell_p && terminal_use_visible_bell_p) + ScreenVisualBell (); + else + { + printf ("%c",'\a'); + fflush (stdout); + } +} + +/* Print NCHARS from STRING to the terminal at the current position. */ +static void +pc_write_chars (string, nchars) + char *string; + int nchars; +{ + if (!nchars) + return; + + if (speech_friendly) + printf ("%.*s",nchars, string); + else + cprintf ("%..*s",nchars, string); +} + +/* Scroll an area of the terminal from START to (and excluding) END, + AMOUNT lines. If AMOUNT is negative, the lines are scrolled + towards the top of the screen, else they are scrolled towards the + bottom of the screen. The lines of the old region which do not + overlap the new region are cleared, to mimic terminal operation. */ +static void +pc_scroll_terminal (start, end, amount) + int start, end, amount; +{ + int line_to_clear = amount > 0 ? start : end + amount; + + /* Move the text. Note that `movetext' expects 1-based coordinates. */ + movetext (1, start + 1, ScreenCols (), end, 1, start + amount + 1); + + /* Now clear the lines which were left unoccupied. */ + if (amount < 0) + amount = -amount; + while (amount--) + { + ScreenSetCursor (line_to_clear++, 0); + clreol (); + } +} + +/* Put the screen in the video mode and colors which Info will use. + Prepare to start using the terminal to read characters singly. */ +static void +pc_prep_terminal (void) +{ + int tty; + + /* Do not set screen height if we already have it, because + doing so erases the screen. */ + if (screenheight != ScreenRows ()) + _set_screen_lines (screenheight); + + /* Don't fail if they asked for screen dimensions that their + hardware cannot support. */ + screenheight = ScreenRows (); + screenwidth = ScreenCols (); + + /* Try setting the colors user asked for. */ + textattr (norm_attr); + ScreenClear (); + + /* Switch console reads to binary mode. */ + tty = fileno (stdin); +#ifdef __DJGPP__ + setmode (tty, O_BINARY); + __djgpp_set_ctrl_c (1); /* re-enable SIGINT generation by Ctrl-C */ +#endif +} + +/* Restore the tty settings back to what they were before we started using + this terminal. */ +static void +pc_unprep_terminal (void) +{ + int tty; + + textattr (outside_info.normattr); + + /* Do not set screen height if we already have it, because + doing so erases the screen. */ + if (outside_info.screenheight != ScreenRows ()) + { + _set_screen_lines (outside_info.screenheight); + textmode (LASTMODE); + } + else + pc_clear_to_eol (); /* for text attributes to really take effect */ + + /* Switch back to text mode on stdin. */ + tty = fileno (stdin); +#ifdef __DJGPP__ + setmode (tty, O_TEXT); +#endif +} + +/* Initialize the terminal which is known as TERMINAL_NAME. If this + terminal doesn't have cursor addressability, `terminal_is_dumb_p' + becomes nonzero. The variables SCREENHEIGHT and SCREENWIDTH are set + to the dimensions that this terminal actually has. The variable + TERMINAL_HAS_META_P becomes nonzero if this terminal supports a Meta + key. Finally, the terminal screen is cleared. */ +static void +pc_initialize_terminal (term_name) + char *term_name; +{ + char *info_colors; + + if (!term_name) + { + term_name = getenv ("TERM"); + if (!term_name) + term_name = "pc-dos"; /* ``what's in a name?'' */ + } + + /* Get current video information, to be restored later. */ + if (outside_info.screenwidth == 0) + gettextinfo (&outside_info); + + /* Current screen colors are the default. */ + norm_attr = outside_info.normattr; + inv_attr = (((outside_info.normattr & 7) << 4) | + ((outside_info.normattr & 0x7f) >> 4)); + + /* Does the user want non-default colors? */ + info_colors = getenv ("INFO_COLORS"); + if ((info_colors != (char *)0) && !speech_friendly) + { + /* Decode a color from a string descriptor. + The descriptor string is a sequence of color specifiers separated + by a non-numeric character. Each color specifier should represent + a small integer which fits into an unsigned char, and can be given + in any base supported by strtoul. Examples of valid descriptors: + + "10 31" + "0x13/0x45" + "007.077" + + The separator between two color specifiers can be any character which + cannot be used in a printed representation of an integer number. */ + char *endp; + unsigned long color_desc = strtoul (info_colors, &endp, 0); + + if (color_desc <= UCHAR_MAX) + { + norm_attr = (unsigned char)color_desc; + color_desc = strtoul (endp + 1, &endp, 0); + if (color_desc <= UCHAR_MAX) + inv_attr = (unsigned char)color_desc; + } + } + + /* We can scroll. */ + terminal_can_scroll = 1; + + /* We know how to produce a visible bell, if somebody's looking... */ + if (!speech_friendly) + terminal_has_visible_bell_p = 1; + + /* We have a Meta key. */ + terminal_has_meta_p = 1; + + /* We are *certainly* NOT dumb! */ + terminal_is_dumb_p = 0; + + pc_get_screen_size (); + + /* Store the arrow keys. */ + term_ku = (char *)find_sequence (K_Up); + term_kd = (char *)find_sequence (K_Down); + term_kr = (char *)find_sequence (K_Right); + term_kl = (char *)find_sequence (K_Left); + + term_kP = (char *)find_sequence (K_PageUp); + term_kN = (char *)find_sequence (K_PageDown); + +#if defined(INFOKEY) + term_kh = (char *)find_sequence (K_Home); + term_ke = (char *)find_sequence (K_End); + term_ki = (char *)find_sequence (K_Insert); + term_kx = (char *)find_sequence (K_Delete); +#endif + + /* Set all the hooks to our PC-specific functions. */ + terminal_begin_inverse_hook = pc_begin_inverse; + terminal_end_inverse_hook = pc_end_inverse; + terminal_prep_terminal_hook = pc_prep_terminal; + terminal_unprep_terminal_hook = pc_unprep_terminal; + terminal_up_line_hook = pc_up_line; + terminal_down_line_hook = pc_down_line; + terminal_clear_screen_hook = pc_clear_screen; + terminal_clear_to_eol_hook = pc_clear_to_eol; + terminal_get_screen_size_hook = pc_get_screen_size; + terminal_goto_xy_hook = pc_goto_xy; + terminal_put_text_hook = pc_put_text; + terminal_ring_bell_hook = pc_ring_bell; + terminal_write_chars_hook = pc_write_chars; + terminal_scroll_terminal_hook = pc_scroll_terminal; +} + +/* **************************************************************** */ +/* */ +/* How to Read Characters From the PC Terminal */ +/* */ +/* **************************************************************** */ + +/* This will most certainly work ONLY with DJGPP. */ +#ifdef __DJGPP__ + +#include <errno.h> +#include <sys/fsext.h> +#include <dpmi.h> + +/* Translation table for some special keys. + Arrow keys which are standard on other keyboards are translated into + standard ESC-sequences, in case somebody rebinds the simple keys + (like C-f, C-b, C-n, etc.). + + The strange "\033\061" prefix in some keys is a numeric argument of + one, which means ``do the next command once''. It is here so that + when the according PC key is pressed in the middle of an incremental + search, Info doesn't see just an ASCII character like `n' or `B', + and doesn't add it to the search string; instead, it will exit the + incremental search and then perform the command. */ +static struct +{ + int inkey; + unsigned char const * const sequence; +} DJGPP_keytab[] = { /* these are for moving between nodes... */ + {K_Control_PageDown, "\033\061n"}, + {K_Control_PageUp, "\033\061p"}, + {K_Control_Up, "\033\061u"}, + {K_Control_Down, "\033\061m"}, + {K_Control_Center, "\033\061l"}, + +#if defined(INFOKEY) + {K_Home, "\033[H"}, /* ...and these are for moving IN a node */ + {K_End, "\033[F"}, /* they're Numeric-Keypad-Keys, so */ +#else + {K_Home, "\001"}, + {K_End, "\005"}, +#endif + {K_Left, "\033[D"}, /* NUMLOCK should be off !! */ + {K_Right, "\033[C"}, + {K_Down, "\033[B"}, + {K_Up, "\033[A"}, + {K_PageDown, "\033[G"}, + {K_PageUp, "\033[I"}, + {K_Control_Left, "\033b"}, + {K_Control_Right, "\033f"}, + {K_Control_Home, "\033<"}, + {K_Control_End, "\033>"}, + +#if defined(INFOKEY) + {K_EHome, "\033[H"}, /* these are also for moving IN a node */ + {K_EEnd, "\033[F"}, /* they're the "extended" (Grey) keys */ +#else + {K_EHome, "\001"}, + {K_EEnd, "\005"}, +#endif + {K_ELeft, "\033[D"}, + {K_ERight, "\033[C"}, + {K_EDown, "\033[B"}, + {K_EUp, "\033[A"}, + {K_EPageDown, "\033[G"}, + {K_EPageUp, "\033[I"}, + {K_Control_ELeft, "\033b"}, + {K_Control_ERight, "\033f"}, + {K_Control_EHome, "\033<"}, + {K_Control_EEnd, "\033>"}, + + {K_BackTab, "\033\011"}, + {K_F1, "\10"}, /* YEAH, gimme that good old F-one-thing */ + {K_Delete, "\177"}, /* to make Kp-Del be DEL (0x7f) */ + {K_EDelete, "\177"}, /* to make Delete be DEL (0x7f) */ +#if defined(INFOKEY) + {K_Insert, "\033[L"}, + {K_EInsert, "\033[L"}, +#endif + + /* These are here to map more Alt-X keys to ESC X sequences. */ + {K_Alt_Q, "\033q"}, + {K_Alt_W, "\033w"}, + {K_Alt_E, "\033e"}, + {K_Alt_R, "\033r"}, + {K_Alt_T, "\033t"}, + {K_Alt_Y, "\033y"}, + {K_Alt_U, "\033u"}, + {K_Alt_I, "\033i"}, + {K_Alt_O, "\033o"}, + {K_Alt_P, "\033p"}, + {K_Alt_LBracket, "\033["}, + {K_Alt_RBracket, "\033]"}, + {K_Alt_Return, "\033\015"}, + {K_Alt_A, "\033a"}, + {K_Alt_S, "\033s"}, + {K_Alt_D, "\033d"}, + {K_Alt_F, "\033f"}, + {K_Alt_G, "\033g"}, + {K_Alt_H, "\033h"}, + {K_Alt_J, "\033j"}, + {K_Alt_K, "\033k"}, + {K_Alt_L, "\033l"}, + {K_Alt_Semicolon, "\033;"}, + {K_Alt_Quote, "\033'"}, + {K_Alt_Backquote, "\033`"}, + {K_Alt_Backslash, "\033\\"}, + {K_Alt_Z, "\033z"}, + {K_Alt_X, "\033x"}, + {K_Alt_C, "\033c"}, + {K_Alt_V, "\033v"}, + {K_Alt_B, "\033b"}, + {K_Alt_N, "\033n"}, + {K_Alt_M, "\033m"}, + {K_Alt_Comma, "\033<"}, /* our reader cannot distinguish between */ + {K_Alt_Period, "\033>"}, /* Alt-. and Alt->, so we cheat a little */ + {K_Alt_Slash, "\033?"}, /* ditto, to get Alt-? */ + {K_Alt_Backspace, "\033\177"}, /* M-DEL, to delete word backwards */ + {K_Alt_1, "\033\061"}, + {K_Alt_2, "\033\062"}, + {K_Alt_3, "\033\063"}, + {K_Alt_4, "\033\064"}, + {K_Alt_5, "\033\065"}, + {K_Alt_6, "\033\066"}, + {K_Alt_7, "\033\067"}, + {K_Alt_8, "\033\070"}, + {K_Alt_9, "\033\071"}, + {K_Alt_0, "\033\060"}, + {K_Alt_Dash, "\033\055"}, + {K_Alt_EPageUp, "\033\033[I"}, + {K_Alt_EPageDown, "\033\033[G"}, + {K_Alt_Equals, "\033\075"}, + {K_Alt_EDelete, "\033\177"}, + {K_Alt_Tab, "\033\011"}, + {0, 0} +}; + +/* Given a key, return the sequence of characters which + our keyboard driver generates. */ +static unsigned const char * +find_sequence (int key) +{ + int i; + + for (i = 0; DJGPP_keytab[i].inkey; i++) + if (key == DJGPP_keytab[i].inkey) + return DJGPP_keytab[i].sequence; + + return (unsigned const char *)NULL; +} + +/* Return zero if a key is pending in the + keyboard buffer, non-zero otherwise. */ +static int +kbd_buffer_empty (void) +{ + __dpmi_regs r; + int retval; + + r.h.ah = 0x11; /* Get enhanced keyboard status */ + __dpmi_int (0x16, &r); + + /* If the keyboard buffer is empty, the Zero Flag will be set. */ + return (r.x.flags & 0x40) == 0x40; +} + +/* The buffered characters pending to be read. + Actually, Info usually reads a single character, but when we + translate a key into a sequence of characters, we keep them here. */ +static unsigned char buffered[512]; + +/* Index of the next buffered character to be returned. */ +static int buf_idx; + +/* Return the number of characters waiting to be read. */ +long +pc_term_chars_avail (void) +{ + if (buf_idx >= sizeof (buffered)) /* paranoia */ + { + buf_idx = 0; + buffered[buf_idx] = '\0'; + return 0; + } + else + return (long)strlen (buffered + buf_idx); +} + +/* Our special terminal keyboard reader. It will be called by + low-level libc functions when the application calls `read' or + the ANSI-standard stream-oriented read functions. If the + caller wants to read the terminal, we redirect the call to + the BIOS keyboard functions, since that lets us recognize more + keys than DOS does. */ +static int +keyboard_read (__FSEXT_Fnumber func, int *retval, va_list rest_args) +{ + /* When we are called, REST_ARGS are: file_descriptor, buf, nbytes. */ + unsigned char *buf; + size_t nbytes, nread = 0; + int fd = va_arg (rest_args, int); + + /* Is this call for us? */ + if (func != __FSEXT_read || !isatty (fd)) + return 0; /* and the usual DOS call will be issued */ + + buf = va_arg (rest_args, unsigned char *); + nbytes = va_arg (rest_args, size_t); + + if (!buf) + { + errno = EINVAL; + *retval = -1; + return 1; + } + if (!nbytes) + { + *retval = 0; + return 1; + } + + /* Loop here until enough bytes has been read. */ + do + { + int key; + + /* If any ``buffered characters'' are left, return as much + of them as the caller wanted. */ + while (buffered[buf_idx] && nbytes) + { + *buf++ = buffered[buf_idx++]; + nread++; + nbytes--; + } + + if (nbytes <= 0) + break; + + /* Wait for another key. + We do that in a busy-waiting loop so we don't get parked + inside a BIOS call, which will effectively disable signals. + While we wait for them to type something, we repeatedly + release the rest of our time slice, so that other programs + in a multitasking environment, such as Windows, get more cycles. */ + while (kbd_buffer_empty ()) + __dpmi_yield (); + + key = getxkey (); + + /* Translate the key if necessary. + Untranslated non-ASCII keys are silently ignored. */ + if ((key & 0x300) != 0) + { + unsigned char const * key_sequence = find_sequence (key); + + if (key_sequence != NULL) + { + strcpy (buffered, key_sequence); + buf_idx = 0; + } + } + else if (key == K_Control_Z) + raise (SIGUSR1); /* we don't have SIGTSTP, so simulate it */ + else if (key <= 0xff) + { + *buf++ = key; + nbytes--; + nread++; + } + } + while (nbytes > 0); + + *retval = nread; + return 1; /* meaning that we handled the call */ +} + +/* Install our keyboard handler. + This is called by the startup code before `main'. */ +static void __attribute__((constructor)) +install_keyboard_handler (void) +{ + __FSEXT_set_function (fileno (stdin), keyboard_read); + + /* We need to set this single hook here; the rest + will be set by pc_initialize_terminal when it is called. */ + terminal_initialize_terminal_hook = pc_initialize_terminal; +} + +#endif /* __DJGPP__ */ + +/* **************************************************************** */ +/* */ +/* Emulation of SIGTSTP on Ctrl-Z */ +/* */ +/* **************************************************************** */ + +#include <limits.h> +#include "signals.h" +#include "session.h" + +#ifndef PATH_MAX +# define PATH_MAX 512 +#endif + +/* Effectively disable signals which aren't defined + (assuming no signal can ever be zero). + SIGINT is ANSI, so we expect it to be always defined. */ +#ifndef SIGUSR1 +# define SIGUSR1 0 +#endif +#ifndef SIGQUIT +# define SIGQUIT 0 +#endif + +int +kill (pid_t pid, int sig) +{ + static char interrupted_msg[] = "Interrupted\r\n"; + static char stopped_msg[] = "Stopped. Type `exit RET' to return.\r\n"; + char cwd[PATH_MAX + 1]; + + if (pid == getpid () + || pid == 0 + || pid == -1 + || pid == -getpid ()) + { + switch (sig) + { + RETSIGTYPE (*old_INT)(int), (*old_QUIT)(int); + + case SIGINT: +#ifdef __DJGPP__ + /* If SIGINT was generated by a readable key, we want to remove + it from the PC keyboard buffer, so that DOS and other + programs never see it. DJGPP signal-handling mechanism + doesn't remove the INT key from the keyboard buffer. */ + if (!kbd_buffer_empty ()) + getxkey (); +#endif + pc_write_chars (interrupted_msg, sizeof (interrupted_msg) - 1); + xexit (1); + case SIGUSR1: + /* Simulate SIGTSTP by invoking a subsidiary shell. */ + pc_goto_xy (0, outside_info.screenheight - 1); + pc_clear_to_eol (); + pc_write_chars (stopped_msg, sizeof (stopped_msg) - 1); + + /* The child shell can change the working directory, so + we need to save and restore it, since it is global. */ + if (!getcwd (cwd, PATH_MAX)) /* should never happen */ + cwd[0] = '\0'; + + /* We don't want to get fatal signals while the subshell runs. */ + old_INT = signal (SIGINT, SIG_IGN); + old_QUIT = signal (SIGQUIT, SIG_IGN); + system (""); + if (*cwd) + chdir (cwd); + signal (SIGINT, old_INT); + signal (SIGQUIT, old_QUIT); + break; + default: + if (sig) + raise (sig); + break; + } + return 0; + } + else + return -1; +} + +/* These should never be called, but they make the linker happy. */ + +void tputs (char *a, int b, int (*c)()) +{ + perror ("tputs"); +} + +char* tgoto (char*a, int b, int c) +{ + perror ("tgoto"); return 0; /* here and below, added dummy retvals */ +} + +int tgetnum (char*a) +{ + perror ("tgetnum"); return 0; +} + +int tgetflag (char*a) +{ + perror ("tgetflag"); return 0; +} + +char* tgetstr (char *a, char **b) +{ + perror ("tgetstr"); return 0; +} + +int tgetent (char*a, char*b) +{ + perror ("tgetent"); return 0; +} + +int tcgetattr(int fildes, struct termios *termios_p) +{ + perror ("tcgetattr"); return 0; +} + +int tcsetattr(int fd, int opt_actions, const struct termios *termios_p) +{ + perror ("tcsetattr"); return 0; +} diff --git a/Build/source/utils/texinfo/info/search.c b/Build/source/utils/texinfo/info/search.c new file mode 100644 index 00000000000..a1d5da6c711 --- /dev/null +++ b/Build/source/utils/texinfo/info/search.c @@ -0,0 +1,497 @@ +/* search.c -- searching large bodies of text. + $Id: search.c,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" + +#include "search.h" +#include "nodes.h" + +/* The search functions take two arguments: + + 1) a string to search for, and + + 2) a pointer to a SEARCH_BINDING which contains the buffer, start, + and end of the search. + + They return a long, which is the offset from the start of the buffer + at which the match was found. An offset of -1 indicates failure. */ + +/* A function which makes a binding with buffer and bounds. */ +SEARCH_BINDING * +make_binding (char *buffer, long int start, long int end) +{ + SEARCH_BINDING *binding; + + binding = (SEARCH_BINDING *)xmalloc (sizeof (SEARCH_BINDING)); + binding->buffer = buffer; + binding->start = start; + binding->end = end; + binding->flags = 0; + + return (binding); +} + +/* Make a copy of BINDING without duplicating the data. */ +SEARCH_BINDING * +copy_binding (SEARCH_BINDING *binding) +{ + SEARCH_BINDING *copy; + + copy = make_binding (binding->buffer, binding->start, binding->end); + copy->flags = binding->flags; + return (copy); +} + + +/* **************************************************************** */ +/* */ +/* The Actual Searching Functions */ +/* */ +/* **************************************************************** */ + +/* Search forwards or backwards for the text delimited by BINDING. + The search is forwards if BINDING->start is greater than BINDING->end. */ +long +search (char *string, SEARCH_BINDING *binding) +{ + long result; + + /* If the search is backwards, then search backwards, otherwise forwards. */ + if (binding->start > binding->end) + result = search_backward (string, binding); + else + result = search_forward (string, binding); + + return (result); +} + +/* Search forwards for STRING through the text delimited in BINDING. */ +long +search_forward (char *string, SEARCH_BINDING *binding) +{ + register int c, i, len; + register char *buff, *end; + char *alternate = (char *)NULL; + + len = strlen (string); + + /* We match characters in the search buffer against STRING and ALTERNATE. + ALTERNATE is a case reversed version of STRING; this is cheaper than + case folding each character before comparison. Alternate is only + used if the case folding bit is turned on in the passed BINDING. */ + + if (binding->flags & S_FoldCase) + { + alternate = xstrdup (string); + + for (i = 0; i < len; i++) + { + if (islower (alternate[i])) + alternate[i] = toupper (alternate[i]); + else if (isupper (alternate[i])) + alternate[i] = tolower (alternate[i]); + } + } + + buff = binding->buffer + binding->start; + end = binding->buffer + binding->end + 1; + + while (buff < (end - len)) + { + for (i = 0; i < len; i++) + { + c = buff[i]; + + if ((c != string[i]) && (!alternate || c != alternate[i])) + break; + } + + if (!string[i]) + { + if (alternate) + free (alternate); + if (binding->flags & S_SkipDest) + buff += len; + return ((long) (buff - binding->buffer)); + } + + buff++; + } + + if (alternate) + free (alternate); + + return ((long) -1); +} + +/* Search for STRING backwards through the text delimited in BINDING. */ +long +search_backward (char *input_string, SEARCH_BINDING *binding) +{ + register int c, i, len; + register char *buff, *end; + char *string; + char *alternate = (char *)NULL; + + len = strlen (input_string); + + /* Reverse the characters in the search string. */ + string = (char *)xmalloc (1 + len); + for (c = 0, i = len - 1; input_string[c]; c++, i--) + string[i] = input_string[c]; + + string[c] = '\0'; + + /* We match characters in the search buffer against STRING and ALTERNATE. + ALTERNATE is a case reversed version of STRING; this is cheaper than + case folding each character before comparison. ALTERNATE is only + used if the case folding bit is turned on in the passed BINDING. */ + + if (binding->flags & S_FoldCase) + { + alternate = xstrdup (string); + + for (i = 0; i < len; i++) + { + if (islower (alternate[i])) + alternate[i] = toupper (alternate[i]); + else if (isupper (alternate[i])) + alternate[i] = tolower (alternate[i]); + } + } + + buff = binding->buffer + binding->start - 1; + end = binding->buffer + binding->end; + + while (buff > (end + len)) + { + for (i = 0; i < len; i++) + { + c = *(buff - i); + + if (c != string[i] && (!alternate || c != alternate[i])) + break; + } + + if (!string[i]) + { + free (string); + if (alternate) + free (alternate); + + if (binding->flags & S_SkipDest) + buff -= len; + return ((long) (1 + (buff - binding->buffer))); + } + + buff--; + } + + free (string); + if (alternate) + free (alternate); + + return ((long) -1); +} + +/* Find STRING in LINE, returning the offset of the end of the string. + Return an offset of -1 if STRING does not appear in LINE. The search + is bound by the end of the line (i.e., either NEWLINE or 0). */ +int +string_in_line (char *string, char *line) +{ + register int end; + SEARCH_BINDING binding; + + /* Find the end of the line. */ + for (end = 0; line[end] && line[end] != '\n'; end++); + + /* Search for STRING within these confines. */ + binding.buffer = line; + binding.start = 0; + binding.end = end; + binding.flags = S_FoldCase | S_SkipDest; + + return (search_forward (string, &binding)); +} + +/* Return non-zero if STRING is the first text to appear at BINDING. */ +int +looking_at (char *string, SEARCH_BINDING *binding) +{ + long search_end; + + search_end = search (string, binding); + + /* If the string was not found, SEARCH_END is -1. If the string was found, + but not right away, SEARCH_END is != binding->start. Otherwise, the + string was found at binding->start. */ + return (search_end == binding->start); +} + +/* **************************************************************** */ +/* */ +/* Small String Searches */ +/* */ +/* **************************************************************** */ + +/* Function names that start with "skip" are passed a string, and return + an offset from the start of that string. Function names that start + with "find" are passed a SEARCH_BINDING, and return an absolute position + marker of the item being searched for. "Find" functions return a value + of -1 if the item being looked for couldn't be found. */ + +/* Return the index of the first non-whitespace character in STRING. */ +int +skip_whitespace (char *string) +{ + register int i; + + for (i = 0; string && whitespace (string[i]); i++); + return (i); +} + +/* Return the index of the first non-whitespace or newline character in + STRING. */ +int +skip_whitespace_and_newlines (char *string) +{ + register int i; + + for (i = 0; string && whitespace_or_newline (string[i]); i++); + return (i); +} + +/* Return the index of the first whitespace character in STRING. */ +int +skip_non_whitespace (char *string) +{ + register int i; + + for (i = 0; string && string[i] && !whitespace (string[i]); i++); + return (i); +} + +/* Return the index of the first non-node character in STRING. Note that + this function contains quite a bit of hair to ignore periods in some + special cases. This is because we here at GNU ship some info files which + contain nodenames that contain periods. No such nodename can start with + a period, or continue with whitespace, newline, or ')' immediately following + the period. If second argument NEWLINES_OKAY is non-zero, newlines should + be skipped while parsing out the nodename specification. */ +int +skip_node_characters (char *string, int newlines_okay) +{ + register int c, i = 0; + int paren_seen = 0; + int paren = 0; + + /* Handle special case. This is when another function has parsed out the + filename component of the node name, and we just want to parse out the + nodename proper. In that case, a period at the start of the nodename + indicates an empty nodename. */ + if (string && *string == '.') + return (0); + + if (string && *string == '(') + { + paren++; + paren_seen++; + i++; + } + + for (; string && (c = string[i]); i++) + { + if (paren) + { + if (c == '(') + paren++; + else if (c == ')') + paren--; + + continue; + } + + /* If the character following the close paren is a space or period, + then this node name has no more characters associated with it. */ + if (c == '\t' || + c == ',' || + c == INFO_TAGSEP || + ((!newlines_okay) && (c == '\n')) || + ((paren_seen && string[i - 1] == ')') && + (c == ' ' || c == '.')) || + (c == '.' && + ( +#if 0 +/* This test causes a node name ending in a period, like `This.', not to + be found. The trailing . is stripped. This occurs in the jargon + file (`I see no X here.' is a node name). */ + (!string[i + 1]) || +#endif + (whitespace_or_newline (string[i + 1])) || + (string[i + 1] == ')')))) + break; + } + return (i); +} + + +/* **************************************************************** */ +/* */ +/* Searching FILE_BUFFER's */ +/* */ +/* **************************************************************** */ + +/* Return the absolute position of the first occurence of a node separator in + BINDING-buffer. The search starts at BINDING->start. Return -1 if no node + separator was found. */ +long +find_node_separator (SEARCH_BINDING *binding) +{ + register long i; + char *body; + + body = binding->buffer; + + /* A node is started by [^L]^_[^L]\n. That is to say, the C-l's are + optional, but the DELETE and NEWLINE are not. This separator holds + true for all separated elements in an Info file, including the tags + table (if present) and the indirect tags table (if present). */ + for (i = binding->start; i < binding->end - 1; i++) + if (((body[i] == INFO_FF && body[i + 1] == INFO_COOKIE) && + (body[i + 2] == '\n' || + (body[i + 2] == INFO_FF && body[i + 3] == '\n'))) || + ((body[i] == INFO_COOKIE) && + (body[i + 1] == '\n' || + (body[i + 1] == INFO_FF && body[i + 2] == '\n')))) + return (i); + return (-1); +} + +/* Return the length of the node separator characters that BODY is + currently pointing at. */ +int +skip_node_separator (char *body) +{ + register int i; + + i = 0; + + if (body[i] == INFO_FF) + i++; + + if (body[i++] != INFO_COOKIE) + return (0); + + if (body[i] == INFO_FF) + i++; + + if (body[i++] != '\n') + return (0); + + return (i); +} + +/* Return the number of characters from STRING to the start of + the next line. */ +int +skip_line (char *string) +{ + register int i; + + for (i = 0; string && string[i] && string[i] != '\n'; i++); + + if (string[i] == '\n') + i++; + + return (i); +} + +/* Return the absolute position of the beginning of a tags table in this + binding starting the search at binding->start. */ +long +find_tags_table (SEARCH_BINDING *binding) +{ + SEARCH_BINDING tmp_search; + long position; + + tmp_search.buffer = binding->buffer; + tmp_search.start = binding->start; + tmp_search.end = binding->end; + tmp_search.flags = S_FoldCase; + + while ((position = find_node_separator (&tmp_search)) != -1 ) + { + tmp_search.start = position; + tmp_search.start += skip_node_separator (tmp_search.buffer + + tmp_search.start); + + if (looking_at (TAGS_TABLE_BEG_LABEL, &tmp_search)) + return (position); + } + return (-1); +} + +/* Return the absolute position of the node named NODENAME in BINDING. + This is a brute force search, and we wish to avoid it when possible. + This function is called when a tag (indirect or otherwise) doesn't + really point to the right node. It returns the absolute position of + the separator preceding the node. */ +long +find_node_in_binding (char *nodename, SEARCH_BINDING *binding) +{ + long position; + int offset, namelen; + SEARCH_BINDING tmp_search; + + namelen = strlen (nodename); + + tmp_search.buffer = binding->buffer; + tmp_search.start = binding->start; + tmp_search.end = binding->end; + tmp_search.flags = 0; + + while ((position = find_node_separator (&tmp_search)) != -1) + { + tmp_search.start = position; + tmp_search.start += skip_node_separator + (tmp_search.buffer + tmp_search.start); + + offset = string_in_line + (INFO_NODE_LABEL, tmp_search.buffer + tmp_search.start); + + if (offset == -1) + continue; + + tmp_search.start += offset; + tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start); + offset = skip_node_characters + (tmp_search.buffer + tmp_search.start, DONT_SKIP_NEWLINES); + + /* Notice that this is an exact match. You cannot grovel through + the buffer with this function looking for random nodes. */ + if ((offset == namelen) && + (tmp_search.buffer[tmp_search.start] == nodename[0]) && + (strncmp (tmp_search.buffer + tmp_search.start, nodename, offset) == 0)) + return (position); + } + return (-1); +} diff --git a/Build/source/utils/texinfo/info/search.h b/Build/source/utils/texinfo/info/search.h new file mode 100644 index 00000000000..3e0610799ba --- /dev/null +++ b/Build/source/utils/texinfo/info/search.h @@ -0,0 +1,75 @@ +/* search.h -- Structure used to search large bodies of text, with bounds. + $Id: search.h,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +/* The search functions take two arguments: + + 1) a string to search for, and + + 2) a pointer to a SEARCH_BINDING which contains the buffer, start, + and end of the search. + + They return a long, which is the offset from the start of the buffer + at which the match was found. An offset of -1 indicates failure. */ + +#ifndef INFO_SEARCH_H +#define INFO_SEARCH_H + +typedef struct { + char *buffer; /* The buffer of text to search. */ + long start; /* Offset of the start of the search. */ + long end; /* Offset of the end of the searh. */ + int flags; /* Flags controlling the type of search. */ +} SEARCH_BINDING; + +#define S_FoldCase 0x01 /* Set means fold case in searches. */ +#define S_SkipDest 0x02 /* Set means return pointing after the dest. */ + +SEARCH_BINDING *make_binding (char *buffer, long int start, long int end); +SEARCH_BINDING *copy_binding (SEARCH_BINDING *binding); +extern long search_forward (char *string, SEARCH_BINDING *binding); +extern long search_backward (char *input_string, SEARCH_BINDING *binding); +extern long search (char *string, SEARCH_BINDING *binding); +extern int looking_at (char *string, SEARCH_BINDING *binding); + +/* Note that STRING_IN_LINE () always returns the offset of the 1st character + after the string. */ +extern int string_in_line (char *string, char *line); + +/* Function names that start with "skip" are passed a string, and return + an offset from the start of that string. Function names that start + with "find" are passed a SEARCH_BINDING, and return an absolute position + marker of the item being searched for. "Find" functions return a value + of -1 if the item being looked for couldn't be found. */ +extern int skip_whitespace (char *string); +extern int skip_non_whitespace (char *string); +extern int skip_whitespace_and_newlines (char *string); +extern int skip_line (char *string); +extern int skip_node_characters (char *string, int newlines_okay); +extern int skip_node_separator (char *body); + +#define DONT_SKIP_NEWLINES 0 +#define SKIP_NEWLINES 1 + +extern long find_node_separator (SEARCH_BINDING *binding); +extern long find_tags_table (SEARCH_BINDING *binding); +extern long find_node_in_binding (char *nodename, SEARCH_BINDING *binding); + +#endif /* not INFO_SEARCH_H */ diff --git a/Build/source/utils/texinfo/info/session.c b/Build/source/utils/texinfo/info/session.c new file mode 100644 index 00000000000..7a8996bbe20 --- /dev/null +++ b/Build/source/utils/texinfo/info/session.c @@ -0,0 +1,5039 @@ +/* session.c -- user windowing interface to Info. + $Id: session.c,v 1.16 2004/12/14 00:15:36 karl Exp $ + + Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "search.h" +#include <sys/ioctl.h> + +#if defined (HAVE_SYS_TIME_H) +# include <sys/time.h> +# define HAVE_STRUCT_TIMEVAL +#endif /* HAVE_SYS_TIME_H */ + +#if defined (HANDLE_MAN_PAGES) +# include "man.h" +#endif + +static void info_clear_pending_input (void); +static void info_set_pending_input (unsigned char key); +static void info_handle_pointer (char *label, WINDOW *window); +static void display_info_keyseq (int expecting_future_input); +char *node_printed_rep (NODE *node); + +/* **************************************************************** */ +/* */ +/* Running an Info Session */ +/* */ +/* **************************************************************** */ + +/* The place that we are reading input from. */ +static FILE *info_input_stream = NULL; + +/* The last executed command. */ +VFunction *info_last_executed_command = NULL; + +/* Becomes non-zero when 'q' is typed to an Info window. */ +int quit_info_immediately = 0; + +/* Array of structures describing for each window which nodes have been + visited in that window. */ +INFO_WINDOW **info_windows = NULL; + +/* Where to add the next window, if we need to add one. */ +static int info_windows_index = 0; + +/* Number of slots allocated to `info_windows'. */ +static int info_windows_slots = 0; + +void remember_window_and_node (WINDOW *window, NODE *node); +void forget_window_and_nodes (WINDOW *window); +void display_startup_message_and_start (void); + +/* Begin an info session finding the nodes specified by FILENAME and NODENAMES. + For each loaded node, create a new window. Always split the largest of the + available windows. */ +void +begin_multiple_window_info_session (char *filename, char **nodenames) +{ + register int i; + WINDOW *window = (WINDOW *)NULL; + + for (i = 0; nodenames[i]; i++) + { + NODE *node; + + node = info_get_node (filename, nodenames[i]); + + if (!node) + break; + + /* If this is the first node, initialize the info session. */ + if (!window) + { + initialize_info_session (node, 1); + window = active_window; + } + else + { + /* Find the largest window in WINDOWS, and make that be the active + one. Then split it and add our window and node to the list + of remembered windows and nodes. Then tile the windows. */ + WINDOW *win, *largest = NULL; + int max_height = 0; + + for (win = windows; win; win = win->next) + if (win->height > max_height) + { + max_height = win->height; + largest = win; + } + + if (!largest) + { + display_update_display (windows); + info_error ((char *) msg_cant_find_window, NULL, NULL); + info_session (); + xexit (0); + } + + active_window = largest; + window = window_make_window (node); + if (window) + { + window_tile_windows (TILE_INTERNALS); + remember_window_and_node (window, node); + } + else + { + display_update_display (windows); + info_error ((char *) msg_win_too_small, NULL, NULL); + info_session (); + xexit (0); + } + } + } + display_startup_message_and_start (); +} + +/* Start an info session with INITIAL_NODE, and an error message in the echo + area made from FORMAT and ARG. */ +void +begin_info_session_with_error (NODE *initial_node, char *format, + void *arg1, void *arg2) +{ + initialize_info_session (initial_node, 1); + info_error (format, arg1, arg2); + info_session (); +} + +/* Start an info session with INITIAL_NODE. */ +void +begin_info_session (NODE *initial_node) +{ + initialize_info_session (initial_node, 1); + display_startup_message_and_start (); +} + +void +display_startup_message_and_start (void) +{ + char *format; + + format = replace_in_documentation + ((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."), + 0); + + window_message_in_echo_area (format, VERSION, NULL); + info_session (); +} + +/* Run an info session with an already initialized window and node. */ +void +info_session (void) +{ + display_update_display (windows); + info_last_executed_command = NULL; + info_read_and_dispatch (); + /* On program exit, leave the cursor at the bottom of the window, and + restore the terminal I/O. */ + terminal_goto_xy (0, screenheight - 1); + terminal_clear_to_eol (); + fflush (stdout); + terminal_unprep_terminal (); + close_dribble_file (); +} + +/* Here is a window-location dependent event loop. Called from the + functions info_session (), and from read_xxx_in_echo_area (). */ +void +info_read_and_dispatch (void) +{ + unsigned char key; + int done; + done = 0; + + while (!done && !quit_info_immediately) + { + int lk = 0; + + /* If we haven't just gone up or down a line, there is no + goal column for this window. */ + if ((info_last_executed_command != (VFunction *) info_next_line) && + (info_last_executed_command != (VFunction *) info_prev_line)) + active_window->goal_column = -1; + + if (echo_area_is_active) + { + lk = echo_area_last_command_was_kill; + echo_area_prep_read (); + } + + if (!info_any_buffered_input_p ()) + display_update_display (windows); + + display_cursor_at_point (active_window); + info_initialize_numeric_arg (); + + initialize_keyseq (); + key = info_get_input_char (); + + /* No errors yet. We just read a character, that's all. Only clear + the echo_area if it is not currently active. */ + if (!echo_area_is_active) + window_clear_echo_area (); + + info_error_was_printed = 0; + + /* Do the selected command. */ + info_dispatch_on_key (key, active_window->keymap); + + if (echo_area_is_active) + { + /* Echo area commands that do killing increment the value of + ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no + change in the value of this variable, the last command + executed was not a kill command. */ + if (lk == echo_area_last_command_was_kill) + echo_area_last_command_was_kill = 0; + + if (ea_last_executed_command == (VFunction *) ea_newline || + info_aborted_echo_area) + { + ea_last_executed_command = (VFunction *)NULL; + done = 1; + } + + if (info_last_executed_command == (VFunction *) info_quit) + quit_info_immediately = 1; + } + else if (info_last_executed_command == (VFunction *) info_quit) + done = 1; + } +} + +/* Found in signals.c */ +extern void initialize_info_signal_handler (void ); + +/* Initialize the first info session by starting the terminal, window, + and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */ +void +initialize_info_session (NODE *node, int clear_screen) +{ + char *term_name = getenv ("TERM"); + terminal_initialize_terminal (term_name); + + if (terminal_is_dumb_p) + { + if (!term_name) + term_name = "dumb"; + + info_error ((char *) msg_term_too_dumb, term_name, NULL); + xexit (1); + } + + if (clear_screen) + { + terminal_prep_terminal (); + terminal_clear_screen (); + } + + initialize_info_keymaps (); + window_initialize_windows (screenwidth, screenheight); + initialize_info_signal_handler (); + display_initialize_display (screenwidth, screenheight); + info_set_node_of_window (0, active_window, node); + + /* Tell the window system how to notify us when a window needs to be + asynchronously deleted (e.g., user resizes window very small). */ + window_deletion_notifier = (VFunction *) forget_window_and_nodes; + + /* If input has not been redirected yet, make it come from unbuffered + standard input. */ + if (!info_input_stream) + { + setbuf (stdin, NULL); + info_input_stream = stdin; + } + + info_windows_initialized_p = 1; +} + +/* Tell Info that input is coming from the file FILENAME. */ +void +info_set_input_from_file (char *filename) +{ + FILE *stream; + + /* Input may include binary characters. */ + stream = fopen (filename, FOPEN_RBIN); + + if (!stream) + return; + + if ((info_input_stream != (FILE *)NULL) && + (info_input_stream != stdin)) + fclose (info_input_stream); + + info_input_stream = stream; + + if (stream != stdin) + display_inhibited = 1; +} + +/* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */ +static INFO_WINDOW * +get_info_window_of_window (WINDOW *window) +{ + register int i; + INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; + + for (i = 0; info_windows && (info_win = info_windows[i]); i++) + if (info_win->window == window) + break; + + return (info_win); +} + +/* Reset the remembered pagetop and point of WINDOW to WINDOW's current + values if the window and node are the same as the current one being + displayed. */ +void +set_remembered_pagetop_and_point (WINDOW *window) +{ + INFO_WINDOW *info_win; + + info_win = get_info_window_of_window (window); + + if (!info_win) + return; + + if (info_win->nodes_index && + (info_win->nodes[info_win->current] == window->node)) + { + info_win->pagetops[info_win->current] = window->pagetop; + info_win->points[info_win->current] = window->point; + } +} + +void +remember_window_and_node (WINDOW *window, NODE *node) +{ + /* See if we already have this window in our list. */ + INFO_WINDOW *info_win = get_info_window_of_window (window); + + /* If the window wasn't already on our list, then make a new entry. */ + if (!info_win) + { + info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW)); + info_win->window = window; + info_win->nodes = (NODE **)NULL; + info_win->pagetops = (int *)NULL; + info_win->points = (long *)NULL; + info_win->current = 0; + info_win->nodes_index = 0; + info_win->nodes_slots = 0; + + add_pointer_to_array (info_win, info_windows_index, info_windows, + info_windows_slots, 10, INFO_WINDOW *); + } + + /* If this node, the current pagetop, and the current point are the + same as the current saved node and pagetop, don't really add this to + the list of history nodes. This may happen only at the very + beginning of the program, I'm not sure. --karl */ + if (info_win->nodes + && info_win->current >= 0 + && info_win->nodes[info_win->current]->contents == node->contents + && info_win->pagetops[info_win->current] == window->pagetop + && info_win->points[info_win->current] == window->point) + return; + + /* Remember this node, the currently displayed pagetop, and the current + location of point in this window. Because we are updating pagetops + and points as well as nodes, it is more efficient to avoid the + add_pointer_to_array macro here. */ + if (info_win->nodes_index + 2 >= info_win->nodes_slots) + { + info_win->nodes_slots += 20; + info_win->nodes = (NODE **) xrealloc (info_win->nodes, + info_win->nodes_slots * sizeof (NODE *)); + info_win->pagetops = (int *) xrealloc (info_win->pagetops, + info_win->nodes_slots * sizeof (int)); + info_win->points = (long *) xrealloc (info_win->points, + info_win->nodes_slots * sizeof (long)); + } + + info_win->nodes[info_win->nodes_index] = node; + info_win->pagetops[info_win->nodes_index] = window->pagetop; + info_win->points[info_win->nodes_index] = window->point; + info_win->current = info_win->nodes_index++; + info_win->nodes[info_win->nodes_index] = NULL; + info_win->pagetops[info_win->nodes_index] = 0; + info_win->points[info_win->nodes_index] = 0; +} + +#define DEBUG_FORGET_WINDOW_AND_NODES +#if defined (DEBUG_FORGET_WINDOW_AND_NODES) +static void +consistency_check_info_windows (void) +{ + register int i; + + for (i = 0; i < info_windows_index; i++) + { + WINDOW *win; + + for (win = windows; win; win = win->next) + if (win == info_windows[i]->window) + break; + + if (!win) + abort (); + } +} +#endif /* DEBUG_FORGET_WINDOW_AND_NODES */ + +/* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */ +void +forget_window_and_nodes (WINDOW *window) +{ + register int i; + INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; + + for (i = 0; info_windows && (info_win = info_windows[i]); i++) + if (info_win->window == window) + break; + + /* If we found the window to forget, then do so. */ + if (info_win) + { + while (i < info_windows_index) + { + info_windows[i] = info_windows[i + 1]; + i++; + } + + info_windows_index--; + info_windows[info_windows_index] = (INFO_WINDOW *)NULL; + + if (info_win->nodes) + { + /* Free the node structures which held onto internal node contents + here. This doesn't free the contents; we have a garbage collector + which does that. */ + for (i = 0; info_win->nodes[i]; i++) + if (internal_info_node_p (info_win->nodes[i])) + free (info_win->nodes[i]); + free (info_win->nodes); + + maybe_free (info_win->pagetops); + maybe_free (info_win->points); + } + + free (info_win); + } +#if defined (DEBUG_FORGET_WINDOW_AND_NODES) + consistency_check_info_windows (); +#endif /* DEBUG_FORGET_WINDOW_AND_NODES */ +} + +/* Set WINDOW to show NODE. Remember the new window in our list of Info + windows. If we are doing automatic footnote display, also try to display + the footnotes for this window. If REMEMBER is nonzero, first call + set_remembered_pagetop_and_point. */ +void +info_set_node_of_window (int remember, WINDOW *window, NODE *node) +{ + if (remember) + set_remembered_pagetop_and_point (window); + + /* Put this node into the window. */ + window_set_node_of_window (window, node); + + /* Remember this node and window in our list of info windows. */ + remember_window_and_node (window, node); + + /* If doing auto-footnote display/undisplay, show the footnotes belonging + to this window's node. */ + if (auto_footnotes_p) + info_get_or_remove_footnotes (window); +} + + +/* **************************************************************** */ +/* */ +/* Info Movement Commands */ +/* */ +/* **************************************************************** */ + +/* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen + to do so. */ +void +set_window_pagetop (WINDOW *window, int desired_top) +{ + int point_line, old_pagetop; + + if (desired_top < 0) + desired_top = 0; + else if (desired_top > window->line_count) + desired_top = window->line_count - 1; + + if (window->pagetop == desired_top) + return; + + old_pagetop = window->pagetop; + window->pagetop = desired_top; + + /* Make sure that point appears in this window. */ + point_line = window_line_of_point (window); + if ((point_line < window->pagetop) || + ((point_line - window->pagetop) > window->height - 1)) + window->point = + window->line_starts[window->pagetop] - window->node->contents; + + window->flags |= W_UpdateWindow; + + /* Find out which direction to scroll, and scroll the window in that + direction. Do this only if there would be a savings in redisplay + time. This is true if the amount to scroll is less than the height + of the window, and if the number of lines scrolled would be greater + than 10 % of the window's height. */ + if (old_pagetop < desired_top) + { + int start, end, amount; + + amount = desired_top - old_pagetop; + + if ((amount >= window->height) || + (((window->height - amount) * 10) < window->height)) + return; + + start = amount + window->first_row; + end = window->height + window->first_row; + + display_scroll_display (start, end, -amount); + } + else + { + int start, end, amount; + + amount = old_pagetop - desired_top; + + if ((amount >= window->height) || + (((window->height - amount) * 10) < window->height)) + return; + + start = window->first_row; + end = (window->first_row + window->height) - amount; + display_scroll_display (start, end, amount); + } +} + +/* Immediately make WINDOW->point visible on the screen, and move the + terminal cursor there. */ +static void +info_show_point (WINDOW *window) +{ + int old_pagetop; + + old_pagetop = window->pagetop; + window_adjust_pagetop (window); + if (old_pagetop != window->pagetop) + { + int new_pagetop; + + new_pagetop = window->pagetop; + window->pagetop = old_pagetop; + set_window_pagetop (window, new_pagetop); + } + + if (window->flags & W_UpdateWindow) + display_update_one_window (window); + + display_cursor_at_point (window); +} + +/* Move WINDOW->point from OLD line index to NEW line index. */ +static void +move_to_new_line (int old, int new, WINDOW *window) +{ + if (old == -1) + { + info_error ((char *) msg_cant_find_point, NULL, NULL); + } + else + { + int goal; + + if (new >= window->line_count || new < 0) + return; + + goal = window_get_goal_column (window); + window->goal_column = goal; + + window->point = window->line_starts[new] - window->node->contents; + window->point += window_chars_to_goal (window->line_starts[new], goal); + info_show_point (window); + } +} + +/* Move WINDOW's point down to the next line if possible. */ +DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line")) +{ + int old_line, new_line; + + if (count < 0) + info_prev_line (window, -count, key); + else + { + old_line = window_line_of_point (window); + new_line = old_line + count; + move_to_new_line (old_line, new_line, window); + } +} + +/* Move WINDOW's point up to the previous line if possible. */ +DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line")) +{ + int old_line, new_line; + + if (count < 0) + info_next_line (window, -count, key); + else + { + old_line = window_line_of_point (window); + new_line = old_line - count; + move_to_new_line (old_line, new_line, window); + } +} + +/* Move WINDOW's point to the end of the true line. */ +DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line")) +{ + register int point, len; + register char *buffer; + + buffer = window->node->contents; + len = window->node->nodelen; + + for (point = window->point; + (point < len) && (buffer[point] != '\n'); + point++); + + if (point != window->point) + { + window->point = point; + info_show_point (window); + } +} + +/* Move WINDOW's point to the beginning of the true line. */ +DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line")) +{ + register int point; + register char *buffer; + + buffer = window->node->contents; + point = window->point; + + for (; (point) && (buffer[point - 1] != '\n'); point--); + + /* If at a line start already, do nothing. */ + if (point != window->point) + { + window->point = point; + info_show_point (window); + } +} + +/* Move point forward in the node. */ +DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character")) +{ + if (count < 0) + info_backward_char (window, -count, key); + else + { + window->point += count; + + if (window->point >= window->node->nodelen) + window->point = window->node->nodelen - 1; + + info_show_point (window); + } +} + +/* Move point backward in the node. */ +DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character")) +{ + if (count < 0) + info_forward_char (window, -count, key); + else + { + window->point -= count; + + if (window->point < 0) + window->point = 0; + + info_show_point (window); + } +} + +#define alphabetic(c) (islower (c) || isupper (c) || isdigit (c)) + +/* Move forward a word in this node. */ +DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word")) +{ + long point; + char *buffer; + int end, c; + + if (count < 0) + { + info_backward_word (window, -count, key); + return; + } + + point = window->point; + buffer = window->node->contents; + end = window->node->nodelen; + + while (count) + { + if (point + 1 >= end) + return; + + /* If we are not in a word, move forward until we are in one. + Then, move forward until we hit a non-alphabetic character. */ + c = buffer[point]; + + if (!alphabetic (c)) + { + while (++point < end) + { + c = buffer[point]; + if (alphabetic (c)) + break; + } + } + + if (point >= end) return; + + while (++point < end) + { + c = buffer[point]; + if (!alphabetic (c)) + break; + } + --count; + } + window->point = point; + info_show_point (window); +} + +DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word")) +{ + long point; + char *buffer; + int c; + + if (count < 0) + { + info_forward_word (window, -count, key); + return; + } + + buffer = window->node->contents; + point = window->point; + + while (count) + { + if (point == 0) + break; + + /* Like info_forward_word (), except that we look at the + characters just before point. */ + + c = buffer[point - 1]; + + if (!alphabetic (c)) + { + while (--point) + { + c = buffer[point - 1]; + if (alphabetic (c)) + break; + } + } + + while (point) + { + c = buffer[point - 1]; + if (!alphabetic (c)) + break; + else + --point; + } + --count; + } + window->point = point; + info_show_point (window); +} + +/* Variable controlling the behaviour of default scrolling when you are + already at the bottom of a node. Possible values are defined in session.h. + The meanings are: + + IS_Continuous Try to get first menu item, or failing that, the + "Next:" pointer, or failing that, the "Up:" and + "Next:" of the up. + IS_NextOnly Try to get "Next:" menu item. + IS_PageOnly Simply give up at the bottom of a node. */ + +int info_scroll_behaviour = IS_Continuous; + +/* Choices used by the completer when reading a value for the user-visible + variable "scroll-behaviour". */ +char *info_scroll_choices[] = { + "Continuous", "Next Only", "Page Only", (char *)NULL +}; + +/* Default window sizes for scrolling commands. */ +int default_window_size = -1; /* meaning 1 window-full */ +int default_scroll_size = -1; /* meaning half screen size */ + +#define INFO_LABEL_FOUND() \ + (info_parsed_nodename || (info_parsed_filename \ + && !is_dir_name (info_parsed_filename))) + +/* Move to 1st menu item, Next, Up/Next, or error in this window. */ +static void +forward_move_node_structure (WINDOW *window, int behaviour) +{ + switch (behaviour) + { + case IS_PageOnly: + info_error ((char *) msg_at_node_bottom, NULL, NULL); + break; + + case IS_NextOnly: + info_next_label_of_node (window->node); + if (!info_parsed_nodename && !info_parsed_filename) + info_error ((char *) msg_no_pointer, (char *) _("Next"), NULL); + else + { + window_message_in_echo_area ((char *) _("Following Next node..."), + NULL, NULL); + info_handle_pointer ("Next", window); + } + break; + + case IS_Continuous: + { + /* First things first. If this node contains a menu, move down + into the menu. */ + { + REFERENCE **menu; + + menu = info_menu_of_node (window->node); + + if (menu) + { + info_free_references (menu); + window_message_in_echo_area ((char *) _("Selecting first menu item..."), + NULL, NULL); + info_menu_digit (window, 1, '1'); + return; + } + } + + /* Okay, this node does not contain a menu. If it contains a + "Next:" pointer, use that. */ + info_next_label_of_node (window->node); + if (INFO_LABEL_FOUND ()) + { + window_message_in_echo_area ((char *) _("Selecting Next node..."), + NULL, NULL); + info_handle_pointer ("Next", window); + return; + } + + /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we + can move "Next:". If that isn't possible, complain that there + are no more nodes. */ + { + int up_counter, old_current; + INFO_WINDOW *info_win; + + /* Remember the current node and location. */ + info_win = get_info_window_of_window (window); + old_current = info_win->current; + + /* Back up through the "Up:" pointers until we have found a "Next:" + that isn't the same as the first menu item found in that node. */ + up_counter = 0; + while (!info_error_was_printed) + { + info_up_label_of_node (window->node); + if (INFO_LABEL_FOUND ()) + { + info_handle_pointer ("Up", window); + if (info_error_was_printed) + continue; + + up_counter++; + + info_next_label_of_node (window->node); + + /* If no "Next" pointer, keep backing up. */ + if (!INFO_LABEL_FOUND ()) + continue; + + /* If this node's first menu item is the same as this node's + Next pointer, keep backing up. */ + if (!info_parsed_filename) + { + REFERENCE **menu; + char *next_nodename; + + /* Remember the name of the Next node, since reading + the menu can overwrite the contents of the + info_parsed_xxx strings. */ + next_nodename = xstrdup (info_parsed_nodename); + + menu = info_menu_of_node (window->node); + if (menu && + (strcmp + (menu[0]->nodename, next_nodename) == 0)) + { + info_free_references (menu); + free (next_nodename); + continue; + } + else + { + /* Restore the world to where it was before + reading the menu contents. */ + info_free_references (menu); + free (next_nodename); + info_next_label_of_node (window->node); + } + } + + /* This node has a "Next" pointer, and it is not the + same as the first menu item found in this node. */ + window_message_in_echo_area + ((char *) _("Moving Up %d time(s), then Next."), + (void *) (long) up_counter, NULL); + + info_handle_pointer ("Next", window); + return; + } + else + { + /* No more "Up" pointers. Print an error, and call it + quits. */ + register int i; + + for (i = 0; i < up_counter; i++) + { + info_win->nodes_index--; + free (info_win->nodes[info_win->nodes_index]); + info_win->nodes[info_win->nodes_index] = (NODE *)NULL; + } + info_win->current = old_current; + window->node = info_win->nodes[old_current]; + window->pagetop = info_win->pagetops[old_current]; + window->point = info_win->points[old_current]; + recalculate_line_starts (window); + window->flags |= W_UpdateWindow; + info_error ((char *) _("No more nodes within this document."), + NULL, NULL); + } + } + } + break; + } + } +} + +/* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */ +static void +backward_move_node_structure (WINDOW *window, int behaviour) +{ + switch (behaviour) + { + case IS_PageOnly: + info_error ((char *) msg_at_node_top, NULL, NULL); + break; + + case IS_NextOnly: + info_prev_label_of_node (window->node); + if (!info_parsed_nodename && !info_parsed_filename) + info_error ((char *) _("No `Prev' for this node."), NULL, NULL); + else + { + window_message_in_echo_area ((char *) _("Moving Prev in this window."), + NULL, NULL); + info_handle_pointer ("Prev", window); + } + break; + + case IS_Continuous: + info_prev_label_of_node (window->node); + + if (!info_parsed_nodename && (!info_parsed_filename + || is_dir_name (info_parsed_filename))) + { + info_up_label_of_node (window->node); + if (!info_parsed_nodename && (!info_parsed_filename + || is_dir_name (info_parsed_filename))) + info_error ((char *) + _("No `Prev' or `Up' for this node within this document."), + NULL, NULL); + else + { + window_message_in_echo_area ((char *) _("Moving Up in this window."), + NULL, NULL); + info_handle_pointer ("Up", window); + } + } + else + { + REFERENCE **menu; + int inhibit_menu_traversing = 0; + + /* Watch out! If this node's Prev is the same as the Up, then + move Up. Otherwise, we could move Prev, and then to the last + menu item in the Prev. This would cause the user to loop + through a subsection of the info file. */ + if (!info_parsed_filename && info_parsed_nodename) + { + char *pnode; + + pnode = xstrdup (info_parsed_nodename); + info_up_label_of_node (window->node); + + if (!info_parsed_filename && info_parsed_nodename && + strcmp (info_parsed_nodename, pnode) == 0) + { + /* The nodes are the same. Inhibit moving to the last + menu item. */ + free (pnode); + inhibit_menu_traversing = 1; + } + else + { + free (pnode); + info_prev_label_of_node (window->node); + } + } + + /* Move to the previous node. If this node now contains a menu, + and we have not inhibited movement to it, move to the node + corresponding to the last menu item. */ + window_message_in_echo_area ((char *) _("Moving Prev in this window."), + NULL, NULL); + info_handle_pointer ("Prev", window); + + if (!inhibit_menu_traversing) + { + while (!info_error_was_printed && + (menu = info_menu_of_node (window->node))) + { + info_free_references (menu); + window_message_in_echo_area + ((char *) _("Moving to `Prev's last menu item."), NULL, NULL); + info_menu_digit (window, 1, '0'); + } + } + } + break; + } +} + +/* Move continuously forward through the node structure of this info file. */ +DECLARE_INFO_COMMAND (info_global_next_node, + _("Move forwards or down through node structure")) +{ + if (count < 0) + info_global_prev_node (window, -count, key); + else + { + while (count && !info_error_was_printed) + { + forward_move_node_structure (window, IS_Continuous); + count--; + } + } +} + +/* Move continuously backward through the node structure of this info file. */ +DECLARE_INFO_COMMAND (info_global_prev_node, + _("Move backwards or up through node structure")) +{ + if (count < 0) + info_global_next_node (window, -count, key); + else + { + while (count && !info_error_was_printed) + { + backward_move_node_structure (window, IS_Continuous); + count--; + } + } +} + +static void _scroll_forward(WINDOW *window, int count, + unsigned char key, int behaviour); +static void _scroll_backward(WINDOW *window, int count, + unsigned char key, int behaviour); + +static void +_scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour) +{ + if (count < 0) + _scroll_backward (window, -count, key, behaviour); + else + { + int desired_top; + + /* Without an explicit numeric argument, scroll the bottom two + lines to the top of this window, Or, if at bottom of window, + and the chosen behaviour is to scroll through nodes get the + "Next" node for this window. */ + if (default_window_size > 0) + desired_top = window->pagetop + default_window_size; + else if (!info_explicit_arg && count == 1) + { + desired_top = window->pagetop + (window->height - 2); + + /* If there are no more lines to scroll here, error, or get + another node, depending on BEHAVIOUR. */ + if (desired_top > window->line_count) + { + forward_move_node_structure (window, behaviour); + return; + } + } + else + desired_top = window->pagetop + count; + + if (desired_top >= window->line_count) + desired_top = window->line_count - 2; + + if (window->pagetop > desired_top) + return; + else + set_window_pagetop (window, desired_top); + } +} + +static void +_scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour) +{ + if (count < 0) + _scroll_forward (window, -count, key, behaviour); + else + { + int desired_top; + + /* Without an explicit numeric argument, scroll the top two lines + to the bottom of this window, or, depending on the selected + behaviour, move to the previous, or Up'th node. */ + if (default_window_size > 0) + desired_top = window->pagetop - default_window_size; + else if (!info_explicit_arg && count == 1) + { + desired_top = window->pagetop - (window->height - 2); + + if ((desired_top < 0) && (window->pagetop == 0)) + { + backward_move_node_structure (window, behaviour); + return; + } + } + else + desired_top = window->pagetop - count; + + if (desired_top < 0) + desired_top = 0; + + set_window_pagetop (window, desired_top); + } +} + +/* Show the next screen of WINDOW's node. */ +DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window")) +{ + _scroll_forward (window, count, key, info_scroll_behaviour); +} + +/* Like info_scroll_forward, but sets default_window_size as a side + effect. */ +DECLARE_INFO_COMMAND (info_scroll_forward_set_window, + _("Scroll forward in this window and set default window size")) +{ + if (info_explicit_arg) + default_window_size = count; + _scroll_forward (window, count, key, info_scroll_behaviour); +} + +/* Show the next screen of WINDOW's node but never advance to next node. */ +DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node")) +{ + _scroll_forward (window, count, key, IS_PageOnly); +} + +/* Like info_scroll_forward_page_only, but sets default_window_size as a side + effect. */ +DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window, + _("Scroll forward in this window staying within node and set default window size")) +{ + if (info_explicit_arg) + default_window_size = count; + _scroll_forward (window, count, key, IS_PageOnly); +} + +/* Show the previous screen of WINDOW's node. */ +DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window")) +{ + _scroll_backward (window, count, key, info_scroll_behaviour); +} + +/* Like info_scroll_backward, but sets default_window_size as a side + effect. */ +DECLARE_INFO_COMMAND (info_scroll_backward_set_window, + _("Scroll backward in this window and set default window size")) +{ + if (info_explicit_arg) + default_window_size = count; + _scroll_backward (window, count, key, info_scroll_behaviour); +} + +/* Show the previous screen of WINDOW's node but never move to previous + node. */ +DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node")) +{ + _scroll_backward (window, count, key, IS_PageOnly); +} + +/* Like info_scroll_backward_page_only, but sets default_window_size as a side + effect. */ +DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window, + _("Scroll backward in this window staying within node and set default window size")) +{ + if (info_explicit_arg) + default_window_size = count; + _scroll_backward (window, count, key, IS_PageOnly); +} + +/* Move to the beginning of the node. */ +DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node")) +{ + window->pagetop = window->point = 0; + window->flags |= W_UpdateWindow; +} + +/* Move to the end of the node. */ +DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node")) +{ + window->point = window->node->nodelen - 1; + info_show_point (window); +} + +/* Scroll the window forward by N lines. */ +DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines")) +{ + if (count < 0) + info_up_line (window, -count, key); + else + { + int desired_top = window->pagetop + count; + + if (desired_top >= window->line_count) + desired_top = window->line_count - 2; + + if (window->pagetop <= desired_top) + set_window_pagetop (window, desired_top); + } +} + +/* Scroll the window backward by N lines. */ +DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines")) +{ + if (count < 0) + info_down_line (window, -count, key); + else + { + int desired_top = window->pagetop - count; + + if (desired_top < 0) + desired_top = 0; + + set_window_pagetop (window, desired_top); + } +} + +/* Scroll the window forward by N lines and remember N as default for + subsequent commands. */ +DECLARE_INFO_COMMAND (info_scroll_half_screen_down, + _("Scroll down by half screen size")) +{ + if (count < 0) + info_scroll_half_screen_up (window, -count, key); + else + { + int scroll_size = (the_screen->height + 1) / 2; + int desired_top; + + if (info_explicit_arg) + default_scroll_size = count; + if (default_scroll_size > 0) + scroll_size = default_scroll_size; + + desired_top = window->pagetop + scroll_size; + if (desired_top >= window->line_count) + desired_top = window->line_count - 2; + + if (window->pagetop <= desired_top) + set_window_pagetop (window, desired_top); + } +} + +/* Scroll the window backward by N lines and remember N as default for + subsequent commands. */ +DECLARE_INFO_COMMAND (info_scroll_half_screen_up, + _("Scroll up by half screen size")) +{ + if (count < 0) + info_scroll_half_screen_down (window, -count, key); + else + { + int scroll_size = (the_screen->height + 1) / 2; + int desired_top; + + if (info_explicit_arg) + default_scroll_size = count; + if (default_scroll_size > 0) + scroll_size = default_scroll_size; + + desired_top = window->pagetop - scroll_size; + if (desired_top < 0) + desired_top = 0; + + set_window_pagetop (window, desired_top); + } +} + +/* **************************************************************** */ +/* */ +/* Commands for Manipulating Windows */ +/* */ +/* **************************************************************** */ + +/* Make the next window in the chain be the active window. */ +DECLARE_INFO_COMMAND (info_next_window, _("Select the next window")) +{ + if (count < 0) + { + info_prev_window (window, -count, key); + return; + } + + /* If no other window, error now. */ + if (!windows->next && !echo_area_is_active) + { + info_error ((char *) msg_one_window, NULL, NULL); + return; + } + + while (count--) + { + if (window->next) + window = window->next; + else + { + if (window == the_echo_area || !echo_area_is_active) + window = windows; + else + window = the_echo_area; + } + } + + if (active_window != window) + { + if (auto_footnotes_p) + info_get_or_remove_footnotes (window); + + window->flags |= W_UpdateWindow; + active_window = window; + } +} + +/* Make the previous window in the chain be the active window. */ +DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window")) +{ + if (count < 0) + { + info_next_window (window, -count, key); + return; + } + + /* Only one window? */ + + if (!windows->next && !echo_area_is_active) + { + info_error ((char *) msg_one_window, NULL, NULL); + return; + } + + while (count--) + { + /* If we are in the echo area, or if the echo area isn't active and we + are in the first window, find the last window in the chain. */ + if (window == the_echo_area || + (window == windows && !echo_area_is_active)) + { + register WINDOW *win, *last = NULL; + + for (win = windows; win; win = win->next) + last = win; + + window = last; + } + else + { + if (window == windows) + window = the_echo_area; + else + window = window->prev; + } + } + + if (active_window != window) + { + if (auto_footnotes_p) + info_get_or_remove_footnotes (window); + + window->flags |= W_UpdateWindow; + active_window = window; + } +} + +/* Split WINDOW into two windows, both showing the same node. If we + are automatically tiling windows, re-tile after the split. */ +DECLARE_INFO_COMMAND (info_split_window, _("Split the current window")) +{ + WINDOW *split, *old_active; + int pagetop; + + /* Remember the current pagetop of the window being split. If it doesn't + change, we can scroll its contents around after the split. */ + pagetop = window->pagetop; + + /* Make the new window. */ + old_active = active_window; + active_window = window; + split = window_make_window (window->node); + active_window = old_active; + + if (!split) + { + info_error ((char *) msg_win_too_small, NULL, NULL); + } + else + { +#if defined (SPLIT_BEFORE_ACTIVE) + /* Try to scroll the old window into its new postion. */ + if (pagetop == window->pagetop) + { + int start, end, amount; + + start = split->first_row; + end = start + window->height; + amount = split->height + 1; + display_scroll_display (start, end, amount); + } +#else /* !SPLIT_BEFORE_ACTIVE */ + /* Make sure point still appears in the active window. */ + info_show_point (window); +#endif /* !SPLIT_BEFORE_ACTIVE */ + + /* If the window just split was one internal to Info, try to display + something else in it. */ + if (internal_info_node_p (split->node)) + { + register int i, j; + INFO_WINDOW *iw; + NODE *node = (NODE *)NULL; + char *filename; + + for (i = 0; (iw = info_windows[i]); i++) + { + for (j = 0; j < iw->nodes_index; j++) + if (!internal_info_node_p (iw->nodes[j])) + { + if (iw->nodes[j]->parent) + filename = iw->nodes[j]->parent; + else + filename = iw->nodes[j]->filename; + + node = info_get_node (filename, iw->nodes[j]->nodename); + if (node) + { + window_set_node_of_window (split, node); + i = info_windows_index - 1; + break; + } + } + } + } + split->pagetop = window->pagetop; + + if (auto_tiling_p) + window_tile_windows (DONT_TILE_INTERNALS); + else + window_adjust_pagetop (split); + + remember_window_and_node (split, split->node); + } +} + +/* Delete WINDOW, forgetting the list of last visited nodes. If we are + automatically displaying footnotes, show or remove the footnotes + window. If we are automatically tiling windows, re-tile after the + deletion. */ +DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window")) +{ + if (!windows->next) + { + info_error ((char *) msg_cant_kill_last, NULL, NULL); + } + else if (window->flags & W_WindowIsPerm) + { + info_error ((char *) _("Cannot delete a permanent window"), NULL, NULL); + } + else + { + info_delete_window_internal (window); + + if (auto_footnotes_p) + info_get_or_remove_footnotes (active_window); + + if (auto_tiling_p) + window_tile_windows (DONT_TILE_INTERNALS); + } +} + +/* Do the physical deletion of WINDOW, and forget this window and + associated nodes. */ +void +info_delete_window_internal (WINDOW *window) +{ + if (windows->next && ((window->flags & W_WindowIsPerm) == 0)) + { + /* We not only delete the window from the display, we forget it from + our list of remembered windows. */ + forget_window_and_nodes (window); + window_delete_window (window); + + if (echo_area_is_active) + echo_area_inform_of_deleted_window (window); + } +} + +/* Just keep WINDOW, deleting all others. */ +DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows")) +{ + int num_deleted; /* The number of windows we deleted. */ + int pagetop, start, end; + + /* Remember a few things about this window. We may be able to speed up + redisplay later by scrolling its contents. */ + pagetop = window->pagetop; + start = window->first_row; + end = start + window->height; + + num_deleted = 0; + + while (1) + { + WINDOW *win; + + /* Find an eligible window and delete it. If no eligible windows + are found, we are done. A window is eligible for deletion if + is it not permanent, and it is not WINDOW. */ + for (win = windows; win; win = win->next) + if (win != window && ((win->flags & W_WindowIsPerm) == 0)) + break; + + if (!win) + break; + + info_delete_window_internal (win); + num_deleted++; + } + + /* Scroll the contents of this window into the right place so that the + user doesn't have to wait any longer than necessary for redisplay. */ + if (num_deleted) + { + int amount; + + amount = (window->first_row - start); + amount -= (window->pagetop - pagetop); + display_scroll_display (start, end, amount); + } + + window->flags |= W_UpdateWindow; +} + +/* Scroll the "other" window of WINDOW. */ +DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window")) +{ + WINDOW *other; + + /* If only one window, give up. */ + if (!windows->next) + { + info_error ((char *) msg_one_window, NULL, NULL); + return; + } + + other = window->next; + + if (!other) + other = window->prev; + + info_scroll_forward (other, count, key); +} + +/* Scroll the "other" window of WINDOW. */ +DECLARE_INFO_COMMAND (info_scroll_other_window_backward, + _("Scroll the other window backward")) +{ + info_scroll_other_window (window, -count, key); +} + +/* Change the size of WINDOW by AMOUNT. */ +DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window")) +{ + window_change_window_height (window, count); +} + +/* When non-zero, tiling takes place automatically when info_split_window + is called. */ +int auto_tiling_p = 0; + +/* Tile all of the visible windows. */ +DECLARE_INFO_COMMAND (info_tile_windows, + _("Divide the available screen space among the visible windows")) +{ + window_tile_windows (TILE_INTERNALS); +} + +/* Toggle the state of this window's wrapping of lines. */ +DECLARE_INFO_COMMAND (info_toggle_wrap, + _("Toggle the state of line wrapping in the current window")) +{ + window_toggle_wrap (window); +} + +/* **************************************************************** */ +/* */ +/* Info Node Commands */ +/* */ +/* **************************************************************** */ + +/* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's + filename is not set. */ +char * +node_printed_rep (NODE *node) +{ + char *rep; + + if (node->filename) + { + char *filename + = filename_non_directory (node->parent ? node->parent : node->filename); + rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1); + sprintf (rep, "(%s)%s", filename, node->nodename); + } + else + rep = node->nodename; + + return rep; +} + + +/* Using WINDOW for various defaults, select the node referenced by ENTRY + in it. If the node is selected, the window and node are remembered. */ +void +info_select_reference (WINDOW *window, REFERENCE *entry) +{ + NODE *node; + char *filename, *nodename, *file_system_error; + + file_system_error = (char *)NULL; + + filename = entry->filename; + if (!filename) + filename = window->node->parent; + if (!filename) + filename = window->node->filename; + + if (filename) + filename = xstrdup (filename); + + if (entry->nodename) + nodename = xstrdup (entry->nodename); + else + nodename = xstrdup ("Top"); + + node = info_get_node (filename, nodename); + + /* Try something a little weird. If the node couldn't be found, and the + reference was of the form "foo::", see if the entry->label can be found + as a file, with a node of "Top". */ + if (!node) + { + if (info_recent_file_error) + file_system_error = xstrdup (info_recent_file_error); + + if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0)) + { + node = info_get_node (entry->label, "Top"); + if (!node && info_recent_file_error) + { + maybe_free (file_system_error); + file_system_error = xstrdup (info_recent_file_error); + } + } + } + + if (!node) + { + if (file_system_error) + info_error (file_system_error, NULL, NULL); + else + info_error ((char *) msg_cant_find_node, nodename, NULL); + } + + maybe_free (file_system_error); + maybe_free (filename); + maybe_free (nodename); + + if (node) + info_set_node_of_window (1, window, node); +} + +/* Parse the node specification in LINE using WINDOW to default the filename. + Select the parsed node in WINDOW and remember it, or error if the node + couldn't be found. */ +static void +info_parse_and_select (char *line, WINDOW *window) +{ + REFERENCE entry; + + info_parse_node (line, DONT_SKIP_NEWLINES); + + entry.nodename = info_parsed_nodename; + entry.filename = info_parsed_filename; + entry.label = "*info-parse-and-select*"; + + info_select_reference (window, &entry); +} + +/* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME + are previously filled, try to get the node represented by them into + WINDOW. The node should have been pointed to by the LABEL pointer of + WINDOW->node. */ +static void +info_handle_pointer (char *label, WINDOW *window) +{ + if (info_parsed_filename || info_parsed_nodename) + { + char *filename, *nodename; + NODE *node; + + filename = nodename = (char *)NULL; + + if (info_parsed_filename) + filename = xstrdup (info_parsed_filename); + else + { + if (window->node->parent) + filename = xstrdup (window->node->parent); + else if (window->node->filename) + filename = xstrdup (window->node->filename); + } + + if (info_parsed_nodename) + nodename = xstrdup (info_parsed_nodename); + else + nodename = xstrdup ("Top"); + + node = info_get_node (filename, nodename); + + if (node) + { + INFO_WINDOW *info_win; + + info_win = get_info_window_of_window (window); + if (info_win) + { + info_win->pagetops[info_win->current] = window->pagetop; + info_win->points[info_win->current] = window->point; + } + info_set_node_of_window (1, window, node); + } + else + { + if (info_recent_file_error) + info_error (info_recent_file_error, NULL, NULL); + else + info_error ((char *) msg_cant_file_node, filename, nodename); + } + + free (filename); + free (nodename); + } + else + { + info_error ((char *) msg_no_pointer, label, NULL); + } +} + +/* Make WINDOW display the "Next:" node of the node currently being + displayed. */ +DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node")) +{ + info_next_label_of_node (window->node); + info_handle_pointer ("Next", window); +} + +/* Make WINDOW display the "Prev:" node of the node currently being + displayed. */ +DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node")) +{ + info_prev_label_of_node (window->node); + info_handle_pointer ("Prev", window); +} + +/* Make WINDOW display the "Up:" node of the node currently being + displayed. */ +DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node")) +{ + info_up_label_of_node (window->node); + info_handle_pointer ("Up", window); +} + +/* Make WINDOW display the last node of this info file. */ +DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file")) +{ + register int i; + FILE_BUFFER *fb = file_buffer_of_window (window); + NODE *node = (NODE *)NULL; + + if (fb && fb->tags) + { + int last_node_tag_idx = -1; + + /* If no explicit argument, or argument of zero, default to the + last node. */ + if (count == 0 || (count == 1 && !info_explicit_arg)) + count = -1; + for (i = 0; count && fb->tags[i]; i++) + if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ + { + count--; + last_node_tag_idx = i; + } + if (count > 0) + i = last_node_tag_idx + 1; + if (i > 0) + node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); + } + + if (!node) + info_error ((char *) _("This window has no additional nodes"), NULL, NULL); + else + info_set_node_of_window (1, window, node); +} + +/* Make WINDOW display the first node of this info file. */ +DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file")) +{ + FILE_BUFFER *fb = file_buffer_of_window (window); + NODE *node = (NODE *)NULL; + + /* If no explicit argument, or argument of zero, default to the + first node. */ + if (count == 0) + count = 1; + if (fb && fb->tags) + { + register int i; + int last_node_tag_idx = -1; + + for (i = 0; count && fb->tags[i]; i++) + if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ + { + count--; + last_node_tag_idx = i; + } + if (count > 0) + i = last_node_tag_idx + 1; + if (i > 0) + node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); + } + + if (!node) + info_error ((char *) _("This window has no additional nodes"), NULL, NULL); + else + info_set_node_of_window (1, window, node); +} + +/* Select the last menu item in WINDOW->node. */ +DECLARE_INFO_COMMAND (info_last_menu_item, + _("Select the last item in this node's menu")) +{ + info_menu_digit (window, 1, '0'); +} + +/* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */ +DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item")) +{ + register int i, item; + register REFERENCE **menu; + + menu = info_menu_of_node (window->node); + + if (!menu) + { + info_error ((char *) msg_no_menu_node, NULL, NULL); + return; + } + + /* We have the menu. See if there are this many items in it. */ + item = key - '0'; + + /* Special case. Item "0" is the last item in this menu. */ + if (item == 0) + for (i = 0; menu[i + 1]; i++); + else + { + for (i = 0; menu[i]; i++) + if (i == item - 1) + break; + } + + if (menu[i]) + { + info_select_reference (window, menu[i]); + if (menu[i]->line_number > 0) + info_next_line (window, menu[i]->line_number - 1, key); + } + else + info_error ((char *) _("There aren't %d items in this menu."), + (void *) (long) item, NULL); + + info_free_references (menu); + return; +} + + + +/* Return a pointer to the xref in XREF_LIST that is nearest to POS, or + NULL if XREF_LIST is empty. That is, if POS is within any of the + given xrefs, return that one. Otherwise, return the one with the + nearest beginning or end. If there are two that are equidistant, + prefer the one forward. The return is in newly-allocated memory, + since the caller frees it. + + This is called from info_menu_or_ref_item with XREF_LIST being all + the xrefs in the node, and POS being point. The ui function that + starts it all off is select-reference-this-line. + + This is not the same logic as in info.el. Info-get-token prefers + searching backwards to searching forwards, and has a hardwired search + limit of 200 chars (in Emacs 21.2). */ + +static REFERENCE ** +nearest_xref (REFERENCE **xref_list, long int pos) +{ + int this_xref; + int nearest = -1; + long best_delta = -1; + + for (this_xref = 0; xref_list[this_xref]; this_xref++) + { + long delta; + REFERENCE *xref = xref_list[this_xref]; + if (xref->start <= pos && pos <= xref->end) + { /* POS is within this xref, we're done */ + nearest = this_xref; + break; + } + + /* See how far POS is from this xref. Take into account the + `*Note' that begins the xref, since as far as the user is + concerned, that's where it starts. */ + delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))), + labs (pos - xref->end)); + + /* It's the <= instead of < that makes us choose the forward xref + of POS if two are equidistant. Of course, because of all the + punctuation surrounding xrefs, it's not necessarily obvious + where one ends. */ + if (delta <= best_delta || best_delta < 0) + { + nearest = this_xref; + best_delta = delta; + } + } + + /* Maybe there was no list to search through. */ + if (nearest < 0) + return NULL; + + /* Ok, we have a nearest xref, make a list of it. */ + { + REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2); + ret[0] = info_copy_reference (xref_list[nearest]); + ret[1] = NULL; + return ret; + } +} + + +/* Read a menu or followed reference from the user defaulting to the + reference found on the current line, and select that node. The + reading is done with completion. BUILDER is the function used + to build the list of references. ASK_P is non-zero if the user + should be prompted, or zero to select the default item. */ +static void +info_menu_or_ref_item (WINDOW *window, int count, + unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p) +{ + char *line; + REFERENCE *entry; + REFERENCE *defentry = NULL; + REFERENCE **menu = (*builder) (window->node); + + if (!menu) + { + if (builder == info_menu_of_node) + info_error ((char *) msg_no_menu_node, NULL, NULL); + else + info_error ((char *) msg_no_xref_node, NULL, NULL); + return; + } + + /* Default the selected reference to the one which is on the line that + point is in. */ + { + REFERENCE **refs = NULL; + int point_line = window_line_of_point (window); + + if (point_line != -1) + { + SEARCH_BINDING binding; + + binding.buffer = window->node->contents; + binding.start = window->line_starts[point_line] - binding.buffer; + if (window->line_starts[point_line + 1]) + binding.end = window->line_starts[point_line + 1] - binding.buffer; + else + binding.end = window->node->nodelen; + binding.flags = 0; + + if (builder == info_menu_of_node) + { + if (point_line) + { + binding.start--; + refs = info_menu_items (&binding); + } + } + else + { +#if defined (HANDLE_MAN_PAGES) + if (window->node->flags & N_IsManPage) + refs = manpage_xrefs_in_binding (window->node, &binding); + else +#endif /* HANDLE_MAN_PAGES */ + refs = nearest_xref (menu, window->point); + } + + if (refs && refs[0]) + { + if (strcmp (refs[0]->label, "Menu") != 0 + || builder == info_xrefs_of_node) + { + int which = 0; + + /* For xrefs, find the closest reference to point, + unless we only have one reference (as we will if + we've called nearest_xref above). It would be better + to have only one piece of code, but the conditions + when we call this are tangled. */ + if (builder == info_xrefs_of_node && refs[1]) + { + int closest = -1; + + for (; refs[which]; which++) + { + if (window->point >= refs[which]->start + && window->point <= refs[which]->end) + { + closest = which; + break; + } + else if (window->point < refs[which]->start) + break; + } + if (which > 0) + { + if (closest == -1) + which--; + else + which = closest; + } + } + + defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + defentry->label = xstrdup (refs[which]->label); + defentry->filename = refs[which]->filename; + defentry->nodename = refs[which]->nodename; + defentry->line_number = refs[which]->line_number; + + if (defentry->filename) + defentry->filename = xstrdup (defentry->filename); + if (defentry->nodename) + defentry->nodename = xstrdup (defentry->nodename); + } + info_free_references (refs); + } + } + } + + /* If we are going to ask the user a question, do it now. */ + if (ask_p) + { + char *prompt; + + /* Build the prompt string. */ + if (builder == info_menu_of_node) + { + if (defentry) + { + prompt = xmalloc (strlen (defentry->label) + + strlen (_("Menu item (%s): "))); + sprintf (prompt, _("Menu item (%s): "), defentry->label); + } + else + prompt = xstrdup (_("Menu item: ")); + } + else + { + if (defentry) + { + prompt = xmalloc (strlen (defentry->label) + + strlen (_("Follow xref (%s): "))); + sprintf (prompt, _("Follow xref (%s): "), defentry->label); + } + else + prompt = xstrdup (_("Follow xref: ")); + } + + line = info_read_completing_in_echo_area (window, prompt, menu); + free (prompt); + + window = active_window; + + /* User aborts, just quit. */ + if (!line) + { + maybe_free (defentry); + info_free_references (menu); + info_abort_key (window, 0, 0); + return; + } + + /* If we had a default and the user accepted it, use that. */ + if (!*line) + { + free (line); + if (defentry) + line = xstrdup (defentry->label); + else + line = (char *)NULL; + } + } + else + { + /* Not going to ask any questions. If we have a default entry, use + that, otherwise return. */ + if (!defentry) + return; + else + line = xstrdup (defentry->label); + } + + if (line) + { + /* It is possible that the references have more than a single + entry with the same label, and also LINE is down-cased, which + complicates matters even more. Try to be as accurate as we + can: if they've chosen the default, use defentry directly. */ + if (defentry && strcmp (line, defentry->label) == 0) + entry = defentry; + else + /* Find the selected label in the references. If there are + more than one label which matches, find the one that's + closest to point. */ + { + register int i; + int best = -1, min_dist = window->node->nodelen; + REFERENCE *ref; + + for (i = 0; menu && (ref = menu[i]); i++) + { + /* Need to use strcasecmp because LINE is downcased + inside info_read_completing_in_echo_area. */ + if (strcasecmp (line, ref->label) == 0) + { + /* ref->end is more accurate estimate of position + for menus than ref->start. Go figure. */ + int dist = abs (window->point - ref->end); + + if (dist < min_dist) + { + min_dist = dist; + best = i; + } + } + } + if (best != -1) + entry = menu[best]; + else + entry = (REFERENCE *)NULL; + } + + if (!entry && defentry) + info_error ((char *) _("The reference disappeared! (%s)."), line, NULL); + else + { + NODE *orig = window->node; + info_select_reference (window, entry); + + if (builder == info_xrefs_of_node && window->node != orig + && !(window->node->flags & N_FromAnchor)) + { /* Search for this reference in the node. */ + long offset; + long start; + + if (window->line_count > 0) + start = window->line_starts[1] - window->node->contents; + else + start = 0; + + offset = + info_target_search_node (window->node, entry->label, start); + + if (offset != -1) + { + window->point = offset; + window_adjust_pagetop (window); + } + } + + if (entry->line_number > 0) + /* next_line starts at line 1? Anyway, the -1 makes it + move to the right line. */ + info_next_line (window, entry->line_number - 1, key); + } + + free (line); + if (defentry) + { + free (defentry->label); + maybe_free (defentry->filename); + maybe_free (defentry->nodename); + free (defentry); + } + } + + info_free_references (menu); + + if (!info_error_was_printed) + window_clear_echo_area (); +} + +/* Read a line (with completion) which is the name of a menu item, + and select that item. */ +DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node")) +{ + info_menu_or_ref_item (window, count, key, info_menu_of_node, 1); +} + +/* Read a line (with completion) which is the name of a reference to + follow, and select the node. */ +DECLARE_INFO_COMMAND + (info_xref_item, _("Read a footnote or cross reference and select its node")) +{ + info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1); +} + +/* Position the cursor at the start of this node's menu. */ +DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu")) +{ + SEARCH_BINDING binding; + long position; + + binding.buffer = window->node->contents; + binding.start = 0; + binding.end = window->node->nodelen; + binding.flags = S_FoldCase | S_SkipDest; + + position = search (INFO_MENU_LABEL, &binding); + + if (position == -1) + info_error ((char *) msg_no_menu_node, NULL, NULL); + else + { + window->point = position; + window_adjust_pagetop (window); + window->flags |= W_UpdateWindow; + } +} + +/* Visit as many menu items as is possible, each in a separate window. */ +DECLARE_INFO_COMMAND (info_visit_menu, + _("Visit as many menu items at once as possible")) +{ + register int i; + REFERENCE *entry, **menu; + + menu = info_menu_of_node (window->node); + + if (!menu) + info_error ((char *) msg_no_menu_node, NULL, NULL); + + for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++) + { + WINDOW *new; + + new = window_make_window (window->node); + window_tile_windows (TILE_INTERNALS); + + if (!new) + info_error ((char *) msg_win_too_small, NULL, NULL); + else + { + active_window = new; + info_select_reference (new, entry); + } + } +} + +/* Read a line of input which is a node name, and go to that node. */ +DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it")) +{ + char *line; + +#define GOTO_COMPLETES +#if defined (GOTO_COMPLETES) + /* Build a completion list of all of the known nodes. */ + { + register int fbi, i; + FILE_BUFFER *current; + REFERENCE **items = (REFERENCE **)NULL; + int items_index = 0; + int items_slots = 0; + + current = file_buffer_of_window (window); + + for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++) + { + FILE_BUFFER *fb; + REFERENCE *entry; + int this_is_the_current_fb; + + fb = info_loaded_files[fbi]; + this_is_the_current_fb = (current == fb); + + entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + entry->filename = entry->nodename = (char *)NULL; + entry->label = (char *)xmalloc (4 + strlen (fb->filename)); + sprintf (entry->label, "(%s)*", fb->filename); + + add_pointer_to_array + (entry, items_index, items, items_slots, 10, REFERENCE *); + + if (fb->tags) + { + for (i = 0; fb->tags[i]; i++) + { + entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + entry->filename = entry->nodename = (char *)NULL; + if (this_is_the_current_fb) + entry->label = xstrdup (fb->tags[i]->nodename); + else + { + entry->label = (char *) xmalloc + (4 + strlen (fb->filename) + + strlen (fb->tags[i]->nodename)); + sprintf (entry->label, "(%s)%s", + fb->filename, fb->tags[i]->nodename); + } + + add_pointer_to_array + (entry, items_index, items, items_slots, 100, REFERENCE *); + } + } + } + line = info_read_maybe_completing (window, (char *) _("Goto node: "), + items); + info_free_references (items); + } +#else /* !GOTO_COMPLETES */ + line = info_read_in_echo_area (window, (char *) _("Goto node: ")); +#endif /* !GOTO_COMPLETES */ + + /* If the user aborted, quit now. */ + if (!line) + { + info_abort_key (window, 0, 0); + return; + } + + canonicalize_whitespace (line); + + if (*line) + info_parse_and_select (line, window); + + free (line); + if (!info_error_was_printed) + window_clear_echo_area (); +} + +/* Follow the menu list in MENUS (list of strings terminated by a NULL + entry) from INITIAL_NODE. If can't continue at any point (no menu or + no menu entry for the next item), return the node so far -- that + might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will + be set to the error message and argument for message, otherwise they + will be NULL. */ + +NODE * +info_follow_menus (NODE *initial_node, char **menus, + const char **errstr, char **errarg1, char **errarg2) +{ + NODE *node = NULL; + *errstr = *errarg1 = *errarg2 = NULL; + + for (; *menus; menus++) + { + static char *first_arg = NULL; + REFERENCE **menu; + REFERENCE *entry; + char *arg = *menus; /* Remember the name of the menu entry we want. */ + + /* A leading space is certainly NOT part of a node name. Most + probably, they typed a space after the separating comma. The + strings in menus[] have their whitespace canonicalized, so + there's at most one space to ignore. */ + if (*arg == ' ') + arg++; + if (!first_arg) + first_arg = arg; + + /* Build and return a list of the menu items in this node. */ + menu = info_menu_of_node (initial_node); + + /* If no menu item in this node, stop here, but let the user + continue to use Info. Perhaps they wanted this node and didn't + realize it. */ + if (!menu) + { + if (arg == first_arg) + { + node = make_manpage_node (first_arg); + if (node) + goto maybe_got_node; + } + *errstr = _("No menu in node `%s'."); + *errarg1 = node_printed_rep (initial_node); + return initial_node; + } + + /* Find the specified menu item. */ + entry = info_get_labeled_reference (arg, menu); + + /* If the item wasn't found, search the list sloppily. Perhaps this + user typed "buffer" when they really meant "Buffers". */ + if (!entry) + { + int i; + int best_guess = -1; + + for (i = 0; (entry = menu[i]); i++) + { + if (strcasecmp (entry->label, arg) == 0) + break; + else + if ((best_guess == -1) + && (strncasecmp (entry->label, arg, strlen (arg)) == 0)) + best_guess = i; + } + + if (!entry && best_guess != -1) + entry = menu[best_guess]; + } + + /* If we still failed to find the reference, start Info with the current + node anyway. It is probably a misspelling. */ + if (!entry) + { + if (arg == first_arg) + { + /* Maybe they typed "info foo" instead of "info -f foo". */ + node = info_get_node (first_arg, 0); + if (node) + add_file_directory_to_path (first_arg); + else + node = make_manpage_node (first_arg); + if (node) + goto maybe_got_node; + } + + info_free_references (menu); + *errstr = _("No menu item `%s' in node `%s'."); + *errarg1 = arg; + *errarg2 = node_printed_rep (initial_node); + return initial_node; + } + + /* We have found the reference that the user specified. If no + filename in this reference, define it. */ + if (!entry->filename) + entry->filename = xstrdup (initial_node->parent ? initial_node->parent + : initial_node->filename); + + /* Try to find this node. */ + node = info_get_node (entry->filename, entry->nodename); + if (!node && arg == first_arg) + { + node = make_manpage_node (first_arg); + if (node) + goto maybe_got_node; + } + + /* Since we cannot find it, try using the label of the entry as a + file, i.e., "(LABEL)Top". */ + if (!node && entry->nodename + && strcmp (entry->label, entry->nodename) == 0) + node = info_get_node (entry->label, "Top"); + + maybe_got_node: + if (!node) + { + *errstr = _("Unable to find node referenced by `%s' in `%s'."); + *errarg1 = xstrdup (entry->label); + *errarg2 = node_printed_rep (initial_node); + info_free_references (menu); + return initial_node; + } + + info_free_references (menu); + + /* Success. Go round the loop again. */ + free (initial_node); + initial_node = node; + } + + return initial_node; +} + +/* Split STR into individual node names by writing null bytes in wherever + there are commas and constructing a list of the resulting pointers. + (We can do this since STR has had canonicalize_whitespace called on it.) + Return array terminated with NULL. */ + +static char ** +split_list_of_nodenames (char *str) +{ + unsigned len = 2; + char **nodes = xmalloc (len * sizeof (char *)); + + nodes[len - 2] = str; + + while (*str++) + { + if (*str == ',') + { + *str++ = 0; /* get past the null byte */ + len++; + nodes = xrealloc (nodes, len * sizeof (char *)); + nodes[len - 2] = str; + } + } + + nodes[len - 1] = NULL; + + return nodes; +} + + +/* Read a line of input which is a sequence of menus (starting from + dir), and follow them. */ +DECLARE_INFO_COMMAND (info_menu_sequence, + _("Read a list of menus starting from dir and follow them")) +{ + char *line = info_read_in_echo_area (window, (char *) _("Follow menus: ")); + + /* If the user aborted, quit now. */ + if (!line) + { + info_abort_key (window, 0, 0); + return; + } + + canonicalize_whitespace (line); + + if (*line) + { + const char *errstr; + char *errarg1, *errarg2; + NODE *dir_node = info_get_node (NULL, NULL); + char **nodes = split_list_of_nodenames (line); + NODE *node = NULL; + + /* If DIR_NODE is NULL, they might be reading a file directly, + like in "info -d . -f ./foo". Try using "Top" instead. */ + if (!dir_node) + { + char *file_name = window->node->parent; + + if (!file_name) + file_name = window->node->filename; + dir_node = info_get_node (file_name, NULL); + } + + /* If we still cannot find the starting point, give up. + We cannot allow a NULL pointer inside info_follow_menus. */ + if (!dir_node) + info_error ((char *) msg_cant_find_node, "Top", NULL); + else + node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2); + + free (nodes); + if (!errstr) + info_set_node_of_window (1, window, node); + else + info_error ((char *) errstr, errarg1, errarg2); + } + + free (line); + if (!info_error_was_printed) + window_clear_echo_area (); +} + +/* Search the menu MENU for a (possibly mis-spelled) entry ARG. + Return the menu entry, or the best guess for what they meant by ARG, + or NULL if there's nothing in this menu seems to fit the bill. + If EXACT is non-zero, allow only exact matches. */ +static REFERENCE * +entry_in_menu (char *arg, REFERENCE **menu, int exact) +{ + REFERENCE *entry; + + /* First, try to find the specified menu item verbatim. */ + entry = info_get_labeled_reference (arg, menu); + + /* If the item wasn't found, search the list sloppily. Perhaps we + have "Option Summary", but ARG is "option". */ + if (!entry && !exact) + { + int i; + int best_guess = -1; + + for (i = 0; (entry = menu[i]); i++) + { + if (strcasecmp (entry->label, arg) == 0) + break; + else + if (strncasecmp (entry->label, arg, strlen (arg)) == 0) + best_guess = i; + } + + if (!entry && best_guess != -1) + entry = menu[best_guess]; + } + + return entry; +} + +/* Find the node that is the best candidate to list the PROGRAM's + invocation info and its command-line options, by looking for menu + items and chains of menu items with characteristic names. */ +void +info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program) +{ + /* The list of node names typical for GNU manuals where the program + usage and specifically the command-line arguments are described. + This is pure heuristics. I gathered these node names by looking + at all the Info files I could put my hands on. If you are + looking for evidence to complain to the GNU project about + non-uniform style of documentation, here you have your case! */ + static const char *invocation_nodes[] = { + "%s invocation", + "Invoking %s", + "Preliminaries", /* m4 has Invoking under Preliminaries! */ + "Invocation", + "Command Arguments",/* Emacs */ + "Invoking `%s'", + "%s options", + "Options", + "Option ", /* e.g. "Option Summary" */ + "Invoking", + "All options", /* tar, paxutils */ + "Arguments", + "%s cmdline", /* ar */ + "%s", /* last resort */ + (const char *)0 + }; + NODE *node = NULL; + REFERENCE **menu; + const char **try_node; + + /* We keep looking deeper and deeper in the menu structure until + there are no more menus or no menu items from the above list. + Some manuals have the invocation node sitting 3 or 4 levels deep + in the menu hierarchy... */ + for (node = initial_node; node; initial_node = node) + { + REFERENCE *entry = NULL; + + /* Build and return a list of the menu items in this node. */ + menu = info_menu_of_node (initial_node); + + /* If no menu item in this node, stop here. Perhaps this node + is the one they need. */ + if (!menu) + break; + + /* Look for node names typical for usage nodes in this menu. */ + for (try_node = invocation_nodes; *try_node; try_node++) + { + char *nodename; + + nodename = xmalloc (strlen (program) + strlen (*try_node)); + sprintf (nodename, *try_node, program); + /* The last resort "%s" is dangerous, so we restrict it + to exact matches here. */ + entry = entry_in_menu (nodename, menu, + strcmp (*try_node, "%s") == 0); + free (nodename); + if (entry) + break; + } + + if (!entry) + break; + + if (!entry->filename) + entry->filename = xstrdup (initial_node->parent ? initial_node->parent + : initial_node->filename); + /* Try to find this node. */ + node = info_get_node (entry->filename, entry->nodename); + info_free_references (menu); + if (!node) + break; + } + + /* We've got our best shot at the invocation node. Now select it. */ + if (initial_node) + info_set_node_of_window (1, window, initial_node); + if (!info_error_was_printed) + window_clear_echo_area (); +} + +/* Given a name of an Info file, find the name of the package it + describes by removing the leading directories and extensions. */ +char * +program_name_from_file_name (char *file_name) +{ + int i; + char *program_name = xstrdup (filename_non_directory (file_name)); + + for (i = strlen (program_name) - 1; i > 0; i--) + if (program_name[i] == '.' + && (FILENAME_CMPN (program_name + i, ".info", 5) == 0 + || FILENAME_CMPN (program_name + i, ".inf", 4) == 0 +#ifdef __MSDOS__ + || FILENAME_CMPN (program_name + i, ".i", 2) == 0 +#endif + || isdigit (program_name[i + 1]))) /* a man page foo.1 */ + { + program_name[i] = 0; + break; + } + return program_name; +} + +DECLARE_INFO_COMMAND (info_goto_invocation_node, + _("Find the node describing program invocation")) +{ + const char *invocation_prompt = _("Find Invocation node of [%s]: "); + char *program_name, *line; + char *default_program_name, *prompt, *file_name; + NODE *top_node; + + /* Intuit the name of the program they are likely to want. + We use the file name of the current Info file as a hint. */ + file_name = window->node->parent ? window->node->parent + : window->node->filename; + default_program_name = program_name_from_file_name (file_name); + + prompt = (char *)xmalloc (strlen (default_program_name) + + strlen (invocation_prompt)); + sprintf (prompt, invocation_prompt, default_program_name); + line = info_read_in_echo_area (window, prompt); + free (prompt); + if (!line) + { + info_abort_key (window, 0, 0); + return; + } + if (*line) + program_name = line; + else + program_name = default_program_name; + + /* In interactive usage they'd probably expect us to begin looking + from the Top node. */ + top_node = info_get_node (file_name, NULL); + if (!top_node) + info_error ((char *) msg_cant_find_node, "Top", NULL); + + info_intuit_options_node (window, top_node, program_name); + free (line); + free (default_program_name); +} + +#if defined (HANDLE_MAN_PAGES) +DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it")) +{ + char *line; + + line = info_read_in_echo_area (window, (char *) _("Get Manpage: ")); + + if (!line) + { + info_abort_key (window, 0, 0); + return; + } + + canonicalize_whitespace (line); + + if (*line) + { + char *goto_command; + + goto_command = (char *)xmalloc + (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line)); + + sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line); + + info_parse_and_select (goto_command, window); + free (goto_command); + } + + free (line); + if (!info_error_was_printed) + window_clear_echo_area (); +} +#endif /* HANDLE_MAN_PAGES */ + +/* Move to the "Top" node in this file. */ +DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file")) +{ + info_parse_and_select ("Top", window); +} + +/* Move to the node "(dir)Top". */ +DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'")) +{ + info_parse_and_select ("(dir)Top", window); +} + + +/* Read the name of a node to kill. The list of available nodes comes + from the nodes appearing in the current window configuration. */ +static char * +read_nodename_to_kill (WINDOW *window) +{ + int iw; + char *nodename; + INFO_WINDOW *info_win; + REFERENCE **menu = NULL; + int menu_index = 0, menu_slots = 0; + char *default_nodename = xstrdup (active_window->node->nodename); + char *prompt = xmalloc (strlen (_("Kill node (%s): ")) + strlen (default_nodename)); + + sprintf (prompt, _("Kill node (%s): "), default_nodename); + + for (iw = 0; (info_win = info_windows[iw]); iw++) + { + REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + entry->label = xstrdup (info_win->window->node->nodename); + entry->filename = entry->nodename = (char *)NULL; + + add_pointer_to_array (entry, menu_index, menu, menu_slots, 10, + REFERENCE *); + } + + nodename = info_read_completing_in_echo_area (window, prompt, menu); + free (prompt); + info_free_references (menu); + if (nodename && !*nodename) + { + free (nodename); + nodename = default_nodename; + } + else + free (default_nodename); + + return nodename; +} + + +/* Delete NODENAME from this window, showing the most + recently selected node in this window. */ +static void +kill_node (WINDOW *window, char *nodename) +{ + int iw, i; + INFO_WINDOW *info_win; + NODE *temp; + + /* If there is no nodename to kill, quit now. */ + if (!nodename) + { + info_abort_key (window, 0, 0); + return; + } + + /* If there is a nodename, find it in our window list. */ + for (iw = 0; (info_win = info_windows[iw]); iw++) + if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0 + && info_win->window == window) + break; + + if (!info_win) + { + if (*nodename) + info_error ((char *) _("Cannot kill node `%s'"), nodename, NULL); + else + window_clear_echo_area (); + + return; + } + + /* If there are no more nodes left anywhere to view, complain and exit. */ + if (info_windows_index == 1 && info_windows[0]->nodes_index == 1) + { + info_error ((char *) _("Cannot kill the last node"), NULL, NULL); + return; + } + + /* INFO_WIN contains the node that the user wants to stop viewing. Delete + this node from the list of nodes previously shown in this window. */ + for (i = info_win->current; i < info_win->nodes_index; i++) + info_win->nodes[i] = info_win->nodes[i + 1]; + + /* There is one less node in this window's history list. */ + info_win->nodes_index--; + + /* Make this window show the most recent history node. */ + info_win->current = info_win->nodes_index - 1; + + /* If there aren't any nodes left in this window, steal one from the + next window. */ + if (info_win->current < 0) + { + INFO_WINDOW *stealer; + int which, pagetop; + long point; + + if (info_windows[iw + 1]) + stealer = info_windows[iw + 1]; + else + stealer = info_windows[0]; + + /* If the node being displayed in the next window is not the most + recently loaded one, get the most recently loaded one. */ + if ((stealer->nodes_index - 1) != stealer->current) + which = stealer->nodes_index - 1; + + /* Else, if there is another node behind the stealers current node, + use that one. */ + else if (stealer->current > 0) + which = stealer->current - 1; + + /* Else, just use the node appearing in STEALER's window. */ + else + which = stealer->current; + + /* Copy this node. */ + { + NODE *copy = xmalloc (sizeof (NODE)); + + temp = stealer->nodes[which]; + point = stealer->points[which]; + pagetop = stealer->pagetops[which]; + + copy->filename = temp->filename; + copy->parent = temp->parent; + copy->nodename = temp->nodename; + copy->contents = temp->contents; + copy->nodelen = temp->nodelen; + copy->flags = temp->flags; + copy->display_pos = temp->display_pos; + + temp = copy; + } + + window_set_node_of_window (info_win->window, temp); + window->point = point; + window->pagetop = pagetop; + remember_window_and_node (info_win->window, temp); + } + else + { + temp = info_win->nodes[info_win->current]; + temp->display_pos = info_win->points[info_win->current]; + window_set_node_of_window (info_win->window, temp); + } + + if (!info_error_was_printed) + window_clear_echo_area (); + + if (auto_footnotes_p) + info_get_or_remove_footnotes (window); +} + +/* Kill current node, thus going back one in the node history. I (karl) + do not think this is completely correct yet, because of the + window-changing stuff in kill_node, but it's a lot better than the + previous implementation, which did not account for nodes being + visited twice at all. */ +DECLARE_INFO_COMMAND (info_history_node, + _("Select the most recently selected node")) +{ + kill_node (window, active_window->node->nodename); +} + +/* Kill named node. */ +DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node")) +{ + char *nodename = read_nodename_to_kill (window); + kill_node (window, nodename); +} + + +/* Read the name of a file and select the entire file. */ +DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it")) +{ + char *line; + + line = info_read_in_echo_area (window, (char *) _("Find file: ")); + if (!line) + { + info_abort_key (active_window, 1, 0); + return; + } + + if (*line) + { + NODE *node; + + node = info_get_node (line, "*"); + if (!node) + { + if (info_recent_file_error) + info_error (info_recent_file_error, NULL, NULL); + else + info_error ((char *) _("Cannot find `%s'."), line, NULL); + } + else + info_set_node_of_window (1, window, node); + + free (line); + } + + if (!info_error_was_printed) + window_clear_echo_area (); +} + +/* **************************************************************** */ +/* */ +/* Dumping and Printing Nodes */ +/* */ +/* **************************************************************** */ + +#define VERBOSE_NODE_DUMPING +static void write_node_to_stream (NODE *node, FILE *stream); +static void dump_node_to_stream (char *filename, char *nodename, + FILE *stream, int dump_subnodes); +static void initialize_dumping (void); + +/* Dump the nodes specified by FILENAME and NODENAMES to the file named + in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump + the nodes which appear in the menu of each node dumped. */ +void +dump_nodes_to_file (char *filename, char **nodenames, + char *output_filename, int dump_subnodes) +{ + register int i; + FILE *output_stream; + + /* Get the stream to print the nodes to. Special case of an output + filename of "-" means to dump the nodes to stdout. */ + if (strcmp (output_filename, "-") == 0) + output_stream = stdout; + else + output_stream = fopen (output_filename, "w"); + + if (!output_stream) + { + info_error ((char *) _("Could not create output file `%s'."), + output_filename, NULL); + return; + } + + /* Print each node to stream. */ + initialize_dumping (); + for (i = 0; nodenames[i]; i++) + dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes); + + if (output_stream != stdout) + fclose (output_stream); + +#if defined (VERBOSE_NODE_DUMPING) + info_error ((char *) _("Done."), NULL, NULL); +#endif /* VERBOSE_NODE_DUMPING */ +} + +/* A place to remember already dumped nodes. */ +static char **dumped_already = (char **)NULL; +static int dumped_already_index = 0; +static int dumped_already_slots = 0; + +static void +initialize_dumping (void) +{ + dumped_already_index = 0; +} + +/* Get and print the node specified by FILENAME and NODENAME to STREAM. + If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear + in the menu of each node dumped. */ +static void +dump_node_to_stream (char *filename, char *nodename, + FILE *stream, int dump_subnodes) +{ + register int i; + NODE *node; + + node = info_get_node (filename, nodename); + + if (!node) + { + if (info_recent_file_error) + info_error (info_recent_file_error, NULL, NULL); + else + { + if (filename && *nodename != '(') + info_error ((char *) msg_cant_file_node, + filename_non_directory (filename), + nodename); + else + info_error ((char *) msg_cant_find_node, nodename, NULL); + } + return; + } + + /* If we have already dumped this node, don't dump it again. */ + for (i = 0; i < dumped_already_index; i++) + if (strcmp (node->nodename, dumped_already[i]) == 0) + { + free (node); + return; + } + add_pointer_to_array (node->nodename, dumped_already_index, dumped_already, + dumped_already_slots, 50, char *); + +#if defined (VERBOSE_NODE_DUMPING) + /* Maybe we should print some information about the node being output. */ + info_error ((char *) _("Writing node %s..."), node_printed_rep (node), NULL); +#endif /* VERBOSE_NODE_DUMPING */ + + write_node_to_stream (node, stream); + + /* If we are dumping subnodes, get the list of menu items in this node, + and dump each one recursively. */ + if (dump_subnodes) + { + REFERENCE **menu = (REFERENCE **)NULL; + + /* If this node is an Index, do not dump the menu references. */ + if (string_in_line ("Index", node->nodename) == -1) + menu = info_menu_of_node (node); + + if (menu) + { + for (i = 0; menu[i]; i++) + { + /* We don't dump Info files which are different than the + current one. */ + if (!menu[i]->filename) + dump_node_to_stream + (filename, menu[i]->nodename, stream, dump_subnodes); + } + info_free_references (menu); + } + } + + free (node); +} + +/* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump + the nodes which appear in the menu of each node dumped. */ +void +dump_node_to_file (NODE *node, char *filename, int dump_subnodes) +{ + FILE *output_stream; + char *nodes_filename; + + /* Get the stream to print this node to. Special case of an output + filename of "-" means to dump the nodes to stdout. */ + if (strcmp (filename, "-") == 0) + output_stream = stdout; + else + output_stream = fopen (filename, "w"); + + if (!output_stream) + { + info_error ((char *) _("Could not create output file `%s'."), filename, + NULL); + return; + } + + if (node->parent) + nodes_filename = node->parent; + else + nodes_filename = node->filename; + + initialize_dumping (); + dump_node_to_stream + (nodes_filename, node->nodename, output_stream, dump_subnodes); + + if (output_stream != stdout) + fclose (output_stream); + +#if defined (VERBOSE_NODE_DUMPING) + info_error ((char *) _("Done."), NULL, NULL); +#endif /* VERBOSE_NODE_DUMPING */ +} + +#if !defined (DEFAULT_INFO_PRINT_COMMAND) +# define DEFAULT_INFO_PRINT_COMMAND "lpr" +#endif /* !DEFAULT_INFO_PRINT_COMMAND */ + +DECLARE_INFO_COMMAND (info_print_node, + _("Pipe the contents of this node through INFO_PRINT_COMMAND")) +{ + print_node (window->node); +} + +/* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */ +void +print_node (NODE *node) +{ + FILE *printer_pipe; + char *print_command = getenv ("INFO_PRINT_COMMAND"); + int piping = 0; + + if (!print_command || !*print_command) + print_command = DEFAULT_INFO_PRINT_COMMAND; + + /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the + (default) text mode, since the printer drivers there need to see + DOS-style CRLF pairs at the end of each line. + + FIXME: if we are to support Mac-style text files, we might need + to convert the text here. */ + + /* INFO_PRINT_COMMAND which says ">file" means write to that file. + Presumably, the name of the file is the local printer device. */ + if (*print_command == '>') + printer_pipe = fopen (++print_command, "w"); + else + { + printer_pipe = popen (print_command, "w"); + piping = 1; + } + + if (!printer_pipe) + { + info_error ((char *) _("Cannot open pipe to `%s'."), print_command, NULL); + return; + } + +#if defined (VERBOSE_NODE_DUMPING) + /* Maybe we should print some information about the node being output. */ + info_error ((char *) _("Printing node %s..."), node_printed_rep (node), NULL); +#endif /* VERBOSE_NODE_DUMPING */ + + write_node_to_stream (node, printer_pipe); + if (piping) + pclose (printer_pipe); + else + fclose (printer_pipe); + +#if defined (VERBOSE_NODE_DUMPING) + info_error ((char *) _("Done."), NULL, NULL); +#endif /* VERBOSE_NODE_DUMPING */ +} + +static void +write_node_to_stream (NODE *node, FILE *stream) +{ + fwrite (node->contents, 1, node->nodelen, stream); +} + +/* **************************************************************** */ +/* */ +/* Info Searching Commands */ +/* */ +/* **************************************************************** */ + +/* Variable controlling the garbage collection of files briefly visited + during searches. Such files are normally gc'ed, unless they were + compressed to begin with. If this variable is non-zero, it says + to gc even those file buffer contents which had to be uncompressed. */ +int gc_compressed_files = 0; + +static void info_gc_file_buffers (void); +static void info_search_1 (WINDOW *window, int count, + unsigned char key, int case_sensitive, int ask_for_string); + +static char *search_string = (char *)NULL; +static int search_string_size = 0; +static int isearch_is_active = 0; + +static int last_search_direction = 0; +static int last_search_case_sensitive = 0; + +/* Return the file buffer which belongs to WINDOW's node. */ +FILE_BUFFER * +file_buffer_of_window (WINDOW *window) +{ + /* If this window has no node, then it has no file buffer. */ + if (!window->node) + return ((FILE_BUFFER *)NULL); + + if (window->node->parent) + return (info_find_file (window->node->parent)); + + if (window->node->filename) + return (info_find_file (window->node->filename)); + + return ((FILE_BUFFER *)NULL); +} + +/* Search for STRING in NODE starting at START. Return -1 if the string + was not found, or the location of the string if it was. If WINDOW is + passed as non-null, set the window's node to be NODE, its point to be + the found string, and readjust the window's pagetop. Final argument + DIR says which direction to search in. If it is positive, search + forward, else backwards. */ +long +info_search_in_node (char *string, NODE *node, long int start, + WINDOW *window, int dir, int case_sensitive) +{ + SEARCH_BINDING binding; + long offset; + + binding.buffer = node->contents; + binding.start = start; + binding.end = node->nodelen; + binding.flags = 0; + if (!case_sensitive) + binding.flags |= S_FoldCase; + + if (dir < 0) + { + binding.end = 0; + binding.flags |= S_SkipDest; + } + + if (binding.start < 0) + return (-1); + + /* For incremental searches, we always wish to skip past the string. */ + if (isearch_is_active) + binding.flags |= S_SkipDest; + + offset = search (string, &binding); + + if (offset != -1 && window) + { + set_remembered_pagetop_and_point (window); + if (window->node != node) + window_set_node_of_window (window, node); + window->point = offset; + window_adjust_pagetop (window); + } + return (offset); +} + +/* Search NODE, looking for the largest possible match of STRING. Start the + search at START. Return the absolute position of the match, or -1, if + no part of the string could be found. */ +long +info_target_search_node (NODE *node, char *string, long int start) +{ + register int i; + long offset = 0; + char *target; + + target = xstrdup (string); + i = strlen (target); + + /* Try repeatedly searching for this string while removing words from + the end of it. */ + while (i) + { + target[i] = '\0'; + offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0); + + if (offset != -1) + break; + + /* Delete the last word from TARGET. */ + for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--); + } + free (target); + return (offset); +} + +/* Search for STRING starting in WINDOW at point. If the string is found + in this node, set point to that position. Otherwise, get the file buffer + associated with WINDOW's node, and search through each node in that file. + If the search fails, return non-zero, else zero. Side-effect window + leaving the node and point where the string was found current. */ +static int +info_search_internal (char *string, WINDOW *window, + int dir, int case_sensitive) +{ + register int i; + FILE_BUFFER *file_buffer; + char *initial_nodename; + long ret, start = 0; + + file_buffer = file_buffer_of_window (window); + initial_nodename = window->node->nodename; + + /* This used to begin from window->point, unless this was a repeated + search command. But invoking search with an argument loses with + that logic, since info_last_executed_command is then set to + info_add_digit_to_numeric_arg. I think there's no sense in + ``finding'' a string that is already under the cursor, anyway. */ + ret = info_search_in_node + (string, window->node, window->point + dir, window, dir, + case_sensitive); + + if (ret != -1) + { + /* We won! */ + if (!echo_area_is_active && !isearch_is_active) + window_clear_echo_area (); + return (0); + } + + /* The string wasn't found in the current node. Search through the + window's file buffer, iff the current node is not "*". */ + if (!file_buffer || (strcmp (initial_nodename, "*") == 0)) + return (-1); + + /* If this file has tags, search through every subfile, starting at + this node's subfile and node. Otherwise, search through the + file's node list. */ + if (file_buffer->tags) + { + register int current_tag = 0, number_of_tags; + char *last_subfile; + TAG *tag; + + /* Find number of tags and current tag. */ + last_subfile = (char *)NULL; + for (i = 0; file_buffer->tags[i]; i++) + if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0) + { + current_tag = i; + last_subfile = file_buffer->tags[i]->filename; + } + + number_of_tags = i; + + /* If there is no last_subfile, our tag wasn't found. */ + if (!last_subfile) + return (-1); + + /* Search through subsequent nodes, wrapping around to the top + of the info file until we find the string or return to this + window's node and point. */ + while (1) + { + NODE *node; + + /* Allow C-g to quit the search, failing it if pressed. */ + return_if_control_g (-1); + + /* Find the next tag that isn't an anchor. */ + for (i = current_tag + dir; i != current_tag; i += dir) + { + if (i < 0) + i = number_of_tags - 1; + else if (i == number_of_tags) + i = 0; + + tag = file_buffer->tags[i]; + if (tag->nodelen != 0) + break; + } + + /* If we got past out starting point, bail out. */ + if (i == current_tag) + return (-1); + current_tag = i; + + if (!echo_area_is_active && (last_subfile != tag->filename)) + { + window_message_in_echo_area + ((char *) _("Searching subfile %s ..."), + filename_non_directory (tag->filename), NULL); + + last_subfile = tag->filename; + } + + node = info_get_node (file_buffer->filename, tag->nodename); + + if (!node) + { + /* If not doing i-search... */ + if (!echo_area_is_active) + { + if (info_recent_file_error) + info_error (info_recent_file_error, NULL, NULL); + else + info_error ((char *) msg_cant_file_node, + filename_non_directory (file_buffer->filename), + tag->nodename); + } + return (-1); + } + + if (dir < 0) + start = tag->nodelen; + + ret = + info_search_in_node (string, node, start, window, dir, + case_sensitive); + + /* Did we find the string in this node? */ + if (ret != -1) + { + /* Yes! We win. */ + remember_window_and_node (window, node); + if (!echo_area_is_active) + window_clear_echo_area (); + return (0); + } + + /* No. Free this node, and make sure that we haven't passed + our starting point. */ + free (node); + + if (strcmp (initial_nodename, tag->nodename) == 0) + return (-1); + } + } + return (-1); +} + +DECLARE_INFO_COMMAND (info_search_case_sensitively, + _("Read a string and search for it case-sensitively")) +{ + last_search_direction = count > 0 ? 1 : -1; + last_search_case_sensitive = 1; + info_search_1 (window, count, key, 1, 1); +} + +DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it")) +{ + last_search_direction = count > 0 ? 1 : -1; + last_search_case_sensitive = 0; + info_search_1 (window, count, key, 0, 1); +} + +DECLARE_INFO_COMMAND (info_search_backward, + _("Read a string and search backward for it")) +{ + last_search_direction = count > 0 ? -1 : 1; + last_search_case_sensitive = 0; + info_search_1 (window, -count, key, 0, 1); +} + +static void +info_search_1 (WINDOW *window, int count, unsigned char key, + int case_sensitive, int ask_for_string) +{ + char *line, *prompt; + int result, old_pagetop; + int direction; + + if (count < 0) + { + direction = -1; + count = -count; + } + else + { + direction = 1; + if (count == 0) + count = 1; /* for backward compatibility */ + } + + /* Read a string from the user, defaulting the search to SEARCH_STRING. */ + if (!search_string) + { + search_string = (char *)xmalloc (search_string_size = 100); + search_string[0] = '\0'; + } + + if (ask_for_string) + { + prompt = (char *)xmalloc (strlen (_("%s%sfor string [%s]: ")) + + strlen (_("Search backward")) + + strlen (_("Search")) + + strlen (_(" case-sensitively ")) + + strlen (_(" ")) + + strlen (search_string)); + + sprintf (prompt, _("%s%sfor string [%s]: "), + direction < 0 ? _("Search backward") : _("Search"), + case_sensitive ? _(" case-sensitively ") : _(" "), + search_string); + + line = info_read_in_echo_area (window, prompt); + free (prompt); + + if (!line) + { + info_abort_key (window, 0, 0); + return; + } + + if (*line) + { + if (strlen (line) + 1 > (unsigned int) search_string_size) + search_string = (char *) xrealloc + (search_string, (search_string_size += 50 + strlen (line))); + + strcpy (search_string, line); + free (line); + } + } + + /* If the search string includes upper-case letters, make the search + case-sensitive. */ + if (case_sensitive == 0) + for (line = search_string; *line; line++) + if (isupper (*line)) + { + case_sensitive = 1; + break; + } + + old_pagetop = active_window->pagetop; + for (result = 0; result == 0 && count--; ) + result = info_search_internal (search_string, + active_window, direction, case_sensitive); + + if (result != 0 && !info_error_was_printed) + info_error ((char *) _("Search failed."), NULL, NULL); + else if (old_pagetop != active_window->pagetop) + { + int new_pagetop; + + new_pagetop = active_window->pagetop; + active_window->pagetop = old_pagetop; + set_window_pagetop (active_window, new_pagetop); + if (auto_footnotes_p) + info_get_or_remove_footnotes (active_window); + } + + /* Perhaps free the unreferenced file buffers that were searched, but + not retained. */ + info_gc_file_buffers (); +} + +DECLARE_INFO_COMMAND (info_search_next, + _("Repeat last search in the same direction")) +{ + if (!last_search_direction) + info_error ((char *) _("No previous search string"), NULL, NULL); + else + info_search_1 (window, last_search_direction * count, + key, last_search_case_sensitive, 0); +} + +DECLARE_INFO_COMMAND (info_search_previous, + _("Repeat last search in the reverse direction")) +{ + if (!last_search_direction) + info_error ((char *) _("No previous search string"), NULL, NULL); + else + info_search_1 (window, -last_search_direction * count, + key, last_search_case_sensitive, 0); +} + +/* **************************************************************** */ +/* */ +/* Incremental Searching */ +/* */ +/* **************************************************************** */ + +static void incremental_search (WINDOW *window, int count, + unsigned char ignore); + +DECLARE_INFO_COMMAND (isearch_forward, + _("Search interactively for a string as you type it")) +{ + incremental_search (window, count, key); +} + +DECLARE_INFO_COMMAND (isearch_backward, + _("Search interactively for a string as you type it")) +{ + incremental_search (window, -count, key); +} + +/* Incrementally search for a string as it is typed. */ +/* The last accepted incremental search string. */ +static char *last_isearch_accepted = (char *)NULL; + +/* The current incremental search string. */ +static char *isearch_string = (char *)NULL; +static int isearch_string_index = 0; +static int isearch_string_size = 0; +static unsigned char isearch_terminate_search_key = ESC; + +/* Array of search states. */ +static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL; +static int isearch_states_index = 0; +static int isearch_states_slots = 0; + +/* Push the state of this search. */ +static void +push_isearch (WINDOW *window, int search_index, int direction, int failing) +{ + SEARCH_STATE *state; + + state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE)); + window_get_state (window, state); + state->search_index = search_index; + state->direction = direction; + state->failing = failing; + + add_pointer_to_array (state, isearch_states_index, isearch_states, + isearch_states_slots, 20, SEARCH_STATE *); +} + +/* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */ +static void +pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing) +{ + SEARCH_STATE *state; + + if (isearch_states_index) + { + isearch_states_index--; + state = isearch_states[isearch_states_index]; + window_set_state (window, state); + *search_index = state->search_index; + *direction = state->direction; + *failing = state->failing; + + free (state); + isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL; + } +} + +/* Free the memory used by isearch_states. */ +static void +free_isearch_states (void) +{ + register int i; + + for (i = 0; i < isearch_states_index; i++) + { + free (isearch_states[i]); + isearch_states[i] = (SEARCH_STATE *)NULL; + } + isearch_states_index = 0; +} + +/* Display the current search in the echo area. */ +static void +show_isearch_prompt (int dir, unsigned char *string, int failing_p) +{ + register int i; + const char *prefix; + char *prompt, *p_rep; + unsigned int prompt_len, p_rep_index, p_rep_size; + + if (dir < 0) + prefix = _("I-search backward: "); + else + prefix = _("I-search: "); + + p_rep_index = p_rep_size = 0; + p_rep = (char *)NULL; + for (i = 0; string[i]; i++) + { + char *rep; + + switch (string[i]) + { + case ' ': rep = " "; break; + case LFD: rep = "\\n"; break; + case TAB: rep = "\\t"; break; + default: + rep = pretty_keyname (string[i]); + } + if ((p_rep_index + strlen (rep) + 1) >= p_rep_size) + p_rep = (char *)xrealloc (p_rep, p_rep_size += 100); + + strcpy (p_rep + p_rep_index, rep); + p_rep_index += strlen (rep); + } + + prompt_len = strlen (prefix) + p_rep_index + 1; + if (failing_p) + prompt_len += strlen (_("Failing ")); + prompt = xmalloc (prompt_len); + sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix, + p_rep ? p_rep : ""); + + window_message_in_echo_area ("%s", prompt, NULL); + maybe_free (p_rep); + free (prompt); + display_cursor_at_point (active_window); +} + +static void +incremental_search (WINDOW *window, int count, unsigned char ignore) +{ + unsigned char key; + int last_search_result, search_result, dir; + SEARCH_STATE mystate, orig_state; + char *p; + int case_sensitive = 0; + + if (count < 0) + dir = -1; + else + dir = 1; + + last_search_result = search_result = 0; + + window_get_state (window, &orig_state); + + isearch_string_index = 0; + if (!isearch_string_size) + isearch_string = (char *)xmalloc (isearch_string_size = 50); + + /* Show the search string in the echo area. */ + isearch_string[isearch_string_index] = '\0'; + show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); + + isearch_is_active = 1; + + while (isearch_is_active) + { + VFunction *func = (VFunction *)NULL; + int quoted = 0; + + /* If a recent display was interrupted, then do the redisplay now if + it is convenient. */ + if (!info_any_buffered_input_p () && display_was_interrupted_p) + { + display_update_one_window (window); + display_cursor_at_point (active_window); + } + + /* Read a character and dispatch on it. */ + key = info_get_input_char (); + window_get_state (window, &mystate); + + if (key == DEL || key == Control ('h')) + { + /* User wants to delete one level of search? */ + if (!isearch_states_index) + { + terminal_ring_bell (); + continue; + } + else + { + pop_isearch + (window, &isearch_string_index, &dir, &search_result); + isearch_string[isearch_string_index] = '\0'; + show_isearch_prompt (dir, (unsigned char *) isearch_string, + search_result); + goto after_search; + } + } + else if (key == Control ('q')) + { + key = info_get_input_char (); + quoted = 1; + } + + /* We are about to search again, or quit. Save the current search. */ + push_isearch (window, isearch_string_index, dir, search_result); + + if (quoted) + goto insert_and_search; + + if (!Meta_p (key) || key > 32) + { + /* If this key is not a keymap, get its associated function, + if any. If it is a keymap, then it's probably ESC from an + arrow key, and we handle that case below. */ + char type = window->keymap[key].type; + func = type == ISFUNC + ? InfoFunction(window->keymap[key].function) + : NULL; /* function member is a Keymap if ISKMAP */ + + if (isprint (key) || (type == ISFUNC && func == NULL)) + { + insert_and_search: + + if (isearch_string_index + 2 >= isearch_string_size) + isearch_string = (char *)xrealloc + (isearch_string, isearch_string_size += 100); + + isearch_string[isearch_string_index++] = key; + isearch_string[isearch_string_index] = '\0'; + goto search_now; + } + else if (func == (VFunction *) isearch_forward + || func == (VFunction *) isearch_backward) + { + /* If this key invokes an incremental search, then this + means that we will either search again in the same + direction, search again in the reverse direction, or + insert the last search string that was accepted through + incremental searching. */ + if ((func == (VFunction *) isearch_forward && dir > 0) || + (func == (VFunction *) isearch_backward && dir < 0)) + { + /* If the user has typed no characters, then insert the + last successful search into the current search string. */ + if (isearch_string_index == 0) + { + /* Of course, there must be something to insert. */ + if (last_isearch_accepted) + { + if (strlen ((char *) last_isearch_accepted) + 1 + >= (unsigned int) isearch_string_size) + isearch_string = (char *) + xrealloc (isearch_string, + isearch_string_size += 10 + + strlen (last_isearch_accepted)); + strcpy (isearch_string, last_isearch_accepted); + isearch_string_index = strlen (isearch_string); + goto search_now; + } + else + continue; + } + else + { + /* Search again in the same direction. This means start + from a new place if the last search was successful. */ + if (search_result == 0) + window->point += dir; + } + } + else + { + /* Reverse the direction of the search. */ + dir = -dir; + } + } + else if (func == (VFunction *) info_abort_key) + { + /* If C-g pressed, and the search is failing, pop the search + stack back to the last unfailed search. */ + if (isearch_states_index && (search_result != 0)) + { + terminal_ring_bell (); + while (isearch_states_index && (search_result != 0)) + pop_isearch + (window, &isearch_string_index, &dir, &search_result); + isearch_string[isearch_string_index] = '\0'; + show_isearch_prompt (dir, (unsigned char *) isearch_string, + search_result); + continue; + } + else + goto exit_search; + } + else + goto exit_search; + } + else + { + exit_search: + /* The character is not printable, or it has a function which is + non-null. Exit the search, remembering the search string. If + the key is not the same as the isearch_terminate_search_key, + then push it into pending input. */ + if (isearch_string_index && func != (VFunction *) info_abort_key) + { + maybe_free (last_isearch_accepted); + last_isearch_accepted = xstrdup (isearch_string); + } + + /* If the key is the isearch_terminate_search_key, but some buffered + input is pending, it is almost invariably because the ESC key is + actually the beginning of an escape sequence, like in case they + pressed an arrow key. So don't gobble the ESC key, push it back + into pending input. */ + /* FIXME: this seems like a kludge! We need a more reliable + mechanism to know when ESC is a separate key and when it is + part of an escape sequence. */ + if (key != RET /* Emacs addicts want RET to get lost */ + && (key != isearch_terminate_search_key + || info_any_buffered_input_p ())) + info_set_pending_input (key); + + if (func == (VFunction *) info_abort_key) + { + if (isearch_states_index) + window_set_state (window, &orig_state); + } + + if (!echo_area_is_active) + window_clear_echo_area (); + + if (auto_footnotes_p) + info_get_or_remove_footnotes (active_window); + + isearch_is_active = 0; + continue; + } + + /* Search for the contents of isearch_string. */ + search_now: + show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); + + /* If the search string includes upper-case letters, make the + search case-sensitive. */ + for (p = isearch_string; *p; p++) + if (isupper (*p)) + { + case_sensitive = 1; + break; + } + + + if (search_result == 0) + { + /* Check to see if the current search string is right here. If + we are looking at it, then don't bother calling the search + function. */ + if (((dir < 0) && + ((case_sensitive ? strncmp : strncasecmp) + (window->node->contents + window->point, + isearch_string, isearch_string_index) == 0)) || + ((dir > 0) && + ((window->point - isearch_string_index) >= 0) && + ((case_sensitive ? strncmp : strncasecmp) + (window->node->contents + + (window->point - (isearch_string_index - 1)), + isearch_string, isearch_string_index) == 0))) + { + if (dir > 0) + window->point++; + } + else + search_result = info_search_internal (isearch_string, + window, dir, case_sensitive); + } + + /* If this search failed, and we didn't already have a failed search, + then ring the terminal bell. */ + if (search_result != 0 && last_search_result == 0) + terminal_ring_bell (); + + after_search: + show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); + + if (search_result == 0) + { + if ((mystate.node == window->node) && + (mystate.pagetop != window->pagetop)) + { + int newtop = window->pagetop; + window->pagetop = mystate.pagetop; + set_window_pagetop (window, newtop); + } + display_update_one_window (window); + display_cursor_at_point (window); + } + + last_search_result = search_result; + } + + /* Free the memory used to remember each search state. */ + free_isearch_states (); + + /* Perhaps GC some file buffers. */ + info_gc_file_buffers (); + + /* After searching, leave the window in the correct state. */ + if (!echo_area_is_active) + window_clear_echo_area (); +} + +/* GC some file buffers. A file buffer can be gc-ed if there we have + no nodes in INFO_WINDOWS that reference this file buffer's contents. + Garbage collecting a file buffer means to free the file buffers + contents. */ +static void +info_gc_file_buffers (void) +{ + register int fb_index, iw_index, i; + register FILE_BUFFER *fb; + register INFO_WINDOW *iw; + + if (!info_loaded_files) + return; + + for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++) + { + int fb_referenced_p = 0; + + /* If already gc-ed, do nothing. */ + if (!fb->contents) + continue; + + /* If this file had to be uncompressed, check to see if we should + gc it. This means that the user-variable "gc-compressed-files" + is non-zero. */ + if ((fb->flags & N_IsCompressed) && !gc_compressed_files) + continue; + + /* If this file's contents are not gc-able, move on. */ + if (fb->flags & N_CannotGC) + continue; + + /* Check each INFO_WINDOW to see if it has any nodes which reference + this file. */ + for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++) + { + for (i = 0; iw->nodes && iw->nodes[i]; i++) + { + if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) || + (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0)) + { + fb_referenced_p = 1; + break; + } + } + } + + /* If this file buffer wasn't referenced, free its contents. */ + if (!fb_referenced_p) + { + free (fb->contents); + fb->contents = (char *)NULL; + } + } +} + +/* **************************************************************** */ +/* */ +/* Traversing and Selecting References */ +/* */ +/* **************************************************************** */ + +/* Move to the next or previous cross reference in this node. */ +static void +info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir) +{ + long firstmenu, firstxref; + long nextmenu, nextxref; + long placement = -1; + long start = 0; + NODE *node = window->node; + + if (dir < 0) + start = node->nodelen; + + /* This search is only allowed to fail if there is no menu or cross + reference in the current node. Otherwise, the first menu or xref + found is moved to. */ + + firstmenu = info_search_in_node + (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0); + + /* FIRSTMENU may point directly to the line defining the menu. Skip that + and go directly to the first item. */ + + if (firstmenu != -1) + { + char *text = node->contents + firstmenu; + + if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) + firstmenu = info_search_in_node + (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0); + } + + firstxref = + info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0); + +#if defined (HANDLE_MAN_PAGES) + if ((firstxref == -1) && (node->flags & N_IsManPage)) + { + firstxref = locate_manpage_xref (node, start, dir); + } +#endif /* HANDLE_MAN_PAGES */ + + if (firstmenu == -1 && firstxref == -1) + { + info_error ((char *) msg_no_xref_node, NULL, NULL); + return; + } + + /* There is at least one cross reference or menu entry in this node. + Try hard to find the next available one. */ + + nextmenu = info_search_in_node + (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); + + nextxref = info_search_in_node + (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); + +#if defined (HANDLE_MAN_PAGES) + if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1)) + nextxref = locate_manpage_xref (node, window->point + dir, dir); +#endif /* HANDLE_MAN_PAGES */ + + /* Ignore "Menu:" as a menu item. */ + if (nextmenu != -1) + { + char *text = node->contents + nextmenu; + + if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) + nextmenu = info_search_in_node + (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0); + } + + /* If there is both a next menu entry, and a next xref entry, choose the + one which occurs first. Otherwise, select the one which actually + appears in this node following point. */ + if (nextmenu != -1 && nextxref != -1) + { + if (((dir == 1) && (nextmenu < nextxref)) || + ((dir == -1) && (nextmenu > nextxref))) + placement = nextmenu + 1; + else + placement = nextxref; + } + else if (nextmenu != -1) + placement = nextmenu + 1; + else if (nextxref != -1) + placement = nextxref; + + /* If there was neither a menu or xref entry appearing in this node after + point, choose the first menu or xref entry appearing in this node. */ + if (placement == -1) + { + if (firstmenu != -1 && firstxref != -1) + { + if (((dir == 1) && (firstmenu < firstxref)) || + ((dir == -1) && (firstmenu > firstxref))) + placement = firstmenu + 1; + else + placement = firstxref; + } + else if (firstmenu != -1) + placement = firstmenu + 1; + else + placement = firstxref; + } + window->point = placement; + window_adjust_pagetop (window); + window->flags |= W_UpdateWindow; +} + +DECLARE_INFO_COMMAND (info_move_to_prev_xref, + _("Move to the previous cross reference")) +{ + if (count < 0) + info_move_to_prev_xref (window, -count, key); + else + info_move_to_xref (window, count, key, -1); +} + +DECLARE_INFO_COMMAND (info_move_to_next_xref, + _("Move to the next cross reference")) +{ + if (count < 0) + info_move_to_next_xref (window, -count, key); + else + info_move_to_xref (window, count, key, 1); +} + +/* Select the menu item or reference that appears on this line. */ +DECLARE_INFO_COMMAND (info_select_reference_this_line, + _("Select reference or menu item appearing on this line")) +{ + char *line; + + if (window->line_starts) + line = window->line_starts[window_line_of_point (window)]; + else + line = ""; + + /* If this line contains a menu item, select that one. */ + if (strncmp ("* ", line, 2) == 0) + info_menu_or_ref_item (window, count, key, info_menu_of_node, 0); + else + info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0); +} + +/* **************************************************************** */ +/* */ +/* Miscellaneous Info Commands */ +/* */ +/* **************************************************************** */ + +/* What to do when C-g is pressed in a window. */ +DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation")) +{ + /* If error printing doesn't oridinarily ring the bell, do it now, + since C-g always rings the bell. Otherwise, let the error printer + do it. */ + if (!info_error_rings_bell_p) + terminal_ring_bell (); + info_error ((char *) _("Quit"), NULL, NULL); + + info_initialize_numeric_arg (); + info_clear_pending_input (); + info_last_executed_command = (VFunction *)NULL; +} + +/* Move the cursor to the desired line of the window. */ +DECLARE_INFO_COMMAND (info_move_to_window_line, + _("Move the cursor to a specific line of the window")) +{ + int line; + + /* With no numeric argument of any kind, default to the center line. */ + if (!info_explicit_arg && count == 1) + line = (window->height / 2) + window->pagetop; + else + { + if (count < 0) + line = (window->height + count) + window->pagetop; + else + line = window->pagetop + count; + } + + /* If the line doesn't appear in this window, make it do so. */ + if ((line - window->pagetop) >= window->height) + line = window->pagetop + (window->height - 1); + + /* If the line is too small, make it fit. */ + if (line < window->pagetop) + line = window->pagetop; + + /* If the selected line is past the bottom of the node, force it back. */ + if (line >= window->line_count) + line = window->line_count - 1; + + window->point = (window->line_starts[line] - window->node->contents); +} + +/* Clear the screen and redraw its contents. Given a numeric argument, + move the line the cursor is on to the COUNT'th line of the window. */ +DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display")) +{ + if ((!info_explicit_arg && count == 1) || echo_area_is_active) + { + terminal_clear_screen (); + display_clear_display (the_display); + window_mark_chain (windows, W_UpdateWindow); + display_update_display (windows); + } + else + { + int desired_line, point_line; + int new_pagetop; + + point_line = window_line_of_point (window) - window->pagetop; + + if (count < 0) + desired_line = window->height + count; + else + desired_line = count; + + if (desired_line < 0) + desired_line = 0; + + if (desired_line >= window->height) + desired_line = window->height - 1; + + if (desired_line == point_line) + return; + + new_pagetop = window->pagetop + (point_line - desired_line); + + set_window_pagetop (window, new_pagetop); + } +} +/* This command does nothing. It is the fact that a key is bound to it + that has meaning. See the code at the top of info_session (). */ +DECLARE_INFO_COMMAND (info_quit, _("Quit using Info")) +{} + + +/* **************************************************************** */ +/* */ +/* Reading Keys and Dispatching on Them */ +/* */ +/* **************************************************************** */ + +/* Declaration only. Special cased in info_dispatch_on_key (). + Doc string is to avoid ugly results with describe_key etc. */ +DECLARE_INFO_COMMAND (info_do_lowercase_version, + _("Run command bound to this key's lowercase variant")) +{} + +static void +dispatch_error (char *keyseq) +{ + char *rep; + + rep = pretty_keyseq (keyseq); + + if (!echo_area_is_active) + info_error ((char *) _("Unknown command (%s)."), rep, NULL); + else + { + char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid"))); + sprintf (temp, _("`%s' is invalid"), rep); + terminal_ring_bell (); + inform_in_echo_area (temp); + free (temp); + } +} + +/* Keeping track of key sequences. */ +static char *info_keyseq = (char *)NULL; +static int info_keyseq_index = 0; +static int info_keyseq_size = 0; +static int info_keyseq_displayed_p = 0; + +/* Initialize the length of the current key sequence. */ +void +initialize_keyseq (void) +{ + info_keyseq_index = 0; + info_keyseq_displayed_p = 0; +} + +/* Add CHARACTER to the current key sequence. */ +void +add_char_to_keyseq (char character) +{ + if (info_keyseq_index + 2 >= info_keyseq_size) + info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10); + + info_keyseq[info_keyseq_index++] = character; + info_keyseq[info_keyseq_index] = '\0'; +} + +/* Display the current value of info_keyseq. If argument EXPECTING is + non-zero, input is expected to be read after the key sequence is + displayed, so add an additional prompting character to the sequence. */ +static void +display_info_keyseq (int expecting_future_input) +{ + char *rep; + + rep = pretty_keyseq (info_keyseq); + if (expecting_future_input) + strcat (rep, "-"); + + if (echo_area_is_active) + inform_in_echo_area (rep); + else + { + window_message_in_echo_area (rep, NULL, NULL); + display_cursor_at_point (active_window); + } + info_keyseq_displayed_p = 1; +} + +/* Called by interactive commands to read a keystroke. */ +unsigned char +info_get_another_input_char (void) +{ + int ready = !info_keyseq_displayed_p; /* ready if new and pending key */ + + /* If there isn't any input currently available, then wait a + moment looking for input. If we don't get it fast enough, + prompt a little bit with the current key sequence. */ + if (!info_keyseq_displayed_p) + { + ready = 1; + if (!info_any_buffered_input_p () && + !info_input_pending_p ()) + { +#if defined (FD_SET) + struct timeval timer; + fd_set readfds; + + FD_ZERO (&readfds); + FD_SET (fileno (info_input_stream), &readfds); + timer.tv_sec = 1; + timer.tv_usec = 750; + ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer); +#else + ready = 0; +#endif /* FD_SET */ + } + } + + if (!ready) + display_info_keyseq (1); + + return (info_get_input_char ()); +} + +/* Do the command associated with KEY in MAP. If the associated command is + really a keymap, then read another key, and dispatch into that map. */ +void +info_dispatch_on_key (unsigned char key, Keymap map) +{ +#if !defined(INFOKEY) + if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert)) + { + if (map[ESC].type == ISKMAP) + { + map = (Keymap)map[ESC].function; + add_char_to_keyseq (ESC); + key = UnMeta (key); + info_dispatch_on_key (key, map); + } + else + { + dispatch_error (info_keyseq); + } + return; + } +#endif /* INFOKEY */ + + switch (map[key].type) + { + case ISFUNC: + { + VFunction *func; + + func = InfoFunction(map[key].function); + if (func != (VFunction *)NULL) + { + /* Special case info_do_lowercase_version (). */ + if (func == (VFunction *) info_do_lowercase_version) + { +#if defined(INFOKEY) + unsigned char lowerkey; + + lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key); + if (lowerkey == key) + { + add_char_to_keyseq (key); + dispatch_error (info_keyseq); + return; + } + info_dispatch_on_key (lowerkey, map); +#else /* !INFOKEY */ + info_dispatch_on_key (tolower (key), map); +#endif /* INFOKEY */ + return; + } + + add_char_to_keyseq (key); + + if (info_keyseq_displayed_p) + display_info_keyseq (0); + + { + WINDOW *where; + + where = active_window; + (*InfoFunction(map[key].function)) + (active_window, info_numeric_arg * info_numeric_arg_sign, key); + + /* If we have input pending, then the last command was a prefix + command. Don't change the value of the last function vars. + Otherwise, remember the last command executed in the var + appropriate to the window in which it was executed. */ + if (!info_input_pending_p ()) + { + if (where == the_echo_area) + ea_last_executed_command = InfoFunction(map[key].function); + else + info_last_executed_command = InfoFunction(map[key].function); + } + } + } + else + { + add_char_to_keyseq (key); + dispatch_error (info_keyseq); + return; + } + } + break; + + case ISKMAP: + add_char_to_keyseq (key); + if (map[key].function != (InfoCommand *)NULL) + { + unsigned char newkey; + + newkey = info_get_another_input_char (); + info_dispatch_on_key (newkey, (Keymap)map[key].function); + } + else + { + dispatch_error (info_keyseq); + return; + } + break; + } +} + +/* **************************************************************** */ +/* */ +/* Numeric Arguments */ +/* */ +/* **************************************************************** */ + +/* Handle C-u style numeric args, as well as M--, and M-digits. */ + +/* Non-zero means that an explicit argument has been passed to this + command, as in C-u C-v. */ +int info_explicit_arg = 0; + +/* The sign of the numeric argument. */ +int info_numeric_arg_sign = 1; + +/* The value of the argument itself. */ +int info_numeric_arg = 1; + +/* Add the current digit to the argument in progress. */ +DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg, + _("Add this digit to the current numeric argument")) +{ + info_numeric_arg_digit_loop (window, 0, key); +} + +/* C-u, universal argument. Multiply the current argument by 4. + Read a key. If the key has nothing to do with arguments, then + dispatch on it. If the key is the abort character then abort. */ +DECLARE_INFO_COMMAND (info_universal_argument, + _("Start (or multiply by 4) the current numeric argument")) +{ + info_numeric_arg *= 4; + info_numeric_arg_digit_loop (window, 0, 0); +} + +/* Create a default argument. */ +void +info_initialize_numeric_arg (void) +{ + info_numeric_arg = info_numeric_arg_sign = 1; + info_explicit_arg = 0; +} + +DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop, + _("Internally used by \\[universal-argument]")) +{ + unsigned char pure_key; + Keymap keymap = window->keymap; + + while (1) + { + if (key) + pure_key = key; + else + { + if (display_was_interrupted_p && !info_any_buffered_input_p ()) + display_update_display (windows); + + if (active_window != the_echo_area) + display_cursor_at_point (active_window); + + pure_key = key = info_get_another_input_char (); + +#if !defined(INFOKEY) + if (Meta_p (key)) + add_char_to_keyseq (ESC); + + add_char_to_keyseq (UnMeta (key)); +#else /* defined(INFOKEY) */ + add_char_to_keyseq (key); +#endif /* defined(INFOKEY) */ + } + +#if !defined(INFOKEY) + if (Meta_p (key)) + key = UnMeta (key); +#endif /* !defined(INFOKEY) */ + + if (keymap[key].type == ISFUNC + && InfoFunction(keymap[key].function) + == (VFunction *) info_universal_argument) + { + info_numeric_arg *= 4; + key = 0; + continue; + } + +#if defined(INFOKEY) + if (Meta_p (key)) + key = UnMeta (key); +#endif /* !defined(INFOKEY) */ + + + if (isdigit (key)) + { + if (info_explicit_arg) + info_numeric_arg = (info_numeric_arg * 10) + (key - '0'); + else + info_numeric_arg = (key - '0'); + info_explicit_arg = 1; + } + else + { + if (key == '-' && !info_explicit_arg) + { + info_numeric_arg_sign = -1; + info_numeric_arg = 1; + } + else + { + info_keyseq_index--; + info_dispatch_on_key (pure_key, keymap); + return; + } + } + key = 0; + } +} + +/* **************************************************************** */ +/* */ +/* Input Character Buffering */ +/* */ +/* **************************************************************** */ + +/* Character waiting to be read next. */ +static int pending_input_character = 0; + +/* How to make there be no pending input. */ +static void +info_clear_pending_input (void) +{ + pending_input_character = 0; +} + +/* How to set the pending input character. */ +static void +info_set_pending_input (unsigned char key) +{ + pending_input_character = key; +} + +/* How to see if there is any pending input. */ +unsigned char +info_input_pending_p (void) +{ + return (pending_input_character); +} + +/* Largest number of characters that we can read in advance. */ +#define MAX_INFO_INPUT_BUFFERING 512 + +static int pop_index = 0, push_index = 0; +static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING]; + +/* Add KEY to the buffer of characters to be read. */ +static void +info_push_typeahead (unsigned char key) +{ + /* Flush all pending input in the case of C-g pressed. */ + if (key == Control ('g')) + { + push_index = pop_index; + info_set_pending_input (Control ('g')); + } + else + { + info_input_buffer[push_index++] = key; + if ((unsigned int) push_index >= sizeof (info_input_buffer)) + push_index = 0; + } +} + +/* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */ +static int +info_input_buffer_space_available (void) +{ + if (pop_index > push_index) + return (pop_index - push_index); + else + return (sizeof (info_input_buffer) - (push_index - pop_index)); +} + +/* Get a key from the buffer of characters to be read. + Return the key in KEY. + Result is non-zero if there was a key, or 0 if there wasn't. */ +static int +info_get_key_from_typeahead (unsigned char *key) +{ + if (push_index == pop_index) + return (0); + + *key = info_input_buffer[pop_index++]; + + if ((unsigned int) pop_index >= sizeof (info_input_buffer)) + pop_index = 0; + + return (1); +} + +int +info_any_buffered_input_p (void) +{ + info_gather_typeahead (); + return (push_index != pop_index); +} + +/* If characters are available to be read, then read them and stuff them into + info_input_buffer. Otherwise, do nothing. */ +void +info_gather_typeahead (void) +{ + register int i = 0; + int tty, space_avail; + long chars_avail; + unsigned char input[MAX_INFO_INPUT_BUFFERING]; + + tty = fileno (info_input_stream); + chars_avail = 0; + + space_avail = info_input_buffer_space_available (); + + /* If we can just find out how many characters there are to read, do so. */ +#if defined (FIONREAD) + { + ioctl (tty, FIONREAD, &chars_avail); + + if (chars_avail > space_avail) + chars_avail = space_avail; + + if (chars_avail) + chars_avail = read (tty, &input[0], chars_avail); + } +#else /* !FIONREAD */ +# if defined (O_NDELAY) + { + int flags; + + flags = fcntl (tty, F_GETFL, 0); + + fcntl (tty, F_SETFL, (flags | O_NDELAY)); + chars_avail = read (tty, &input[0], space_avail); + fcntl (tty, F_SETFL, flags); + + if (chars_avail == -1) + chars_avail = 0; + } +# else /* !O_NDELAY */ +# ifdef __DJGPP__ + { + extern long pc_term_chars_avail (void); + + if (isatty (tty)) + chars_avail = pc_term_chars_avail (); + else + { + /* We could be more accurate by calling ltell, but we have no idea + whether tty is buffered by stdio functions, and if so, how many + characters are already waiting in the buffer. So we punt. */ + struct stat st; + + if (fstat (tty, &st) < 0) + chars_avail = 1; + else + chars_avail = st.st_size; + } + if (chars_avail > space_avail) + chars_avail = space_avail; + if (chars_avail) + chars_avail = read (tty, &input[0], chars_avail); + } +# endif/* __DJGPP__ */ +# endif /* O_NDELAY */ +#endif /* !FIONREAD */ + + while (i < chars_avail) + { + info_push_typeahead (input[i]); + i++; + } +} + +/* How to read a single character. */ +unsigned char +info_get_input_char (void) +{ + unsigned char keystroke; + + info_gather_typeahead (); + + if (pending_input_character) + { + keystroke = pending_input_character; + pending_input_character = 0; + } + else if (info_get_key_from_typeahead (&keystroke) == 0) + { + int rawkey; + unsigned char c; + int tty = fileno (info_input_stream); + + /* Using stream I/O causes FIONREAD etc to fail to work + so unless someone can find a portable way of finding + out how many characters are currently buffered, we + should stay with away from stream I/O. + --Egil Kvaleberg <egilk@sn.no>, January 1997. */ +#ifdef EINTR + /* Keep reading if we got EINTR, so that we don't just exit. + --Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>, + 22 Dec 1997. */ + { + int n; + do + n = read (tty, &c, 1); + while (n == -1 && errno == EINTR); + rawkey = n == 1 ? c : EOF; + } +#else + rawkey = (read (tty, &c, 1) == 1) ? c : EOF; +#endif + + keystroke = rawkey; + + if (rawkey == EOF) + { + if (info_input_stream != stdin) + { + fclose (info_input_stream); + info_input_stream = stdin; + tty = fileno (info_input_stream); + display_inhibited = 0; + display_update_display (windows); + display_cursor_at_point (active_window); + rawkey = (read (tty, &c, 1) == 1) ? c : EOF; + keystroke = rawkey; + } + + if (rawkey == EOF) + { + terminal_unprep_terminal (); + close_dribble_file (); + xexit (0); + } + } + } + + if (info_dribble_file) + dribble (keystroke); + + return keystroke; +} diff --git a/Build/source/utils/texinfo/info/session.h b/Build/source/utils/texinfo/info/session.h new file mode 100644 index 00000000000..62f8ffe57d2 --- /dev/null +++ b/Build/source/utils/texinfo/info/session.h @@ -0,0 +1,237 @@ +/* session.h -- Functions found in session.c. + $Id: session.h,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1998, 1999, 2001, 2002, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef SESSION_H +#define SESSION_H + +#include "info.h" +#include "dribble.h" + +/* All commands that can be invoked from within info_session () receive + arguments in the same way. This simple define declares the header + of a function named NAME, with associated documentation DOC. The + documentation string is groveled out of the source files by the + utility program `makedoc', which is also responsible for making + the documentation/function-pointer maps. */ +#define DECLARE_INFO_COMMAND(name, doc) \ +void name (WINDOW *window, int count, unsigned char key) + +/* Variables found in session.h. */ +extern VFunction *info_last_executed_command; + +/* Variable controlling the garbage collection of files briefly visited + during searches. Such files are normally gc'ed, unless they were + compressed to begin with. If this variable is non-zero, it says + to gc even those file buffer contents which had to be uncompressed. */ +extern int gc_compressed_files; + +/* When non-zero, tiling takes place automatically when info_split_window + is called. */ +extern int auto_tiling_p; + +/* Variable controlling the behaviour of default scrolling when you are + already at the bottom of a node. */ +extern int info_scroll_behaviour; +extern char *info_scroll_choices[]; + +/* Values for info_scroll_behaviour. */ +#define IS_Continuous 0 /* Try to get first menu item, or failing that, the + "Next:" pointer, or failing that, the "Up:" and + "Next:" of the up. */ +#define IS_NextOnly 1 /* Try to get "Next:" menu item. */ +#define IS_PageOnly 2 /* Simply give up at the bottom of a node. */ + +/* Utility functions found in session.c */ +extern void info_dispatch_on_key (unsigned char key, Keymap map); +extern unsigned char info_get_input_char (void); +extern unsigned char info_get_another_input_char (void); +extern unsigned char info_input_pending_p (void); +extern void remember_window_and_node (WINDOW *window, NODE *node); +extern void set_remembered_pagetop_and_point (WINDOW *window); +extern void set_window_pagetop (WINDOW *window, int desired_top); +extern void info_set_node_of_window (int remember, WINDOW *window, + NODE *node); +extern void initialize_keyseq (void); +extern void add_char_to_keyseq (char character); +extern void info_gather_typeahead (void); +extern FILE_BUFFER *file_buffer_of_window (WINDOW *window); +extern long info_search_in_node (char *string, NODE *node, + long int start, WINDOW *window, int dir, int case_sensitive); +extern long info_target_search_node (NODE *node, char *string, + long int start); +extern void info_select_reference (WINDOW *window, REFERENCE *entry); +extern int info_any_buffered_input_p (void); +extern void print_node (NODE *node); +extern void dump_node_to_file (NODE *node, char *filename, + int dump_subnodes); +extern void dump_nodes_to_file (char *filename, char **nodenames, + char *output_filename, int dump_subnodes); +extern char *program_name_from_file_name (char *file_name); + +/* Do the physical deletion of WINDOW, and forget this window and + associated nodes. */ +extern void info_delete_window_internal (WINDOW *window); + +/* Tell Info that input is coming from the file FILENAME. */ +extern void info_set_input_from_file (char *filename); + +#define return_if_control_g(val) \ + do { \ + info_gather_typeahead (); \ + if (info_input_pending_p () == Control ('g')) \ + return (val); \ + } while (0) + +/* The names of the functions that run an info session. */ + +/* Starting an info session. */ +extern void begin_multiple_window_info_session (char *filename, + char **nodenames); +extern void begin_info_session (NODE *initial_node); +extern void begin_info_session_with_error (NODE *initial_node, + char *format, void *arg1, void *arg2); +extern void info_session (void); +extern void initialize_info_session (NODE *node, int clear_screen); +extern void info_read_and_dispatch (void); +extern void info_intuit_options_node (WINDOW *window, + NODE *initial_node, char *program); + +/* Moving the point within a node. */ +extern void info_next_line (WINDOW *window, int count, unsigned char key); +extern void info_prev_line (WINDOW *window, int count, unsigned char key); +extern void info_end_of_line (WINDOW *window, int count, unsigned char key); +extern void info_beginning_of_line (WINDOW *window, int count, unsigned char key); +extern void info_forward_char (WINDOW *window, int count, unsigned char key); +extern void info_backward_char (WINDOW *window, int count, unsigned char key); +extern void info_forward_word (WINDOW *window, int count, unsigned char key); +extern void info_backward_word (WINDOW *window, int count, unsigned char key); +extern void info_beginning_of_node (WINDOW *window, int count, unsigned char key); +extern void info_end_of_node (WINDOW *window, int count, unsigned char key); +extern void info_move_to_prev_xref (WINDOW *window, int count, unsigned char key); +extern void info_move_to_next_xref (WINDOW *window, int count, unsigned char key); + +/* Scrolling text within a window. */ +extern void info_scroll_forward (WINDOW *window, int count, unsigned char key); +extern void info_scroll_backward (WINDOW *window, int count, unsigned char key); +extern void info_redraw_display (WINDOW *window, int count, unsigned char key); +extern void info_toggle_wrap (WINDOW *window, int count, unsigned char key); +extern void info_move_to_window_line (WINDOW *window, int count, + unsigned char key); +extern void info_up_line (WINDOW *window, int count, unsigned char key); +extern void info_down_line (WINDOW *window, int count, unsigned char key); +extern void info_scroll_half_screen_down (WINDOW *window, int count, + unsigned char key); +extern void info_scroll_half_screen_up (WINDOW *window, int count, + unsigned char key); +extern void info_scroll_forward_set_window (WINDOW *window, int count, + unsigned char key); +extern void info_scroll_forward_page_only (WINDOW *window, int count, + unsigned char key); +extern void info_scroll_forward_page_only_set_window (WINDOW *window, int count, + unsigned char key); +extern void info_scroll_backward_set_window (WINDOW *window, int count, + unsigned char key); +extern void info_scroll_backward_page_only (WINDOW *window, int count, + unsigned char key); +extern void info_scroll_backward_page_only_set_window (WINDOW *window, int count, + unsigned char key); +extern void info_scroll_other_window_backward (WINDOW *window, int count, + unsigned char key); + +/* Manipulating multiple windows. */ +extern void info_split_window (WINDOW *window, int count, unsigned char key); +extern void info_delete_window (WINDOW *window, int count, unsigned char key); +extern void info_keep_one_window (WINDOW *window, int count, unsigned char key); +extern void info_grow_window (WINDOW *window, int count, unsigned char key); +extern void info_scroll_other_window (WINDOW *window, int count, + unsigned char key); +extern void info_tile_windows (WINDOW *window, int count, unsigned char key); +extern void info_next_window (WINDOW *window, int count, unsigned char key); +extern void info_prev_window (WINDOW *window, int count, unsigned char key); + +/* Selecting nodes. */ +extern void info_next_node (WINDOW *window, int count, unsigned char key); +extern void info_prev_node (WINDOW *window, int count, unsigned char key); +extern void info_up_node (WINDOW *window, int count, unsigned char key); +extern void info_last_node (WINDOW *window, int count, unsigned char key); +extern void info_first_node (WINDOW *window, int count, unsigned char key); +extern void info_history_node (WINDOW *window, int count, unsigned char key); +extern void info_goto_node (WINDOW *window, int count, unsigned char key); +extern void info_goto_invocation_node (WINDOW *window, int count, + unsigned char key); +extern void info_top_node (WINDOW *window, int count, unsigned char key); +extern void info_dir_node (WINDOW *window, int count, unsigned char key); +extern void info_global_next_node (WINDOW *window, int count, unsigned char key); +extern void info_global_prev_node (WINDOW *window, int count, unsigned char key); +extern void info_kill_node (WINDOW *window, int count, unsigned char key); +extern void info_view_file (WINDOW *window, int count, unsigned char key); +extern void info_menu_sequence (WINDOW *window, int count, unsigned char key); +extern NODE *info_follow_menus (NODE *initial_node, char **menus, + const char **errstr, char **errarg1, char **errarg2); +extern void info_man (WINDOW *window, int count, unsigned char key); +extern void list_visited_nodes (WINDOW *window, int count, unsigned char key); +extern void select_visited_node (WINDOW *window, int count, unsigned char key); + +/* Selecting cross references. */ +extern void info_menu_digit (WINDOW *window, int count, unsigned char key); +extern void info_menu_item (WINDOW *window, int count, unsigned char key); +extern void info_xref_item (WINDOW *window, int count, unsigned char key); +extern void info_find_menu (WINDOW *window, int count, unsigned char key); +extern void info_select_reference_this_line (WINDOW *window, int count, + unsigned char key); +extern void info_last_menu_item (WINDOW *window, int count, unsigned char key); +extern void info_visit_menu (WINDOW *window, int count, unsigned char key); + +/* Hacking numeric arguments. */ +extern int info_explicit_arg, info_numeric_arg, info_numeric_arg_sign; + +extern void info_add_digit_to_numeric_arg (WINDOW *window, int count, + unsigned char key); +extern void info_universal_argument (WINDOW *window, int count, + unsigned char key); +extern void info_initialize_numeric_arg (void); +extern void info_numeric_arg_digit_loop (WINDOW *window, int count, + unsigned char key); + +/* Searching commands. */ +extern void info_search (WINDOW *window, int count, unsigned char key); +extern void isearch_forward (WINDOW *window, int count, unsigned char key); +extern void isearch_backward (WINDOW *window, int count, unsigned char key); +extern void info_search_case_sensitively (WINDOW *window, int count, + unsigned char key); +extern void info_search_backward (WINDOW *window, int count, unsigned char key); +extern void info_search_next (WINDOW *window, int count, unsigned char key); +extern void info_search_previous (WINDOW *window, int count, unsigned char key); + +/* Dumping and printing nodes. */ +extern void info_print_node (WINDOW *window, int count, unsigned char key); + +/* Footnotes. */ +extern void info_show_footnotes (WINDOW *window, int count, unsigned char key); + +/* Miscellaneous commands. */ +extern void info_abort_key (WINDOW *window, int count, unsigned char key); +extern void info_quit (WINDOW *window, int count, unsigned char key); +extern void info_do_lowercase_version (WINDOW *window, int count, + unsigned char key); + +#endif /* not SESSION_H */ diff --git a/Build/source/utils/texinfo/info/signals.c b/Build/source/utils/texinfo/info/signals.c new file mode 100644 index 00000000000..724768b4ec7 --- /dev/null +++ b/Build/source/utils/texinfo/info/signals.c @@ -0,0 +1,295 @@ +/* signals.c -- install and maintain signal handlers. + $Id: signals.c,v 1.7 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1994, 1995, 1998, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "signals.h" + +void initialize_info_signal_handler (void); + +/* **************************************************************** */ +/* */ +/* Pretending That We Have POSIX Signals */ +/* */ +/* **************************************************************** */ + +#if !defined (HAVE_SIGPROCMASK) && defined (HAVE_SIGSETMASK) +/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */ +static void +sigprocmask (int operation, int *newset, int *oldset) +{ + switch (operation) + { + case SIG_UNBLOCK: + sigsetmask (sigblock (0) & ~(*newset)); + break; + + case SIG_BLOCK: + *oldset = sigblock (*newset); + break; + + case SIG_SETMASK: + sigsetmask (*newset); + break; + + default: + abort (); + } +} +#endif /* !HAVE_SIGPROCMASK && HAVE_SIGSETMASK */ + +/* **************************************************************** */ +/* */ +/* Signal Handling for Info */ +/* */ +/* **************************************************************** */ + +#if defined (HAVE_SIGACTION) || defined (HAVE_SIGPROCMASK) ||\ + defined (HAVE_SIGSETMASK) +static void +mask_termsig (sigset_t *set) +{ +# if defined (SIGTSTP) + sigaddset (set, SIGTSTP); + sigaddset (set, SIGTTOU); + sigaddset (set, SIGTTIN); +# endif +# if defined (SIGWINCH) + sigaddset (set, SIGWINCH); +# endif +#if defined (SIGQUIT) + sigaddset (set, SIGQUIT); +#endif +#if defined (SIGINT) + sigaddset (set, SIGINT); +#endif +# if defined (SIGUSR1) + sigaddset (set, SIGUSR1); +# endif +} +#endif /* HAVE_SIGACTION || HAVE_SIGPROCMASK || HAVE_SIGSETMASK */ + +static RETSIGTYPE info_signal_proc (int sig); +#if defined (HAVE_SIGACTION) +typedef struct sigaction signal_info; +signal_info info_signal_handler; + +static void +set_termsig (int sig, signal_info *old) +{ + sigaction (sig, &info_signal_handler, old); +} + +static void +restore_termsig (int sig, const signal_info *saved) +{ + sigaction (sig, saved, NULL); +} +#else /* !HAVE_SIGACTION */ +typedef RETSIGTYPE (*signal_info) (); +#define set_termsig(sig, old) (void)(*(old) = signal (sig, info_signal_proc)) +#define restore_termsig(sig, saved) (void)signal (sig, *(saved)) +#define info_signal_handler info_signal_proc +static int term_conf_busy = 0; +#endif /* !HAVE_SIGACTION */ + +static signal_info old_TSTP, old_TTOU, old_TTIN; +static signal_info old_WINCH, old_INT, old_USR1; +static signal_info old_QUIT; + +void +initialize_info_signal_handler (void) +{ +#ifdef SA_NOCLDSTOP + /* (Based on info from Paul Eggert found in coreutils.) Don't use + HAVE_SIGACTION to decide whether to use the sa_handler, sa_flags, + sa_mask members, as some systems (Solaris 7+) don't define them. Use + SA_NOCLDSTOP instead; it's been part of POSIX.1 since day 1 (in 1988). */ + info_signal_handler.sa_handler = info_signal_proc; + info_signal_handler.sa_flags = 0; + mask_termsig (&info_signal_handler.sa_mask); +#endif /* SA_NOCLDSTOP */ + +#if defined (SIGTSTP) + set_termsig (SIGTSTP, &old_TSTP); + set_termsig (SIGTTOU, &old_TTOU); + set_termsig (SIGTTIN, &old_TTIN); +#endif /* SIGTSTP */ + +#if defined (SIGWINCH) + set_termsig (SIGWINCH, &old_WINCH); +#endif + +#if defined (SIGQUIT) + set_termsig (SIGQUIT, &old_QUIT); +#endif + +#if defined (SIGINT) + set_termsig (SIGINT, &old_INT); +#endif + +#if defined (SIGUSR1) + /* Used by DJGPP to simulate SIGTSTP on Ctrl-Z. */ + set_termsig (SIGUSR1, &old_USR1); +#endif +} + +static void +redisplay_after_signal (void) +{ + terminal_clear_screen (); + display_clear_display (the_display); + window_mark_chain (windows, W_UpdateWindow); + display_update_display (windows); + display_cursor_at_point (active_window); + fflush (stdout); +} + +static void +reset_info_window_sizes (void) +{ + terminal_goto_xy (0, 0); + fflush (stdout); + terminal_unprep_terminal (); + terminal_get_screen_size (); + terminal_prep_terminal (); + display_initialize_display (screenwidth, screenheight); + window_new_screen_size (screenwidth, screenheight); + redisplay_after_signal (); +} + +static RETSIGTYPE +info_signal_proc (int sig) +{ + signal_info *old_signal_handler = NULL; + +#if !defined (HAVE_SIGACTION) + /* best effort: first increment this counter and later block signals */ + if (term_conf_busy) + return; + term_conf_busy++; +#if defined (HAVE_SIGPROCMASK) || defined (HAVE_SIGSETMASK) + { + sigset_t nvar, ovar; + sigemptyset (&nvar); + mask_termsig (&nvar); + sigprocmask (SIG_BLOCK, &nvar, &ovar); + } +#endif /* HAVE_SIGPROCMASK || HAVE_SIGSETMASK */ +#endif /* !HAVE_SIGACTION */ + switch (sig) + { +#if defined (SIGTSTP) + case SIGTSTP: + case SIGTTOU: + case SIGTTIN: +#endif +#if defined (SIGQUIT) + case SIGQUIT: +#endif +#if defined (SIGINT) + case SIGINT: +#endif + { +#if defined (SIGTSTP) + if (sig == SIGTSTP) + old_signal_handler = &old_TSTP; + if (sig == SIGTTOU) + old_signal_handler = &old_TTOU; + if (sig == SIGTTIN) + old_signal_handler = &old_TTIN; +#endif /* SIGTSTP */ +#if defined (SIGQUIT) + if (sig == SIGQUIT) + old_signal_handler = &old_QUIT; +#endif /* SIGQUIT */ +#if defined (SIGINT) + if (sig == SIGINT) + old_signal_handler = &old_INT; +#endif /* SIGINT */ + + /* For stop signals, restore the terminal IO, leave the cursor + at the bottom of the window, and stop us. */ + terminal_goto_xy (0, screenheight - 1); + terminal_clear_to_eol (); + fflush (stdout); + terminal_unprep_terminal (); + restore_termsig (sig, old_signal_handler); + UNBLOCK_SIGNAL (sig); + kill (getpid (), sig); + + /* The program is returning now. Restore our signal handler, + turn on terminal handling, redraw the screen, and place the + cursor where it belongs. */ + terminal_prep_terminal (); + set_termsig (sig, old_signal_handler); + /* window size might be changed while sleeping */ + reset_info_window_sizes (); + } + break; + +#if defined (SIGWINCH) || defined (SIGUSR1) +#ifdef SIGWINCH + case SIGWINCH: +#endif +#ifdef SIGUSR1 + case SIGUSR1: +#endif + { + /* Turn off terminal IO, tell our parent that the window has changed, + then reinitialize the terminal and rebuild our windows. */ +#ifdef SIGWINCH + if (sig == SIGWINCH) + old_signal_handler = &old_WINCH; +#endif +#ifdef SIGUSR1 + if (sig == SIGUSR1) + old_signal_handler = &old_USR1; +#endif + terminal_goto_xy (0, 0); + fflush (stdout); + terminal_unprep_terminal (); /* needless? */ + restore_termsig (sig, old_signal_handler); + UNBLOCK_SIGNAL (sig); + kill (getpid (), sig); + + /* After our old signal handler returns... */ + set_termsig (sig, old_signal_handler); /* needless? */ + terminal_prep_terminal (); + reset_info_window_sizes (); + } + break; +#endif /* SIGWINCH || SIGUSR1 */ + } +#if !defined (HAVE_SIGACTION) + /* at this time it is safer to perform unblock after decrement */ + term_conf_busy--; +#if defined (HAVE_SIGPROCMASK) || defined (HAVE_SIGSETMASK) + { + sigset_t nvar, ovar; + sigemptyset (&nvar); + mask_termsig (&nvar); + sigprocmask (SIG_UNBLOCK, &nvar, &ovar); + } +#endif /* HAVE_SIGPROCMASK || HAVE_SIGSETMASK */ +#endif /* !HAVE_SIGACTION */ +} +/* vim: set sw=2 cino={1s>2sn-s^-se-s: */ diff --git a/Build/source/utils/texinfo/info/signals.h b/Build/source/utils/texinfo/info/signals.h new file mode 100644 index 00000000000..2e3761e37b8 --- /dev/null +++ b/Build/source/utils/texinfo/info/signals.h @@ -0,0 +1,96 @@ +/* signals.h -- header to include system dependent signal definitions. + $Id: signals.h,v 1.2 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1994, 1995, 1997, 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_SIGNALS_H +#define INFO_SIGNALS_H + +#include <sys/types.h> +#include <signal.h> + +/* For sysV68 --phdm@info.ucl.ac.be. */ +#if !defined (SIGCHLD) && defined (SIGCLD) +#define SIGCHLD SIGCLD +#endif + +#if !defined (HAVE_SIGPROCMASK) && !defined (sigmask) +# define sigmask(x) (1 << ((x)-1)) +#endif /* !HAVE_SIGPROCMASK && !sigmask */ + +/* Without SA_NOCLDSTOP, sigset_t might end up being undefined even + though we have sigprocmask, on older systems, according to Nelson + Beebe. The test is from coreutils/sort.c, via Paul Eggert. */ +#if !defined (HAVE_SIGPROCMASK) || !defined (SA_NOCLDSTOP) +# if !defined (SIG_BLOCK) +# define SIG_UNBLOCK 1 +# define SIG_BLOCK 2 +# define SIG_SETMASK 3 +# endif /* SIG_BLOCK */ + +/* Type of a signal set. */ +# define sigset_t int + +/* Make SET have no signals in it. */ +# define sigemptyset(set) (*(set) = (sigset_t)0x0) + +/* Make SET have the full range of signal specifications possible. */ +# define sigfillset(set) (*(set) = (sigset_t)0xffffffffff) + +/* Add SIG to the contents of SET. */ +# define sigaddset(set, sig) *(set) |= sigmask (sig) + +/* Delete SIG from the contents of SET. */ +# define sigdelset(set, sig) *(set) &= ~(sigmask (sig)) + +/* Tell if SET contains SIG. */ +# define sigismember(set, sig) (*(set) & (sigmask (sig))) + +/* Suspend the process until the reception of one of the signals + not present in SET. */ +# define sigsuspend(set) sigpause (*(set)) +#endif /* !HAVE_SIGPROCMASK */ + +#if defined (HAVE_SIGPROCMASK) || defined (HAVE_SIGSETMASK) +/* These definitions are used both in POSIX and non-POSIX implementations. */ + +#define BLOCK_SIGNAL(sig) \ + do { \ + sigset_t nvar, ovar; \ + sigemptyset (&nvar); \ + sigemptyset (&ovar); \ + sigaddset (&nvar, sig); \ + sigprocmask (SIG_BLOCK, &nvar, &ovar); \ + } while (0) + +#define UNBLOCK_SIGNAL(sig) \ + do { \ + sigset_t nvar, ovar; \ + sigemptyset (&ovar); \ + sigemptyset (&nvar); \ + sigaddset (&nvar, sig); \ + sigprocmask (SIG_UNBLOCK, &nvar, &ovar); \ + } while (0) + +#else /* !HAVE_SIGPROCMASK && !HAVE_SIGSETMASK */ +# define BLOCK_SIGNAL(sig) +# define UNBLOCK_SIGNAL(sig) +#endif /* !HAVE_SIGPROCMASK && !HAVE_SIGSETMASK */ + +#endif /* not INFO_SIGNALS_H */ diff --git a/Build/source/utils/texinfo/info/termdep.h b/Build/source/utils/texinfo/info/termdep.h new file mode 100644 index 00000000000..45f38aca5b3 --- /dev/null +++ b/Build/source/utils/texinfo/info/termdep.h @@ -0,0 +1,59 @@ +/* termdep.h -- system things that terminal.c depends on. + $Id: termdep.h,v 1.2 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1996, 1997, 1998, 2001, 2002 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_TERMDEP_H +#define INFO_TERMDEP_H + +/* NeXT supplies <termios.h> but it is broken. Probably Autoconf should + have a separate test, but anyway ... */ +#ifdef NeXT +#undef HAVE_TERMIOS_H +#endif + +#ifdef HAVE_TERMIOS_H +# include <termios.h> +#else +# if defined (HAVE_TERMIO_H) +# include <termio.h> +# if defined (HAVE_SYS_PTEM_H) +# if defined (M_UNIX) || !defined (M_XENIX) +# include <sys/stream.h> +# include <sys/ptem.h> +# undef TIOCGETC +# else /* M_XENIX */ +# define tchars tc +# endif /* M_XENIX */ +# endif /* HAVE_SYS_PTEM_H */ +# else /* !HAVE_TERMIO_H */ +# include <sgtty.h> +# endif /* !HAVE_TERMIO_H */ +#endif /* !HAVE_TERMIOS_H */ + +#ifdef GWINSZ_IN_SYS_IOCTL +# include <sys/ioctl.h> +#endif + +#ifdef HAVE_SYS_TTOLD_H +# include <sys/ttold.h> +#endif /* HAVE_SYS_TTOLD_H */ + +#endif /* not INFO_TERMDEP_H */ diff --git a/Build/source/utils/texinfo/info/terminal.c b/Build/source/utils/texinfo/info/terminal.c new file mode 100644 index 00000000000..9238643d456 --- /dev/null +++ b/Build/source/utils/texinfo/info/terminal.c @@ -0,0 +1,873 @@ +/* terminal.c -- how to handle the physical terminal for Info. + $Id: terminal.c,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1996, 1997, 1998, + 1999, 2001, 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Originally written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "terminal.h" +#include "termdep.h" + +#include <sys/types.h> +#include <signal.h> + +/* The Unix termcap interface code. */ +#ifdef HAVE_NCURSES_TERMCAP_H +#include <ncurses/termcap.h> +#else +#ifdef HAVE_TERMCAP_H +#include <termcap.h> +#else +/* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC. + Unfortunately, PC is a global variable used by the termcap library. */ +#undef PC + +/* Termcap requires these variables, whether we access them or not. */ +char *BC, *UP; +char PC; /* Pad character */ +short ospeed; /* Terminal output baud rate */ +extern int tgetnum (), tgetflag (), tgetent (); +extern char *tgetstr (), *tgoto (); +extern void tputs (); +#endif /* not HAVE_TERMCAP_H */ +#endif /* not HAVE_NCURSES_TERMCAP_H */ + +/* Function "hooks". If you make one of these point to a function, that + function is called when appropriate instead of its namesake. Your + function is called with exactly the same arguments that were passed + to the namesake function. */ +VFunction *terminal_begin_inverse_hook = (VFunction *)NULL; +VFunction *terminal_end_inverse_hook = (VFunction *)NULL; +VFunction *terminal_prep_terminal_hook = (VFunction *)NULL; +VFunction *terminal_unprep_terminal_hook = (VFunction *)NULL; +VFunction *terminal_up_line_hook = (VFunction *)NULL; +VFunction *terminal_down_line_hook = (VFunction *)NULL; +VFunction *terminal_clear_screen_hook = (VFunction *)NULL; +VFunction *terminal_clear_to_eol_hook = (VFunction *)NULL; +VFunction *terminal_get_screen_size_hook = (VFunction *)NULL; +VFunction *terminal_goto_xy_hook = (VFunction *)NULL; +VFunction *terminal_initialize_terminal_hook = (VFunction *)NULL; +VFunction *terminal_new_terminal_hook = (VFunction *)NULL; +VFunction *terminal_put_text_hook = (VFunction *)NULL; +VFunction *terminal_ring_bell_hook = (VFunction *)NULL; +VFunction *terminal_write_chars_hook = (VFunction *)NULL; +VFunction *terminal_scroll_terminal_hook = (VFunction *)NULL; + +/* **************************************************************** */ +/* */ +/* Terminal and Termcap */ +/* */ +/* **************************************************************** */ + +/* A buffer which holds onto the current terminal description, and a pointer + used to float within it. And the name of the terminal. */ +static char *term_buffer = NULL; +static char *term_string_buffer = NULL; +static char *term_name; + +/* Some strings to control terminal actions. These are output by tputs (). */ +static char *term_goto, *term_clreol, *term_cr, *term_clrpag; +static char *term_begin_use, *term_end_use; +static char *term_AL, *term_DL, *term_al, *term_dl; + +static char *term_keypad_on, *term_keypad_off; + +/* How to go up a line. */ +static char *term_up; + +/* How to go down a line. */ +static char *term_dn; + +/* An audible bell, if the terminal can be made to make noise. */ +static char *audible_bell; + +/* A visible bell, if the terminal can be made to flash the screen. */ +static char *visible_bell; + +/* The string to write to turn on the meta key, if this term has one. */ +static char *term_mm; + +/* The string to turn on inverse mode, if this term has one. */ +static char *term_invbeg; + +/* The string to turn off inverse mode, if this term has one. */ +static char *term_invend; + +/* Although I can't find any documentation that says this is supposed to + return its argument, all the code I've looked at (termutils, less) + does so, so fine. */ +static int +output_character_function (int c) +{ + putc (c, stdout); + return c; +} + +/* Macro to send STRING to the terminal. */ +#define send_to_terminal(string) \ + do { \ + if (string) \ + tputs (string, 1, output_character_function); \ + } while (0) + +/* Tell the terminal that we will be doing cursor addressable motion. */ +static void +terminal_begin_using_terminal (void) +{ + RETSIGTYPE (*sigsave) (int signum); + + if (term_keypad_on) + send_to_terminal (term_keypad_on); + + if (!term_begin_use || !*term_begin_use) + return; + +#ifdef SIGWINCH + sigsave = signal (SIGWINCH, SIG_IGN); +#endif + + send_to_terminal (term_begin_use); + fflush (stdout); + if (STREQ (term_name, "sun-cmd")) + /* Without this fflush and sleep, running info in a shelltool or + cmdtool (TERM=sun-cmd) with scrollbars loses -- the scrollbars are + not restored properly. + From: strube@physik3.gwdg.de (Hans Werner Strube). */ + sleep (1); + +#ifdef SIGWINCH + signal (SIGWINCH, sigsave); +#endif +} + +/* Tell the terminal that we will not be doing any more cursor + addressable motion. */ +static void +terminal_end_using_terminal (void) +{ + RETSIGTYPE (*sigsave) (int signum); + + if (term_keypad_off) + send_to_terminal (term_keypad_off); + + if (!term_end_use || !*term_end_use) + return; + +#ifdef SIGWINCH + sigsave = signal (SIGWINCH, SIG_IGN); +#endif + + send_to_terminal (term_end_use); + fflush (stdout); + if (STREQ (term_name, "sun-cmd")) + /* See comments at other sleep. */ + sleep (1); + +#ifdef SIGWINCH + signal (SIGWINCH, sigsave); +#endif +} + +/* **************************************************************** */ +/* */ +/* Necessary Terminal Functions */ +/* */ +/* **************************************************************** */ + +/* The functions and variables on this page implement the user visible + portion of the terminal interface. */ + +/* The width and height of the terminal. */ +int screenwidth, screenheight; + +/* Non-zero means this terminal can't really do anything. */ +int terminal_is_dumb_p = 0; + +/* Non-zero means that this terminal has a meta key. */ +int terminal_has_meta_p = 0; + +/* Non-zero means that this terminal can produce a visible bell. */ +int terminal_has_visible_bell_p = 0; + +/* Non-zero means to use that visible bell if at all possible. */ +int terminal_use_visible_bell_p = 0; + +/* Non-zero means that the terminal can do scrolling. */ +int terminal_can_scroll = 0; + +/* The key sequences output by the arrow keys, if this terminal has any. */ +char *term_ku = NULL; +char *term_kd = NULL; +char *term_kr = NULL; +char *term_kl = NULL; +char *term_kP = NULL; /* page-up */ +char *term_kN = NULL; /* page-down */ +char *term_kh = NULL; /* home */ +char *term_ke = NULL; /* end */ +char *term_kD = NULL; /* delete */ +char *term_ki = NULL; /* ins */ +char *term_kx = NULL; /* del */ + +/* Move the cursor to the terminal location of X and Y. */ +void +terminal_goto_xy (int x, int y) +{ + if (terminal_goto_xy_hook) + (*terminal_goto_xy_hook) (x, y); + else + { + if (term_goto) + tputs (tgoto (term_goto, x, y), 1, output_character_function); + } +} + +/* Print STRING to the terminal at the current position. */ +void +terminal_put_text (char *string) +{ + if (terminal_put_text_hook) + (*terminal_put_text_hook) (string); + else + { + printf ("%s", string); + } +} + +/* Print NCHARS from STRING to the terminal at the current position. */ +void +terminal_write_chars (char *string, int nchars) +{ + if (terminal_write_chars_hook) + (*terminal_write_chars_hook) (string, nchars); + else + { + if (nchars) + fwrite (string, 1, nchars, stdout); + } +} + +/* Clear from the current position of the cursor to the end of the line. */ +void +terminal_clear_to_eol (void) +{ + if (terminal_clear_to_eol_hook) + (*terminal_clear_to_eol_hook) (); + else + { + send_to_terminal (term_clreol); + } +} + +/* Clear the entire terminal screen. */ +void +terminal_clear_screen (void) +{ + if (terminal_clear_screen_hook) + (*terminal_clear_screen_hook) (); + else + { + send_to_terminal (term_clrpag); + } +} + +/* Move the cursor up one line. */ +void +terminal_up_line (void) +{ + if (terminal_up_line_hook) + (*terminal_up_line_hook) (); + else + { + send_to_terminal (term_up); + } +} + +/* Move the cursor down one line. */ +void +terminal_down_line (void) +{ + if (terminal_down_line_hook) + (*terminal_down_line_hook) (); + else + { + send_to_terminal (term_dn); + } +} + +/* Turn on reverse video if possible. */ +void +terminal_begin_inverse (void) +{ + if (terminal_begin_inverse_hook) + (*terminal_begin_inverse_hook) (); + else + { + send_to_terminal (term_invbeg); + } +} + +/* Turn off reverse video if possible. */ +void +terminal_end_inverse (void) +{ + if (terminal_end_inverse_hook) + (*terminal_end_inverse_hook) (); + else + { + send_to_terminal (term_invend); + } +} + +/* Ring the terminal bell. The bell is run visibly if it both has one and + terminal_use_visible_bell_p is non-zero. */ +void +terminal_ring_bell (void) +{ + if (terminal_ring_bell_hook) + (*terminal_ring_bell_hook) (); + else + { + if (terminal_has_visible_bell_p && terminal_use_visible_bell_p) + send_to_terminal (visible_bell); + else + send_to_terminal (audible_bell); + } +} + +/* At the line START, delete COUNT lines from the terminal display. */ +static void +terminal_delete_lines (int start, int count) +{ + int lines; + + /* Normalize arguments. */ + if (start < 0) + start = 0; + + lines = screenheight - start; + terminal_goto_xy (0, start); + if (term_DL) + tputs (tgoto (term_DL, 0, count), lines, output_character_function); + else + { + while (count--) + tputs (term_dl, lines, output_character_function); + } + + fflush (stdout); +} + +/* At the line START, insert COUNT lines in the terminal display. */ +static void +terminal_insert_lines (int start, int count) +{ + int lines; + + /* Normalize arguments. */ + if (start < 0) + start = 0; + + lines = screenheight - start; + terminal_goto_xy (0, start); + + if (term_AL) + tputs (tgoto (term_AL, 0, count), lines, output_character_function); + else + { + while (count--) + tputs (term_al, lines, output_character_function); + } + + fflush (stdout); +} + +/* Scroll an area of the terminal, starting with the region from START + to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled + towards the top of the screen, else they are scrolled towards the + bottom of the screen. */ +void +terminal_scroll_terminal (int start, int end, int amount) +{ + if (!terminal_can_scroll) + return; + + /* Any scrolling at all? */ + if (amount == 0) + return; + + if (terminal_scroll_terminal_hook) + (*terminal_scroll_terminal_hook) (start, end, amount); + else + { + /* If we are scrolling down, delete AMOUNT lines at END. Then insert + AMOUNT lines at START. */ + if (amount > 0) + { + terminal_delete_lines (end, amount); + terminal_insert_lines (start, amount); + } + + /* If we are scrolling up, delete AMOUNT lines before START. This + actually does the upwards scroll. Then, insert AMOUNT lines + after the already scrolled region (i.e., END - AMOUNT). */ + if (amount < 0) + { + int abs_amount = -amount; + terminal_delete_lines (start - abs_amount, abs_amount); + terminal_insert_lines (end - abs_amount, abs_amount); + } + } +} + +/* Re-initialize the terminal considering that the TERM/TERMCAP variable + has changed. */ +void +terminal_new_terminal (char *terminal_name) +{ + if (terminal_new_terminal_hook) + (*terminal_new_terminal_hook) (terminal_name); + else + { + terminal_initialize_terminal (terminal_name); + } +} + +/* Set the global variables SCREENWIDTH and SCREENHEIGHT. */ +void +terminal_get_screen_size (void) +{ + if (terminal_get_screen_size_hook) + (*terminal_get_screen_size_hook) (); + else + { + screenwidth = screenheight = 0; + +#if defined (TIOCGWINSZ) + { + struct winsize window_size; + + if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0) + { + screenwidth = (int) window_size.ws_col; + screenheight = (int) window_size.ws_row; + } + } +#endif /* TIOCGWINSZ */ + + /* Environment variable COLUMNS overrides setting of "co". */ + if (screenwidth <= 0) + { + char *sw = getenv ("COLUMNS"); + + if (sw) + screenwidth = atoi (sw); + + if (screenwidth <= 0) + screenwidth = tgetnum ("co"); + } + + /* Environment variable LINES overrides setting of "li". */ + if (screenheight <= 0) + { + char *sh = getenv ("LINES"); + + if (sh) + screenheight = atoi (sh); + + if (screenheight <= 0) + screenheight = tgetnum ("li"); + } + + /* If all else fails, default to 80x24 terminal. */ + if (screenwidth <= 0) + screenwidth = 80; + + if (screenheight <= 0) + screenheight = 24; + } +} + +/* Initialize the terminal which is known as TERMINAL_NAME. If this + terminal doesn't have cursor addressability, `terminal_is_dumb_p' + becomes nonzero. The variables SCREENHEIGHT and SCREENWIDTH are set + to the dimensions that this terminal actually has. The variable + TERMINAL_HAS_META_P becomes nonzero if this terminal supports a Meta + key. Finally, the terminal screen is cleared. */ +void +terminal_initialize_terminal (char *terminal_name) +{ + char *buffer; + + terminal_is_dumb_p = 0; + + if (terminal_initialize_terminal_hook) + { + (*terminal_initialize_terminal_hook) (terminal_name); + return; + } + + term_name = terminal_name ? terminal_name : getenv ("TERM"); + if (!term_name) + term_name = "dumb"; + + if (!term_string_buffer) + term_string_buffer = xmalloc (2048); + + if (!term_buffer) + term_buffer = xmalloc (2048); + + buffer = term_string_buffer; + + term_clrpag = term_cr = term_clreol = NULL; + + /* HP-UX 11.x returns 0 for OK --jeff.hull@state.co.us. */ + if (tgetent (term_buffer, term_name) < 0) + { + terminal_is_dumb_p = 1; + screenwidth = 80; + screenheight = 24; + term_cr = "\r"; + term_up = term_dn = audible_bell = visible_bell = NULL; + term_ku = term_kd = term_kl = term_kr = NULL; + term_kP = term_kN = NULL; + term_kh = term_ke = NULL; + term_kD = NULL; + return; + } + + BC = tgetstr ("pc", &buffer); + PC = BC ? *BC : 0; + +#if defined (HAVE_TERMIOS_H) + { + struct termios ti; + if (tcgetattr (fileno(stdout), &ti) != -1) + ospeed = cfgetospeed (&ti); + else + ospeed = B9600; + } +#else +# if defined (TIOCGETP) + { + struct sgttyb sg; + + if (ioctl (fileno (stdout), TIOCGETP, &sg) != -1) + ospeed = sg.sg_ospeed; + else + ospeed = B9600; + } +# else + ospeed = B9600; +# endif /* !TIOCGETP */ +#endif + + term_cr = tgetstr ("cr", &buffer); + term_clreol = tgetstr ("ce", &buffer); + term_clrpag = tgetstr ("cl", &buffer); + term_goto = tgetstr ("cm", &buffer); + + /* Find out about this terminal's scrolling capability. */ + term_AL = tgetstr ("AL", &buffer); + term_DL = tgetstr ("DL", &buffer); + term_al = tgetstr ("al", &buffer); + term_dl = tgetstr ("dl", &buffer); + + terminal_can_scroll = ((term_AL || term_al) && (term_DL || term_dl)); + + term_invbeg = tgetstr ("mr", &buffer); + if (term_invbeg) + term_invend = tgetstr ("me", &buffer); + else + term_invend = NULL; + + if (!term_cr) + term_cr = "\r"; + + terminal_get_screen_size (); + + term_up = tgetstr ("up", &buffer); + term_dn = tgetstr ("dn", &buffer); + visible_bell = tgetstr ("vb", &buffer); + terminal_has_visible_bell_p = (visible_bell != NULL); + audible_bell = tgetstr ("bl", &buffer); + if (!audible_bell) + audible_bell = "\007"; + term_begin_use = tgetstr ("ti", &buffer); + term_end_use = tgetstr ("te", &buffer); + + term_keypad_on = tgetstr ("ks", &buffer); + term_keypad_off = tgetstr ("ke", &buffer); + + /* Check to see if this terminal has a meta key. */ + terminal_has_meta_p = (tgetflag ("km") || tgetflag ("MT")); + if (terminal_has_meta_p) + { + term_mm = tgetstr ("mm", &buffer); + } + else + { + term_mm = NULL; + } + + /* Attempt to find the arrow keys. */ + term_ku = tgetstr ("ku", &buffer); + term_kd = tgetstr ("kd", &buffer); + term_kr = tgetstr ("kr", &buffer); + term_kl = tgetstr ("kl", &buffer); + + term_kP = tgetstr ("kP", &buffer); + term_kN = tgetstr ("kN", &buffer); + +#if defined(INFOKEY) + term_kh = tgetstr ("kh", &buffer); + term_ke = tgetstr ("@7", &buffer); + term_ki = tgetstr ("kI", &buffer); + term_kx = tgetstr ("kD", &buffer); +#endif /* defined(INFOKEY) */ + + /* Home and end keys. */ + term_kh = tgetstr ("kh", &buffer); + term_ke = tgetstr ("@7", &buffer); + + term_kD = tgetstr ("kD", &buffer); + + /* If this terminal is not cursor addressable, then it is really dumb. */ + if (!term_goto) + terminal_is_dumb_p = 1; +} + +/* How to read characters from the terminal. */ + +#if defined (HAVE_TERMIOS_H) +struct termios original_termios, ttybuff; +#else +# if defined (HAVE_TERMIO_H) +/* A buffer containing the terminal mode flags upon entry to info. */ +struct termio original_termio, ttybuff; +# else /* !HAVE_TERMIO_H */ +/* Buffers containing the terminal mode flags upon entry to info. */ +int original_tty_flags = 0; +int original_lmode; +struct sgttyb ttybuff; + +# if defined(TIOCGETC) && defined(M_XENIX) +/* SCO 3.2v5.0.2 defines but does not support TIOCGETC. Gak. Maybe + better fix would be to use Posix termios in preference. --gildea, + 1jul99. */ +# undef TIOCGETC +# endif + +# if defined (TIOCGETC) +/* A buffer containing the terminal interrupt characters upon entry + to Info. */ +struct tchars original_tchars; +# endif + +# if defined (TIOCGLTC) +/* A buffer containing the local terminal mode characters upon entry + to Info. */ +struct ltchars original_ltchars; +# endif +# endif /* !HAVE_TERMIO_H */ +#endif /* !HAVE_TERMIOS_H */ + +/* Prepare to start using the terminal to read characters singly. */ +void +terminal_prep_terminal (void) +{ + int tty; + + if (terminal_prep_terminal_hook) + { + (*terminal_prep_terminal_hook) (); + return; + } + + terminal_begin_using_terminal (); + + tty = fileno (stdin); + +#if defined (HAVE_TERMIOS_H) + tcgetattr (tty, &original_termios); + tcgetattr (tty, &ttybuff); +#else +# if defined (HAVE_TERMIO_H) + ioctl (tty, TCGETA, &original_termio); + ioctl (tty, TCGETA, &ttybuff); +# endif +#endif + +#if defined (HAVE_TERMIOS_H) || defined (HAVE_TERMIO_H) + ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON); +/* These output flags are not part of POSIX, so only use them if they + are defined. */ +#ifdef ONLCR + ttybuff.c_oflag &= ~ONLCR ; +#endif +#ifdef OCRNL + ttybuff.c_oflag &= ~OCRNL; +#endif + ttybuff.c_lflag &= (~ICANON & ~ECHO); + + ttybuff.c_cc[VMIN] = 1; + ttybuff.c_cc[VTIME] = 0; + + if (ttybuff.c_cc[VINTR] == '\177') + ttybuff.c_cc[VINTR] = -1; + + if (ttybuff.c_cc[VQUIT] == '\177') + ttybuff.c_cc[VQUIT] = -1; + +#ifdef VLNEXT + if (ttybuff.c_cc[VLNEXT] == '\026') + ttybuff.c_cc[VLNEXT] = -1; +#endif /* VLNEXT */ +#endif /* TERMIOS or TERMIO */ + +/* cf. emacs/src/sysdep.c for being sure output is on. */ +#if defined (HAVE_TERMIOS_H) + /* linux kernel 2.2.x needs a TCOFF followed by a TCOON to turn output + back on if the user presses ^S at the very beginning; just a TCOON + doesn't work. --Kevin Ryde <user42@zip.com.au>, 16jun2000. */ + tcsetattr (tty, TCSANOW, &ttybuff); +# ifdef TCOON + tcflow (tty, TCOOFF); + tcflow (tty, TCOON); +# endif +#else +# if defined (HAVE_TERMIO_H) + ioctl (tty, TCSETA, &ttybuff); +# ifdef TCXONC + ioctl (tty, TCXONC, 1); +# endif +# endif +#endif + +#if !defined (HAVE_TERMIOS_H) && !defined (HAVE_TERMIO_H) + ioctl (tty, TIOCGETP, &ttybuff); + + if (!original_tty_flags) + original_tty_flags = ttybuff.sg_flags; + + /* Make this terminal pass 8 bits around while we are using it. */ +# if defined (PASS8) + ttybuff.sg_flags |= PASS8; +# endif /* PASS8 */ + +# if defined (TIOCLGET) && defined (LPASS8) + { + int flags; + ioctl (tty, TIOCLGET, &flags); + original_lmode = flags; + flags |= LPASS8; + ioctl (tty, TIOCLSET, &flags); + } +# endif /* TIOCLGET && LPASS8 */ + +# if defined (TIOCGETC) + { + struct tchars temp; + + ioctl (tty, TIOCGETC, &original_tchars); + temp = original_tchars; + + /* C-s and C-q. */ + temp.t_startc = temp.t_stopc = -1; + + /* Often set to C-d. */ + temp.t_eofc = -1; + + /* If the a quit or interrupt character conflicts with one of our + commands, then make it go away. */ + if (temp.t_intrc == '\177') + temp.t_intrc = -1; + + if (temp.t_quitc == '\177') + temp.t_quitc = -1; + + ioctl (tty, TIOCSETC, &temp); + } +# endif /* TIOCGETC */ + +# if defined (TIOCGLTC) + { + struct ltchars temp; + + ioctl (tty, TIOCGLTC, &original_ltchars); + temp = original_ltchars; + + /* Make the interrupt keys go away. Just enough to make people happy. */ + temp.t_lnextc = -1; /* C-v. */ + temp.t_dsuspc = -1; /* C-y. */ + temp.t_flushc = -1; /* C-o. */ + ioctl (tty, TIOCSLTC, &temp); + } +# endif /* TIOCGLTC */ + + ttybuff.sg_flags &= ~ECHO; + ttybuff.sg_flags |= CBREAK; + ioctl (tty, TIOCSETN, &ttybuff); +#endif /* !HAVE_TERMIOS_H && !HAVE_TERMIO_H */ +} + +/* Restore the tty settings back to what they were before we started using + this terminal. */ +void +terminal_unprep_terminal (void) +{ + int tty; + + if (terminal_unprep_terminal_hook) + { + (*terminal_unprep_terminal_hook) (); + return; + } + + tty = fileno (stdin); + +#if defined (HAVE_TERMIOS_H) + tcsetattr (tty, TCSANOW, &original_termios); +#else +# if defined (HAVE_TERMIO_H) + ioctl (tty, TCSETA, &original_termio); +# else /* !HAVE_TERMIO_H */ + ioctl (tty, TIOCGETP, &ttybuff); + ttybuff.sg_flags = original_tty_flags; + ioctl (tty, TIOCSETN, &ttybuff); + +# if defined (TIOCGETC) + ioctl (tty, TIOCSETC, &original_tchars); +# endif /* TIOCGETC */ + +# if defined (TIOCGLTC) + ioctl (tty, TIOCSLTC, &original_ltchars); +# endif /* TIOCGLTC */ + +# if defined (TIOCLGET) && defined (LPASS8) + ioctl (tty, TIOCLSET, &original_lmode); +# endif /* TIOCLGET && LPASS8 */ + +# endif /* !HAVE_TERMIO_H */ +#endif /* !HAVE_TERMIOS_H */ + terminal_end_using_terminal (); +} + +#ifdef __MSDOS__ +# include "pcterm.c" +#endif diff --git a/Build/source/utils/texinfo/info/terminal.h b/Build/source/utils/texinfo/info/terminal.h new file mode 100644 index 00000000000..dbf6b496ff8 --- /dev/null +++ b/Build/source/utils/texinfo/info/terminal.h @@ -0,0 +1,130 @@ +/* terminal.h -- The external interface to terminal I/O. + $Id: terminal.h,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1996, 1997, 2001, 2002, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#if !defined (TERMINAL_H) +#define TERMINAL_H + +#include "info.h" + +/* For almost every function externally visible from terminal.c, there is + a corresponding "hook" function which can be bound in order to replace + the functionality of the one found in terminal.c. This is how we go + about implemented X window display. */ + +/* The width and height of the terminal. */ +extern int screenwidth, screenheight; + +/* Non-zero means this terminal can't really do anything. */ +extern int terminal_is_dumb_p; + +/* Non-zero means that this terminal has a meta key. */ +extern int terminal_has_meta_p; + +/* Non-zero means that this terminal can produce a visible bell. */ +extern int terminal_has_visible_bell_p; + +/* Non-zero means to use that visible bell if at all possible. */ +extern int terminal_use_visible_bell_p; + +/* Non-zero means that this terminal can scroll lines up and down. */ +extern int terminal_can_scroll; + +/* Initialize the terminal which is known as TERMINAL_NAME. If this terminal + doesn't have cursor addressability, TERMINAL_IS_DUMB_P becomes non-zero. + The variables SCREENHEIGHT and SCREENWIDTH are set to the dimensions that + this terminal actually has. The variable TERMINAL_HAS_META_P becomes non- + zero if this terminal supports a Meta key. */ +extern void terminal_initialize_terminal (char *terminal_name); +extern VFunction *terminal_initialize_terminal_hook; + +/* Return the current screen width and height in the variables + SCREENWIDTH and SCREENHEIGHT. */ +extern void terminal_get_screen_size (void); +extern VFunction *terminal_get_screen_size_hook; + +/* Save and restore tty settings. */ +extern void terminal_prep_terminal (void); +extern void terminal_unprep_terminal (void); + +extern VFunction *terminal_prep_terminal_hook; +extern VFunction *terminal_unprep_terminal_hook; + +/* Re-initialize the terminal to TERMINAL_NAME. */ +extern void terminal_new_terminal (char *terminal_name); +extern VFunction *terminal_new_terminal_hook; + +/* Move the cursor to the terminal location of X and Y. */ +extern void terminal_goto_xy (int x, int y); +extern VFunction *terminal_goto_xy_hook; + +/* Print STRING to the terminal at the current position. */ +extern void terminal_put_text (char *string); +extern VFunction *terminal_put_text_hook; + +/* Print NCHARS from STRING to the terminal at the current position. */ +extern void terminal_write_chars (char *string, int nchars); +extern VFunction *terminal_write_chars_hook; + +/* Clear from the current position of the cursor to the end of the line. */ +extern void terminal_clear_to_eol (void); +extern VFunction *terminal_clear_to_eol_hook; + +/* Clear the entire terminal screen. */ +extern void terminal_clear_screen (void); +extern VFunction *terminal_clear_screen_hook; + +/* Move the cursor up one line. */ +extern void terminal_up_line (void); +extern VFunction *terminal_up_line_hook; + +/* Move the cursor down one line. */ +extern void terminal_down_line (void); +extern VFunction *terminal_down_line_hook; + +/* Turn on reverse video if possible. */ +extern void terminal_begin_inverse (void); +extern VFunction *terminal_begin_inverse_hook; + +/* Turn off reverse video if possible. */ +extern void terminal_end_inverse (void); +extern VFunction *terminal_end_inverse_hook; + +/* Scroll an area of the terminal, starting with the region from START + to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled + towards the top of the screen, else they are scrolled towards the + bottom of the screen. */ +extern void terminal_scroll_terminal (int start, int end, int amount); +extern VFunction *terminal_scroll_terminal_hook; + +/* Ring the terminal bell. The bell is run visibly if it both has one and + terminal_use_visible_bell_p is non-zero. */ +extern void terminal_ring_bell (void); +extern VFunction *terminal_ring_bell_hook; + +/* The key sequences output by special keys, if this terminal has any. */ +extern char *term_ku, *term_kd, *term_kr, *term_kl; +extern char *term_kP, *term_kN; +extern char *term_ke, *term_kh; +extern char *term_kx, *term_ki; +extern char *term_kD; + +#endif /* !TERMINAL_H */ diff --git a/Build/source/utils/texinfo/info/tilde.c b/Build/source/utils/texinfo/info/tilde.c new file mode 100644 index 00000000000..ac32abd040a --- /dev/null +++ b/Build/source/utils/texinfo/info/tilde.c @@ -0,0 +1,342 @@ +/* tilde.c -- tilde expansion code (~/foo := $HOME/foo). + $Id: tilde.c,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1996, 1998, 1999, + 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +/* Include config.h before doing alloca. */ +#include "info.h" +#include "tilde.h" + +#if defined (TEST) || defined (STATIC_MALLOC) +static void *xmalloc (), *xrealloc (); +#endif /* TEST || STATIC_MALLOC */ + +/* The default value of tilde_additional_prefixes. This is set to + whitespace preceding a tilde so that simple programs which do not + perform any word separation get desired behaviour. */ +static char *default_prefixes[] = + { " ~", "\t~", (char *)NULL }; + +/* The default value of tilde_additional_suffixes. This is set to + whitespace or newline so that simple programs which do not + perform any word separation get desired behaviour. */ +static char *default_suffixes[] = + { " ", "\n", (char *)NULL }; + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +CFunction *tilde_expansion_failure_hook = (CFunction *)NULL; + +/* When non-null, this is a NULL terminated array of strings which + are duplicates for a tilde prefix. Bash uses this to expand + `=~' and `:~'. */ +char **tilde_additional_prefixes = default_prefixes; + +/* When non-null, this is a NULL terminated array of strings which match + the end of a username, instead of just "/". Bash sets this to + `:' and `=~'. */ +char **tilde_additional_suffixes = default_suffixes; + +/* Find the start of a tilde expansion in STRING, and return the index of + the tilde which starts the expansion. Place the length of the text + which identified this tilde starter in LEN, excluding the tilde itself. */ +static int +tilde_find_prefix (char *string, int *len) +{ + register int i, j, string_len; + register char **prefixes = tilde_additional_prefixes; + + string_len = strlen (string); + *len = 0; + + if (!*string || *string == '~') + return (0); + + if (prefixes) + { + for (i = 0; i < string_len; i++) + { + for (j = 0; prefixes[j]; j++) + { + if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0) + { + *len = strlen (prefixes[j]) - 1; + return (i + *len); + } + } + } + } + return (string_len); +} + +/* Find the end of a tilde expansion in STRING, and return the index of + the character which ends the tilde definition. */ +static int +tilde_find_suffix (char *string) +{ + register int i, j, string_len; + register char **suffixes = tilde_additional_suffixes; + + string_len = strlen (string); + + for (i = 0; i < string_len; i++) + { + if (IS_SLASH (string[i]) || !string[i]) + break; + + for (j = 0; suffixes && suffixes[j]; j++) + { + if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0) + return (i); + } + } + return (i); +} + +/* Return a new string which is the result of tilde expanding STRING. */ +char * +tilde_expand (char *string) +{ + char *result; + int result_size, result_index; + + result_size = result_index = 0; + result = (char *)NULL; + + /* Scan through STRING expanding tildes as we come to them. */ + while (1) + { + register int start, end; + char *tilde_word, *expansion; + int len; + + /* Make START point to the tilde which starts the expansion. */ + start = tilde_find_prefix (string, &len); + + /* Copy the skipped text into the result. */ + if ((result_index + start + 1) > result_size) + result = (char *)xrealloc (result, 1 + (result_size += (start + 20))); + + strncpy (result + result_index, string, start); + result_index += start; + + /* Advance STRING to the starting tilde. */ + string += start; + + /* Make END be the index of one after the last character of the + username. */ + end = tilde_find_suffix (string); + + /* If both START and END are zero, we are all done. */ + if (!start && !end) + break; + + /* Expand the entire tilde word, and copy it into RESULT. */ + tilde_word = (char *)xmalloc (1 + end); + strncpy (tilde_word, string, end); + tilde_word[end] = '\0'; + string += end; + + expansion = tilde_expand_word (tilde_word); + free (tilde_word); + + len = strlen (expansion); + if ((result_index + len + 1) > result_size) + result = (char *)xrealloc (result, 1 + (result_size += (len + 20))); + + strcpy (result + result_index, expansion); + result_index += len; + free (expansion); + } + + result[result_index] = '\0'; + + return (result); +} + +/* Do the work of tilde expansion on FILENAME. FILENAME starts with a + tilde. If there is no expansion, call tilde_expansion_failure_hook. */ +char * +tilde_expand_word (char *filename) +{ + char *dirname = filename ? xstrdup (filename) : NULL; + + if (dirname && *dirname == '~') + { + char *temp_name; + if (!dirname[1] || IS_SLASH (dirname[1])) + { + /* Prepend $HOME to the rest of the string. */ + char *temp_home = getenv ("HOME"); + + /* If there is no HOME variable, look up the directory in + the password database. */ + if (!temp_home) + { + struct passwd *entry; + + entry = (struct passwd *) getpwuid (getuid ()); + if (entry) + temp_home = entry->pw_dir; + } + + temp_name = xmalloc (1 + strlen (&dirname[1]) + + (temp_home ? strlen (temp_home) : 0)); + if (temp_home) + strcpy (temp_name, temp_home); + else + temp_name[0] = 0; + strcat (temp_name, &dirname[1]); + free (dirname); + dirname = xstrdup (temp_name); + free (temp_name); + } + else + { + struct passwd *user_entry; + char *username = xmalloc (257); + int i, c; + + for (i = 1; (c = dirname[i]); i++) + { + if (IS_SLASH (c)) + break; + else + username[i - 1] = c; + } + username[i - 1] = 0; + + if (!(user_entry = (struct passwd *) getpwnam (username))) + { + /* If the calling program has a special syntax for + expanding tildes, and we couldn't find a standard + expansion, then let them try. */ + if (tilde_expansion_failure_hook) + { + char *expansion = (*tilde_expansion_failure_hook) (username); + + if (expansion) + { + temp_name = xmalloc (1 + strlen (expansion) + + strlen (&dirname[i])); + strcpy (temp_name, expansion); + strcat (temp_name, &dirname[i]); + free (expansion); + goto return_name; + } + } + /* We shouldn't report errors. */ + } + else + { + temp_name = xmalloc (1 + strlen (user_entry->pw_dir) + + strlen (&dirname[i])); + strcpy (temp_name, user_entry->pw_dir); + strcat (temp_name, &dirname[i]); + + return_name: + free (dirname); + dirname = xstrdup (temp_name); + free (temp_name); + } + + endpwent (); + free (username); + } + } + return dirname; +} + + +#if defined (TEST) +#undef NULL +#include <stdio.h> + +main (argc, argv) + int argc; + char **argv; +{ + char *result, line[512]; + int done = 0; + + while (!done) + { + printf ("~expand: "); + fflush (stdout); + + if (!gets (line)) + strcpy (line, "done"); + + if ((strcmp (line, "done") == 0) || + (strcmp (line, "quit") == 0) || + (strcmp (line, "exit") == 0)) + { + done = 1; + break; + } + + result = tilde_expand (line); + printf (" --> %s\n", result); + free (result); + } + xexit (0); +} + +static void memory_error_and_abort (); + +static void * +xmalloc (bytes) + int bytes; +{ + void *temp = (void *)malloc (bytes); + + if (!temp) + memory_error_and_abort (); + return (temp); +} + +static void * +xrealloc (pointer, bytes) + void *pointer; + int bytes; +{ + void *temp; + + if (!pointer) + temp = (char *)malloc (bytes); + else + temp = (char *)realloc (pointer, bytes); + + if (!temp) + memory_error_and_abort (); + + return (temp); +} + +static void +memory_error_and_abort () +{ + fprintf (stderr, _("readline: Out of virtual memory!\n")); + abort (); +} +#endif /* TEST */ + diff --git a/Build/source/utils/texinfo/info/tilde.h b/Build/source/utils/texinfo/info/tilde.h new file mode 100644 index 00000000000..b499f3efea2 --- /dev/null +++ b/Build/source/utils/texinfo/info/tilde.h @@ -0,0 +1,54 @@ +/* tilde.h: Externally available variables and function in libtilde.a. + $Id: tilde.h,v 1.3 2004/04/11 17:56:46 karl Exp $ + + This file has appeared in prior works by the Free Software Foundation; + thus it carries copyright dates from 1988 through 1993. + + Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef TILDE_H +#define TILDE_H + +#include "info.h" + +/* If non-null, this contains the address of a function to call if the + standard meaning for expanding a tilde fails. The function is called + with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + which is the expansion, or a NULL pointer if there is no expansion. */ +extern CFunction *tilde_expansion_failure_hook; + +/* When non-null, this is a NULL terminated array of strings which + are duplicates for a tilde prefix. Bash uses this to expand + `=~' and `:~'. */ +extern char **tilde_additional_prefixes; + +/* When non-null, this is a NULL terminated array of strings which match + the end of a username, instead of just "/". Bash sets this to + `:' and `=~'. */ +extern char **tilde_additional_suffixes; + +/* Return a new string which is the result of tilde expanding STRING. */ +extern char *tilde_expand (char *string); + +/* Do the work of tilde expansion on FILENAME. FILENAME starts with a + tilde. If there is no expansion, call tilde_expansion_failure_hook. */ +extern char *tilde_expand_word (char *filename); + +#endif /* not TILDE_H */ diff --git a/Build/source/utils/texinfo/info/variables.c b/Build/source/utils/texinfo/info/variables.c new file mode 100644 index 00000000000..3636801a49b --- /dev/null +++ b/Build/source/utils/texinfo/info/variables.c @@ -0,0 +1,305 @@ +/* variables.c -- how to manipulate user visible variables in Info. + $Id: variables.c,v 1.3 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1997, 2001, 2002, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "variables.h" + +/* **************************************************************** */ +/* */ +/* User Visible Variables in Info */ +/* */ +/* **************************************************************** */ + +/* Choices used by the completer when reading a zero/non-zero value for + a variable. */ +static char *on_off_choices[] = { "Off", "On", (char *)NULL }; + +VARIABLE_ALIST info_variables[] = { + { "automatic-footnotes", + N_("When \"On\", footnotes appear and disappear automatically"), + &auto_footnotes_p, (char **)on_off_choices }, + + { "automatic-tiling", + N_("When \"On\", creating or deleting a window resizes other windows"), + &auto_tiling_p, (char **)on_off_choices }, + + { "visible-bell", + N_("When \"On\", flash the screen instead of ringing the bell"), + &terminal_use_visible_bell_p, (char **)on_off_choices }, + + { "errors-ring-bell", + N_("When \"On\", errors cause the bell to ring"), + &info_error_rings_bell_p, (char **)on_off_choices }, + + { "gc-compressed-files", + N_("When \"On\", Info garbage collects files which had to be uncompressed"), + &gc_compressed_files, (char **)on_off_choices }, + { "show-index-match", + N_("When \"On\", the portion of the matched search string is highlighted"), + &show_index_match, (char **)on_off_choices }, + + { "scroll-behaviour", + N_("Controls what happens when scrolling is requested at the end of a node"), + &info_scroll_behaviour, (char **)info_scroll_choices }, + + { "scroll-step", + N_("The number lines to scroll when the cursor moves out of the window"), + &window_scroll_step, (char **)NULL }, + + { "ISO-Latin", + N_("When \"On\", Info accepts and displays ISO Latin characters"), + &ISO_Latin_p, (char **)on_off_choices }, + + { (char *)NULL, (char *)NULL, (int *)NULL, (char **)NULL } +}; + +DECLARE_INFO_COMMAND (describe_variable, _("Explain the use of a variable")) +{ + VARIABLE_ALIST *var; + char *description; + + /* Get the variable's name. */ + var = read_variable_name ((char *) _("Describe variable: "), window); + + if (!var) + return; + + description = (char *)xmalloc (20 + strlen (var->name) + + strlen (_(var->doc))); + + if (var->choices) + sprintf (description, "%s (%s): %s.", + var->name, var->choices[*(var->value)], _(var->doc)); + else + sprintf (description, "%s (%d): %s.", + var->name, *(var->value), _(var->doc)); + + window_message_in_echo_area ("%s", description, NULL); + free (description); +} + +DECLARE_INFO_COMMAND (set_variable, _("Set the value of an Info variable")) +{ + VARIABLE_ALIST *var; + char *line; + + /* Get the variable's name and value. */ + var = read_variable_name ((char *) _("Set variable: "), window); + + if (!var) + return; + + /* Read a new value for this variable. */ + { + char prompt[100]; + + if (!var->choices) + { + int potential_value; + + if (info_explicit_arg || count != 1) + potential_value = count; + else + potential_value = *(var->value); + + sprintf (prompt, _("Set %s to value (%d): "), + var->name, potential_value); + line = info_read_in_echo_area (active_window, prompt); + + /* If no error was printed, clear the echo area. */ + if (!info_error_was_printed) + window_clear_echo_area (); + + /* User aborted? */ + if (!line) + return; + + /* If the user specified a value, get that, otherwise, we are done. */ + canonicalize_whitespace (line); + if (*line) + *(var->value) = atoi (line); + else + *(var->value) = potential_value; + + free (line); + } + else + { + register int i; + REFERENCE **array = (REFERENCE **)NULL; + int array_index = 0; + int array_slots = 0; + + for (i = 0; var->choices[i]; i++) + { + REFERENCE *entry; + + entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + entry->label = xstrdup (var->choices[i]); + entry->nodename = (char *)NULL; + entry->filename = (char *)NULL; + + add_pointer_to_array + (entry, array_index, array, array_slots, 10, REFERENCE *); + } + + sprintf (prompt, _("Set %s to value (%s): "), + var->name, var->choices[*(var->value)]); + + /* Ask the completer to read a variable value for us. */ + line = info_read_completing_in_echo_area (window, prompt, array); + + info_free_references (array); + + if (!echo_area_is_active) + window_clear_echo_area (); + + /* User aborted? */ + if (!line) + { + info_abort_key (active_window, 0, 0); + return; + } + + /* User accepted default choice? If so, no change. */ + if (!*line) + { + free (line); + return; + } + + /* Find the choice in our list of choices. */ + for (i = 0; var->choices[i]; i++) + if (strcmp (var->choices[i], line) == 0) + break; + + if (var->choices[i]) + *(var->value) = i; + } + } +} + +/* Read the name of an Info variable in the echo area and return the + address of a VARIABLE_ALIST member. A return value of NULL indicates + that no variable could be read. */ +VARIABLE_ALIST * +read_variable_name (char *prompt, WINDOW *window) +{ + register int i; + char *line; + REFERENCE **variables; + + /* Get the completion array of variable names. */ + variables = make_variable_completions_array (); + + /* Ask the completer to read a variable for us. */ + line = + info_read_completing_in_echo_area (window, prompt, variables); + + info_free_references (variables); + + if (!echo_area_is_active) + window_clear_echo_area (); + + /* User aborted? */ + if (!line) + { + info_abort_key (active_window, 0, 0); + return ((VARIABLE_ALIST *)NULL); + } + + /* User accepted "default"? (There is none.) */ + if (!*line) + { + free (line); + return ((VARIABLE_ALIST *)NULL); + } + + /* Find the variable in our list of variables. */ + for (i = 0; info_variables[i].name; i++) + if (strcmp (info_variables[i].name, line) == 0) + break; + + if (!info_variables[i].name) + return ((VARIABLE_ALIST *)NULL); + else + return (&(info_variables[i])); +} + +/* Make an array of REFERENCE which actually contains the names of the + variables available in Info. */ +REFERENCE ** +make_variable_completions_array (void) +{ + register int i; + REFERENCE **array = (REFERENCE **)NULL; + int array_index = 0, array_slots = 0; + + for (i = 0; info_variables[i].name; i++) + { + REFERENCE *entry; + + entry = (REFERENCE *) xmalloc (sizeof (REFERENCE)); + entry->label = xstrdup (info_variables[i].name); + entry->nodename = (char *)NULL; + entry->filename = (char *)NULL; + + add_pointer_to_array + (entry, array_index, array, array_slots, 200, REFERENCE *); + } + + return (array); +} + +#if defined(INFOKEY) + +void +set_variable_to_value(char *name, char *value) +{ + register int i; + + /* Find the variable in our list of variables. */ + for (i = 0; info_variables[i].name; i++) + if (strcmp(info_variables[i].name, name) == 0) + break; + + if (!info_variables[i].name) + return; + + if (info_variables[i].choices) + { + register int j; + + /* Find the choice in our list of choices. */ + for (j = 0; info_variables[i].choices[j]; j++) + if (strcmp (info_variables[i].choices[j], value) == 0) + break; + + if (info_variables[i].choices[j]) + *info_variables[i].value = j; + } + else + { + *info_variables[i].value = atoi(value); + } +} + +#endif /* INFOKEY */ diff --git a/Build/source/utils/texinfo/info/variables.h b/Build/source/utils/texinfo/info/variables.h new file mode 100644 index 00000000000..7e5b08239b0 --- /dev/null +++ b/Build/source/utils/texinfo/info/variables.h @@ -0,0 +1,66 @@ +/* variables.h -- Description of user visible variables in Info. + $Id: variables.h,v 1.3 2004/04/11 17:56:46 karl Exp $ + + This file is part of GNU Info, a program for reading online documentation + stored in Info format. + + Copyright (C) 1993, 1997, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_VARIABLES_H +#define INFO_VARIABLES_H + +/* A variable (in the Info sense) is an integer value with a user-visible + name. You may supply an array of strings to complete over when the + variable is set; in that case, the variable is set to the index of the + string that the user chose. If you supply a null list, the user can + set the variable to a numeric value. */ + +/* Structure describing a user visible variable. */ +typedef struct { + char *name; /* Polite name. */ + char *doc; /* Documentation string. */ + int *value; /* Address of value. */ + char **choices; /* Array of strings or NULL if numeric only. */ +} VARIABLE_ALIST; + +/* Read the name of an Info variable in the echo area and return the + address of a VARIABLE_ALIST member. A return value of NULL indicates + that no variable could be read. */ +extern VARIABLE_ALIST *read_variable_name (char *prompt, WINDOW *window); + +/* Make an array of REFERENCE which actually contains the names of the + variables available in Info. */ +extern REFERENCE **make_variable_completions_array (void); + +/* Set the value of an info variable. */ +extern void set_variable (WINDOW *window, int count, unsigned char key); +extern void describe_variable (WINDOW *window, int count, unsigned char key); + +/* The list of user-visible variables. */ +extern int auto_footnotes_p; +extern int auto_tiling_p; +extern int terminal_use_visible_bell_p; +extern int info_error_rings_bell_p; +extern int gc_compressed_files; +extern int show_index_match; +extern int info_scroll_behaviour; +extern int window_scroll_step; +extern int ISO_Latin_p; + +#endif /* not INFO_VARIABLES_H */ diff --git a/Build/source/utils/texinfo/info/window.c b/Build/source/utils/texinfo/info/window.c new file mode 100644 index 00000000000..cf5665b038d --- /dev/null +++ b/Build/source/utils/texinfo/info/window.c @@ -0,0 +1,1554 @@ +/* window.c -- windows in Info. + $Id: window.c,v 1.4 2004/04/11 17:56:46 karl Exp $ + + Copyright (C) 1993, 1997, 1998, 2001, 2002, 2003, 2004 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#include "info.h" +#include "nodes.h" +#include "window.h" +#include "display.h" +#include "info-utils.h" +#include "infomap.h" + +/* The window which describes the screen. */ +WINDOW *the_screen = NULL; + +/* The window which describes the echo area. */ +WINDOW *the_echo_area = NULL; + +/* The list of windows in Info. */ +WINDOW *windows = NULL; + +/* Pointer to the active window in WINDOW_LIST. */ +WINDOW *active_window = NULL; + +/* The size of the echo area in Info. It never changes, irregardless of the + size of the screen. */ +#define ECHO_AREA_HEIGHT 1 + +/* Macro returns the amount of space that the echo area truly requires relative + to the entire screen. */ +#define echo_area_required (1 + the_echo_area->height) + +/* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA. + Create the first window ever. + You pass the dimensions of the total screen size. */ +void +window_initialize_windows (int width, int height) +{ + the_screen = xmalloc (sizeof (WINDOW)); + the_echo_area = xmalloc (sizeof (WINDOW)); + windows = xmalloc (sizeof (WINDOW)); + active_window = windows; + + zero_mem (the_screen, sizeof (WINDOW)); + zero_mem (the_echo_area, sizeof (WINDOW)); + zero_mem (active_window, sizeof (WINDOW)); + + /* None of these windows has a goal column yet. */ + the_echo_area->goal_column = -1; + active_window->goal_column = -1; + the_screen->goal_column = -1; + + /* The active and echo_area windows are visible. + The echo_area is permanent. + The screen is permanent. */ + active_window->flags = W_WindowVisible; + the_echo_area->flags = W_WindowIsPerm | W_InhibitMode | W_WindowVisible; + the_screen->flags = W_WindowIsPerm; + + /* The height of the echo area never changes. It is statically set right + here, and it must be at least 1 line for display. The size of the + initial window cannot be the same size as the screen, since the screen + includes the echo area. So, we make the height of the initial window + equal to the screen's displayable region minus the height of the echo + area. */ + the_echo_area->height = ECHO_AREA_HEIGHT; + active_window->height = the_screen->height - 1 - the_echo_area->height; + window_new_screen_size (width, height); + + /* The echo area uses a different keymap than normal info windows. */ + the_echo_area->keymap = echo_area_keymap; + active_window->keymap = info_keymap; +} + +/* Given that the size of the screen has changed to WIDTH and HEIGHT + from whatever it was before (found in the_screen->height, ->width), + change the size (and possibly location) of each window in the screen. + If a window would become too small, call the function DELETER on it, + after deleting the window from our chain of windows. If DELETER is NULL, + nothing extra is done. The last window can never be deleted, but it can + become invisible. */ + +/* If non-null, a function to call with WINDOW as argument when the function + window_new_screen_size () has deleted WINDOW. */ +VFunction *window_deletion_notifier = NULL; + +void +window_new_screen_size (int width, int height) +{ + register WINDOW *win; + int delta_height, delta_each, delta_leftover; + int numwins; + + /* If no change, do nothing. */ + if (width == the_screen->width && height == the_screen->height) + return; + + /* If the new window height is too small, make it be zero. */ + if (height < (WINDOW_MIN_SIZE + the_echo_area->height)) + height = 0; + if (width < 0) + width = 0; + + /* Find out how many windows will change. */ + for (numwins = 0, win = windows; win; win = win->next, numwins++); + + /* See if some windows will need to be deleted. This is the case if + the screen is getting smaller, and the available space divided by + the number of windows is less than WINDOW_MIN_SIZE. In that case, + delete some windows and try again until there is either enough + space to divy up among the windows, or until there is only one + window left. */ + while ((height - echo_area_required) / numwins <= WINDOW_MIN_SIZE) + { + /* If only one window, make the size of it be zero, and return + immediately. */ + if (!windows->next) + { + windows->height = 0; + maybe_free (windows->line_starts); + windows->line_starts = NULL; + windows->line_count = 0; + break; + } + + /* If we have some temporary windows, delete one of them. */ + for (win = windows; win; win = win->next) + if (win->flags & W_TempWindow) + break; + + /* Otherwise, delete the first window, and try again. */ + if (!win) + win = windows; + + if (window_deletion_notifier) + (*window_deletion_notifier) (win); + + window_delete_window (win); + numwins--; + } + + /* The screen has changed height and width. */ + delta_height = height - the_screen->height; /* This is how much. */ + the_screen->height = height; /* This is the new height. */ + the_screen->width = width; /* This is the new width. */ + + /* Set the start of the echo area. */ + the_echo_area->first_row = height - the_echo_area->height; + the_echo_area->width = width; + + /* Check to see if the screen can really be changed this way. */ + if ((!windows->next) && ((windows->height == 0) && (delta_height < 0))) + return; + + /* Divide the change in height among the available windows. */ + delta_each = delta_height / numwins; + delta_leftover = delta_height - (delta_each * numwins); + + /* Change the height of each window in the chain by delta_each. Change + the height of the last window in the chain by delta_each and by the + leftover amount of change. Change the width of each window to be + WIDTH. */ + for (win = windows; win; win = win->next) + { + if ((win->width != width) && ((win->flags & W_InhibitMode) == 0)) + { + win->width = width; + maybe_free (win->modeline); + win->modeline = xmalloc (1 + width); + } + + win->height += delta_each; + + /* If the previous height of this window was zero, it was the only + window, and it was not visible. Thus we need to compensate for + the echo_area. */ + if (win->height == delta_each) + win->height -= (1 + the_echo_area->height); + + /* If this is not the first window in the chain, then change the + first row of it. We cannot just add delta_each to the first row, + since this window's first row is the sum of the collective increases + that have gone before it. So we just add one to the location of the + previous window's modeline. */ + if (win->prev) + win->first_row = (win->prev->first_row + win->prev->height) + 1; + + /* The last window in the chain gets the extra space (or shrinkage). */ + if (!win->next) + win->height += delta_leftover; + + if (win->node) + recalculate_line_starts (win); + + win->flags |= W_UpdateWindow; + } + + /* If the screen got smaller, check over the windows just shrunk to + keep them within bounds. Some of the windows may have gotten smaller + than WINDOW_MIN_HEIGHT in which case some of the other windows are + larger than the available display space in the screen. Because of our + intial test above, we know that there is enough space for all of the + windows. */ + if ((delta_each < 0) && ((windows->height != 0) && windows->next)) + { + int avail; + + avail = the_screen->height - (numwins + the_echo_area->height); + win = windows; + + while (win) + { + if ((win->height < WINDOW_MIN_HEIGHT) || + (win->height > avail)) + { + WINDOW *lastwin = NULL; + + /* Split the space among the available windows. */ + delta_each = avail / numwins; + delta_leftover = avail - (delta_each * numwins); + + for (win = windows; win; win = win->next) + { + lastwin = win; + if (win->prev) + win->first_row = + (win->prev->first_row + win->prev->height) + 1; + win->height = delta_each; + } + + /* Give the leftover space (if any) to the last window. */ + lastwin->height += delta_leftover; + break; + } + else + win= win->next; + } + } +} + +/* Make a new window showing NODE, and return that window structure. + If NODE is passed as NULL, then show the node showing in the active + window. If the window could not be made return a NULL pointer. The + active window is not changed.*/ +WINDOW * +window_make_window (NODE *node) +{ + WINDOW *window; + + if (!node) + node = active_window->node; + + /* If there isn't enough room to make another window, return now. */ + if ((active_window->height / 2) < WINDOW_MIN_SIZE) + return (NULL); + + /* Make and initialize the new window. + The fudging about with -1 and +1 is because the following window in the + chain cannot start at window->height, since that is where the modeline + for the previous window is displayed. The inverse adjustment is made + in window_delete_window (). */ + window = xmalloc (sizeof (WINDOW)); + window->width = the_screen->width; + window->height = (active_window->height / 2) - 1; +#if defined (SPLIT_BEFORE_ACTIVE) + window->first_row = active_window->first_row; +#else + window->first_row = active_window->first_row + + (active_window->height - window->height); +#endif + window->keymap = info_keymap; + window->goal_column = -1; + window->modeline = xmalloc (1 + window->width); + window->line_starts = NULL; + window->flags = W_UpdateWindow | W_WindowVisible; + window_set_node_of_window (window, node); + + /* Adjust the height of the old active window. */ + active_window->height -= (window->height + 1); +#if defined (SPLIT_BEFORE_ACTIVE) + active_window->first_row += (window->height + 1); +#endif + active_window->flags |= W_UpdateWindow; + + /* Readjust the new and old windows so that their modelines and contents + will be displayed correctly. */ +#if defined (NOTDEF) + /* We don't have to do this for WINDOW since window_set_node_of_window () + already did. */ + window_adjust_pagetop (window); + window_make_modeline (window); +#endif /* NOTDEF */ + + /* We do have to readjust the existing active window. */ + window_adjust_pagetop (active_window); + window_make_modeline (active_window); + +#if defined (SPLIT_BEFORE_ACTIVE) + /* This window is just before the active one. The active window gets + bumped down one. The active window is not changed. */ + window->next = active_window; + + window->prev = active_window->prev; + active_window->prev = window; + + if (window->prev) + window->prev->next = window; + else + windows = window; +#else + /* This window is just after the active one. Which window is active is + not changed. */ + window->prev = active_window; + window->next = active_window->next; + active_window->next = window; + if (window->next) + window->next->prev = window; +#endif /* !SPLIT_BEFORE_ACTIVE */ + return (window); +} + +/* These useful macros make it possible to read the code in + window_change_window_height (). */ +#define grow_me_shrinking_next(me, next, diff) \ + do { \ + me->height += diff; \ + next->height -= diff; \ + next->first_row += diff; \ + window_adjust_pagetop (next); \ + } while (0) + +#define grow_me_shrinking_prev(me, prev, diff) \ + do { \ + me->height += diff; \ + prev->height -= diff; \ + me->first_row -=diff; \ + window_adjust_pagetop (prev); \ + } while (0) + +#define shrink_me_growing_next(me, next, diff) \ + do { \ + me->height -= diff; \ + next->height += diff; \ + next->first_row -= diff; \ + window_adjust_pagetop (next); \ + } while (0) + +#define shrink_me_growing_prev(me, prev, diff) \ + do { \ + me->height -= diff; \ + prev->height += diff; \ + me->first_row += diff; \ + window_adjust_pagetop (prev); \ + } while (0) + +/* Change the height of WINDOW by AMOUNT. This also automagically adjusts + the previous and next windows in the chain. If there is only one user + window, then no change takes place. */ +void +window_change_window_height (WINDOW *window, int amount) +{ + register WINDOW *win, *prev, *next; + + /* If there is only one window, or if the amount of change is zero, + return immediately. */ + if (!windows->next || amount == 0) + return; + + /* Find this window in our chain. */ + for (win = windows; win; win = win->next) + if (win == window) + break; + + /* If the window is isolated (i.e., doesn't appear in our window list, + then quit now. */ + if (!win) + return; + + /* Change the height of this window by AMOUNT, if that is possible. + It can be impossible if there isn't enough available room on the + screen, or if the resultant window would be too small. */ + + prev = window->prev; + next = window->next; + + /* WINDOW decreasing in size? */ + if (amount < 0) + { + int abs_amount = -amount; /* It is easier to deal with this way. */ + + /* If the resultant window would be too small, stop here. */ + if ((window->height - abs_amount) < WINDOW_MIN_HEIGHT) + return; + + /* If we have two neighboring windows, choose the smaller one to get + larger. */ + if (next && prev) + { + if (prev->height < next->height) + shrink_me_growing_prev (window, prev, abs_amount); + else + shrink_me_growing_next (window, next, abs_amount); + } + else if (next) + shrink_me_growing_next (window, next, abs_amount); + else + shrink_me_growing_prev (window, prev, abs_amount); + } + + /* WINDOW increasing in size? */ + if (amount > 0) + { + int total_avail, next_avail = 0, prev_avail = 0; + + if (next) + next_avail = next->height - WINDOW_MIN_SIZE; + + if (prev) + prev_avail = prev->height - WINDOW_MIN_SIZE; + + total_avail = next_avail + prev_avail; + + /* If there isn't enough space available to grow this window, give up. */ + if (amount > total_avail) + return; + + /* If there aren't two neighboring windows, or if one of the neighbors + is larger than the other one by at least AMOUNT, grow that one. */ + if ((next && !prev) || ((next_avail - amount) >= prev_avail)) + grow_me_shrinking_next (window, next, amount); + else if ((prev && !next) || ((prev_avail - amount) >= next_avail)) + grow_me_shrinking_prev (window, prev, amount); + else + { + int change; + + /* This window has two neighbors. They both must be shrunk in to + make enough space for WINDOW to grow. Make them both the same + size. */ + if (prev_avail > next_avail) + { + change = prev_avail - next_avail; + grow_me_shrinking_prev (window, prev, change); + amount -= change; + } + else + { + change = next_avail - prev_avail; + grow_me_shrinking_next (window, next, change); + amount -= change; + } + + /* Both neighbors are the same size. Split the difference in + AMOUNT between them. */ + while (amount) + { + window->height++; + amount--; + + /* Odd numbers grow next, even grow prev. */ + if (amount & 1) + { + prev->height--; + window->first_row--; + } + else + { + next->height--; + next->first_row++; + } + } + window_adjust_pagetop (prev); + window_adjust_pagetop (next); + } + } + if (prev) + prev->flags |= W_UpdateWindow; + + if (next) + next->flags |= W_UpdateWindow; + + window->flags |= W_UpdateWindow; + window_adjust_pagetop (window); +} + +/* Tile all of the windows currently displayed in the global variable + WINDOWS. If argument STYLE is TILE_INTERNALS, tile windows displaying + internal nodes as well, otherwise do not change the height of such + windows. */ +void +window_tile_windows (int style) +{ + WINDOW *win, *last_adjusted; + int numwins, avail, per_win_height, leftover; + int do_internals; + + numwins = avail = 0; + do_internals = (style == TILE_INTERNALS); + + for (win = windows; win; win = win->next) + if (do_internals || !win->node || + (win->node->flags & N_IsInternal) == 0) + { + avail += win->height; + numwins++; + } + + if (numwins <= 1 || !the_screen->height) + return; + + /* Find the size for each window. Divide the size of the usable portion + of the screen by the number of windows. */ + per_win_height = avail / numwins; + leftover = avail - (per_win_height * numwins); + + last_adjusted = NULL; + for (win = windows; win; win = win->next) + { + if (do_internals || !win->node || + (win->node->flags & N_IsInternal) == 0) + { + last_adjusted = win; + win->height = per_win_height; + } + } + + if (last_adjusted) + last_adjusted->height += leftover; + + /* Readjust the first_row of every window in the chain. */ + for (win = windows; win; win = win->next) + { + if (win->prev) + win->first_row = win->prev->first_row + win->prev->height + 1; + + window_adjust_pagetop (win); + win->flags |= W_UpdateWindow; + } +} + +/* Toggle the state of line wrapping in WINDOW. This can do a bit of fancy + redisplay. */ +void +window_toggle_wrap (WINDOW *window) +{ + if (window->flags & W_NoWrap) + window->flags &= ~W_NoWrap; + else + window->flags |= W_NoWrap; + + if (window != the_echo_area) + { + char **old_starts; + int old_lines, old_pagetop; + + old_starts = window->line_starts; + old_lines = window->line_count; + old_pagetop = window->pagetop; + + calculate_line_starts (window); + + /* Make sure that point appears within this window. */ + window_adjust_pagetop (window); + + /* If the pagetop hasn't changed maybe we can do some scrolling now + to speed up the display. Many of the line starts will be the same, + so scrolling here is a very good optimization.*/ + if (old_pagetop == window->pagetop) + display_scroll_line_starts + (window, old_pagetop, old_starts, old_lines); + maybe_free (old_starts); + } + window->flags |= W_UpdateWindow; +} + +/* Set WINDOW to display NODE. */ +void +window_set_node_of_window (WINDOW *window, NODE *node) +{ + window->node = node; + window->pagetop = 0; + window->point = 0; + recalculate_line_starts (window); + window->flags |= W_UpdateWindow; + /* The display_pos member is nonzero if we're displaying an anchor. */ + window->point = node ? node->display_pos : 0; + window_adjust_pagetop (window); + window_make_modeline (window); +} + +/* Delete WINDOW from the list of known windows. If this window was the + active window, make the next window in the chain be the active window. + If the active window is the next or previous window, choose that window + as the recipient of the extra space. Otherwise, prefer the next window. */ +void +window_delete_window (WINDOW *window) +{ + WINDOW *next, *prev, *window_to_fix; + + next = window->next; + prev = window->prev; + + /* You cannot delete the only window or a permanent window. */ + if ((!next && !prev) || (window->flags & W_WindowIsPerm)) + return; + + if (next) + next->prev = prev; + + if (!prev) + windows = next; + else + prev->next = next; + + if (window->line_starts) + free (window->line_starts); + + if (window->modeline) + free (window->modeline); + + if (window == active_window) + { + /* If there isn't a next window, then there must be a previous one, + since we cannot delete the last window. If there is a next window, + prefer to use that as the active window. */ + if (next) + active_window = next; + else + active_window = prev; + } + + if (next && active_window == next) + window_to_fix = next; + else if (prev && active_window == prev) + window_to_fix = prev; + else if (next) + window_to_fix = next; + else if (prev) + window_to_fix = prev; + else + window_to_fix = windows; + + if (window_to_fix->first_row > window->first_row) + { + int diff; + + /* Try to adjust the visible part of the node so that as little + text as possible has to move. */ + diff = window_to_fix->first_row - window->first_row; + window_to_fix->first_row = window->first_row; + + window_to_fix->pagetop -= diff; + if (window_to_fix->pagetop < 0) + window_to_fix->pagetop = 0; + } + + /* The `+ 1' is to offset the difference between the first_row locations. + See the code in window_make_window (). */ + window_to_fix->height += window->height + 1; + window_to_fix->flags |= W_UpdateWindow; + + free (window); +} + +/* For every window in CHAIN, set the flags member to have FLAG set. */ +void +window_mark_chain (WINDOW *chain, int flag) +{ + register WINDOW *win; + + for (win = chain; win; win = win->next) + win->flags |= flag; +} + +/* For every window in CHAIN, clear the flags member of FLAG. */ +void +window_unmark_chain (WINDOW *chain, int flag) +{ + register WINDOW *win; + + for (win = chain; win; win = win->next) + win->flags &= ~flag; +} + +/* Return the number of characters it takes to display CHARACTER on the + screen at HPOS. */ +int +character_width (int character, int hpos) +{ + int printable_limit = 127; + int width = 1; + + if (ISO_Latin_p) + printable_limit = 255; + + if (character > printable_limit) + width = 3; + else if (iscntrl (character)) + { + switch (character) + { + case '\r': + case '\n': + width = the_screen->width - hpos; + break; + case '\t': + width = ((hpos + 8) & 0xf8) - hpos; + break; + default: + width = 2; + } + } + else if (character == DEL) + width = 2; + + return (width); +} + +/* Return the number of characters it takes to display STRING on the screen + at HPOS. */ +int +string_width (char *string, int hpos) +{ + register int i, width, this_char_width; + + for (width = 0, i = 0; string[i]; i++) + { + /* Support ANSI escape sequences for -R. */ + if (raw_escapes_p + && string[i] == '\033' + && string[i+1] == '[' + && isdigit (string[i+2]) + && (string[i+3] == 'm' + || (isdigit (string[i+3]) && string[i+4] == 'm'))) + { + while (string[i] != 'm') + i++; + this_char_width = 0; + } + else + this_char_width = character_width (string[i], hpos); + width += this_char_width; + hpos += this_char_width; + } + return (width); +} + +/* Quickly guess the approximate number of lines that NODE would + take to display. This really only counts carriage returns. */ +int +window_physical_lines (NODE *node) +{ + register int i, lines; + char *contents; + + if (!node) + return (0); + + contents = node->contents; + for (i = 0, lines = 1; i < node->nodelen; i++) + if (contents[i] == '\n') + lines++; + + return (lines); +} + +/* Calculate a list of line starts for the node belonging to WINDOW. The line + starts are pointers to the actual text within WINDOW->NODE. */ +void +calculate_line_starts (WINDOW *window) +{ + register int i, hpos; + char **line_starts = NULL; + int line_starts_index = 0, line_starts_slots = 0; + int bump_index; + NODE *node; + + window->line_starts = NULL; + window->line_count = 0; + node = window->node; + + if (!node) + return; + + /* Grovel the node starting at the top, and for each line calculate the + width of the characters appearing in that line. Add each line start + to our array. */ + i = 0; + hpos = 0; + bump_index = 0; + + while (i < node->nodelen) + { + char *line = node->contents + i; + unsigned int cwidth, c; + + add_pointer_to_array (line, line_starts_index, line_starts, + line_starts_slots, 100, char *); + if (bump_index) + { + i++; + bump_index = 0; + } + + while (1) + { + /* The cast to unsigned char is for 8-bit characters, which + could be passed as negative integers to character_width + and wreak havoc on some naive implementations of iscntrl. */ + c = (unsigned char) node->contents[i]; + + /* Support ANSI escape sequences for -R. */ + if (raw_escapes_p + && c == '\033' + && node->contents[i+1] == '[' + && isdigit (node->contents[i+2])) + { + if (node->contents[i+3] == 'm') + { + i += 3; + cwidth = 0; + } + else if (isdigit (node->contents[i+3]) + && node->contents[i+4] == 'm') + { + i += 4; + cwidth = 0; + } + else + cwidth = character_width (c, hpos); + } + else + cwidth = character_width (c, hpos); + + /* If this character fits within this line, just do the next one. */ + if ((hpos + cwidth) < (unsigned int) window->width) + { + i++; + hpos += cwidth; + continue; + } + else + { + /* If this character would position the cursor at the start of + the next printed screen line, then do the next line. */ + if (c == '\n' || c == '\r' || c == '\t') + { + i++; + hpos = 0; + break; + } + else + { + /* This character passes the window width border. Postion + the cursor after the printed character, but remember this + line start as where this character is. A bit tricky. */ + + /* If this window doesn't wrap lines, proceed to the next + physical line here. */ + if (window->flags & W_NoWrap) + { + hpos = 0; + while (i < node->nodelen && node->contents[i] != '\n') + i++; + + if (node->contents[i] == '\n') + i++; + } + else + { + hpos = the_screen->width - hpos; + bump_index++; + } + break; + } + } + } + } + window->line_starts = line_starts; + window->line_count = line_starts_index; +} + +/* Given WINDOW, recalculate the line starts for the node it displays. */ +void +recalculate_line_starts (WINDOW *window) +{ + maybe_free (window->line_starts); + calculate_line_starts (window); +} + +/* Global variable control redisplay of scrolled windows. If non-zero, it + is the desired number of lines to scroll the window in order to make + point visible. A user might set this to 1 for smooth scrolling. If + set to zero, the line containing point is centered within the window. */ +int window_scroll_step = 0; + +/* Adjust the pagetop of WINDOW such that the cursor point will be visible. */ +void +window_adjust_pagetop (WINDOW *window) +{ + register int line = 0; + char *contents; + + if (!window->node) + return; + + contents = window->node->contents; + + /* Find the first printed line start which is after WINDOW->point. */ + for (line = 0; line < window->line_count; line++) + { + char *line_start; + + line_start = window->line_starts[line]; + + if ((line_start - contents) > window->point) + break; + } + + /* The line index preceding the line start which is past point is the + one containing point. */ + line--; + + /* If this line appears in the current displayable page, do nothing. + Otherwise, adjust the top of the page to make this line visible. */ + if ((line < window->pagetop) || + (line - window->pagetop > (window->height - 1))) + { + /* The user-settable variable "scroll-step" is used to attempt + to make point visible, iff it is non-zero. If that variable + is zero, then the line containing point is centered within + the window. */ + if (window_scroll_step < window->height) + { + if ((line < window->pagetop) && + ((window->pagetop - window_scroll_step) <= line)) + window->pagetop -= window_scroll_step; + else if ((line - window->pagetop > (window->height - 1)) && + ((line - (window->pagetop + window_scroll_step) + < window->height))) + window->pagetop += window_scroll_step; + else + window->pagetop = line - ((window->height - 1) / 2); + } + else + window->pagetop = line - ((window->height - 1) / 2); + + if (window->pagetop < 0) + window->pagetop = 0; + window->flags |= W_UpdateWindow; + } +} + +/* Return the index of the line containing point. */ +int +window_line_of_point (WINDOW *window) +{ + register int i, start = 0; + + /* Try to optimize. Check to see if point is past the pagetop for + this window, and if so, start searching forward from there. */ + if ((window->pagetop > -1 && window->pagetop < window->line_count) && + (window->line_starts[window->pagetop] - window->node->contents) + <= window->point) + start = window->pagetop; + + for (i = start; i < window->line_count; i++) + { + if ((window->line_starts[i] - window->node->contents) > window->point) + break; + } + + return (i - 1); +} + +/* Get and return the goal column for this window. */ +int +window_get_goal_column (WINDOW *window) +{ + if (!window->node) + return (-1); + + if (window->goal_column != -1) + return (window->goal_column); + + /* Okay, do the work. Find the printed offset of the cursor + in this window. */ + return (window_get_cursor_column (window)); +} + +/* Get and return the printed column offset of the cursor in this window. */ +int +window_get_cursor_column (WINDOW *window) +{ + int i, hpos, end; + char *line; + + i = window_line_of_point (window); + + if (i < 0) + return (-1); + + line = window->line_starts[i]; + end = window->point - (line - window->node->contents); + + for (hpos = 0, i = 0; i < end; i++) + { + /* Support ANSI escape sequences for -R. */ + if (raw_escapes_p + && line[i] == '\033' + && line[i+1] == '[' + && isdigit (line[i+2])) + { + if (line[i+3] == 'm') + i += 3; + else if (isdigit (line[i+3]) && line[i+4] == 'm') + i += 4; + else + hpos += character_width (line[i], hpos); + } + else + hpos += character_width (line[i], hpos); + } + + return (hpos); +} + +/* Count the number of characters in LINE that precede the printed column + offset of GOAL. */ +int +window_chars_to_goal (char *line, int goal) +{ + register int i, check = 0, hpos; + + for (hpos = 0, i = 0; line[i] != '\n'; i++) + { + /* Support ANSI escape sequences for -R. */ + if (raw_escapes_p + && line[i] == '\033' + && line[i+1] == '[' + && isdigit (line[i+2]) + && (line[i+3] == 'm' + || (isdigit (line[i+3]) && line[i+4] == 'm'))) + while (line[i] != 'm') + i++; + else + check = hpos + character_width (line[i], hpos); + + if (check > goal) + break; + + hpos = check; + } + return (i); +} + +/* Create a modeline for WINDOW, and store it in window->modeline. */ +void +window_make_modeline (WINDOW *window) +{ + register int i; + char *modeline; + char location_indicator[4]; + int lines_remaining; + + /* Only make modelines for those windows which have one. */ + if (window->flags & W_InhibitMode) + return; + + /* Find the number of lines actually displayed in this window. */ + lines_remaining = window->line_count - window->pagetop; + + if (window->pagetop == 0) + { + if (lines_remaining <= window->height) + strcpy (location_indicator, "All"); + else + strcpy (location_indicator, "Top"); + } + else + { + if (lines_remaining <= window->height) + strcpy (location_indicator, "Bot"); + else + { + float pt, lc; + int percentage; + + pt = (float)window->pagetop; + lc = (float)window->line_count; + + percentage = 100 * (pt / lc); + + sprintf (location_indicator, "%2d%%", percentage); + } + } + + /* Calculate the maximum size of the information to stick in MODELINE. */ + { + int modeline_len = 0; + char *parent = NULL, *filename = "*no file*"; + char *nodename = "*no node*"; + const char *update_message = NULL; + NODE *node = window->node; + + if (node) + { + if (node->nodename) + nodename = node->nodename; + + if (node->parent) + { + parent = filename_non_directory (node->parent); + modeline_len += strlen ("Subfile: ") + strlen (node->filename); + } + + if (node->filename) + filename = filename_non_directory (node->filename); + + if (node->flags & N_UpdateTags) + update_message = _("--*** Tags out of Date ***"); + } + + if (update_message) + modeline_len += strlen (update_message); + modeline_len += strlen (filename); + modeline_len += strlen (nodename); + modeline_len += 4; /* strlen (location_indicator). */ + + /* 10 for the decimal representation of the number of lines in this + node, and the remainder of the text that can appear in the line. */ + modeline_len += 10 + strlen (_("-----Info: (), lines ----, ")); + modeline_len += window->width; + + modeline = xmalloc (1 + modeline_len); + + /* Special internal windows have no filename. */ + if (!parent && !*filename) + sprintf (modeline, _("-%s---Info: %s, %d lines --%s--"), + (window->flags & W_NoWrap) ? "$" : "-", + nodename, window->line_count, location_indicator); + else + sprintf (modeline, _("-%s%s-Info: (%s)%s, %d lines --%s--"), + (window->flags & W_NoWrap) ? "$" : "-", + (node && (node->flags & N_IsCompressed)) ? "zz" : "--", + parent ? parent : filename, + nodename, window->line_count, location_indicator); + + if (parent) + sprintf (modeline + strlen (modeline), _(" Subfile: %s"), filename); + + if (update_message) + sprintf (modeline + strlen (modeline), "%s", update_message); + + i = strlen (modeline); + + if (i >= window->width) + modeline[window->width] = '\0'; + else + { + while (i < window->width) + modeline[i++] = '-'; + modeline[i] = '\0'; + } + + strcpy (window->modeline, modeline); + free (modeline); + } +} + +/* Make WINDOW start displaying at PERCENT percentage of its node. */ +void +window_goto_percentage (WINDOW *window, int percent) +{ + int desired_line; + + if (!percent) + desired_line = 0; + else + desired_line = + (int) ((float)window->line_count * ((float)percent / 100.0)); + + window->pagetop = desired_line; + window->point = + window->line_starts[window->pagetop] - window->node->contents; + window->flags |= W_UpdateWindow; + window_make_modeline (window); +} + +/* Get the state of WINDOW, and save it in STATE. */ +void +window_get_state (WINDOW *window, SEARCH_STATE *state) +{ + state->node = window->node; + state->pagetop = window->pagetop; + state->point = window->point; +} + +/* Set the node, pagetop, and point of WINDOW. */ +void +window_set_state (WINDOW *window, SEARCH_STATE *state) +{ + if (window->node != state->node) + window_set_node_of_window (window, state->node); + window->pagetop = state->pagetop; + window->point = state->point; +} + + +/* Manipulating home-made nodes. */ + +/* A place to buffer echo area messages. */ +static NODE *echo_area_node = NULL; + +/* Make the node of the_echo_area be an empty one. */ +static void +free_echo_area (void) +{ + if (echo_area_node) + { + maybe_free (echo_area_node->contents); + free (echo_area_node); + } + + echo_area_node = NULL; + window_set_node_of_window (the_echo_area, echo_area_node); +} + +/* Clear the echo area, removing any message that is already present. + The echo area is cleared immediately. */ +void +window_clear_echo_area (void) +{ + free_echo_area (); + display_update_one_window (the_echo_area); +} + +/* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2. + The arguments are treated similar to printf () arguments, but not all of + printf () hair is present. The message appears immediately. If there was + already a message appearing in the echo area, it is removed. */ +void +window_message_in_echo_area (char *format, void *arg1, void *arg2) +{ + free_echo_area (); + echo_area_node = build_message_node (format, arg1, arg2); + window_set_node_of_window (the_echo_area, echo_area_node); + display_update_one_window (the_echo_area); +} + +/* Place a temporary message in the echo area built from FORMAT, ARG1 + and ARG2. The message appears immediately, but does not destroy + any existing message. A future call to unmessage_in_echo_area () + restores the old contents. */ +static NODE **old_echo_area_nodes = NULL; +static int old_echo_area_nodes_index = 0; +static int old_echo_area_nodes_slots = 0; + +void +message_in_echo_area (char *format, void *arg1, void *arg2) +{ + if (echo_area_node) + { + add_pointer_to_array (echo_area_node, old_echo_area_nodes_index, + old_echo_area_nodes, old_echo_area_nodes_slots, + 4, NODE *); + } + echo_area_node = NULL; + window_message_in_echo_area (format, arg1, arg2); +} + +void +unmessage_in_echo_area (void) +{ + free_echo_area (); + + if (old_echo_area_nodes_index) + echo_area_node = old_echo_area_nodes[--old_echo_area_nodes_index]; + + window_set_node_of_window (the_echo_area, echo_area_node); + display_update_one_window (the_echo_area); +} + +/* A place to build a message. */ +static char *message_buffer = NULL; +static int message_buffer_index = 0; +static int message_buffer_size = 0; + +/* Ensure that there is enough space to stuff LENGTH characters into + MESSAGE_BUFFER. */ +static void +message_buffer_resize (int length) +{ + if (!message_buffer) + { + message_buffer_size = length + 1; + message_buffer = xmalloc (message_buffer_size); + message_buffer_index = 0; + } + + while (message_buffer_size <= message_buffer_index + length) + message_buffer = (char *) + xrealloc (message_buffer, + message_buffer_size += 100 + (2 * length)); +} + +/* Format MESSAGE_BUFFER with the results of printing FORMAT with ARG1 and + ARG2. */ +static void +build_message_buffer (char *format, void *arg1, void *arg2, void *arg3) +{ + register int i, len; + void *args[3]; + int arg_index = 0; + + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + + len = strlen (format); + + message_buffer_resize (len); + + for (i = 0; format[i]; i++) + { + if (format[i] != '%') + { + message_buffer[message_buffer_index++] = format[i]; + len--; + } + else + { + char c; + char *fmt_start = format + i; + char *fmt; + int fmt_len, formatted_len; + int paramed = 0; + + format_again: + i++; + while (format[i] && strchr ("-. +0123456789", format[i])) + i++; + c = format[i]; + + if (c == '\0') + abort (); + + if (c == '$') { + /* position parameter parameter */ + /* better to use bprintf from bfox's metahtml? */ + arg_index = atoi(fmt_start + 1) - 1; + if (arg_index < 0) + arg_index = 0; + if (arg_index >= 2) + arg_index = 1; + paramed = 1; + goto format_again; + } + + fmt_len = format + i - fmt_start + 1; + fmt = (char *) xmalloc (fmt_len + 1); + strncpy (fmt, fmt_start, fmt_len); + fmt[fmt_len] = '\0'; + + if (paramed) { + /* removed positioned parameter */ + char *p; + for (p = fmt + 1; *p && *p != '$'; p++) { + ; + } + strcpy(fmt + 1, p + 1); + } + + /* If we have "%-98s", maybe 98 calls for a longer string. */ + if (fmt_len > 2) + { + int j; + + for (j = fmt_len - 2; j >= 0; j--) + if (isdigit (fmt[j]) || fmt[j] == '$') + break; + + formatted_len = atoi (fmt + j); + } + else + formatted_len = c == 's' ? 0 : 1; /* %s can produce empty string */ + + switch (c) + { + case '%': /* Insert a percent sign. */ + message_buffer_resize (len + formatted_len); + sprintf + (message_buffer + message_buffer_index, fmt, "%"); + message_buffer_index += formatted_len; + break; + + case 's': /* Insert the current arg as a string. */ + { + char *string; + int string_len; + + string = (char *)args[arg_index++]; + string_len = strlen (string); + + if (formatted_len > string_len) + string_len = formatted_len; + message_buffer_resize (len + string_len); + sprintf + (message_buffer + message_buffer_index, fmt, string); + message_buffer_index += string_len; + } + break; + + case 'd': /* Insert the current arg as an integer. */ + { + long long_val; + int integer; + + long_val = (long)args[arg_index++]; + integer = (int)long_val; + + message_buffer_resize (len + formatted_len > 32 + ? formatted_len : 32); + sprintf + (message_buffer + message_buffer_index, fmt, integer); + message_buffer_index = strlen (message_buffer); + } + break; + + case 'c': /* Insert the current arg as a character. */ + { + long long_val; + int character; + + long_val = (long)args[arg_index++]; + character = (int)long_val; + + message_buffer_resize (len + formatted_len); + sprintf + (message_buffer + message_buffer_index, fmt, character); + message_buffer_index += formatted_len; + } + break; + + default: + abort (); + } + free (fmt); + } + } + message_buffer[message_buffer_index] = '\0'; +} + +/* Build a new node which has FORMAT printed with ARG1 and ARG2 as the + contents. */ +NODE * +build_message_node (char *format, void *arg1, void *arg2) +{ + NODE *node; + + message_buffer_index = 0; + build_message_buffer (format, arg1, arg2, 0); + + node = message_buffer_to_node (); + return (node); +} + +/* Convert the contents of the message buffer to a node. */ +NODE * +message_buffer_to_node (void) +{ + NODE *node; + + node = xmalloc (sizeof (NODE)); + node->filename = NULL; + node->parent = NULL; + node->nodename = NULL; + node->flags = 0; + node->display_pos =0; + + /* Make sure that this buffer ends with a newline. */ + node->nodelen = 1 + strlen (message_buffer); + node->contents = xmalloc (1 + node->nodelen); + strcpy (node->contents, message_buffer); + node->contents[node->nodelen - 1] = '\n'; + node->contents[node->nodelen] = '\0'; + return (node); +} + +/* Useful functions can be called from outside of window.c. */ +void +initialize_message_buffer (void) +{ + message_buffer_index = 0; +} + +/* Print FORMAT with ARG1,2 to the end of the current message buffer. */ +void +printf_to_message_buffer (char *format, void *arg1, void *arg2, void *arg3) +{ + build_message_buffer (format, arg1, arg2, arg3); +} + +/* Return the current horizontal position of the "cursor" on the most + recently output message buffer line. */ +int +message_buffer_length_this_line (void) +{ + register int i; + + if (!message_buffer_index) + return (0); + + for (i = message_buffer_index; i && message_buffer[i - 1] != '\n'; i--); + + return (string_width (message_buffer + i, 0)); +} + +/* Pad STRING to COUNT characters by inserting blanks. */ +int +pad_to (int count, char *string) +{ + register int i; + + i = strlen (string); + + if (i >= count) + string[i++] = ' '; + else + { + while (i < count) + string[i++] = ' '; + } + string[i] = '\0'; + + return (i); +} diff --git a/Build/source/utils/texinfo/info/window.h b/Build/source/utils/texinfo/info/window.h new file mode 100644 index 00000000000..44f620f0a46 --- /dev/null +++ b/Build/source/utils/texinfo/info/window.h @@ -0,0 +1,241 @@ +/* window.h -- Structure and flags used in manipulating Info windows. + $Id: window.h,v 1.3 2004/04/11 17:56:46 karl Exp $ + + This file is part of GNU Info, a program for reading online documentation + stored in Info format. + + Copyright (C) 1993, 1997, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under 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. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Written by Brian Fox (bfox@ai.mit.edu). */ + +#ifndef INFO_WINDOW_H +#define INFO_WINDOW_H + +#include "infomap.h" +#include "nodes.h" + +/* Smallest number of visible lines in a window. The actual height is + always one more than this number because each window has a modeline. */ +#define WINDOW_MIN_HEIGHT 2 + +/* Smallest number of screen lines that can be used to fully present a + window. This number includes the modeline of the window. */ +#define WINDOW_MIN_SIZE (WINDOW_MIN_HEIGHT + 1) + +/* The exact same elements are used within the WINDOW_STATE structure and a + subsection of the WINDOW structure. We could define a structure which + contains this elements, and include that structure in each of WINDOW_STATE + and WINDOW. But that would lead references in the code such as + window->state->node which we would like to avoid. Instead, we #define the + elements here, and simply include the define in both data structures. Thus, + if you need to change window state information, here is where you would + do it. NB> The last element does NOT end with a semi-colon. */ +#define WINDOW_STATE_DECL \ + NODE *node; /* The node displayed in this window. */ \ + int pagetop; /* LINE_STARTS[PAGETOP] is first line in WINDOW. */ \ + long point /* Offset within NODE of the cursor position. */ + +/* Structure which defines a window. Windows are doubly linked, next + and prev. The list of windows is kept on WINDOWS. The structure member + window->height is the total height of the window. The position location + (0, window->height + window->first_row) is the first character of this + windows modeline. The number of lines that can be displayed in a window + is equal to window->height - 1. */ +typedef struct window_struct +{ + struct window_struct *next; /* Next window in this chain. */ + struct window_struct *prev; /* Previous window in this chain. */ + int width; /* Width of this window. */ + int height; /* Height of this window. */ + int first_row; /* Offset of the first line in the_screen. */ + int goal_column; /* The column we would like the cursor to appear in. */ + Keymap keymap; /* Keymap used to read commands in this window. */ + WINDOW_STATE_DECL; /* Node, pagetop and point. */ + char *modeline; /* Calculated text of the modeline for this window. */ + char **line_starts; /* Array of printed line starts for this node. */ + int line_count; /* Number of lines appearing in LINE_STARTS. */ + int flags; /* See below for details. */ +} WINDOW; + +typedef struct { + WINDOW_STATE_DECL; /* What gets saved. */ +} WINDOW_STATE; + +/* Structure defining the current state of an incremental search. */ +typedef struct { + WINDOW_STATE_DECL; /* The node, pagetop and point. */ + int search_index; /* Offset of the last char in the search string. */ + int direction; /* The direction that this search is heading in. */ + int failing; /* Whether or not this search failed. */ +} SEARCH_STATE; + +#define W_UpdateWindow 0x01 /* WINDOW needs updating. */ +#define W_WindowIsPerm 0x02 /* This WINDOW is a permanent object. */ +#define W_WindowVisible 0x04 /* This WINDOW is currently visible. */ +#define W_InhibitMode 0x08 /* This WINDOW has no modeline. */ +#define W_NoWrap 0x10 /* Lines do not wrap in this window. */ +#define W_InputWindow 0x20 /* Window accepts input. */ +#define W_TempWindow 0x40 /* Window is less important. */ + +extern WINDOW *windows; /* List of visible Info windows. */ +extern WINDOW *active_window; /* The currently active window. */ +extern WINDOW *the_screen; /* The Info screen is just another window. */ +extern WINDOW *the_echo_area; /* THE_ECHO_AREA is a window in THE_SCREEN. */ + +/* Global variable control redisplay of scrolled windows. If non-zero, it + is the desired number of lines to scroll the window in order to make + point visible. A user might set this to 1 for smooth scrolling. If + set to zero, the line containing point is centered within the window. */ +extern int window_scroll_step; + + /* Make the modeline member for WINDOW. */ +extern void window_make_modeline (WINDOW *window); + +/* Initalize the window system by creating THE_SCREEN and THE_ECHO_AREA. + Create the first window ever, and make it permanent. + You pass WIDTH and HEIGHT; the dimensions of the total screen size. */ +extern void window_initialize_windows (int width, int height); + +/* Make a new window showing NODE, and return that window structure. + The new window is made to be the active window. If NODE is passed + as NULL, then show the node showing in the active window. If the + window could not be made return a NULL pointer. The active window + is not changed.*/ +extern WINDOW *window_make_window (NODE *node); + +/* Delete WINDOW from the list of known windows. If this window was the + active window, make the next window in the chain be the active window, + or the previous window in the chain if there is no next window. */ +extern void window_delete_window (WINDOW *window); + +/* A function to call when the screen changes size, and some windows have + to get deleted. The function is called with the window to be deleted + as an argument, and it can't do anything about the window getting deleted; + it can only clean up dangling references to that window. */ +extern VFunction *window_deletion_notifier; + +/* Set WINDOW to display NODE. */ +extern void window_set_node_of_window (WINDOW *window, NODE *node); + +/* Tell the window system that the size of the screen has changed. This + causes lots of interesting things to happen. The permanent windows + are resized, as well as every visible window. You pass WIDTH and HEIGHT; + the dimensions of the total screen size. */ +extern void window_new_screen_size (int width, int height); + +/* Change the height of WINDOW by AMOUNT. This also automagically adjusts + the previous and next windows in the chain. If there is only one user + window, then no change takes place. */ +extern void window_change_window_height (WINDOW *window, int amount); + +/* Adjust the pagetop of WINDOW such that the cursor point will be visible. */ +extern void window_adjust_pagetop (WINDOW *window); + +/* Tile all of the windows currently displayed in the global variable + WINDOWS. If argument DO_INTERNALS is non-zero, tile windows displaying + internal nodes as well. */ +#define DONT_TILE_INTERNALS 0 +#define TILE_INTERNALS 1 +extern void window_tile_windows (int style); + +/* Toggle the state of line wrapping in WINDOW. This can do a bit of fancy + redisplay. */ +extern void window_toggle_wrap (WINDOW *window); + +/* For every window in CHAIN, set the flags member to have FLAG set. */ +extern void window_mark_chain (WINDOW *chain, int flag); + +/* For every window in CHAIN, clear the flags member of FLAG. */ +extern void window_unmark_chain (WINDOW *chain, int flag); + +/* Make WINDOW start displaying at PERCENT percentage of its node. */ +extern void window_goto_percentage (WINDOW *window, int percent); + +/* Build a new node which has FORMAT printed with ARG1 and ARG2 as the + contents. */ +extern NODE *build_message_node (char *format, void *arg1, void *arg2); + +/* Useful functions can be called from outside of window.c. */ +extern void initialize_message_buffer (void); + +/* Print FORMAT with ARG1,2 to the end of the current message buffer. */ +extern void printf_to_message_buffer (char *format, void *arg1, void *arg2, + void *arg3); + +/* Convert the contents of the message buffer to a node. */ +extern NODE *message_buffer_to_node (void); + +/* Return the length of the most recently printed line in message buffer. */ +extern int message_buffer_length_this_line (void); + +/* Pad STRING to COUNT characters by inserting blanks. */ +extern int pad_to (int count, char *string); + +/* Make a message appear in the echo area, built from FORMAT, ARG1 and ARG2. + The arguments are treated similar to printf () arguments, but not all of + printf () hair is present. The message appears immediately. If there was + already a message appearing in the echo area, it is removed. */ +extern void window_message_in_echo_area (char *format, void *arg1, void *arg2); + +/* Place a temporary message in the echo area built from FORMAT, ARG1 + and ARG2. The message appears immediately, but does not destroy + any existing message. A future call to unmessage_in_echo_area () + restores the old contents. */ +extern void message_in_echo_area (char *format, void *arg1, void *arg2); +extern void unmessage_in_echo_area (void); + +/* Clear the echo area, removing any message that is already present. + The echo area is cleared immediately. */ +extern void window_clear_echo_area (void); + +/* Quickly guess the approximate number of lines to that NODE would + take to display. This really only counts carriage returns. */ +extern int window_physical_lines (NODE *node); + +/* Calculate a list of line starts for the node belonging to WINDOW. The line + starts are pointers to the actual text within WINDOW->NODE. */ +extern void calculate_line_starts (WINDOW *window); + +/* Given WINDOW, recalculate the line starts for the node it displays. */ +extern void recalculate_line_starts (WINDOW *window); + +/* Return the number of characters it takes to display CHARACTER on the + screen at HPOS. */ +extern int character_width (int character, int hpos); + +/* Return the number of characters it takes to display STRING on the + screen at HPOS. */ +extern int string_width (char *string, int hpos); + +/* Return the index of the line containing point. */ +extern int window_line_of_point (WINDOW *window); + +/* Get and return the goal column for this window. */ +extern int window_get_goal_column (WINDOW *window); + +/* Get and return the printed column offset of the cursor in this window. */ +extern int window_get_cursor_column (WINDOW *window); + +/* Get and Set the node, pagetop, and point of WINDOW. */ +extern void window_get_state (WINDOW *window, SEARCH_STATE *state); +extern void window_set_state (WINDOW *window, SEARCH_STATE *state); + +/* Count the number of characters in LINE that precede the printed column + offset of GOAL. */ +extern int window_chars_to_goal (char *line, int goal); + +#endif /* not INFO_WINDOW_H */ |