diff options
Diffstat (limited to '')
73 files changed, 29534 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..c513bad --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,217 @@ +## Process this file with automake to produce Makefile.in +## +## Copyright (C) 1994, 1995 Graeme Wilford. +## Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +## 2011, 2012 Colin Watson. +## +## This file is part of man-db. +## +## man-db 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 of the License, or +## (at your option) any later version. +## +## man-db 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 man-db; if not, write to the Free Software Foundation, +## Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +pkglibexecdir = $(libexecdir)/@PACKAGE@ + +SUBDIRS = . tests + +bin_PROGRAMS = \ + catman \ + lexgrog \ + man \ + man-recode \ + mandb \ + manpath \ + whatis +sbin_PROGRAMS = accessdb +pkglibexec_PROGRAMS = globbing manconv zsoelim +noinst_DATA = man_db.conf + +EXTRA_DIST = lexgrog.c zsoelim.c + +AM_CPPFLAGS = \ + -I$(top_builddir)/include \ + -I$(top_builddir)/gl/lib \ + -I$(top_srcdir)/gl/lib \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/libdb \ + -DCONFIG_FILE=\"$(config_file)\" \ + -DAPROPOS=\"$(bindir)/$(TRANS_APROPOS)\" \ + -DAPROPOS_NAME=\"$(TRANS_APROPOS)\" \ + -DMAN=\"$(bindir)/$(TRANS_MAN)\" \ + -DMANCONV=\"$(pkglibexecdir)/$(TRANS_MANCONV)\" \ + -DMANDB=\"$(bindir)/$(TRANS_MANDB)\" \ + -DWHATIS=\"$(bindir)/$(TRANS_WHATIS)\" \ + -DZSOELIM=\"$(pkglibexecdir)/$(TRANS_ZSOELIM)\" +AM_CFLAGS = \ + $(WARN_CFLAGS) \ + $(libpipeline_CFLAGS) + +LIBMAN = $(top_builddir)/lib/libman.la $(top_builddir)/gl/lib/libgnu.la \ + @LTLIBINTL@ +LIBMANDB = $(top_builddir)/libdb/libmandb.la $(LIBMAN) $(DBLIBS) + +accessdb_LDADD = $(LIBMANDB) +catman_LDADD = $(LIBMANDB) $(libpipeline_LIBS) +globbing_LDADD = $(LIBMAN) +lexgrog_LDADD = $(LIBMAN) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +man_LDADD = $(LIBMANDB) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +man_recode_LDADD = $(LIBMANDB) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +manconv_LDADD = $(LIBMAN) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +mandb_LDADD = $(LIBMANDB) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +manpath_LDADD = $(LIBMAN) +whatis_LDADD = $(LIBMANDB) $(libpipeline_LIBS) $(LTLIBICONV) +zsoelim_LDADD = $(LIBMAN) $(LIBCOMPRESS) $(libpipeline_LIBS) + +accessdb_SOURCES = \ + accessdb.c +catman_SOURCES = \ + catman.c \ + globbing.c \ + globbing.h \ + manp.c \ + manp.h +globbing_SOURCES = \ + globbing.c \ + globbing.h \ + globbing_test.c +lexgrog_SOURCES = \ + compression.c \ + decompress.c \ + decompress.h \ + descriptions.c \ + descriptions.h \ + filenames.c \ + filenames.h \ + globbing.c \ + globbing.h \ + lexgrog.l \ + lexgrog_test.c \ + manconv.c \ + manconv.h \ + manconv_client.c \ + manconv_client.h \ + ult_src.c \ + ult_src.h +man_SOURCES = \ + compression.c \ + decompress.c \ + decompress.h \ + filenames.c \ + filenames.h \ + globbing.c \ + globbing.h \ + man.c \ + manconv.c \ + manconv.h \ + manconv_client.c \ + manconv_client.h \ + manp.c \ + manp.h \ + ult_src.c \ + ult_src.h \ + zsoelim.h \ + zsoelim.l +man_recode_SOURCES = \ + compression.c \ + decompress.c \ + decompress.h \ + man-recode.c \ + manconv.c \ + manconv.h \ + manconv_client.c \ + manconv_client.h +manconv_SOURCES = \ + decompress.c \ + decompress.h \ + manconv.c \ + manconv.h \ + manconv_main.c +mandb_SOURCES = \ + check_mandirs.c \ + check_mandirs.h \ + compression.c \ + decompress.c \ + decompress.h \ + descriptions.c \ + descriptions.h \ + descriptions_store.c \ + filenames.c \ + filenames.h \ + globbing.c \ + globbing.h \ + lexgrog.l \ + manconv.c \ + manconv.h \ + manconv_client.c \ + manconv_client.h \ + mandb.c \ + manp.c \ + manp.h \ + straycats.c \ + ult_src.c \ + ult_src.h +manpath_SOURCES = \ + globbing.c \ + globbing.h \ + manp.c \ + manp.h \ + manpath.c +whatis_SOURCES = \ + globbing.c \ + globbing.h \ + manp.c \ + manp.h \ + whatis.c +zsoelim_SOURCES = \ + decompress.c \ + decompress.h \ + globbing.c \ + globbing.h \ + manp.c \ + manp.h \ + zsoelim.h \ + zsoelim.l \ + zsoelim_main.c + +CLEANFILES = apropos man_db.conf + +apropos$(EXEEXT): whatis$(EXEEXT) + rm -f $@ + $(LN_S) whatis$(EXEEXT) $@ + +all-am: apropos$(EXEEXT) + +install-exec-hook: + if [ "$(man_owner)" ] && [ "$(man_mode)" = 6755 ]; then \ + chown $(man_owner):$(man_owner) \ + $(DESTDIR)$(bindir)/$(TRANS_MAN) \ + $(DESTDIR)$(bindir)/$(TRANS_MANDB); \ + fi + chmod $(man_mode) \ + $(DESTDIR)$(bindir)/$(TRANS_MAN) \ + $(DESTDIR)$(bindir)/$(TRANS_MANDB) + cd $(DESTDIR)$(bindir) && rm -f $(TRANS_APROPOS)$(EXEEXT) && \ + $(LN_S) $(TRANS_WHATIS)$(EXEEXT) $(TRANS_APROPOS)$(EXEEXT) + +install-data-hook: + @if test -f $(DESTDIR)$(config_file); then \ + echo "$(DESTDIR)$(config_file) already exists; overwrite manually if necessary"; \ + else \ + test -z "$(config_file_dirname)" || $(MKDIR_P) "$(DESTDIR)$(config_file_dirname)"; \ + echo " $(INSTALL_DATA) man_db.conf $(DESTDIR)$(config_file)"; \ + $(INSTALL_DATA) man_db.conf $(DESTDIR)$(config_file); \ + fi + +uninstall-hook: + rm -f $(DESTDIR)$(bindir)/$(TRANS_APROPOS)$(EXEEXT) + @echo "Please remove $(DESTDIR)$(config_file) manually if necessary" diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..1703509 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,2448 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 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@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = catman$(EXEEXT) lexgrog$(EXEEXT) man$(EXEEXT) \ + man-recode$(EXEEXT) mandb$(EXEEXT) manpath$(EXEEXT) \ + whatis$(EXEEXT) +sbin_PROGRAMS = accessdb$(EXEEXT) +pkglibexec_PROGRAMS = globbing$(EXEEXT) manconv$(EXEEXT) \ + zsoelim$(EXEEXT) +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/man-arg-automatic-create.m4 \ + $(top_srcdir)/m4/man-arg-automatic-update.m4 \ + $(top_srcdir)/m4/man-arg-cache-owner.m4 \ + $(top_srcdir)/m4/man-arg-cats.m4 \ + $(top_srcdir)/m4/man-arg-config-file.m4 \ + $(top_srcdir)/m4/man-arg-db.m4 \ + $(top_srcdir)/m4/man-arg-device.m4 \ + $(top_srcdir)/m4/man-arg-mandirs.m4 \ + $(top_srcdir)/m4/man-arg-manual.m4 \ + $(top_srcdir)/m4/man-arg-override-dir.m4 \ + $(top_srcdir)/m4/man-arg-sections.m4 \ + $(top_srcdir)/m4/man-arg-setuid.m4 \ + $(top_srcdir)/m4/man-arg-systemdsystemunitdir.m4 \ + $(top_srcdir)/m4/man-arg-systemdtmpfilesdir.m4 \ + $(top_srcdir)/m4/man-arg-undoc.m4 $(top_srcdir)/m4/man-bdb.m4 \ + $(top_srcdir)/m4/man-check-progs.m4 \ + $(top_srcdir)/m4/man-compress-lib.m4 \ + $(top_srcdir)/m4/man-gnu-nroff.m4 \ + $(top_srcdir)/m4/man-heirloom-nroff.m4 \ + $(top_srcdir)/m4/man-libseccomp.m4 \ + $(top_srcdir)/m4/man-linguas.m4 $(top_srcdir)/m4/man-po4a.m4 \ + $(top_srcdir)/m4/man-tar-sort-name.m4 \ + $(top_srcdir)/m4/man-trans-subst.m4 \ + $(top_srcdir)/gl/m4/00gnulib.m4 \ + $(top_srcdir)/gl/m4/__inline.m4 \ + $(top_srcdir)/gl/m4/absolute-header.m4 \ + $(top_srcdir)/gl/m4/alloca.m4 $(top_srcdir)/gl/m4/argp.m4 \ + $(top_srcdir)/gl/m4/asm-underscore.m4 \ + $(top_srcdir)/gl/m4/btowc.m4 \ + $(top_srcdir)/gl/m4/builtin-expect.m4 \ + $(top_srcdir)/gl/m4/canonicalize.m4 \ + $(top_srcdir)/gl/m4/chdir-long.m4 $(top_srcdir)/gl/m4/chown.m4 \ + $(top_srcdir)/gl/m4/clock_time.m4 $(top_srcdir)/gl/m4/close.m4 \ + $(top_srcdir)/gl/m4/closedir.m4 $(top_srcdir)/gl/m4/codeset.m4 \ + $(top_srcdir)/gl/m4/ctype.m4 $(top_srcdir)/gl/m4/d-ino.m4 \ + $(top_srcdir)/gl/m4/d-type.m4 $(top_srcdir)/gl/m4/dirent_h.m4 \ + $(top_srcdir)/gl/m4/dirfd.m4 $(top_srcdir)/gl/m4/dirname.m4 \ + $(top_srcdir)/gl/m4/double-slash-root.m4 \ + $(top_srcdir)/gl/m4/dup.m4 $(top_srcdir)/gl/m4/dup2.m4 \ + $(top_srcdir)/gl/m4/eealloc.m4 $(top_srcdir)/gl/m4/environ.m4 \ + $(top_srcdir)/gl/m4/errno_h.m4 $(top_srcdir)/gl/m4/error.m4 \ + $(top_srcdir)/gl/m4/exponentd.m4 \ + $(top_srcdir)/gl/m4/extensions.m4 \ + $(top_srcdir)/gl/m4/extern-inline.m4 \ + $(top_srcdir)/gl/m4/fchdir.m4 $(top_srcdir)/gl/m4/fcntl-o.m4 \ + $(top_srcdir)/gl/m4/fcntl.m4 $(top_srcdir)/gl/m4/fcntl_h.m4 \ + $(top_srcdir)/gl/m4/fdopendir.m4 \ + $(top_srcdir)/gl/m4/filenamecat.m4 \ + $(top_srcdir)/gl/m4/flexmember.m4 \ + $(top_srcdir)/gl/m4/float_h.m4 $(top_srcdir)/gl/m4/flock.m4 \ + $(top_srcdir)/gl/m4/fnmatch.m4 \ + $(top_srcdir)/gl/m4/fnmatch_h.m4 $(top_srcdir)/gl/m4/fstat.m4 \ + $(top_srcdir)/gl/m4/fstatat.m4 $(top_srcdir)/gl/m4/futimens.m4 \ + $(top_srcdir)/gl/m4/getcwd-abort-bug.m4 \ + $(top_srcdir)/gl/m4/getcwd-path-max.m4 \ + $(top_srcdir)/gl/m4/getcwd.m4 $(top_srcdir)/gl/m4/getdelim.m4 \ + $(top_srcdir)/gl/m4/getdtablesize.m4 \ + $(top_srcdir)/gl/m4/getline.m4 $(top_srcdir)/gl/m4/getlogin.m4 \ + $(top_srcdir)/gl/m4/getlogin_r.m4 \ + $(top_srcdir)/gl/m4/getopt.m4 \ + $(top_srcdir)/gl/m4/getpagesize.m4 \ + $(top_srcdir)/gl/m4/getprogname.m4 \ + $(top_srcdir)/gl/m4/gettext.m4 $(top_srcdir)/gl/m4/gettime.m4 \ + $(top_srcdir)/gl/m4/gettimeofday.m4 \ + $(top_srcdir)/gl/m4/glibc21.m4 $(top_srcdir)/gl/m4/glob.m4 \ + $(top_srcdir)/gl/m4/glob_h.m4 \ + $(top_srcdir)/gl/m4/gnulib-common.m4 \ + $(top_srcdir)/gl/m4/gnulib-comp.m4 \ + $(top_srcdir)/gl/m4/host-cpu-c-abi.m4 \ + $(top_srcdir)/gl/m4/iconv.m4 $(top_srcdir)/gl/m4/idpriv.m4 \ + $(top_srcdir)/gl/m4/include_next.m4 \ + $(top_srcdir)/gl/m4/intlmacosx.m4 \ + $(top_srcdir)/gl/m4/intmax_t.m4 \ + $(top_srcdir)/gl/m4/inttypes.m4 \ + $(top_srcdir)/gl/m4/inttypes_h.m4 $(top_srcdir)/gl/m4/ioctl.m4 \ + $(top_srcdir)/gl/m4/isblank.m4 \ + $(top_srcdir)/gl/m4/langinfo_h.m4 \ + $(top_srcdir)/gl/m4/largefile.m4 $(top_srcdir)/gl/m4/lchown.m4 \ + $(top_srcdir)/gl/m4/lib-ignore.m4 \ + $(top_srcdir)/gl/m4/lib-ld.m4 $(top_srcdir)/gl/m4/lib-link.m4 \ + $(top_srcdir)/gl/m4/lib-prefix.m4 \ + $(top_srcdir)/gl/m4/libtool.m4 $(top_srcdir)/gl/m4/limits-h.m4 \ + $(top_srcdir)/gl/m4/localcharset.m4 \ + $(top_srcdir)/gl/m4/locale-fr.m4 \ + $(top_srcdir)/gl/m4/locale-ja.m4 \ + $(top_srcdir)/gl/m4/locale-zh.m4 \ + $(top_srcdir)/gl/m4/locale_h.m4 \ + $(top_srcdir)/gl/m4/localeconv.m4 \ + $(top_srcdir)/gl/m4/localtime-buffer.m4 \ + $(top_srcdir)/gl/m4/lock.m4 $(top_srcdir)/gl/m4/lstat.m4 \ + $(top_srcdir)/gl/m4/ltoptions.m4 \ + $(top_srcdir)/gl/m4/ltsugar.m4 \ + $(top_srcdir)/gl/m4/ltversion.m4 \ + $(top_srcdir)/gl/m4/lt~obsolete.m4 \ + $(top_srcdir)/gl/m4/malloc.m4 $(top_srcdir)/gl/m4/malloca.m4 \ + $(top_srcdir)/gl/m4/manywarnings.m4 \ + $(top_srcdir)/gl/m4/mbrtowc.m4 $(top_srcdir)/gl/m4/mbsinit.m4 \ + $(top_srcdir)/gl/m4/mbsrtowcs.m4 \ + $(top_srcdir)/gl/m4/mbstate_t.m4 $(top_srcdir)/gl/m4/mbtowc.m4 \ + $(top_srcdir)/gl/m4/memchr.m4 $(top_srcdir)/gl/m4/memmem.m4 \ + $(top_srcdir)/gl/m4/mempcpy.m4 $(top_srcdir)/gl/m4/memrchr.m4 \ + $(top_srcdir)/gl/m4/minmax.m4 $(top_srcdir)/gl/m4/mkdir.m4 \ + $(top_srcdir)/gl/m4/mkdtemp.m4 $(top_srcdir)/gl/m4/mkstemp.m4 \ + $(top_srcdir)/gl/m4/mmap-anon.m4 $(top_srcdir)/gl/m4/mode_t.m4 \ + $(top_srcdir)/gl/m4/msvc-inval.m4 \ + $(top_srcdir)/gl/m4/msvc-nothrow.m4 \ + $(top_srcdir)/gl/m4/multiarch.m4 \ + $(top_srcdir)/gl/m4/nanosleep.m4 \ + $(top_srcdir)/gl/m4/nl_langinfo.m4 $(top_srcdir)/gl/m4/nls.m4 \ + $(top_srcdir)/gl/m4/nocrash.m4 \ + $(top_srcdir)/gl/m4/nonblocking.m4 \ + $(top_srcdir)/gl/m4/off_t.m4 \ + $(top_srcdir)/gl/m4/open-cloexec.m4 \ + $(top_srcdir)/gl/m4/open-slash.m4 $(top_srcdir)/gl/m4/open.m4 \ + $(top_srcdir)/gl/m4/openat.m4 $(top_srcdir)/gl/m4/opendir.m4 \ + $(top_srcdir)/gl/m4/pathmax.m4 $(top_srcdir)/gl/m4/po.m4 \ + $(top_srcdir)/gl/m4/printf.m4 $(top_srcdir)/gl/m4/progtest.m4 \ + $(top_srcdir)/gl/m4/pthread_rwlock_rdlock.m4 \ + $(top_srcdir)/gl/m4/raise.m4 $(top_srcdir)/gl/m4/rawmemchr.m4 \ + $(top_srcdir)/gl/m4/readdir.m4 $(top_srcdir)/gl/m4/readlink.m4 \ + $(top_srcdir)/gl/m4/realloc.m4 $(top_srcdir)/gl/m4/regex.m4 \ + $(top_srcdir)/gl/m4/rename.m4 $(top_srcdir)/gl/m4/renameat.m4 \ + $(top_srcdir)/gl/m4/rewinddir.m4 $(top_srcdir)/gl/m4/rmdir.m4 \ + $(top_srcdir)/gl/m4/same.m4 $(top_srcdir)/gl/m4/save-cwd.m4 \ + $(top_srcdir)/gl/m4/select.m4 $(top_srcdir)/gl/m4/setenv.m4 \ + $(top_srcdir)/gl/m4/setlocale_null.m4 \ + $(top_srcdir)/gl/m4/sigaction.m4 \ + $(top_srcdir)/gl/m4/signal_h.m4 \ + $(top_srcdir)/gl/m4/signalblocking.m4 \ + $(top_srcdir)/gl/m4/size_max.m4 $(top_srcdir)/gl/m4/sleep.m4 \ + $(top_srcdir)/gl/m4/socketlib.m4 \ + $(top_srcdir)/gl/m4/sockets.m4 $(top_srcdir)/gl/m4/socklen.m4 \ + $(top_srcdir)/gl/m4/ssize_t.m4 \ + $(top_srcdir)/gl/m4/stat-time.m4 $(top_srcdir)/gl/m4/stat.m4 \ + $(top_srcdir)/gl/m4/std-gnu11.m4 \ + $(top_srcdir)/gl/m4/stdalign.m4 $(top_srcdir)/gl/m4/stdarg.m4 \ + $(top_srcdir)/gl/m4/stdbool.m4 $(top_srcdir)/gl/m4/stddef_h.m4 \ + $(top_srcdir)/gl/m4/stdint.m4 $(top_srcdir)/gl/m4/stdint_h.m4 \ + $(top_srcdir)/gl/m4/stdio_h.m4 $(top_srcdir)/gl/m4/stdlib_h.m4 \ + $(top_srcdir)/gl/m4/strcase.m4 \ + $(top_srcdir)/gl/m4/strcasestr.m4 \ + $(top_srcdir)/gl/m4/strchrnul.m4 $(top_srcdir)/gl/m4/strdup.m4 \ + $(top_srcdir)/gl/m4/strerror.m4 \ + $(top_srcdir)/gl/m4/string_h.m4 \ + $(top_srcdir)/gl/m4/strings_h.m4 \ + $(top_srcdir)/gl/m4/strndup.m4 $(top_srcdir)/gl/m4/strnlen.m4 \ + $(top_srcdir)/gl/m4/strsep.m4 \ + $(top_srcdir)/gl/m4/sys_file_h.m4 \ + $(top_srcdir)/gl/m4/sys_ioctl_h.m4 \ + $(top_srcdir)/gl/m4/sys_select_h.m4 \ + $(top_srcdir)/gl/m4/sys_socket_h.m4 \ + $(top_srcdir)/gl/m4/sys_stat_h.m4 \ + $(top_srcdir)/gl/m4/sys_time_h.m4 \ + $(top_srcdir)/gl/m4/sys_types_h.m4 \ + $(top_srcdir)/gl/m4/sys_uio_h.m4 \ + $(top_srcdir)/gl/m4/sysexits.m4 \ + $(top_srcdir)/gl/m4/tempname.m4 \ + $(top_srcdir)/gl/m4/threadlib.m4 $(top_srcdir)/gl/m4/time_h.m4 \ + $(top_srcdir)/gl/m4/timespec.m4 \ + $(top_srcdir)/gl/m4/unistd-safer.m4 \ + $(top_srcdir)/gl/m4/unistd_h.m4 $(top_srcdir)/gl/m4/unlink.m4 \ + $(top_srcdir)/gl/m4/unlinkat.m4 $(top_srcdir)/gl/m4/utime.m4 \ + $(top_srcdir)/gl/m4/utime_h.m4 $(top_srcdir)/gl/m4/utimens.m4 \ + $(top_srcdir)/gl/m4/utimes.m4 \ + $(top_srcdir)/gl/m4/vasnprintf.m4 \ + $(top_srcdir)/gl/m4/vasprintf.m4 \ + $(top_srcdir)/gl/m4/visibility.m4 \ + $(top_srcdir)/gl/m4/vsnprintf.m4 \ + $(top_srcdir)/gl/m4/warn-on-use.m4 \ + $(top_srcdir)/gl/m4/warnings.m4 $(top_srcdir)/gl/m4/wchar_h.m4 \ + $(top_srcdir)/gl/m4/wchar_t.m4 $(top_srcdir)/gl/m4/wcrtomb.m4 \ + $(top_srcdir)/gl/m4/wctype_h.m4 $(top_srcdir)/gl/m4/wint_t.m4 \ + $(top_srcdir)/gl/m4/wmemchr.m4 $(top_srcdir)/gl/m4/wmempcpy.m4 \ + $(top_srcdir)/gl/m4/xalloc.m4 $(top_srcdir)/gl/m4/xgetcwd.m4 \ + $(top_srcdir)/gl/m4/xsize.m4 $(top_srcdir)/gl/m4/xstrndup.m4 \ + $(top_srcdir)/gl/m4/xvasprintf.m4 \ + $(top_srcdir)/gl/m4/zzgnulib.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = man_db.conf +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" \ + "$(DESTDIR)$(sbindir)" +PROGRAMS = $(bin_PROGRAMS) $(pkglibexec_PROGRAMS) $(sbin_PROGRAMS) +am_accessdb_OBJECTS = accessdb.$(OBJEXT) +accessdb_OBJECTS = $(am_accessdb_OBJECTS) +am__DEPENDENCIES_1 = $(top_builddir)/lib/libman.la \ + $(top_builddir)/gl/lib/libgnu.la +am__DEPENDENCIES_2 = +am__DEPENDENCIES_3 = $(top_builddir)/libdb/libmandb.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) +accessdb_DEPENDENCIES = $(am__DEPENDENCIES_3) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am_catman_OBJECTS = catman.$(OBJEXT) globbing.$(OBJEXT) manp.$(OBJEXT) +catman_OBJECTS = $(am_catman_OBJECTS) +catman_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_2) +am_globbing_OBJECTS = globbing.$(OBJEXT) globbing_test.$(OBJEXT) +globbing_OBJECTS = $(am_globbing_OBJECTS) +globbing_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_lexgrog_OBJECTS = compression.$(OBJEXT) decompress.$(OBJEXT) \ + descriptions.$(OBJEXT) filenames.$(OBJEXT) globbing.$(OBJEXT) \ + lexgrog.$(OBJEXT) lexgrog_test.$(OBJEXT) manconv.$(OBJEXT) \ + manconv_client.$(OBJEXT) ult_src.$(OBJEXT) +lexgrog_OBJECTS = $(am_lexgrog_OBJECTS) +lexgrog_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_2) +am_man_OBJECTS = compression.$(OBJEXT) decompress.$(OBJEXT) \ + filenames.$(OBJEXT) globbing.$(OBJEXT) man.$(OBJEXT) \ + manconv.$(OBJEXT) manconv_client.$(OBJEXT) manp.$(OBJEXT) \ + ult_src.$(OBJEXT) zsoelim.$(OBJEXT) +man_OBJECTS = $(am_man_OBJECTS) +man_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_2) +am_man_recode_OBJECTS = compression.$(OBJEXT) decompress.$(OBJEXT) \ + man-recode.$(OBJEXT) manconv.$(OBJEXT) \ + manconv_client.$(OBJEXT) +man_recode_OBJECTS = $(am_man_recode_OBJECTS) +man_recode_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_2) +am_manconv_OBJECTS = decompress.$(OBJEXT) manconv.$(OBJEXT) \ + manconv_main.$(OBJEXT) +manconv_OBJECTS = $(am_manconv_OBJECTS) +manconv_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_2) +am_mandb_OBJECTS = check_mandirs.$(OBJEXT) compression.$(OBJEXT) \ + decompress.$(OBJEXT) descriptions.$(OBJEXT) \ + descriptions_store.$(OBJEXT) filenames.$(OBJEXT) \ + globbing.$(OBJEXT) lexgrog.$(OBJEXT) manconv.$(OBJEXT) \ + manconv_client.$(OBJEXT) mandb.$(OBJEXT) manp.$(OBJEXT) \ + straycats.$(OBJEXT) ult_src.$(OBJEXT) +mandb_OBJECTS = $(am_mandb_OBJECTS) +mandb_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_2) +am_manpath_OBJECTS = globbing.$(OBJEXT) manp.$(OBJEXT) \ + manpath.$(OBJEXT) +manpath_OBJECTS = $(am_manpath_OBJECTS) +manpath_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_whatis_OBJECTS = globbing.$(OBJEXT) manp.$(OBJEXT) whatis.$(OBJEXT) +whatis_OBJECTS = $(am_whatis_OBJECTS) +whatis_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_2) +am_zsoelim_OBJECTS = decompress.$(OBJEXT) globbing.$(OBJEXT) \ + manp.$(OBJEXT) zsoelim.$(OBJEXT) zsoelim_main.$(OBJEXT) +zsoelim_OBJECTS = $(am_zsoelim_OBJECTS) +zsoelim_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_2) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/accessdb.Po ./$(DEPDIR)/catman.Po \ + ./$(DEPDIR)/check_mandirs.Po ./$(DEPDIR)/compression.Po \ + ./$(DEPDIR)/decompress.Po ./$(DEPDIR)/descriptions.Po \ + ./$(DEPDIR)/descriptions_store.Po ./$(DEPDIR)/filenames.Po \ + ./$(DEPDIR)/globbing.Po ./$(DEPDIR)/globbing_test.Po \ + ./$(DEPDIR)/lexgrog.Po ./$(DEPDIR)/lexgrog_test.Po \ + ./$(DEPDIR)/man-recode.Po ./$(DEPDIR)/man.Po \ + ./$(DEPDIR)/manconv.Po ./$(DEPDIR)/manconv_client.Po \ + ./$(DEPDIR)/manconv_main.Po ./$(DEPDIR)/mandb.Po \ + ./$(DEPDIR)/manp.Po ./$(DEPDIR)/manpath.Po \ + ./$(DEPDIR)/straycats.Po ./$(DEPDIR)/ult_src.Po \ + ./$(DEPDIR)/whatis.Po ./$(DEPDIR)/zsoelim.Po \ + ./$(DEPDIR)/zsoelim_main.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ || +LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS) +LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS) +AM_V_LEX = $(am__v_LEX_@AM_V@) +am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@) +am__v_LEX_0 = @echo " LEX " $@; +am__v_LEX_1 = +YLWRAP = $(top_srcdir)/build-aux/ylwrap +SOURCES = $(accessdb_SOURCES) $(catman_SOURCES) $(globbing_SOURCES) \ + $(lexgrog_SOURCES) $(man_SOURCES) $(man_recode_SOURCES) \ + $(manconv_SOURCES) $(mandb_SOURCES) $(manpath_SOURCES) \ + $(whatis_SOURCES) $(zsoelim_SOURCES) +DIST_SOURCES = $(accessdb_SOURCES) $(catman_SOURCES) \ + $(globbing_SOURCES) $(lexgrog_SOURCES) $(man_SOURCES) \ + $(man_recode_SOURCES) $(manconv_SOURCES) $(mandb_SOURCES) \ + $(manpath_SOURCES) $(whatis_SOURCES) $(zsoelim_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(noinst_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/man_db.conf.in \ + $(top_srcdir)/build-aux/depcomp $(top_srcdir)/build-aux/ylwrap \ + lexgrog.c zsoelim.c +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +pkglibexecdir = $(libexecdir)/@PACKAGE@ +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALLOCA_H = @ALLOCA_H@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +ASM_SYMBOL_PREFIX = @ASM_SYMBOL_PREFIX@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ +BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ +BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ +BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ +BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DBLIBS = @DBLIBS@ +DBTYPE = @DBTYPE@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@ +EMULTIHOP_VALUE = @EMULTIHOP_VALUE@ +ENOLINK_HIDDEN = @ENOLINK_HIDDEN@ +ENOLINK_VALUE = @ENOLINK_VALUE@ +EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@ +EOVERFLOW_VALUE = @EOVERFLOW_VALUE@ +ERRNO_H = @ERRNO_H@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FLOAT_H = @FLOAT_H@ +FNMATCH_H = @FNMATCH_H@ +GETOPT_CDEFS_H = @GETOPT_CDEFS_H@ +GETOPT_H = @GETOPT_H@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GLIBC21 = @GLIBC21@ +GLOB_H = @GLOB_H@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNULIB_ACCEPT = @GNULIB_ACCEPT@ +GNULIB_ACCEPT4 = @GNULIB_ACCEPT4@ +GNULIB_ACCESS = @GNULIB_ACCESS@ +GNULIB_ALPHASORT = @GNULIB_ALPHASORT@ +GNULIB_ATOLL = @GNULIB_ATOLL@ +GNULIB_BIND = @GNULIB_BIND@ +GNULIB_BTOWC = @GNULIB_BTOWC@ +GNULIB_CALLOC_POSIX = @GNULIB_CALLOC_POSIX@ +GNULIB_CANONICALIZE_FILE_NAME = @GNULIB_CANONICALIZE_FILE_NAME@ +GNULIB_CHDIR = @GNULIB_CHDIR@ +GNULIB_CHOWN = @GNULIB_CHOWN@ +GNULIB_CLOSE = @GNULIB_CLOSE@ +GNULIB_CLOSEDIR = @GNULIB_CLOSEDIR@ +GNULIB_CONNECT = @GNULIB_CONNECT@ +GNULIB_COPY_FILE_RANGE = @GNULIB_COPY_FILE_RANGE@ +GNULIB_CREAT = @GNULIB_CREAT@ +GNULIB_CTIME = @GNULIB_CTIME@ +GNULIB_DIRFD = @GNULIB_DIRFD@ +GNULIB_DPRINTF = @GNULIB_DPRINTF@ +GNULIB_DUP = @GNULIB_DUP@ +GNULIB_DUP2 = @GNULIB_DUP2@ +GNULIB_DUP3 = @GNULIB_DUP3@ +GNULIB_DUPLOCALE = @GNULIB_DUPLOCALE@ +GNULIB_ENVIRON = @GNULIB_ENVIRON@ +GNULIB_EUIDACCESS = @GNULIB_EUIDACCESS@ +GNULIB_EXPLICIT_BZERO = @GNULIB_EXPLICIT_BZERO@ +GNULIB_FACCESSAT = @GNULIB_FACCESSAT@ +GNULIB_FCHDIR = @GNULIB_FCHDIR@ +GNULIB_FCHMODAT = @GNULIB_FCHMODAT@ +GNULIB_FCHOWNAT = @GNULIB_FCHOWNAT@ +GNULIB_FCLOSE = @GNULIB_FCLOSE@ +GNULIB_FCNTL = @GNULIB_FCNTL@ +GNULIB_FDATASYNC = @GNULIB_FDATASYNC@ +GNULIB_FDOPEN = @GNULIB_FDOPEN@ +GNULIB_FDOPENDIR = @GNULIB_FDOPENDIR@ +GNULIB_FFLUSH = @GNULIB_FFLUSH@ +GNULIB_FFS = @GNULIB_FFS@ +GNULIB_FFSL = @GNULIB_FFSL@ +GNULIB_FFSLL = @GNULIB_FFSLL@ +GNULIB_FGETC = @GNULIB_FGETC@ +GNULIB_FGETS = @GNULIB_FGETS@ +GNULIB_FLOCK = @GNULIB_FLOCK@ +GNULIB_FNMATCH = @GNULIB_FNMATCH@ +GNULIB_FOPEN = @GNULIB_FOPEN@ +GNULIB_FPRINTF = @GNULIB_FPRINTF@ +GNULIB_FPRINTF_POSIX = @GNULIB_FPRINTF_POSIX@ +GNULIB_FPURGE = @GNULIB_FPURGE@ +GNULIB_FPUTC = @GNULIB_FPUTC@ +GNULIB_FPUTS = @GNULIB_FPUTS@ +GNULIB_FREAD = @GNULIB_FREAD@ +GNULIB_FREOPEN = @GNULIB_FREOPEN@ +GNULIB_FSCANF = @GNULIB_FSCANF@ +GNULIB_FSEEK = @GNULIB_FSEEK@ +GNULIB_FSEEKO = @GNULIB_FSEEKO@ +GNULIB_FSTAT = @GNULIB_FSTAT@ +GNULIB_FSTATAT = @GNULIB_FSTATAT@ +GNULIB_FSYNC = @GNULIB_FSYNC@ +GNULIB_FTELL = @GNULIB_FTELL@ +GNULIB_FTELLO = @GNULIB_FTELLO@ +GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@ +GNULIB_FUTIMENS = @GNULIB_FUTIMENS@ +GNULIB_FWRITE = @GNULIB_FWRITE@ +GNULIB_GETC = @GNULIB_GETC@ +GNULIB_GETCHAR = @GNULIB_GETCHAR@ +GNULIB_GETCWD = @GNULIB_GETCWD@ +GNULIB_GETDELIM = @GNULIB_GETDELIM@ +GNULIB_GETDOMAINNAME = @GNULIB_GETDOMAINNAME@ +GNULIB_GETDTABLESIZE = @GNULIB_GETDTABLESIZE@ +GNULIB_GETENTROPY = @GNULIB_GETENTROPY@ +GNULIB_GETGROUPS = @GNULIB_GETGROUPS@ +GNULIB_GETHOSTNAME = @GNULIB_GETHOSTNAME@ +GNULIB_GETLINE = @GNULIB_GETLINE@ +GNULIB_GETLOADAVG = @GNULIB_GETLOADAVG@ +GNULIB_GETLOGIN = @GNULIB_GETLOGIN@ +GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@ +GNULIB_GETOPT_POSIX = @GNULIB_GETOPT_POSIX@ +GNULIB_GETPAGESIZE = @GNULIB_GETPAGESIZE@ +GNULIB_GETPASS = @GNULIB_GETPASS@ +GNULIB_GETPEERNAME = @GNULIB_GETPEERNAME@ +GNULIB_GETSOCKNAME = @GNULIB_GETSOCKNAME@ +GNULIB_GETSOCKOPT = @GNULIB_GETSOCKOPT@ +GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@ +GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@ +GNULIB_GETUSERSHELL = @GNULIB_GETUSERSHELL@ +GNULIB_GLOB = @GNULIB_GLOB@ +GNULIB_GL_UNISTD_H_GETOPT = @GNULIB_GL_UNISTD_H_GETOPT@ +GNULIB_GRANTPT = @GNULIB_GRANTPT@ +GNULIB_GROUP_MEMBER = @GNULIB_GROUP_MEMBER@ +GNULIB_IMAXABS = @GNULIB_IMAXABS@ +GNULIB_IMAXDIV = @GNULIB_IMAXDIV@ +GNULIB_IOCTL = @GNULIB_IOCTL@ +GNULIB_ISATTY = @GNULIB_ISATTY@ +GNULIB_ISBLANK = @GNULIB_ISBLANK@ +GNULIB_ISWBLANK = @GNULIB_ISWBLANK@ +GNULIB_ISWCTYPE = @GNULIB_ISWCTYPE@ +GNULIB_ISWDIGIT = @GNULIB_ISWDIGIT@ +GNULIB_ISWXDIGIT = @GNULIB_ISWXDIGIT@ +GNULIB_LCHMOD = @GNULIB_LCHMOD@ +GNULIB_LCHOWN = @GNULIB_LCHOWN@ +GNULIB_LINK = @GNULIB_LINK@ +GNULIB_LINKAT = @GNULIB_LINKAT@ +GNULIB_LISTEN = @GNULIB_LISTEN@ +GNULIB_LOCALECONV = @GNULIB_LOCALECONV@ +GNULIB_LOCALENAME = @GNULIB_LOCALENAME@ +GNULIB_LOCALTIME = @GNULIB_LOCALTIME@ +GNULIB_LSEEK = @GNULIB_LSEEK@ +GNULIB_LSTAT = @GNULIB_LSTAT@ +GNULIB_MALLOC_POSIX = @GNULIB_MALLOC_POSIX@ +GNULIB_MBRLEN = @GNULIB_MBRLEN@ +GNULIB_MBRTOWC = @GNULIB_MBRTOWC@ +GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@ +GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@ +GNULIB_MBSCHR = @GNULIB_MBSCHR@ +GNULIB_MBSCSPN = @GNULIB_MBSCSPN@ +GNULIB_MBSINIT = @GNULIB_MBSINIT@ +GNULIB_MBSLEN = @GNULIB_MBSLEN@ +GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@ +GNULIB_MBSNLEN = @GNULIB_MBSNLEN@ +GNULIB_MBSNRTOWCS = @GNULIB_MBSNRTOWCS@ +GNULIB_MBSPBRK = @GNULIB_MBSPBRK@ +GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@ +GNULIB_MBSRCHR = @GNULIB_MBSRCHR@ +GNULIB_MBSRTOWCS = @GNULIB_MBSRTOWCS@ +GNULIB_MBSSEP = @GNULIB_MBSSEP@ +GNULIB_MBSSPN = @GNULIB_MBSSPN@ +GNULIB_MBSSTR = @GNULIB_MBSSTR@ +GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@ +GNULIB_MBTOWC = @GNULIB_MBTOWC@ +GNULIB_MEMCHR = @GNULIB_MEMCHR@ +GNULIB_MEMMEM = @GNULIB_MEMMEM@ +GNULIB_MEMPCPY = @GNULIB_MEMPCPY@ +GNULIB_MEMRCHR = @GNULIB_MEMRCHR@ +GNULIB_MKDIRAT = @GNULIB_MKDIRAT@ +GNULIB_MKDTEMP = @GNULIB_MKDTEMP@ +GNULIB_MKFIFO = @GNULIB_MKFIFO@ +GNULIB_MKFIFOAT = @GNULIB_MKFIFOAT@ +GNULIB_MKNOD = @GNULIB_MKNOD@ +GNULIB_MKNODAT = @GNULIB_MKNODAT@ +GNULIB_MKOSTEMP = @GNULIB_MKOSTEMP@ +GNULIB_MKOSTEMPS = @GNULIB_MKOSTEMPS@ +GNULIB_MKSTEMP = @GNULIB_MKSTEMP@ +GNULIB_MKSTEMPS = @GNULIB_MKSTEMPS@ +GNULIB_MKTIME = @GNULIB_MKTIME@ +GNULIB_NANOSLEEP = @GNULIB_NANOSLEEP@ +GNULIB_NL_LANGINFO = @GNULIB_NL_LANGINFO@ +GNULIB_NONBLOCKING = @GNULIB_NONBLOCKING@ +GNULIB_OBSTACK_PRINTF = @GNULIB_OBSTACK_PRINTF@ +GNULIB_OBSTACK_PRINTF_POSIX = @GNULIB_OBSTACK_PRINTF_POSIX@ +GNULIB_OPEN = @GNULIB_OPEN@ +GNULIB_OPENAT = @GNULIB_OPENAT@ +GNULIB_OPENDIR = @GNULIB_OPENDIR@ +GNULIB_OVERRIDES_STRUCT_STAT = @GNULIB_OVERRIDES_STRUCT_STAT@ +GNULIB_OVERRIDES_WINT_T = @GNULIB_OVERRIDES_WINT_T@ +GNULIB_PCLOSE = @GNULIB_PCLOSE@ +GNULIB_PERROR = @GNULIB_PERROR@ +GNULIB_PIPE = @GNULIB_PIPE@ +GNULIB_PIPE2 = @GNULIB_PIPE2@ +GNULIB_POPEN = @GNULIB_POPEN@ +GNULIB_POSIX_OPENPT = @GNULIB_POSIX_OPENPT@ +GNULIB_PREAD = @GNULIB_PREAD@ +GNULIB_PRINTF = @GNULIB_PRINTF@ +GNULIB_PRINTF_POSIX = @GNULIB_PRINTF_POSIX@ +GNULIB_PSELECT = @GNULIB_PSELECT@ +GNULIB_PTHREAD_SIGMASK = @GNULIB_PTHREAD_SIGMASK@ +GNULIB_PTSNAME = @GNULIB_PTSNAME@ +GNULIB_PTSNAME_R = @GNULIB_PTSNAME_R@ +GNULIB_PUTC = @GNULIB_PUTC@ +GNULIB_PUTCHAR = @GNULIB_PUTCHAR@ +GNULIB_PUTENV = @GNULIB_PUTENV@ +GNULIB_PUTS = @GNULIB_PUTS@ +GNULIB_PWRITE = @GNULIB_PWRITE@ +GNULIB_QSORT_R = @GNULIB_QSORT_R@ +GNULIB_RAISE = @GNULIB_RAISE@ +GNULIB_RANDOM = @GNULIB_RANDOM@ +GNULIB_RANDOM_R = @GNULIB_RANDOM_R@ +GNULIB_RAWMEMCHR = @GNULIB_RAWMEMCHR@ +GNULIB_READ = @GNULIB_READ@ +GNULIB_READDIR = @GNULIB_READDIR@ +GNULIB_READLINK = @GNULIB_READLINK@ +GNULIB_READLINKAT = @GNULIB_READLINKAT@ +GNULIB_REALLOCARRAY = @GNULIB_REALLOCARRAY@ +GNULIB_REALLOC_POSIX = @GNULIB_REALLOC_POSIX@ +GNULIB_REALPATH = @GNULIB_REALPATH@ +GNULIB_RECV = @GNULIB_RECV@ +GNULIB_RECVFROM = @GNULIB_RECVFROM@ +GNULIB_REMOVE = @GNULIB_REMOVE@ +GNULIB_RENAME = @GNULIB_RENAME@ +GNULIB_RENAMEAT = @GNULIB_RENAMEAT@ +GNULIB_REWINDDIR = @GNULIB_REWINDDIR@ +GNULIB_RMDIR = @GNULIB_RMDIR@ +GNULIB_RPMATCH = @GNULIB_RPMATCH@ +GNULIB_SCANDIR = @GNULIB_SCANDIR@ +GNULIB_SCANF = @GNULIB_SCANF@ +GNULIB_SECURE_GETENV = @GNULIB_SECURE_GETENV@ +GNULIB_SELECT = @GNULIB_SELECT@ +GNULIB_SEND = @GNULIB_SEND@ +GNULIB_SENDTO = @GNULIB_SENDTO@ +GNULIB_SETENV = @GNULIB_SETENV@ +GNULIB_SETHOSTNAME = @GNULIB_SETHOSTNAME@ +GNULIB_SETLOCALE = @GNULIB_SETLOCALE@ +GNULIB_SETLOCALE_NULL = @GNULIB_SETLOCALE_NULL@ +GNULIB_SETSOCKOPT = @GNULIB_SETSOCKOPT@ +GNULIB_SHUTDOWN = @GNULIB_SHUTDOWN@ +GNULIB_SIGACTION = @GNULIB_SIGACTION@ +GNULIB_SIGNAL_H_SIGPIPE = @GNULIB_SIGNAL_H_SIGPIPE@ +GNULIB_SIGPROCMASK = @GNULIB_SIGPROCMASK@ +GNULIB_SLEEP = @GNULIB_SLEEP@ +GNULIB_SNPRINTF = @GNULIB_SNPRINTF@ +GNULIB_SOCKET = @GNULIB_SOCKET@ +GNULIB_SPRINTF_POSIX = @GNULIB_SPRINTF_POSIX@ +GNULIB_STAT = @GNULIB_STAT@ +GNULIB_STDIO_H_NONBLOCKING = @GNULIB_STDIO_H_NONBLOCKING@ +GNULIB_STDIO_H_SIGPIPE = @GNULIB_STDIO_H_SIGPIPE@ +GNULIB_STPCPY = @GNULIB_STPCPY@ +GNULIB_STPNCPY = @GNULIB_STPNCPY@ +GNULIB_STRCASESTR = @GNULIB_STRCASESTR@ +GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@ +GNULIB_STRDUP = @GNULIB_STRDUP@ +GNULIB_STRERROR = @GNULIB_STRERROR@ +GNULIB_STRERROR_R = @GNULIB_STRERROR_R@ +GNULIB_STRFTIME = @GNULIB_STRFTIME@ +GNULIB_STRNCAT = @GNULIB_STRNCAT@ +GNULIB_STRNDUP = @GNULIB_STRNDUP@ +GNULIB_STRNLEN = @GNULIB_STRNLEN@ +GNULIB_STRPBRK = @GNULIB_STRPBRK@ +GNULIB_STRPTIME = @GNULIB_STRPTIME@ +GNULIB_STRSEP = @GNULIB_STRSEP@ +GNULIB_STRSIGNAL = @GNULIB_STRSIGNAL@ +GNULIB_STRSTR = @GNULIB_STRSTR@ +GNULIB_STRTOD = @GNULIB_STRTOD@ +GNULIB_STRTOIMAX = @GNULIB_STRTOIMAX@ +GNULIB_STRTOK_R = @GNULIB_STRTOK_R@ +GNULIB_STRTOLD = @GNULIB_STRTOLD@ +GNULIB_STRTOLL = @GNULIB_STRTOLL@ +GNULIB_STRTOULL = @GNULIB_STRTOULL@ +GNULIB_STRTOUMAX = @GNULIB_STRTOUMAX@ +GNULIB_STRVERSCMP = @GNULIB_STRVERSCMP@ +GNULIB_SYMLINK = @GNULIB_SYMLINK@ +GNULIB_SYMLINKAT = @GNULIB_SYMLINKAT@ +GNULIB_SYSTEM_POSIX = @GNULIB_SYSTEM_POSIX@ +GNULIB_TIMEGM = @GNULIB_TIMEGM@ +GNULIB_TIME_R = @GNULIB_TIME_R@ +GNULIB_TIME_RZ = @GNULIB_TIME_RZ@ +GNULIB_TMPFILE = @GNULIB_TMPFILE@ +GNULIB_TOWCTRANS = @GNULIB_TOWCTRANS@ +GNULIB_TRUNCATE = @GNULIB_TRUNCATE@ +GNULIB_TTYNAME_R = @GNULIB_TTYNAME_R@ +GNULIB_TZSET = @GNULIB_TZSET@ +GNULIB_UNISTD_H_NONBLOCKING = @GNULIB_UNISTD_H_NONBLOCKING@ +GNULIB_UNISTD_H_SIGPIPE = @GNULIB_UNISTD_H_SIGPIPE@ +GNULIB_UNLINK = @GNULIB_UNLINK@ +GNULIB_UNLINKAT = @GNULIB_UNLINKAT@ +GNULIB_UNLOCKPT = @GNULIB_UNLOCKPT@ +GNULIB_UNSETENV = @GNULIB_UNSETENV@ +GNULIB_USLEEP = @GNULIB_USLEEP@ +GNULIB_UTIME = @GNULIB_UTIME@ +GNULIB_UTIMENSAT = @GNULIB_UTIMENSAT@ +GNULIB_VASPRINTF = @GNULIB_VASPRINTF@ +GNULIB_VDPRINTF = @GNULIB_VDPRINTF@ +GNULIB_VFPRINTF = @GNULIB_VFPRINTF@ +GNULIB_VFPRINTF_POSIX = @GNULIB_VFPRINTF_POSIX@ +GNULIB_VFSCANF = @GNULIB_VFSCANF@ +GNULIB_VPRINTF = @GNULIB_VPRINTF@ +GNULIB_VPRINTF_POSIX = @GNULIB_VPRINTF_POSIX@ +GNULIB_VSCANF = @GNULIB_VSCANF@ +GNULIB_VSNPRINTF = @GNULIB_VSNPRINTF@ +GNULIB_VSPRINTF_POSIX = @GNULIB_VSPRINTF_POSIX@ +GNULIB_WCPCPY = @GNULIB_WCPCPY@ +GNULIB_WCPNCPY = @GNULIB_WCPNCPY@ +GNULIB_WCRTOMB = @GNULIB_WCRTOMB@ +GNULIB_WCSCASECMP = @GNULIB_WCSCASECMP@ +GNULIB_WCSCAT = @GNULIB_WCSCAT@ +GNULIB_WCSCHR = @GNULIB_WCSCHR@ +GNULIB_WCSCMP = @GNULIB_WCSCMP@ +GNULIB_WCSCOLL = @GNULIB_WCSCOLL@ +GNULIB_WCSCPY = @GNULIB_WCSCPY@ +GNULIB_WCSCSPN = @GNULIB_WCSCSPN@ +GNULIB_WCSDUP = @GNULIB_WCSDUP@ +GNULIB_WCSFTIME = @GNULIB_WCSFTIME@ +GNULIB_WCSLEN = @GNULIB_WCSLEN@ +GNULIB_WCSNCASECMP = @GNULIB_WCSNCASECMP@ +GNULIB_WCSNCAT = @GNULIB_WCSNCAT@ +GNULIB_WCSNCMP = @GNULIB_WCSNCMP@ +GNULIB_WCSNCPY = @GNULIB_WCSNCPY@ +GNULIB_WCSNLEN = @GNULIB_WCSNLEN@ +GNULIB_WCSNRTOMBS = @GNULIB_WCSNRTOMBS@ +GNULIB_WCSPBRK = @GNULIB_WCSPBRK@ +GNULIB_WCSRCHR = @GNULIB_WCSRCHR@ +GNULIB_WCSRTOMBS = @GNULIB_WCSRTOMBS@ +GNULIB_WCSSPN = @GNULIB_WCSSPN@ +GNULIB_WCSSTR = @GNULIB_WCSSTR@ +GNULIB_WCSTOK = @GNULIB_WCSTOK@ +GNULIB_WCSWIDTH = @GNULIB_WCSWIDTH@ +GNULIB_WCSXFRM = @GNULIB_WCSXFRM@ +GNULIB_WCTOB = @GNULIB_WCTOB@ +GNULIB_WCTOMB = @GNULIB_WCTOMB@ +GNULIB_WCTRANS = @GNULIB_WCTRANS@ +GNULIB_WCTYPE = @GNULIB_WCTYPE@ +GNULIB_WCWIDTH = @GNULIB_WCWIDTH@ +GNULIB_WMEMCHR = @GNULIB_WMEMCHR@ +GNULIB_WMEMCMP = @GNULIB_WMEMCMP@ +GNULIB_WMEMCPY = @GNULIB_WMEMCPY@ +GNULIB_WMEMMOVE = @GNULIB_WMEMMOVE@ +GNULIB_WMEMPCPY = @GNULIB_WMEMPCPY@ +GNULIB_WMEMSET = @GNULIB_WMEMSET@ +GNULIB_WRITE = @GNULIB_WRITE@ +GNULIB__EXIT = @GNULIB__EXIT@ +GREP = @GREP@ +HAVE_ACCEPT4 = @HAVE_ACCEPT4@ +HAVE_ALLOCA_H = @HAVE_ALLOCA_H@ +HAVE_ALPHASORT = @HAVE_ALPHASORT@ +HAVE_ATOLL = @HAVE_ATOLL@ +HAVE_BTOWC = @HAVE_BTOWC@ +HAVE_C99_STDINT_H = @HAVE_C99_STDINT_H@ +HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@ +HAVE_CHOWN = @HAVE_CHOWN@ +HAVE_CLOSEDIR = @HAVE_CLOSEDIR@ +HAVE_COPY_FILE_RANGE = @HAVE_COPY_FILE_RANGE@ +HAVE_CRTDEFS_H = @HAVE_CRTDEFS_H@ +HAVE_DECL_DIRFD = @HAVE_DECL_DIRFD@ +HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@ +HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@ +HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@ +HAVE_DECL_FDOPENDIR = @HAVE_DECL_FDOPENDIR@ +HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@ +HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@ +HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@ +HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@ +HAVE_DECL_GETDOMAINNAME = @HAVE_DECL_GETDOMAINNAME@ +HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@ +HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@ +HAVE_DECL_GETLOGIN = @HAVE_DECL_GETLOGIN@ +HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@ +HAVE_DECL_GETPAGESIZE = @HAVE_DECL_GETPAGESIZE@ +HAVE_DECL_GETUSERSHELL = @HAVE_DECL_GETUSERSHELL@ +HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@ +HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@ +HAVE_DECL_INITSTATE = @HAVE_DECL_INITSTATE@ +HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@ +HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@ +HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@ +HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@ +HAVE_DECL_SETENV = @HAVE_DECL_SETENV@ +HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@ +HAVE_DECL_SETSTATE = @HAVE_DECL_SETSTATE@ +HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@ +HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@ +HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@ +HAVE_DECL_STRNCASECMP = @HAVE_DECL_STRNCASECMP@ +HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@ +HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@ +HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@ +HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@ +HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@ +HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@ +HAVE_DECL_TRUNCATE = @HAVE_DECL_TRUNCATE@ +HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@ +HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@ +HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@ +HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@ +HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@ +HAVE_DIRENT_H = @HAVE_DIRENT_H@ +HAVE_DPRINTF = @HAVE_DPRINTF@ +HAVE_DUP2 = @HAVE_DUP2@ +HAVE_DUP3 = @HAVE_DUP3@ +HAVE_DUPLOCALE = @HAVE_DUPLOCALE@ +HAVE_EUIDACCESS = @HAVE_EUIDACCESS@ +HAVE_EXPLICIT_BZERO = @HAVE_EXPLICIT_BZERO@ +HAVE_FACCESSAT = @HAVE_FACCESSAT@ +HAVE_FCHDIR = @HAVE_FCHDIR@ +HAVE_FCHMODAT = @HAVE_FCHMODAT@ +HAVE_FCHOWNAT = @HAVE_FCHOWNAT@ +HAVE_FCNTL = @HAVE_FCNTL@ +HAVE_FDATASYNC = @HAVE_FDATASYNC@ +HAVE_FDOPENDIR = @HAVE_FDOPENDIR@ +HAVE_FEATURES_H = @HAVE_FEATURES_H@ +HAVE_FFS = @HAVE_FFS@ +HAVE_FFSL = @HAVE_FFSL@ +HAVE_FFSLL = @HAVE_FFSLL@ +HAVE_FLOCK = @HAVE_FLOCK@ +HAVE_FNMATCH = @HAVE_FNMATCH@ +HAVE_FNMATCH_H = @HAVE_FNMATCH_H@ +HAVE_FREELOCALE = @HAVE_FREELOCALE@ +HAVE_FSEEKO = @HAVE_FSEEKO@ +HAVE_FSTATAT = @HAVE_FSTATAT@ +HAVE_FSYNC = @HAVE_FSYNC@ +HAVE_FTELLO = @HAVE_FTELLO@ +HAVE_FTRUNCATE = @HAVE_FTRUNCATE@ +HAVE_FUTIMENS = @HAVE_FUTIMENS@ +HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@ +HAVE_GETENTROPY = @HAVE_GETENTROPY@ +HAVE_GETGROUPS = @HAVE_GETGROUPS@ +HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@ +HAVE_GETLOGIN = @HAVE_GETLOGIN@ +HAVE_GETOPT_H = @HAVE_GETOPT_H@ +HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@ +HAVE_GETPASS = @HAVE_GETPASS@ +HAVE_GETSUBOPT = @HAVE_GETSUBOPT@ +HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@ +HAVE_GLOB = @HAVE_GLOB@ +HAVE_GLOB_H = @HAVE_GLOB_H@ +HAVE_GLOB_PATTERN_P = @HAVE_GLOB_PATTERN_P@ +HAVE_GRANTPT = @HAVE_GRANTPT@ +HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@ +HAVE_IMAXDIV_T = @HAVE_IMAXDIV_T@ +HAVE_INITSTATE = @HAVE_INITSTATE@ +HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ +HAVE_ISBLANK = @HAVE_ISBLANK@ +HAVE_ISWBLANK = @HAVE_ISWBLANK@ +HAVE_ISWCNTRL = @HAVE_ISWCNTRL@ +HAVE_LANGINFO_ALTMON = @HAVE_LANGINFO_ALTMON@ +HAVE_LANGINFO_CODESET = @HAVE_LANGINFO_CODESET@ +HAVE_LANGINFO_ERA = @HAVE_LANGINFO_ERA@ +HAVE_LANGINFO_H = @HAVE_LANGINFO_H@ +HAVE_LANGINFO_T_FMT_AMPM = @HAVE_LANGINFO_T_FMT_AMPM@ +HAVE_LANGINFO_YESEXPR = @HAVE_LANGINFO_YESEXPR@ +HAVE_LCHMOD = @HAVE_LCHMOD@ +HAVE_LCHOWN = @HAVE_LCHOWN@ +HAVE_LINK = @HAVE_LINK@ +HAVE_LINKAT = @HAVE_LINKAT@ +HAVE_LSTAT = @HAVE_LSTAT@ +HAVE_MAX_ALIGN_T = @HAVE_MAX_ALIGN_T@ +HAVE_MBRLEN = @HAVE_MBRLEN@ +HAVE_MBRTOWC = @HAVE_MBRTOWC@ +HAVE_MBSINIT = @HAVE_MBSINIT@ +HAVE_MBSLEN = @HAVE_MBSLEN@ +HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@ +HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@ +HAVE_MBTOWC = @HAVE_MBTOWC@ +HAVE_MEMCHR = @HAVE_MEMCHR@ +HAVE_MEMPCPY = @HAVE_MEMPCPY@ +HAVE_MKDIRAT = @HAVE_MKDIRAT@ +HAVE_MKDTEMP = @HAVE_MKDTEMP@ +HAVE_MKFIFO = @HAVE_MKFIFO@ +HAVE_MKFIFOAT = @HAVE_MKFIFOAT@ +HAVE_MKNOD = @HAVE_MKNOD@ +HAVE_MKNODAT = @HAVE_MKNODAT@ +HAVE_MKOSTEMP = @HAVE_MKOSTEMP@ +HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@ +HAVE_MKSTEMP = @HAVE_MKSTEMP@ +HAVE_MKSTEMPS = @HAVE_MKSTEMPS@ +HAVE_MSVC_INVALID_PARAMETER_HANDLER = @HAVE_MSVC_INVALID_PARAMETER_HANDLER@ +HAVE_NANOSLEEP = @HAVE_NANOSLEEP@ +HAVE_NEWLOCALE = @HAVE_NEWLOCALE@ +HAVE_NL_LANGINFO = @HAVE_NL_LANGINFO@ +HAVE_OPENAT = @HAVE_OPENAT@ +HAVE_OPENDIR = @HAVE_OPENDIR@ +HAVE_OS_H = @HAVE_OS_H@ +HAVE_PCLOSE = @HAVE_PCLOSE@ +HAVE_PIPE = @HAVE_PIPE@ +HAVE_PIPE2 = @HAVE_PIPE2@ +HAVE_POPEN = @HAVE_POPEN@ +HAVE_POSIX_OPENPT = @HAVE_POSIX_OPENPT@ +HAVE_POSIX_SIGNALBLOCKING = @HAVE_POSIX_SIGNALBLOCKING@ +HAVE_PREAD = @HAVE_PREAD@ +HAVE_PSELECT = @HAVE_PSELECT@ +HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@ +HAVE_PTSNAME = @HAVE_PTSNAME@ +HAVE_PTSNAME_R = @HAVE_PTSNAME_R@ +HAVE_PWRITE = @HAVE_PWRITE@ +HAVE_QSORT_R = @HAVE_QSORT_R@ +HAVE_RAISE = @HAVE_RAISE@ +HAVE_RANDOM = @HAVE_RANDOM@ +HAVE_RANDOM_H = @HAVE_RANDOM_H@ +HAVE_RANDOM_R = @HAVE_RANDOM_R@ +HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@ +HAVE_READDIR = @HAVE_READDIR@ +HAVE_READLINK = @HAVE_READLINK@ +HAVE_READLINKAT = @HAVE_READLINKAT@ +HAVE_REALLOCARRAY = @HAVE_REALLOCARRAY@ +HAVE_REALPATH = @HAVE_REALPATH@ +HAVE_RENAMEAT = @HAVE_RENAMEAT@ +HAVE_REWINDDIR = @HAVE_REWINDDIR@ +HAVE_RPMATCH = @HAVE_RPMATCH@ +HAVE_SA_FAMILY_T = @HAVE_SA_FAMILY_T@ +HAVE_SCANDIR = @HAVE_SCANDIR@ +HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@ +HAVE_SETENV = @HAVE_SETENV@ +HAVE_SETHOSTNAME = @HAVE_SETHOSTNAME@ +HAVE_SETSTATE = @HAVE_SETSTATE@ +HAVE_SIGACTION = @HAVE_SIGACTION@ +HAVE_SIGHANDLER_T = @HAVE_SIGHANDLER_T@ +HAVE_SIGINFO_T = @HAVE_SIGINFO_T@ +HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ +HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ +HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ +HAVE_SIGSET_T = @HAVE_SIGSET_T@ +HAVE_SLEEP = @HAVE_SLEEP@ +HAVE_STDINT_H = @HAVE_STDINT_H@ +HAVE_STPCPY = @HAVE_STPCPY@ +HAVE_STPNCPY = @HAVE_STPNCPY@ +HAVE_STRCASECMP = @HAVE_STRCASECMP@ +HAVE_STRCASESTR = @HAVE_STRCASESTR@ +HAVE_STRCHRNUL = @HAVE_STRCHRNUL@ +HAVE_STRINGS_H = @HAVE_STRINGS_H@ +HAVE_STRPBRK = @HAVE_STRPBRK@ +HAVE_STRPTIME = @HAVE_STRPTIME@ +HAVE_STRSEP = @HAVE_STRSEP@ +HAVE_STRTOD = @HAVE_STRTOD@ +HAVE_STRTOLD = @HAVE_STRTOLD@ +HAVE_STRTOLL = @HAVE_STRTOLL@ +HAVE_STRTOULL = @HAVE_STRTOULL@ +HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@ +HAVE_STRUCT_SIGACTION_SA_SIGACTION = @HAVE_STRUCT_SIGACTION_SA_SIGACTION@ +HAVE_STRUCT_SOCKADDR_STORAGE = @HAVE_STRUCT_SOCKADDR_STORAGE@ +HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY = @HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY@ +HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@ +HAVE_STRVERSCMP = @HAVE_STRVERSCMP@ +HAVE_SYMLINK = @HAVE_SYMLINK@ +HAVE_SYMLINKAT = @HAVE_SYMLINKAT@ +HAVE_SYSEXITS_H = @HAVE_SYSEXITS_H@ +HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ +HAVE_SYS_CDEFS_H = @HAVE_SYS_CDEFS_H@ +HAVE_SYS_FILE_H = @HAVE_SYS_FILE_H@ +HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ +HAVE_SYS_IOCTL_H = @HAVE_SYS_IOCTL_H@ +HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@ +HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@ +HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@ +HAVE_SYS_SOCKET_H = @HAVE_SYS_SOCKET_H@ +HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@ +HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ +HAVE_SYS_UIO_H = @HAVE_SYS_UIO_H@ +HAVE_TIMEGM = @HAVE_TIMEGM@ +HAVE_TIMEZONE_T = @HAVE_TIMEZONE_T@ +HAVE_TYPE_VOLATILE_SIG_ATOMIC_T = @HAVE_TYPE_VOLATILE_SIG_ATOMIC_T@ +HAVE_TZSET = @HAVE_TZSET@ +HAVE_UNISTD_H = @HAVE_UNISTD_H@ +HAVE_UNLINKAT = @HAVE_UNLINKAT@ +HAVE_UNLOCKPT = @HAVE_UNLOCKPT@ +HAVE_USLEEP = @HAVE_USLEEP@ +HAVE_UTIME = @HAVE_UTIME@ +HAVE_UTIMENSAT = @HAVE_UTIMENSAT@ +HAVE_UTIME_H = @HAVE_UTIME_H@ +HAVE_VASPRINTF = @HAVE_VASPRINTF@ +HAVE_VDPRINTF = @HAVE_VDPRINTF@ +HAVE_VISIBILITY = @HAVE_VISIBILITY@ +HAVE_WCHAR_H = @HAVE_WCHAR_H@ +HAVE_WCHAR_T = @HAVE_WCHAR_T@ +HAVE_WCPCPY = @HAVE_WCPCPY@ +HAVE_WCPNCPY = @HAVE_WCPNCPY@ +HAVE_WCRTOMB = @HAVE_WCRTOMB@ +HAVE_WCSCASECMP = @HAVE_WCSCASECMP@ +HAVE_WCSCAT = @HAVE_WCSCAT@ +HAVE_WCSCHR = @HAVE_WCSCHR@ +HAVE_WCSCMP = @HAVE_WCSCMP@ +HAVE_WCSCOLL = @HAVE_WCSCOLL@ +HAVE_WCSCPY = @HAVE_WCSCPY@ +HAVE_WCSCSPN = @HAVE_WCSCSPN@ +HAVE_WCSDUP = @HAVE_WCSDUP@ +HAVE_WCSFTIME = @HAVE_WCSFTIME@ +HAVE_WCSLEN = @HAVE_WCSLEN@ +HAVE_WCSNCASECMP = @HAVE_WCSNCASECMP@ +HAVE_WCSNCAT = @HAVE_WCSNCAT@ +HAVE_WCSNCMP = @HAVE_WCSNCMP@ +HAVE_WCSNCPY = @HAVE_WCSNCPY@ +HAVE_WCSNLEN = @HAVE_WCSNLEN@ +HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@ +HAVE_WCSPBRK = @HAVE_WCSPBRK@ +HAVE_WCSRCHR = @HAVE_WCSRCHR@ +HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@ +HAVE_WCSSPN = @HAVE_WCSSPN@ +HAVE_WCSSTR = @HAVE_WCSSTR@ +HAVE_WCSTOK = @HAVE_WCSTOK@ +HAVE_WCSWIDTH = @HAVE_WCSWIDTH@ +HAVE_WCSXFRM = @HAVE_WCSXFRM@ +HAVE_WCTRANS_T = @HAVE_WCTRANS_T@ +HAVE_WCTYPE_H = @HAVE_WCTYPE_H@ +HAVE_WCTYPE_T = @HAVE_WCTYPE_T@ +HAVE_WINSOCK2_H = @HAVE_WINSOCK2_H@ +HAVE_WINT_T = @HAVE_WINT_T@ +HAVE_WMEMCHR = @HAVE_WMEMCHR@ +HAVE_WMEMCMP = @HAVE_WMEMCMP@ +HAVE_WMEMCPY = @HAVE_WMEMCPY@ +HAVE_WMEMMOVE = @HAVE_WMEMMOVE@ +HAVE_WMEMPCPY = @HAVE_WMEMPCPY@ +HAVE_WMEMSET = @HAVE_WMEMSET@ +HAVE_WS2TCPIP_H = @HAVE_WS2TCPIP_H@ +HAVE_XLOCALE_H = @HAVE_XLOCALE_H@ +HAVE__BOOL = @HAVE__BOOL@ +HAVE__EXIT = @HAVE__EXIT@ +IGNORE_UNUSED_LIBRARIES_CFLAGS = @IGNORE_UNUSED_LIBRARIES_CFLAGS@ +INCLUDE_NEXT = @INCLUDE_NEXT@ +INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@ +INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBCOMPRESS = @LIBCOMPRESS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMULTITHREAD = @LIBMULTITHREAD@ +LIBOBJS = @LIBOBJS@ +LIBPMULTITHREAD = @LIBPMULTITHREAD@ +LIBPTHREAD = @LIBPTHREAD@ +LIBS = @LIBS@ +LIBSOCKET = @LIBSOCKET@ +LIBSTDTHREAD = @LIBSTDTHREAD@ +LIBTHREAD = @LIBTHREAD@ +LIBTOOL = @LIBTOOL@ +LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@ +LIB_GETLOGIN = @LIB_GETLOGIN@ +LIB_HARD_LOCALE = @LIB_HARD_LOCALE@ +LIB_MBRTOWC = @LIB_MBRTOWC@ +LIB_NANOSLEEP = @LIB_NANOSLEEP@ +LIB_NL_LANGINFO = @LIB_NL_LANGINFO@ +LIB_SCHED_YIELD = @LIB_SCHED_YIELD@ +LIB_SELECT = @LIB_SELECT@ +LIB_SETLOCALE_NULL = @LIB_SETLOCALE_NULL@ +LIMITS_H = @LIMITS_H@ +LINGUAS = @LINGUAS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@ +LOCALE_FR = @LOCALE_FR@ +LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@ +LOCALE_JA = @LOCALE_JA@ +LOCALE_ZH_CN = @LOCALE_ZH_CN@ +LTALLOCA = @LTALLOCA@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBMULTITHREAD = @LTLIBMULTITHREAD@ +LTLIBOBJS = @LTLIBOBJS@ +LTLIBTHREAD = @LTLIBTHREAD@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANDIR_LAYOUT = @MANDIR_LAYOUT@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAN_SUBDIRS = @MAN_SUBDIRS@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NEXT_AS_FIRST_DIRECTIVE_CTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_CTYPE_H@ +NEXT_AS_FIRST_DIRECTIVE_DIRENT_H = @NEXT_AS_FIRST_DIRECTIVE_DIRENT_H@ +NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@ +NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@ +NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@ +NEXT_AS_FIRST_DIRECTIVE_FNMATCH_H = @NEXT_AS_FIRST_DIRECTIVE_FNMATCH_H@ +NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@ +NEXT_AS_FIRST_DIRECTIVE_GLOB_H = @NEXT_AS_FIRST_DIRECTIVE_GLOB_H@ +NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@ +NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H = @NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H@ +NEXT_AS_FIRST_DIRECTIVE_LIMITS_H = @NEXT_AS_FIRST_DIRECTIVE_LIMITS_H@ +NEXT_AS_FIRST_DIRECTIVE_LOCALE_H = @NEXT_AS_FIRST_DIRECTIVE_LOCALE_H@ +NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@ +NEXT_AS_FIRST_DIRECTIVE_STDARG_H = @NEXT_AS_FIRST_DIRECTIVE_STDARG_H@ +NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@ +NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@ +NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@ +NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@ +NEXT_AS_FIRST_DIRECTIVE_STRINGS_H = @NEXT_AS_FIRST_DIRECTIVE_STRINGS_H@ +NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@ +NEXT_AS_FIRST_DIRECTIVE_SYSEXITS_H = @NEXT_AS_FIRST_DIRECTIVE_SYSEXITS_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_FILE_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_FILE_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H@ +NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@ +NEXT_AS_FIRST_DIRECTIVE_UTIME_H = @NEXT_AS_FIRST_DIRECTIVE_UTIME_H@ +NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@ +NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H@ +NEXT_CTYPE_H = @NEXT_CTYPE_H@ +NEXT_DIRENT_H = @NEXT_DIRENT_H@ +NEXT_ERRNO_H = @NEXT_ERRNO_H@ +NEXT_FCNTL_H = @NEXT_FCNTL_H@ +NEXT_FLOAT_H = @NEXT_FLOAT_H@ +NEXT_FNMATCH_H = @NEXT_FNMATCH_H@ +NEXT_GETOPT_H = @NEXT_GETOPT_H@ +NEXT_GLOB_H = @NEXT_GLOB_H@ +NEXT_INTTYPES_H = @NEXT_INTTYPES_H@ +NEXT_LANGINFO_H = @NEXT_LANGINFO_H@ +NEXT_LIMITS_H = @NEXT_LIMITS_H@ +NEXT_LOCALE_H = @NEXT_LOCALE_H@ +NEXT_SIGNAL_H = @NEXT_SIGNAL_H@ +NEXT_STDARG_H = @NEXT_STDARG_H@ +NEXT_STDDEF_H = @NEXT_STDDEF_H@ +NEXT_STDINT_H = @NEXT_STDINT_H@ +NEXT_STDIO_H = @NEXT_STDIO_H@ +NEXT_STDLIB_H = @NEXT_STDLIB_H@ +NEXT_STRINGS_H = @NEXT_STRINGS_H@ +NEXT_STRING_H = @NEXT_STRING_H@ +NEXT_SYSEXITS_H = @NEXT_SYSEXITS_H@ +NEXT_SYS_FILE_H = @NEXT_SYS_FILE_H@ +NEXT_SYS_IOCTL_H = @NEXT_SYS_IOCTL_H@ +NEXT_SYS_SELECT_H = @NEXT_SYS_SELECT_H@ +NEXT_SYS_SOCKET_H = @NEXT_SYS_SOCKET_H@ +NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@ +NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@ +NEXT_SYS_TYPES_H = @NEXT_SYS_TYPES_H@ +NEXT_SYS_UIO_H = @NEXT_SYS_UIO_H@ +NEXT_TIME_H = @NEXT_TIME_H@ +NEXT_UNISTD_H = @NEXT_UNISTD_H@ +NEXT_UTIME_H = @NEXT_UTIME_H@ +NEXT_WCHAR_H = @NEXT_WCHAR_H@ +NEXT_WCTYPE_H = @NEXT_WCTYPE_H@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PO4A = @PO4A@ +POSUB = @POSUB@ +PRAGMA_COLUMNS = @PRAGMA_COLUMNS@ +PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@ +PRIPTR_PREFIX = @PRIPTR_PREFIX@ +PRI_MACROS_BROKEN = @PRI_MACROS_BROKEN@ +PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@ +PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ +RANLIB = @RANLIB@ +REPLACE_ACCESS = @REPLACE_ACCESS@ +REPLACE_BTOWC = @REPLACE_BTOWC@ +REPLACE_CALLOC = @REPLACE_CALLOC@ +REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@ +REPLACE_CHOWN = @REPLACE_CHOWN@ +REPLACE_CLOSE = @REPLACE_CLOSE@ +REPLACE_CLOSEDIR = @REPLACE_CLOSEDIR@ +REPLACE_CREAT = @REPLACE_CREAT@ +REPLACE_CTIME = @REPLACE_CTIME@ +REPLACE_DIRFD = @REPLACE_DIRFD@ +REPLACE_DPRINTF = @REPLACE_DPRINTF@ +REPLACE_DUP = @REPLACE_DUP@ +REPLACE_DUP2 = @REPLACE_DUP2@ +REPLACE_DUPLOCALE = @REPLACE_DUPLOCALE@ +REPLACE_FACCESSAT = @REPLACE_FACCESSAT@ +REPLACE_FCHMODAT = @REPLACE_FCHMODAT@ +REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@ +REPLACE_FCLOSE = @REPLACE_FCLOSE@ +REPLACE_FCNTL = @REPLACE_FCNTL@ +REPLACE_FDOPEN = @REPLACE_FDOPEN@ +REPLACE_FDOPENDIR = @REPLACE_FDOPENDIR@ +REPLACE_FFLUSH = @REPLACE_FFLUSH@ +REPLACE_FNMATCH = @REPLACE_FNMATCH@ +REPLACE_FOPEN = @REPLACE_FOPEN@ +REPLACE_FPRINTF = @REPLACE_FPRINTF@ +REPLACE_FPURGE = @REPLACE_FPURGE@ +REPLACE_FREELOCALE = @REPLACE_FREELOCALE@ +REPLACE_FREOPEN = @REPLACE_FREOPEN@ +REPLACE_FSEEK = @REPLACE_FSEEK@ +REPLACE_FSEEKO = @REPLACE_FSEEKO@ +REPLACE_FSTAT = @REPLACE_FSTAT@ +REPLACE_FSTATAT = @REPLACE_FSTATAT@ +REPLACE_FTELL = @REPLACE_FTELL@ +REPLACE_FTELLO = @REPLACE_FTELLO@ +REPLACE_FTRUNCATE = @REPLACE_FTRUNCATE@ +REPLACE_FUTIMENS = @REPLACE_FUTIMENS@ +REPLACE_GETCWD = @REPLACE_GETCWD@ +REPLACE_GETDELIM = @REPLACE_GETDELIM@ +REPLACE_GETDOMAINNAME = @REPLACE_GETDOMAINNAME@ +REPLACE_GETDTABLESIZE = @REPLACE_GETDTABLESIZE@ +REPLACE_GETGROUPS = @REPLACE_GETGROUPS@ +REPLACE_GETLINE = @REPLACE_GETLINE@ +REPLACE_GETLOGIN_R = @REPLACE_GETLOGIN_R@ +REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@ +REPLACE_GETPASS = @REPLACE_GETPASS@ +REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@ +REPLACE_GLOB = @REPLACE_GLOB@ +REPLACE_GLOB_PATTERN_P = @REPLACE_GLOB_PATTERN_P@ +REPLACE_GMTIME = @REPLACE_GMTIME@ +REPLACE_INITSTATE = @REPLACE_INITSTATE@ +REPLACE_IOCTL = @REPLACE_IOCTL@ +REPLACE_ISATTY = @REPLACE_ISATTY@ +REPLACE_ISWBLANK = @REPLACE_ISWBLANK@ +REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@ +REPLACE_ISWDIGIT = @REPLACE_ISWDIGIT@ +REPLACE_ISWXDIGIT = @REPLACE_ISWXDIGIT@ +REPLACE_ITOLD = @REPLACE_ITOLD@ +REPLACE_LCHOWN = @REPLACE_LCHOWN@ +REPLACE_LINK = @REPLACE_LINK@ +REPLACE_LINKAT = @REPLACE_LINKAT@ +REPLACE_LOCALECONV = @REPLACE_LOCALECONV@ +REPLACE_LOCALTIME = @REPLACE_LOCALTIME@ +REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@ +REPLACE_LSEEK = @REPLACE_LSEEK@ +REPLACE_LSTAT = @REPLACE_LSTAT@ +REPLACE_MALLOC = @REPLACE_MALLOC@ +REPLACE_MBRLEN = @REPLACE_MBRLEN@ +REPLACE_MBRTOWC = @REPLACE_MBRTOWC@ +REPLACE_MBSINIT = @REPLACE_MBSINIT@ +REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@ +REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@ +REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@ +REPLACE_MBTOWC = @REPLACE_MBTOWC@ +REPLACE_MEMCHR = @REPLACE_MEMCHR@ +REPLACE_MEMMEM = @REPLACE_MEMMEM@ +REPLACE_MKDIR = @REPLACE_MKDIR@ +REPLACE_MKFIFO = @REPLACE_MKFIFO@ +REPLACE_MKNOD = @REPLACE_MKNOD@ +REPLACE_MKSTEMP = @REPLACE_MKSTEMP@ +REPLACE_MKTIME = @REPLACE_MKTIME@ +REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@ +REPLACE_NEWLOCALE = @REPLACE_NEWLOCALE@ +REPLACE_NL_LANGINFO = @REPLACE_NL_LANGINFO@ +REPLACE_NULL = @REPLACE_NULL@ +REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@ +REPLACE_OPEN = @REPLACE_OPEN@ +REPLACE_OPENAT = @REPLACE_OPENAT@ +REPLACE_OPENDIR = @REPLACE_OPENDIR@ +REPLACE_PERROR = @REPLACE_PERROR@ +REPLACE_POPEN = @REPLACE_POPEN@ +REPLACE_PREAD = @REPLACE_PREAD@ +REPLACE_PRINTF = @REPLACE_PRINTF@ +REPLACE_PSELECT = @REPLACE_PSELECT@ +REPLACE_PTHREAD_SIGMASK = @REPLACE_PTHREAD_SIGMASK@ +REPLACE_PTSNAME = @REPLACE_PTSNAME@ +REPLACE_PTSNAME_R = @REPLACE_PTSNAME_R@ +REPLACE_PUTENV = @REPLACE_PUTENV@ +REPLACE_PWRITE = @REPLACE_PWRITE@ +REPLACE_QSORT_R = @REPLACE_QSORT_R@ +REPLACE_RAISE = @REPLACE_RAISE@ +REPLACE_RANDOM = @REPLACE_RANDOM@ +REPLACE_RANDOM_R = @REPLACE_RANDOM_R@ +REPLACE_READ = @REPLACE_READ@ +REPLACE_READLINK = @REPLACE_READLINK@ +REPLACE_READLINKAT = @REPLACE_READLINKAT@ +REPLACE_REALLOC = @REPLACE_REALLOC@ +REPLACE_REALPATH = @REPLACE_REALPATH@ +REPLACE_REMOVE = @REPLACE_REMOVE@ +REPLACE_RENAME = @REPLACE_RENAME@ +REPLACE_RENAMEAT = @REPLACE_RENAMEAT@ +REPLACE_RMDIR = @REPLACE_RMDIR@ +REPLACE_SELECT = @REPLACE_SELECT@ +REPLACE_SETENV = @REPLACE_SETENV@ +REPLACE_SETLOCALE = @REPLACE_SETLOCALE@ +REPLACE_SETSTATE = @REPLACE_SETSTATE@ +REPLACE_SLEEP = @REPLACE_SLEEP@ +REPLACE_SNPRINTF = @REPLACE_SNPRINTF@ +REPLACE_SPRINTF = @REPLACE_SPRINTF@ +REPLACE_STAT = @REPLACE_STAT@ +REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@ +REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@ +REPLACE_STPNCPY = @REPLACE_STPNCPY@ +REPLACE_STRCASESTR = @REPLACE_STRCASESTR@ +REPLACE_STRCHRNUL = @REPLACE_STRCHRNUL@ +REPLACE_STRDUP = @REPLACE_STRDUP@ +REPLACE_STRERROR = @REPLACE_STRERROR@ +REPLACE_STRERROR_R = @REPLACE_STRERROR_R@ +REPLACE_STRFTIME = @REPLACE_STRFTIME@ +REPLACE_STRNCAT = @REPLACE_STRNCAT@ +REPLACE_STRNDUP = @REPLACE_STRNDUP@ +REPLACE_STRNLEN = @REPLACE_STRNLEN@ +REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@ +REPLACE_STRSTR = @REPLACE_STRSTR@ +REPLACE_STRTOD = @REPLACE_STRTOD@ +REPLACE_STRTOIMAX = @REPLACE_STRTOIMAX@ +REPLACE_STRTOK_R = @REPLACE_STRTOK_R@ +REPLACE_STRTOLD = @REPLACE_STRTOLD@ +REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@ +REPLACE_STRUCT_LCONV = @REPLACE_STRUCT_LCONV@ +REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@ +REPLACE_SYMLINK = @REPLACE_SYMLINK@ +REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@ +REPLACE_TIMEGM = @REPLACE_TIMEGM@ +REPLACE_TMPFILE = @REPLACE_TMPFILE@ +REPLACE_TOWLOWER = @REPLACE_TOWLOWER@ +REPLACE_TRUNCATE = @REPLACE_TRUNCATE@ +REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@ +REPLACE_TZSET = @REPLACE_TZSET@ +REPLACE_UNLINK = @REPLACE_UNLINK@ +REPLACE_UNLINKAT = @REPLACE_UNLINKAT@ +REPLACE_UNSETENV = @REPLACE_UNSETENV@ +REPLACE_USLEEP = @REPLACE_USLEEP@ +REPLACE_UTIME = @REPLACE_UTIME@ +REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@ +REPLACE_VASPRINTF = @REPLACE_VASPRINTF@ +REPLACE_VDPRINTF = @REPLACE_VDPRINTF@ +REPLACE_VFPRINTF = @REPLACE_VFPRINTF@ +REPLACE_VPRINTF = @REPLACE_VPRINTF@ +REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@ +REPLACE_VSPRINTF = @REPLACE_VSPRINTF@ +REPLACE_WCRTOMB = @REPLACE_WCRTOMB@ +REPLACE_WCSFTIME = @REPLACE_WCSFTIME@ +REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@ +REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@ +REPLACE_WCSTOK = @REPLACE_WCSTOK@ +REPLACE_WCSWIDTH = @REPLACE_WCSWIDTH@ +REPLACE_WCTOB = @REPLACE_WCTOB@ +REPLACE_WCTOMB = @REPLACE_WCTOMB@ +REPLACE_WCWIDTH = @REPLACE_WCWIDTH@ +REPLACE_WRITE = @REPLACE_WRITE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ +SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ +STDALIGN_H = @STDALIGN_H@ +STDARG_H = @STDARG_H@ +STDBOOL_H = @STDBOOL_H@ +STDDEF_H = @STDDEF_H@ +STDINT_H = @STDINT_H@ +STRIP = @STRIP@ +SYSEXITS_H = @SYSEXITS_H@ +SYS_IOCTL_H_HAVE_WINSOCK2_H = @SYS_IOCTL_H_HAVE_WINSOCK2_H@ +SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@ +TBL_X_FORMAT = @TBL_X_FORMAT@ +TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@ +TRANS_APROPOS = @TRANS_APROPOS@ +TRANS_APROPOS_UPPER = @TRANS_APROPOS_UPPER@ +TRANS_CATMAN = @TRANS_CATMAN@ +TRANS_CATMAN_UPPER = @TRANS_CATMAN_UPPER@ +TRANS_LEXGROG = @TRANS_LEXGROG@ +TRANS_LEXGROG_UPPER = @TRANS_LEXGROG_UPPER@ +TRANS_MAN = @TRANS_MAN@ +TRANS_MANCONV = @TRANS_MANCONV@ +TRANS_MANCONV_UPPER = @TRANS_MANCONV_UPPER@ +TRANS_MANDB = @TRANS_MANDB@ +TRANS_MANDB_UPPER = @TRANS_MANDB_UPPER@ +TRANS_MANPATH = @TRANS_MANPATH@ +TRANS_MANPATH_UPPER = @TRANS_MANPATH_UPPER@ +TRANS_MAN_RECODE = @TRANS_MAN_RECODE@ +TRANS_MAN_RECODE_UPPER = @TRANS_MAN_RECODE_UPPER@ +TRANS_MAN_UPPER = @TRANS_MAN_UPPER@ +TRANS_WHATIS = @TRANS_WHATIS@ +TRANS_WHATIS_UPPER = @TRANS_WHATIS_UPPER@ +TRANS_ZSOELIM = @TRANS_ZSOELIM@ +TRANS_ZSOELIM_UPPER = @TRANS_ZSOELIM_UPPER@ +TROFF = @TROFF@ +UINT32_MAX_LT_UINTMAX_MAX = @UINT32_MAX_LT_UINTMAX_MAX@ +UINT64_MAX_EQ_ULONG_MAX = @UINT64_MAX_EQ_ULONG_MAX@ +UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@ +UNISTD_H_DEFINES_STRUCT_TIMESPEC = @UNISTD_H_DEFINES_STRUCT_TIMESPEC@ +UNISTD_H_HAVE_SYS_RANDOM_H = @UNISTD_H_HAVE_SYS_RANDOM_H@ +UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@ +UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +USE_NLS = @USE_NLS@ +UTIME_H = @UTIME_H@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ +WINDOWS_64_BIT_OFF_T = @WINDOWS_64_BIT_OFF_T@ +WINDOWS_64_BIT_ST_SIZE = @WINDOWS_64_BIT_ST_SIZE@ +WINDOWS_STAT_INODES = @WINDOWS_STAT_INODES@ +WINDOWS_STAT_TIMESPEC = @WINDOWS_STAT_TIMESPEC@ +WINT_T_SUFFIX = @WINT_T_SUFFIX@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +browser = @browser@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +bunzip2 = @bunzip2@ +bzip2 = @bzip2@ +cache_top_owner = @cache_top_owner@ +cat = @cat@ +col = @col@ +compress = @compress@ +compress_ext = @compress_ext@ +compressor = @compressor@ +config_file = @config_file@ +config_file_basename = @config_file_basename@ +config_file_dirname = @config_file_dirname@ +datadir = @datadir@ +datarootdir = @datarootdir@ +date = @date@ +docdir = @docdir@ +dvidir = @dvidir@ +eqn = @eqn@ +exec_prefix = @exec_prefix@ +gl_LIBOBJS = @gl_LIBOBJS@ +gl_LTLIBOBJS = @gl_LTLIBOBJS@ +gltests_LIBOBJS = @gltests_LIBOBJS@ +gltests_LTLIBOBJS = @gltests_LTLIBOBJS@ +gltests_WITNESS = @gltests_WITNESS@ +grap = @grap@ +grep = @grep@ +gunzip = @gunzip@ +gzip = @gzip@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libpipeline_CFLAGS = @libpipeline_CFLAGS@ +libpipeline_LIBS = @libpipeline_LIBS@ +libseccomp_CFLAGS = @libseccomp_CFLAGS@ +libseccomp_LIBS = @libseccomp_LIBS@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lzip = @lzip@ +lzma = @lzma@ +man_mode = @man_mode@ +man_owner = @man_owner@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +neqn = @neqn@ +nroff = @nroff@ +oldincludedir = @oldincludedir@ +override_dir = @override_dir@ +pager = @pager@ +pdfdir = @pdfdir@ +pic = @pic@ +preconv = @preconv@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +refer = @refer@ +roff_version = @roff_version@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sections = @sections@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ +target_alias = @target_alias@ +tbl = @tbl@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +tr = @tr@ +troff = @troff@ +troff_as_troff_input = @troff_as_troff_input@ +uncompress = @uncompress@ +unlzip = @unlzip@ +unlzma = @unlzma@ +unxz = @unxz@ +unzstd = @unzstd@ +vgrind = @vgrind@ +xz = @xz@ +zstd = @zstd@ +SUBDIRS = . tests +noinst_DATA = man_db.conf +EXTRA_DIST = lexgrog.c zsoelim.c +AM_CPPFLAGS = \ + -I$(top_builddir)/include \ + -I$(top_builddir)/gl/lib \ + -I$(top_srcdir)/gl/lib \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/libdb \ + -DCONFIG_FILE=\"$(config_file)\" \ + -DAPROPOS=\"$(bindir)/$(TRANS_APROPOS)\" \ + -DAPROPOS_NAME=\"$(TRANS_APROPOS)\" \ + -DMAN=\"$(bindir)/$(TRANS_MAN)\" \ + -DMANCONV=\"$(pkglibexecdir)/$(TRANS_MANCONV)\" \ + -DMANDB=\"$(bindir)/$(TRANS_MANDB)\" \ + -DWHATIS=\"$(bindir)/$(TRANS_WHATIS)\" \ + -DZSOELIM=\"$(pkglibexecdir)/$(TRANS_ZSOELIM)\" + +AM_CFLAGS = \ + $(WARN_CFLAGS) \ + $(libpipeline_CFLAGS) + +LIBMAN = $(top_builddir)/lib/libman.la $(top_builddir)/gl/lib/libgnu.la \ + @LTLIBINTL@ + +LIBMANDB = $(top_builddir)/libdb/libmandb.la $(LIBMAN) $(DBLIBS) +accessdb_LDADD = $(LIBMANDB) +catman_LDADD = $(LIBMANDB) $(libpipeline_LIBS) +globbing_LDADD = $(LIBMAN) +lexgrog_LDADD = $(LIBMAN) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +man_LDADD = $(LIBMANDB) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +man_recode_LDADD = $(LIBMANDB) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +manconv_LDADD = $(LIBMAN) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +mandb_LDADD = $(LIBMANDB) $(LIBCOMPRESS) $(libpipeline_LIBS) $(LTLIBICONV) +manpath_LDADD = $(LIBMAN) +whatis_LDADD = $(LIBMANDB) $(libpipeline_LIBS) $(LTLIBICONV) +zsoelim_LDADD = $(LIBMAN) $(LIBCOMPRESS) $(libpipeline_LIBS) +accessdb_SOURCES = \ + accessdb.c + +catman_SOURCES = \ + catman.c \ + globbing.c \ + globbing.h \ + manp.c \ + manp.h + +globbing_SOURCES = \ + globbing.c \ + globbing.h \ + globbing_test.c + +lexgrog_SOURCES = \ + compression.c \ + decompress.c \ + decompress.h \ + descriptions.c \ + descriptions.h \ + filenames.c \ + filenames.h \ + globbing.c \ + globbing.h \ + lexgrog.l \ + lexgrog_test.c \ + manconv.c \ + manconv.h \ + manconv_client.c \ + manconv_client.h \ + ult_src.c \ + ult_src.h + +man_SOURCES = \ + compression.c \ + decompress.c \ + decompress.h \ + filenames.c \ + filenames.h \ + globbing.c \ + globbing.h \ + man.c \ + manconv.c \ + manconv.h \ + manconv_client.c \ + manconv_client.h \ + manp.c \ + manp.h \ + ult_src.c \ + ult_src.h \ + zsoelim.h \ + zsoelim.l + +man_recode_SOURCES = \ + compression.c \ + decompress.c \ + decompress.h \ + man-recode.c \ + manconv.c \ + manconv.h \ + manconv_client.c \ + manconv_client.h + +manconv_SOURCES = \ + decompress.c \ + decompress.h \ + manconv.c \ + manconv.h \ + manconv_main.c + +mandb_SOURCES = \ + check_mandirs.c \ + check_mandirs.h \ + compression.c \ + decompress.c \ + decompress.h \ + descriptions.c \ + descriptions.h \ + descriptions_store.c \ + filenames.c \ + filenames.h \ + globbing.c \ + globbing.h \ + lexgrog.l \ + manconv.c \ + manconv.h \ + manconv_client.c \ + manconv_client.h \ + mandb.c \ + manp.c \ + manp.h \ + straycats.c \ + ult_src.c \ + ult_src.h + +manpath_SOURCES = \ + globbing.c \ + globbing.h \ + manp.c \ + manp.h \ + manpath.c + +whatis_SOURCES = \ + globbing.c \ + globbing.h \ + manp.c \ + manp.h \ + whatis.c + +zsoelim_SOURCES = \ + decompress.c \ + decompress.h \ + globbing.c \ + globbing.h \ + manp.c \ + manp.h \ + zsoelim.h \ + zsoelim.l \ + zsoelim_main.c + +CLEANFILES = apropos man_db.conf +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .l .lo .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 ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + 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 +$(am__aclocal_m4_deps): +man_db.conf: $(top_builddir)/config.status $(srcdir)/man_db.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-pkglibexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files + +clean-pkglibexecPROGRAMS: + @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +accessdb$(EXEEXT): $(accessdb_OBJECTS) $(accessdb_DEPENDENCIES) $(EXTRA_accessdb_DEPENDENCIES) + @rm -f accessdb$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(accessdb_OBJECTS) $(accessdb_LDADD) $(LIBS) + +catman$(EXEEXT): $(catman_OBJECTS) $(catman_DEPENDENCIES) $(EXTRA_catman_DEPENDENCIES) + @rm -f catman$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(catman_OBJECTS) $(catman_LDADD) $(LIBS) + +globbing$(EXEEXT): $(globbing_OBJECTS) $(globbing_DEPENDENCIES) $(EXTRA_globbing_DEPENDENCIES) + @rm -f globbing$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(globbing_OBJECTS) $(globbing_LDADD) $(LIBS) + +lexgrog$(EXEEXT): $(lexgrog_OBJECTS) $(lexgrog_DEPENDENCIES) $(EXTRA_lexgrog_DEPENDENCIES) + @rm -f lexgrog$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(lexgrog_OBJECTS) $(lexgrog_LDADD) $(LIBS) + +man$(EXEEXT): $(man_OBJECTS) $(man_DEPENDENCIES) $(EXTRA_man_DEPENDENCIES) + @rm -f man$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(man_OBJECTS) $(man_LDADD) $(LIBS) + +man-recode$(EXEEXT): $(man_recode_OBJECTS) $(man_recode_DEPENDENCIES) $(EXTRA_man_recode_DEPENDENCIES) + @rm -f man-recode$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(man_recode_OBJECTS) $(man_recode_LDADD) $(LIBS) + +manconv$(EXEEXT): $(manconv_OBJECTS) $(manconv_DEPENDENCIES) $(EXTRA_manconv_DEPENDENCIES) + @rm -f manconv$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(manconv_OBJECTS) $(manconv_LDADD) $(LIBS) + +mandb$(EXEEXT): $(mandb_OBJECTS) $(mandb_DEPENDENCIES) $(EXTRA_mandb_DEPENDENCIES) + @rm -f mandb$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(mandb_OBJECTS) $(mandb_LDADD) $(LIBS) + +manpath$(EXEEXT): $(manpath_OBJECTS) $(manpath_DEPENDENCIES) $(EXTRA_manpath_DEPENDENCIES) + @rm -f manpath$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(manpath_OBJECTS) $(manpath_LDADD) $(LIBS) + +whatis$(EXEEXT): $(whatis_OBJECTS) $(whatis_DEPENDENCIES) $(EXTRA_whatis_DEPENDENCIES) + @rm -f whatis$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(whatis_OBJECTS) $(whatis_LDADD) $(LIBS) + +zsoelim$(EXEEXT): $(zsoelim_OBJECTS) $(zsoelim_DEPENDENCIES) $(EXTRA_zsoelim_DEPENDENCIES) + @rm -f zsoelim$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(zsoelim_OBJECTS) $(zsoelim_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accessdb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/catman.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_mandirs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compression.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decompress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/descriptions.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/descriptions_store.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filenames.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globbing.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globbing_test.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lexgrog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lexgrog_test.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/man-recode.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/man.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manconv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manconv_client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manconv_main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mandb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manpath.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/straycats.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ult_src.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/whatis.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zsoelim.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zsoelim_main.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.l.c: + $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -rm -f lexgrog.c + -rm -f zsoelim.c +clean: clean-recursive + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-pkglibexecPROGRAMS clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/accessdb.Po + -rm -f ./$(DEPDIR)/catman.Po + -rm -f ./$(DEPDIR)/check_mandirs.Po + -rm -f ./$(DEPDIR)/compression.Po + -rm -f ./$(DEPDIR)/decompress.Po + -rm -f ./$(DEPDIR)/descriptions.Po + -rm -f ./$(DEPDIR)/descriptions_store.Po + -rm -f ./$(DEPDIR)/filenames.Po + -rm -f ./$(DEPDIR)/globbing.Po + -rm -f ./$(DEPDIR)/globbing_test.Po + -rm -f ./$(DEPDIR)/lexgrog.Po + -rm -f ./$(DEPDIR)/lexgrog_test.Po + -rm -f ./$(DEPDIR)/man-recode.Po + -rm -f ./$(DEPDIR)/man.Po + -rm -f ./$(DEPDIR)/manconv.Po + -rm -f ./$(DEPDIR)/manconv_client.Po + -rm -f ./$(DEPDIR)/manconv_main.Po + -rm -f ./$(DEPDIR)/mandb.Po + -rm -f ./$(DEPDIR)/manp.Po + -rm -f ./$(DEPDIR)/manpath.Po + -rm -f ./$(DEPDIR)/straycats.Po + -rm -f ./$(DEPDIR)/ult_src.Po + -rm -f ./$(DEPDIR)/whatis.Po + -rm -f ./$(DEPDIR)/zsoelim.Po + -rm -f ./$(DEPDIR)/zsoelim_main.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-pkglibexecPROGRAMS \ + install-sbinPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/accessdb.Po + -rm -f ./$(DEPDIR)/catman.Po + -rm -f ./$(DEPDIR)/check_mandirs.Po + -rm -f ./$(DEPDIR)/compression.Po + -rm -f ./$(DEPDIR)/decompress.Po + -rm -f ./$(DEPDIR)/descriptions.Po + -rm -f ./$(DEPDIR)/descriptions_store.Po + -rm -f ./$(DEPDIR)/filenames.Po + -rm -f ./$(DEPDIR)/globbing.Po + -rm -f ./$(DEPDIR)/globbing_test.Po + -rm -f ./$(DEPDIR)/lexgrog.Po + -rm -f ./$(DEPDIR)/lexgrog_test.Po + -rm -f ./$(DEPDIR)/man-recode.Po + -rm -f ./$(DEPDIR)/man.Po + -rm -f ./$(DEPDIR)/manconv.Po + -rm -f ./$(DEPDIR)/manconv_client.Po + -rm -f ./$(DEPDIR)/manconv_main.Po + -rm -f ./$(DEPDIR)/mandb.Po + -rm -f ./$(DEPDIR)/manp.Po + -rm -f ./$(DEPDIR)/manpath.Po + -rm -f ./$(DEPDIR)/straycats.Po + -rm -f ./$(DEPDIR)/ult_src.Po + -rm -f ./$(DEPDIR)/whatis.Po + -rm -f ./$(DEPDIR)/zsoelim.Po + -rm -f ./$(DEPDIR)/zsoelim_main.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-pkglibexecPROGRAMS \ + uninstall-sbinPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-hook +.MAKE: $(am__recursive_targets) install-am install-data-am \ + install-exec-am install-strip uninstall-am + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool clean-pkglibexecPROGRAMS \ + clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-data-hook install-dvi install-dvi-am \ + install-exec install-exec-am install-exec-hook install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkglibexecPROGRAMS \ + install-ps install-ps-am install-sbinPROGRAMS install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-hook \ + uninstall-pkglibexecPROGRAMS uninstall-sbinPROGRAMS + +.PRECIOUS: Makefile + + +apropos$(EXEEXT): whatis$(EXEEXT) + rm -f $@ + $(LN_S) whatis$(EXEEXT) $@ + +all-am: apropos$(EXEEXT) + +install-exec-hook: + if [ "$(man_owner)" ] && [ "$(man_mode)" = 6755 ]; then \ + chown $(man_owner):$(man_owner) \ + $(DESTDIR)$(bindir)/$(TRANS_MAN) \ + $(DESTDIR)$(bindir)/$(TRANS_MANDB); \ + fi + chmod $(man_mode) \ + $(DESTDIR)$(bindir)/$(TRANS_MAN) \ + $(DESTDIR)$(bindir)/$(TRANS_MANDB) + cd $(DESTDIR)$(bindir) && rm -f $(TRANS_APROPOS)$(EXEEXT) && \ + $(LN_S) $(TRANS_WHATIS)$(EXEEXT) $(TRANS_APROPOS)$(EXEEXT) + +install-data-hook: + @if test -f $(DESTDIR)$(config_file); then \ + echo "$(DESTDIR)$(config_file) already exists; overwrite manually if necessary"; \ + else \ + test -z "$(config_file_dirname)" || $(MKDIR_P) "$(DESTDIR)$(config_file_dirname)"; \ + echo " $(INSTALL_DATA) man_db.conf $(DESTDIR)$(config_file)"; \ + $(INSTALL_DATA) man_db.conf $(DESTDIR)$(config_file); \ + fi + +uninstall-hook: + rm -f $(DESTDIR)$(bindir)/$(TRANS_APROPOS)$(EXEEXT) + @echo "Please remove $(DESTDIR)$(config_file) manually if necessary" + +# 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/src/accessdb.c b/src/accessdb.c new file mode 100644 index 0000000..5de7d7f --- /dev/null +++ b/src/accessdb.c @@ -0,0 +1,168 @@ +/* + * accessdb.c: show every key/content pair in the database. + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2002 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Tue Apr 26 12:56:44 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#include "argp.h" +#include "progname.h" +#include "xvasprintf.h" + +#include "gettext.h" +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "error.h" + +#include "mydbm.h" + +const char *cat_root; + +char *database; + +const char *argp_program_version = "accessdb " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("[MAN DATABASE]"); +static const char doc[] = "\v" N_("The man database defaults to %s%s."); + +static struct argp_option options[] = { + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'd': + debug_level = true; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP & + ~ARGP_HELP_PRE_DOC); + break; + case ARGP_KEY_ARG: + if (database) + argp_usage (state); + database = arg; + return 0; + case ARGP_KEY_NO_ARGS: + database = mkdbname (cat_root); + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static char *help_filter (int key, const char *text, void *input _GL_UNUSED) +{ + switch (key) { + case ARGP_KEY_HELP_PRE_DOC: + /* We have no pre-options help text, but the input + * text may contain header junk due to gettext (""). + */ + return NULL; + case ARGP_KEY_HELP_POST_DOC: + return xasprintf (text, cat_root, MAN_DB); + default: + return (char *) text; + } +} +#pragma GCC diagnostic pop + +static struct argp argp = { options, parse_opt, args_doc, doc, 0, + help_filter }; + +int main (int argc, char *argv[]) +{ + MYDBM_FILE dbf; + datum key; + int ret = OK; + + set_program_name (argv[0]); + + init_debug (); + init_locale (); + + if (is_directory (FHS_CAT_ROOT) == 1) + cat_root = FHS_CAT_ROOT; + else if (is_directory (CAT_ROOT) == 1) + cat_root = CAT_ROOT; + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + + dbf = MYDBM_RDOPEN (database); + if (dbf && dbver_rd (dbf)) { + MYDBM_CLOSE (dbf); + dbf = NULL; + } + if (!dbf) + error (FATAL, errno, _("can't open %s for reading"), database); + assert (dbf); /* help the compiler prove that later accesses are OK */ + + key = MYDBM_FIRSTKEY (dbf); + + while (MYDBM_DPTR (key) != NULL) { + datum content, nextkey; + char *t, *nicekey; + + content = MYDBM_FETCH (dbf, key); + if (!MYDBM_DPTR (content)) { + debug ("key %s has no content!\n", MYDBM_DPTR (key)); + ret = FATAL; + goto next; + } + nicekey = xstrdup (MYDBM_DPTR (key)); + while ( (t = strchr (nicekey, '\t')) ) + *t = '~'; + while ( (t = strchr (MYDBM_DPTR (content), '\t')) ) + *t = ' '; + printf ("%s -> \"%s\"\n", nicekey, MYDBM_DPTR (content)); + free (nicekey); + MYDBM_FREE_DPTR (content); +next: + nextkey = MYDBM_NEXTKEY (dbf, key); + MYDBM_FREE_DPTR (key); + key = nextkey; + } + + MYDBM_CLOSE (dbf); + exit (ret); +} diff --git a/src/catman.c b/src/catman.c new file mode 100644 index 0000000..326ff52 --- /dev/null +++ b/src/catman.c @@ -0,0 +1,435 @@ +/* + * catman.c: create and/or update cat files + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008, 2009, 2010, 2011 + * Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Thu Dec 8 00:03:12 GMT 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + */ + +/* MAX_ARGS must be >= 7, 5 for options, 1 for page and 1 for NULL */ +#define MAX_ARGS 1024 + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdio.h> +#include <assert.h> +#include <sys/types.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +#ifndef NAME_MAX +# if defined(_POSIX_VERSION) && defined(_POSIX_NAME_MAX) +# define NAME_MAX _POSIX_NAME_MAX +# else /* !_POSIX_VERSION */ +# ifdef MAXNAMLEN +# define NAME_MAX MAXNAMLEN +# else /* !MAXNAMLEN */ +# define NAME_MAX 255 /* default to max */ +# endif /* MAXNAMLEN */ +# endif /* _POSIX_VERSION */ +#endif /* !NAME_MAX */ + +#ifndef ARG_MAX +# if defined(_POSIX_VERSION) && defined(_POSIX_ARG_MAX) +# define ARG_MAX _POSIX_ARG_MAX +# else /* !_POSIX_VERSION */ +# define ARG_MAX 4096 /* default to min */ +# endif /* _POSIX_VERSION */ +#endif /* !ARG_MAX */ + +#include "argp.h" +#include "gl_list.h" +#include "progname.h" + +#include "gettext.h" +#include <locale.h> +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "cleanup.h" +#include "error.h" +#include "glcontainers.h" +#include "pipeline.h" + +#include "mydbm.h" +#include "db_storage.h" + +#include "manp.h" + +/* globals */ +int quiet = 1; +MYDBM_FILE dbf_close_post_fork; +char *manp; +extern char *user_config_file; + +static const char **sections; + +const char *argp_program_version = "catman " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("[SECTION...]"); + +static struct argp_option options[] = { + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "manpath", 'M', N_("PATH"), 0, N_("set search path for manual pages to PATH") }, + { "config-file", 'C', N_("FILE"), 0, N_("use this user configuration file") }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + char *mansect; + + switch (key) { + case 'd': + debug_level = true; + return 0; + case 'M': + manp = arg; + return 0; + case 'C': + user_config_file = arg; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP); + break; + case ARGP_KEY_ARGS: + sections = xmalloc ((state->argc - state->next + 1) * + sizeof *sections); + memcpy (sections, state->argv + state->next, + (state->argc - state->next) * + sizeof *sections); + sections[state->argc - state->next] = NULL; + return 0; + case ARGP_KEY_NO_ARGS: + mansect = getenv ("MANSECT"); + if (mansect && *mansect) { + /* MANSECT contains sections */ + const char *sec; + int i = 0; + + mansect = xstrdup (mansect); + sections = NULL; + for (sec = strtok (mansect, ":"); sec; + sec = strtok (NULL, ":")) { + sections = xnrealloc + (sections, i + 2, + sizeof *sections); + sections[i++] = sec; + } + if (sections) + sections[i] = NULL; + free (mansect); + } else { + /* use default sections */ + static const char *std_sections[] = + STD_SECTIONS; + sections = std_sections; + } + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp = { options, parse_opt, args_doc }; + +static char *locale; + +static gl_list_t manpathlist; + +static void post_fork (void) +{ + pop_all_cleanups (); + if (dbf_close_post_fork) + MYDBM_CLOSE (dbf_close_post_fork); +} + +/* Execute man with the appropriate catman args. Always frees cmd. */ +static void catman (pipecmd *cmd) +{ + pipeline *p; + int status; + + if (debug_level) { + /* just show the command, but don't execute it */ + fputs ("man command = ", stderr); + pipecmd_dump (cmd, stderr); + putc ('\n', stderr); + pipecmd_free (cmd); + return; + } + + p = pipeline_new_commands (cmd, (void *) 0); + status = pipeline_run (p); + if (status) + error (CHILD_FAIL, 0, + _("man command failed with exit status %d"), status); +} + +/* Add key to this command, stripping off tab-and-following if necessary. + * Return length of argument. + */ +static size_t add_arg (pipecmd *cmd, datum key) +{ + char *tab; + size_t len; + + tab = strrchr (MYDBM_DPTR (key), '\t'); + if (tab == MYDBM_DPTR (key)) + tab = NULL; + + if (tab) + *tab = '\0'; + pipecmd_arg (cmd, MYDBM_DPTR (key)); + len = strlen (MYDBM_DPTR (key)); + debug ("key: '%s' (%zu), len: %zu\n", + MYDBM_DPTR (key), (size_t) MYDBM_DSIZE (key), len); + if (tab) + *tab = '\t'; + + return len; +} + +/* find all pages that are in the supplied manpath and section and that are + ultimate source files. */ +static int parse_for_sec (const char *database, + const char *manpath, const char *section) +{ + MYDBM_FILE dbf; + pipecmd *basecmd, *cmd; + datum key; + size_t arg_size, initial_bit; + int message = 1, first_arg; + + dbf = MYDBM_RDOPEN (database); + if (!dbf) { + error (0, errno, _("cannot read database %s"), database); + return 1; + } + if (dbver_rd (dbf)) { + MYDBM_CLOSE (dbf); + return 1; + } + dbf_close_post_fork = dbf; + + basecmd = pipecmd_new (MAN); + pipecmd_clearenv (basecmd); + + /* As we supply a NULL environment to save precious execve() space, + we must also supply a locale if necessary */ + if (locale) { + pipecmd_args (basecmd, "-L", locale, (void *) 0); + initial_bit = sizeof "-L" + strlen (locale) + 1; + } else + initial_bit = 0; + + pipecmd_args (basecmd, "-caM", manpath, (void *) 0); /* manpath */ + pipecmd_args (basecmd, "-S", section, (void *) 0); /* section */ + + initial_bit += sizeof MAN + sizeof "-caM" + + strlen (manpath) + strlen (section) + 2; + + cmd = pipecmd_dup (basecmd); + first_arg = pipecmd_get_nargs (cmd); + + arg_size = initial_bit; + key = MYDBM_FIRSTKEY (dbf); + + while (MYDBM_DPTR (key) != NULL) { + datum nextkey; + + /* ignore db identifier keys */ + if (*MYDBM_DPTR (key) != '$') { + datum content; + + content = MYDBM_FETCH (dbf, key); + + if (!MYDBM_DPTR (content)) + error (FATAL, 0, + _( "NULL content for key: %s"), + MYDBM_DPTR (key)); + + /* ignore overflow entries */ + if (*MYDBM_DPTR (content) != '\t') { + struct mandata entry; + + split_content (dbf, MYDBM_DPTR (content), + &entry); + + /* Accept if the entry is an ultimate manual + page and the section matches the one we're + currently dealing with */ + if (entry.id == ULT_MAN && + strcmp (entry.sec, section) == 0) { + if (message) { + printf (_("\nUpdating cat files for section %s of man hierarchy %s\n"), + section, manpath); + message = 0; + } + + arg_size += add_arg (cmd, key) + 1; + + debug ("arg space free: %zu bytes\n", + ARG_MAX - arg_size); + + /* Check to see if we have enough room + to add another max sized filename + and that we haven't run out of array + space too */ + if (arg_size >= ARG_MAX - NAME_MAX || + pipecmd_get_nargs (cmd) == + MAX_ARGS) { + catman (cmd); + + cmd = pipecmd_dup (basecmd); + arg_size = initial_bit; + } + } + + /* == MYDBM_DPTR (content), freed below */ + entry.addr = NULL; + free_mandata_elements (&entry); + } + + /* we don't need the content ever again */ + assert (MYDBM_DPTR (content)); /* just to be sure */ + MYDBM_FREE_DPTR (content); + } + + nextkey = MYDBM_NEXTKEY (dbf, key); + MYDBM_FREE_DPTR (key); + key = nextkey; + } + + dbf_close_post_fork = NULL; + MYDBM_CLOSE (dbf); + if (pipecmd_get_nargs (cmd) > first_arg) + catman (cmd); + else + pipecmd_free (cmd); + + pipecmd_free (basecmd); + + return 0; +} + +static int check_access (const char *directory) +{ + if (!CAN_ACCESS (directory, W_OK)) { + error (0, errno, _("cannot write within %s"), directory); + return 1; + } + + return 0; +} + +int main (int argc, char *argv[]) +{ + char *sys_manp; + char *mp; + const char **sp; + + set_program_name (argv[0]); + + init_debug (); + pipeline_install_post_fork (post_fork); + + init_locale (); + locale = setlocale (LC_MESSAGES, NULL); + if (locale) + locale = xstrdup (locale); + else + locale = xstrdup ("C"); + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + + for (sp = sections; *sp; sp++) + debug ("sections: %s\n", *sp); + + /* Deal with the MANPATH */ + + /* This is required for get_catpath(), regardless */ + sys_manp = get_manpath (NULL); + + /* pick up the system manpath or use the supplied one */ + if (!manp) { + manp = get_mandb_manpath (); + if (!manp) + manp = sys_manp; + } + + /* get the manpath as a list of pointers */ + manpathlist = create_pathlist (manp); + + GL_LIST_FOREACH_START (manpathlist, mp) { + char *catpath, *database; + size_t len; + + catpath = get_catpath (mp, SYSTEM_CAT | USER_CAT); + + if (catpath) { + if (is_directory (catpath) != 1) { + free (catpath); + continue; + } + database = mkdbname (catpath); + } else { + if (is_directory (mp) != 1) + continue; + database = mkdbname (mp); + catpath = xstrdup (mp); + } + + len = strlen (catpath); + + for (sp = sections; *sp; sp++) { + *(catpath + len) = '\0'; + catpath = appendstr (catpath, "/cat", *sp, (void *) 0); + if (is_directory (catpath) != 1) + continue; + if (check_access (catpath)) + continue; + if (parse_for_sec (database, mp, *sp)) { + error (0, 0, _("unable to update %s"), mp); + break; + } + } + + free (database); + free (catpath); + } GL_LIST_FOREACH_END (manpathlist); + + free_pathlist (manpathlist); + free (locale); + exit (OK); +} diff --git a/src/check_mandirs.c b/src/check_mandirs.c new file mode 100644 index 0000000..d28a6be --- /dev/null +++ b/src/check_mandirs.c @@ -0,0 +1,1081 @@ +/* + * check_mandirs.c: used to auto-update the database caches + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2007, 2008, 2009, 2010, 2011 + * Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Mon May 2 17:36:33 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + * + * CJW: Many changes to whatis parsing. Added database purging. + * See ChangeLog for details. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <errno.h> +#include <ctype.h> +#include <dirent.h> +#include <unistd.h> + +#include "dirname.h" +#include "gl_array_list.h" +#include "gl_hash_map.h" +#include "gl_xlist.h" +#include "gl_xmap.h" +#include "stat-time.h" +#include "timespec.h" +#include "xvasprintf.h" + +#include "gettext.h" +#define _(String) gettext (String) + +#include "manconfig.h" + +#include "error.h" +#include "glcontainers.h" +#include "orderfiles.h" +#include "security.h" + +#include "mydbm.h" +#include "db_storage.h" + +#include "descriptions.h" +#include "filenames.h" +#include "globbing.h" +#include "manp.h" +#include "ult_src.h" +#include "check_mandirs.h" + +bool opt_test; /* don't update db */ +int pages; +bool force_rescan = false; + +gl_map_t whatis_map = NULL; + +struct whatis { + char *whatis; + gl_list_t trace; +}; + +static void whatis_free (const void *value) +{ + struct whatis *whatis = (struct whatis *) value; + + free (whatis->whatis); + gl_list_free (whatis->trace); + free (whatis); +} + +static void gripe_multi_extensions (const char *path, const char *sec, + const char *name, const char *ext) +{ + if (quiet < 2) + error (0, 0, + _("warning: %s/man%s/%s.%s*: competing extensions"), + path, sec, name, ext); +} + +/* Test whether an errno value is EAGAIN or (on systems where it differs) + * EWOULDBLOCK. This is a separate function mainly in order to be able to + * control GCC diagnostics in one place. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlogical-op" +static inline bool is_eagain (int err) +{ + return err == EAGAIN || err == EWOULDBLOCK; +} +#pragma GCC diagnostic pop + +static void gripe_rwopen_failed (const char *database) +{ + if (errno == EACCES || errno == EROFS) + debug ("database %s is read-only\n", database); + else if (is_eagain (errno)) + debug ("database %s is locked by another process\n", database); + else { +#ifdef MAN_DB_UPDATES + if (!quiet) +#endif /* MAN_DB_UPDATES */ + error (0, errno, _("can't update index cache %s"), + database); + } +} + +/* Take absolute filename and path (for ult_src) and do sanity checks on + * file. Also check that file is non-zero in length and is not already in + * the db. If not, find its ult_src() and see if we have the whatis cached, + * otherwise cache it in case we trace another manpage back to it. Next, + * store it in the db along with any references found in the whatis. + */ +void test_manfile (MYDBM_FILE dbf, const char *file, const char *path) +{ + char *manpage_base; + const char *ult; + struct lexgrog lg; + char *manpage; + struct mandata info, *exists; + struct stat buf; + size_t len; + gl_list_t ult_trace = NULL; + const struct whatis *whatis; + + memset (&lg, 0, sizeof (struct lexgrog)); + memset (&info, 0, sizeof (struct mandata)); + + manpage = filename_info (file, &info, NULL); + if (!manpage) + return; + manpage_base = manpage + strlen (manpage) + 1; + + len = strlen (manpage) + 1; /* skip over directory name */ + len += strlen (manpage + len) + 1; /* skip over base name */ + len += strlen (manpage + len); /* skip over section ext */ + + /* to get mtime info */ + (void) lstat (file, &buf); + info.mtime = get_stat_mtime (&buf); + + /* check that our file actually contains some data */ + if (buf.st_size == 0) { + /* man-db pre 2.3 place holder ? */ + free (manpage); + return; + } + + /* See if we already have it, before going any further. This will + * save both an ult_src() and a find_name(), amongst other wastes of + * time. + */ + exists = dblookup_exact (dbf, manpage_base, info.ext, true); + + /* Ensure we really have the actual page. Gzip keeps the mtime the + * same when it compresses, so we have to compare compression + * extensions as well. + */ + if (exists) { + if (strcmp (exists->comp, info.comp ? info.comp : "-") == 0) { + if (timespec_cmp (exists->mtime, info.mtime) == 0 && + exists->id < WHATIS_MAN) { + free_mandata_struct (exists); + free (manpage); + return; + } + } else { + char *abs_filename; + + /* see if the cached file actually exists. It's + evident at this point that we have multiple + comp extensions */ + abs_filename = make_filename (path, NULL, + exists, "man"); + if (!abs_filename) { + if (!opt_test) + dbdelete (dbf, manpage_base, exists); + } else { + gripe_multi_extensions (path, exists->sec, + manpage_base, + exists->ext); + free (abs_filename); + free_mandata_struct (exists); + free (manpage); + return; + } + } + free_mandata_struct (exists); + } + + /* Check if it happens to be a symlink/hardlink to something already + * in our cache. This just does some extra checks to avoid scanning + * links quite so many times. + */ + { + /* Avoid too much noise in debug output */ + bool save_debug = debug_level; + debug_level = false; + ult = ult_src (file, path, &buf, SOFT_LINK | HARD_LINK, NULL); + debug_level = save_debug; + } + + if (!ult) { + /* already warned about this, don't do so again */ + debug ("test_manfile(): bad link %s\n", file); + free (manpage); + return; + } + + if (!whatis_map) + whatis_map = new_string_map (GL_HASH_MAP, whatis_free); + + whatis = gl_map_get (whatis_map, ult); + if (!whatis) { + if (!STRNEQ (ult, file, len)) + debug ("\ntest_manfile(): link not in cache:\n" + " source = %s\n" + " target = %s\n", file, ult); + /* Trace the file to its ultimate source, otherwise we'll be + * looking for whatis info in files containing only '.so + * manx/foo.x', which will give us an unobtainable whatis + * for the entry. */ + ult_trace = new_string_list (GL_ARRAY_LIST, true); + ult = ult_src (file, path, &buf, + SO_LINK | SOFT_LINK | HARD_LINK, ult_trace); + } + + if (!ult) { + if (quiet < 2) + error (0, 0, + _("warning: %s: bad symlink or ROFF `.so' request"), + file); + free (manpage); + return; + } + + pages++; /* pages seen so far */ + + if (strncmp (ult, file, len) == 0) + info.id = ULT_MAN; /* ultimate source file */ + else + info.id = SO_MAN; /* .so, sym or hard linked file */ + + /* Ok, here goes: Use a hash tree to store the ult_srcs with + * their whatis. Anytime after, check the hash tree, if it's there, + * use it. This saves us a find_name() which is a real hog. + * + * Use the full path in ult as the hash key so we don't have to + * clear the hash between calls. + */ + + if (whatis) + lg.whatis = whatis->whatis ? xstrdup (whatis->whatis) : NULL; + else { + /* Cache miss; go and get the whatis info in its raw state. */ + char *file_base = base_name (file); + struct whatis *new_whatis; + + lg.type = MANPAGE; + drop_effective_privs (); + find_name (ult, file_base, &lg, NULL); + free (file_base); + regain_effective_privs (); + + new_whatis = XMALLOC (struct whatis); + new_whatis->whatis = lg.whatis ? xstrdup (lg.whatis) : NULL; + /* We filled out ult_trace above. */ + new_whatis->trace = ult_trace; + gl_map_put (whatis_map, xstrdup (ult), new_whatis); + whatis = new_whatis; + } + + debug ("\"%s\"\n", lg.whatis); + + /* split up the raw whatis data and store references */ + info.pointer = NULL; /* direct page, so far */ + info.filter = lg.filters; + if (lg.whatis) { + gl_list_t descs = parse_descriptions (manpage_base, lg.whatis); + if (!opt_test) + store_descriptions (dbf, descs, &info, path, + manpage_base, whatis->trace); + gl_list_free (descs); + } else if (quiet < 2) { + (void) stat (ult, &buf); + if (buf.st_size == 0) + error (0, 0, _("warning: %s: ignoring empty file"), + ult); + else + error (0, 0, + _("warning: %s: whatis parse for %s(%s) failed"), + ult, manpage_base, info.ext); + } + + free (manpage); + free (lg.whatis); +} + +static void add_dir_entries (MYDBM_FILE dbf, const char *path, char *infile) +{ + char *manpage; + int len; + struct dirent *newdir; + DIR *dir; + gl_list_t names; + const char *name; + + manpage = xasprintf ("%s/%s/", path, infile); + len = strlen (manpage); + + /* + * All filename entries in this dir should either be valid manpages + * or . files (such as current, parent dir). + */ + + dir = opendir (infile); + if (!dir) { + error (0, errno, _("can't search directory %s"), manpage); + free (manpage); + return; + } + + names = new_string_list (GL_ARRAY_LIST, false); + + /* strlen(newdir->d_name) could be replaced by newdir->d_reclen */ + + while ((newdir = readdir (dir)) != NULL) { + if (*newdir->d_name == '.' && + strlen (newdir->d_name) < (size_t) 3) + continue; + gl_list_add_last (names, xstrdup (newdir->d_name)); + } + closedir (dir); + + order_files (infile, &names); + + GL_LIST_FOREACH_START (names, name) { + manpage = appendstr (manpage, name, (void *) 0); + test_manfile (dbf, manpage, path); + *(manpage + len) = '\0'; + } GL_LIST_FOREACH_END (names); + + gl_list_free (names); + free (manpage); +} + +#ifdef MAN_OWNER +extern uid_t uid; /* current effective user id */ +extern gid_t gid; /* current effective group id */ + +/* Fix a path's ownership if possible and necessary. */ +void chown_if_possible (const char *path) +{ + struct stat st; + struct passwd *man_owner = get_man_owner (); + + if (lstat (path, &st) != 0) + return; + + if ((uid == 0 || + (uid == man_owner->pw_uid && st.st_uid == man_owner->pw_uid && + gid == man_owner->pw_gid)) && + (st.st_uid != man_owner->pw_uid || + st.st_gid != man_owner->pw_gid)) { + debug ("fixing ownership of %s\n", path); + if (lchown (path, man_owner->pw_uid, man_owner->pw_gid) < 0) + error (FATAL, 0, _("can't chown %s"), path); + } +} +#else /* !MAN_OWNER */ +void chown_if_possible (const char *path _GL_UNUSED) +{ +} +#endif /* MAN_OWNER */ + +/* create the catman hierarchy if it doesn't exist */ +static void mkcatdirs (const char *mandir, const char *catdir) +{ + char *manname, *catname; + + if (catdir) { + int oldmask = umask (022); + /* first the base catdir */ + if (is_directory (catdir) != 1) { + regain_effective_privs (); + if (mkdir (catdir, 0755) < 0) { + if (!quiet) + error (0, 0, + _("warning: cannot create catdir %s"), + catdir); + debug ("warning: cannot create catdir %s\n", + catdir); + } else + debug ("created base catdir %s\n", catdir); + chown_if_possible (catdir); + drop_effective_privs (); + } + /* then the hierarchy */ + catname = xasprintf ("%s/cat1", catdir); + manname = xasprintf ("%s/man1", mandir); + if (is_directory (catdir) == 1) { + int j; + regain_effective_privs (); + debug ("creating catdir hierarchy %s ", catdir); + for (j = 1; j <= 9; j++) { + catname[strlen (catname) - 1] = '0' + j; + manname[strlen (manname) - 1] = '0' + j; + if ((is_directory (manname) == 1) + && (is_directory (catname) != 1)) { + if (mkdir (catname, 0755) < 0) { + if (!quiet) + error (0, 0, _("warning: cannot create catdir %s"), catname); + debug ("warning: cannot create catdir %s\n", catname); + } else + debug (" cat%d", j); + chown_if_possible (catname); + } + } + debug ("\n"); + drop_effective_privs (); + } + free (catname); + free (manname); + umask (oldmask); + } +} + +/* We used to install cat directories with the setgid bit set, but this + * wasn't very useful and introduces the ability to escalate privileges to + * that group: + * https://www.halfdog.net/Security/2015/SetgidDirectoryPrivilegeEscalation/ + */ +static void fix_permissions (const char *dir) +{ + struct stat st; + + if (stat (dir, &st) == 0) { + if ((st.st_mode & S_ISGID) != 0) { + int status; + + debug ("removing setgid bit from %s\n", dir); + status = chmod (dir, st.st_mode & ~S_ISGID); + if (status) + error (0, errno, _("can't chmod %s"), dir); + } + + chown_if_possible (dir); + } +} + +static void fix_permissions_tree (const char *catdir) +{ + if (is_directory (catdir) == 1) { + char *catname; + int i; + + fix_permissions (catdir); + catname = xasprintf ("%s/cat1", catdir); + for (i = 1; i <= 9; ++i) { + catname[strlen (catname) - 1] = '0' + i; + fix_permissions (catname); + } + free (catname); + } +} + +/* + * accepts the raw man dir tree eg. "/usr/man" and the time stored in the db + * any dirs of the tree that have been modified (ie added to) will then be + * scanned for new files, which are then added to the db. + */ +static int testmandirs (const char *database, + const char *path, const char *catpath, + struct timespec last, int create) +{ + DIR *dir; + struct dirent *mandir; + int amount = 0; + int created = 0; + + debug ("Testing %s for new files\n", path); + + if (catpath) + fix_permissions_tree (catpath); + + dir = opendir (path); + if (!dir) { + error (0, errno, _("can't search directory %s"), path); + return 0; + } + + if (chdir (path) != 0) { + error (0, errno, _("can't change to directory %s"), path); + closedir (dir); + return 0; + } + + while( (mandir = readdir (dir)) ) { + struct stat stbuf; + struct timespec mtime; + MYDBM_FILE dbf; + + if (strncmp (mandir->d_name, "man", 3) != 0) + continue; + + debug ("Examining %s\n", mandir->d_name); + + if (stat (mandir->d_name, &stbuf) != 0) /* stat failed */ + continue; + if (!S_ISDIR(stbuf.st_mode)) /* not a directory */ + continue; + mtime = get_stat_mtime (&stbuf); + if (last.tv_sec && timespec_cmp (mtime, last) <= 0) { + /* scanned already */ + debug ("%s modified %ld.%09ld, " + "db modified %ld.%09ld\n", + mandir->d_name, + (long) mtime.tv_sec, (long) mtime.tv_nsec, + (long) last.tv_sec, (long) last.tv_nsec); + continue; + } + + debug ("\tsubdirectory %s has been 'modified'\n", + mandir->d_name); + + if (create && !created) { + /* We seem to have something to do, so create the + * database now. + */ + mkcatdirs (path, catpath); + + /* Open the db in CTRW mode to store the $ver$ ID */ + + dbf = MYDBM_CTRWOPEN (database); + if (dbf == NULL) { + if (errno == EACCES || errno == EROFS) { + debug ("database %s is read-only\n", + database); + closedir (dir); + return 0; + } else { + error (0, errno, + _("can't create index cache %s"), + database); + closedir (dir); + return -errno; + } + } + + dbver_wr (dbf); + + created = 1; + } else + dbf = MYDBM_RWOPEN(database); + + if (!dbf) { + gripe_rwopen_failed (database); + closedir (dir); + return 0; + } + + if (!quiet) { + int tty = isatty (STDERR_FILENO); + + if (tty) + fprintf (stderr, "\r"); + fprintf (stderr, + _("Updating index cache for path " + "`%s/%s'. Wait..."), path, mandir->d_name); + if (!tty) + fprintf (stderr, "\n"); + } + add_dir_entries (dbf, path, mandir->d_name); + MYDBM_CLOSE (dbf); + amount++; + } + closedir (dir); + + return amount; +} + +/* update the modification timestamp of `database' */ +static void update_db_time (const char *database) +{ + MYDBM_FILE dbf; + struct timespec now; + + /* Open the db in RW to update its mtime */ + /* we know that this should succeed because we just updated the db! */ + dbf = MYDBM_RWOPEN (database); + if (dbf == NULL) { + if (is_eagain (errno)) + /* Another mandb process is probably running. With + * any luck it will update the mtime ... + */ + debug ("database %s is locked by another process\n", + database); + else { +#ifdef MAN_DB_UPDATES + if (!quiet) +#endif /* MAN_DB_UPDATES */ + error (0, errno, + _("can't update index cache %s"), + database); + } + return; + } + now.tv_sec = 0; + now.tv_nsec = UTIME_NOW; + MYDBM_SET_TIME (dbf, now); + + MYDBM_CLOSE (dbf); +} + +/* routine to prepare/create the db prior to calling testmandirs() */ +int create_db (const char *database, const char *manpath, const char *catpath) +{ + struct timespec time_zero; + int amount; + + debug ("create_db(%s): %s\n", manpath, database); + + time_zero.tv_sec = 0; + time_zero.tv_nsec = 0; + amount = testmandirs (database, manpath, catpath, time_zero, 1); + + if (amount) { + update_db_time (database); + if (!quiet) + fputs (_("done.\n"), stderr); + } + + return amount; +} + +/* Make sure an existing database is essentially sane. */ +static bool sanity_check_db (MYDBM_FILE dbf) +{ + datum key; + + if (dbver_rd (dbf)) + return false; + + key = MYDBM_FIRSTKEY (dbf); + while (MYDBM_DPTR (key) != NULL) { + datum content, nextkey; + + content = MYDBM_FETCH (dbf, key); + if (!MYDBM_DPTR (content)) { + debug ("warning: %s has a key with no content (%s); " + "rebuilding\n", dbf->name, MYDBM_DPTR (key)); + MYDBM_FREE_DPTR (key); + return false; + } + MYDBM_FREE_DPTR (content); + nextkey = MYDBM_NEXTKEY (dbf, key); + MYDBM_FREE_DPTR (key); + key = nextkey; + } + + return true; +} + +/* routine to update the db, ensure that it is consistent with the + filesystem */ +int update_db (const char *database, const char *manpath, const char *catpath) +{ + MYDBM_FILE dbf; + struct timespec mtime; + int new; + + dbf = MYDBM_RDOPEN (database); + if (dbf && !sanity_check_db (dbf)) { + MYDBM_CLOSE (dbf); + dbf = NULL; + } + if (!dbf) { + debug ("failed to open %s O_RDONLY\n", database); + return EOF; + } + mtime = MYDBM_GET_TIME (dbf); + MYDBM_CLOSE (dbf); + + debug ("update_db(): %ld.%09ld\n", + (long) mtime.tv_sec, (long) mtime.tv_nsec); + new = testmandirs (database, manpath, catpath, mtime, 0); + + if (new) { + update_db_time (database); + if (!quiet) + fputs (_("done.\n"), stderr); + } + + return new; +} + +/* Purge any entries pointing to name. This currently assumes that pointers + * are always shallow, which may not be a good assumption yet; it should be + * close, though. + */ +void purge_pointers (MYDBM_FILE dbf, const char *name) +{ + datum key = MYDBM_FIRSTKEY (dbf); + + debug ("Purging pointers to vanished page \"%s\"\n", name); + + while (MYDBM_DPTR (key) != NULL) { + datum content, nextkey; + struct mandata entry; + char *nicekey, *tab; + + /* Ignore db identifier keys. */ + if (*MYDBM_DPTR (key) == '$') + goto pointers_next; + + content = MYDBM_FETCH (dbf, key); + if (!MYDBM_DPTR (content)) + return; + + /* Get just the name. */ + nicekey = xstrdup (MYDBM_DPTR (key)); + tab = strchr (nicekey, '\t'); + if (tab) + *tab = '\0'; + + if (*MYDBM_DPTR (content) == '\t') + goto pointers_contentnext; + + split_content (dbf, MYDBM_DPTR (content), &entry); + if (entry.id != SO_MAN && entry.id != WHATIS_MAN) + goto pointers_contentnext; + + if (STREQ (entry.pointer, name)) { + if (!opt_test) + dbdelete (dbf, nicekey, &entry); + else + debug ("%s(%s): pointer vanished, " + "would delete\n", nicekey, entry.ext); + } + +pointers_contentnext: + free (nicekey); + MYDBM_FREE_DPTR (content); +pointers_next: + nextkey = MYDBM_NEXTKEY (dbf, key); + MYDBM_FREE_DPTR (key); + key = nextkey; + } +} + +/* Count the number of exact extension matches returned from look_for_file() + * (which may return inexact extension matches in some cases). It may turn + * out that this is better handled in look_for_file() itself. + */ +static int count_glob_matches (const char *name, const char *ext, + gl_list_t source, struct timespec db_mtime) +{ + const char *walk; + int count = 0; + + GL_LIST_FOREACH_START (source, walk) { + struct mandata info; + struct stat statbuf; + char *buf; + + memset (&info, 0, sizeof (struct mandata)); + + if (stat (walk, &statbuf) == -1) { + debug ("count_glob_matches: excluding %s " + "because stat failed\n", walk); + continue; + } + if (db_mtime.tv_sec != (time_t) -1 && + timespec_cmp (get_stat_mtime (&statbuf), db_mtime) <= 0) { + debug ("count_glob_matches: excluding %s, " + "no newer than database\n", walk); + continue; + } + + buf = filename_info (walk, &info, name); + if (buf) { + if (STREQ (ext, info.ext)) + ++count; + free (info.name); + free (buf); + } + } GL_LIST_FOREACH_END (source); + + return count; +} + +/* Decide whether to purge a reference to a "normal" (ULT_MAN or SO_MAN) + * page. + */ +static int purge_normal (MYDBM_FILE dbf, const char *name, + struct mandata *info, gl_list_t found) +{ + struct timespec t; + + /* TODO: On some systems, the cat page extension differs from the + * man page extension, so this may be too strict. + */ + t.tv_sec = -1; + t.tv_nsec = -1; + if (count_glob_matches (name, info->ext, found, t)) + return 0; + + if (!opt_test) + dbdelete (dbf, name, info); + else + debug ("%s(%s): missing page, would delete\n", + name, info->ext); + + return 1; +} + +/* Decide whether to purge a reference to a WHATIS_MAN or WHATIS_CAT page. */ +static int purge_whatis (MYDBM_FILE dbf, const char *path, int cat, + const char *name, struct mandata *info, + gl_list_t found, struct timespec db_mtime) +{ + /* TODO: On some systems, the cat page extension differs from the + * man page extension, so this may be too strict. + */ + if (count_glob_matches (name, info->ext, found, db_mtime)) { + /* If the page exists and didn't beforehand, then presumably + * we're about to rescan, which will replace the WHATIS_MAN + * entry with something better. However, there have been + * bugs that created false WHATIS_MAN entries, so force the + * rescan just to be sure; since in the absence of a bug we + * would rescan anyway, this isn't a problem. + */ + if (!force_rescan) + debug ("%s(%s): whatis replaced by real page; " + "forcing a rescan just in case\n", + name, info->ext); + force_rescan = true; + return 0; + } else if (STREQ (info->pointer, "-")) { + /* This is broken; a WHATIS_MAN should never have an empty + * pointer field. This might have happened due to the first + * name in a page being different from what the file name + * says; that's fixed now, so delete and force a rescan. + */ + if (!opt_test) + dbdelete (dbf, name, info); + else + debug ("%s(%s): whatis with empty pointer, " + "would delete\n", name, info->ext); + + if (!force_rescan) + debug ("%s(%s): whatis had empty pointer; " + "forcing a rescan just in case\n", + name, info->ext); + force_rescan = true; + return 1; + } else { + /* Does the real page still exist? */ + gl_list_t real_found; + bool save_debug = debug_level; + struct timespec t; + int count; + + debug_level = false; + real_found = look_for_file (path, info->ext, + info->pointer, cat, LFF_MATCHCASE); + debug_level = save_debug; + + t.tv_sec = -1; + t.tv_nsec = -1; + count = count_glob_matches (info->pointer, info->ext, + real_found, t); + gl_list_free (real_found); + if (count) + return 0; + + if (!opt_test) + dbdelete (dbf, name, info); + else + debug ("%s(%s): whatis target was deleted, " + "would delete\n", name, info->ext); + return 1; + } +} + +/* Check that multi keys are correctly constructed. */ +static int check_multi_key (const char *name, const char *content) +{ + const char *walk, *next; + + if (!*content) + return 0; + + for (walk = content; walk && *walk; walk = next) { + /* The name in the multi key should only differ from the + * name of the key itself in its case, if at all. + */ + int valid = 1; + ++walk; /* skip over initial tab */ + next = strchr (walk, '\t'); + if (next) { + if (strncasecmp (name, walk, next - walk)) + valid = 0; + } else { + if (strcasecmp (name, walk)) + valid = 0; + } + if (!valid) { + debug ("%s: broken multi key \"%s\", " + "forcing a rescan\n", name, content); + force_rescan = true; + return 1; + } + + /* If the name was valid, skip over the extension and + * continue the scan. + */ + walk = next; + next = walk ? strchr (walk + 1, '\t') : NULL; + } + + return 0; +} + +/* Go through the database and purge references to man pages that no longer + * exist. + */ +int purge_missing (const char *database, + const char *manpath, const char *catpath, + int will_run_mandb) +{ +#ifdef NDBM + char *dirfile; +#endif + struct stat st; + int db_exists; + MYDBM_FILE dbf; + datum key; + int count = 0; + struct timespec db_mtime; + +#ifdef NDBM + dirfile = xasprintf ("%s.dir", database); + db_exists = stat (dirfile, &st) == 0; + free (dirfile); +#else + db_exists = stat (database, &st) == 0; +#endif + if (!db_exists) + /* nothing to purge */ + return 0; + + if (!quiet) + printf (_("Purging old database entries in %s...\n"), manpath); + + dbf = MYDBM_RWOPEN (database); + if (!dbf) { + gripe_rwopen_failed (database); + return 0; + } + if (!sanity_check_db (dbf)) { + MYDBM_CLOSE (dbf); + dbf = NULL; + return 0; + } + db_mtime = MYDBM_GET_TIME (dbf); + + key = MYDBM_FIRSTKEY (dbf); + + while (MYDBM_DPTR (key) != NULL) { + datum content, nextkey; + struct mandata entry; + char *nicekey, *tab; + bool save_debug; + gl_list_t found; + + /* Ignore db identifier keys. */ + if (*MYDBM_DPTR (key) == '$') { + nextkey = MYDBM_NEXTKEY (dbf, key); + MYDBM_FREE_DPTR (key); + key = nextkey; + continue; + } + + content = MYDBM_FETCH (dbf, key); + if (!MYDBM_DPTR (content)) { + nextkey = MYDBM_NEXTKEY (dbf, key); + MYDBM_FREE_DPTR (key); + key = nextkey; + continue; + } + + /* Get just the name. */ + nicekey = xstrdup (MYDBM_DPTR (key)); + tab = strchr (nicekey, '\t'); + if (tab) + *tab = '\0'; + + /* Deal with multi keys. */ + if (*MYDBM_DPTR (content) == '\t') { + if (check_multi_key (nicekey, MYDBM_DPTR (content))) + MYDBM_DELETE (dbf, key); + free (nicekey); + MYDBM_FREE_DPTR (content); + nextkey = MYDBM_NEXTKEY (dbf, key); + MYDBM_FREE_DPTR (key); + key = nextkey; + continue; + } + + split_content (dbf, MYDBM_DPTR (content), &entry); + + save_debug = debug_level; + debug_level = false; /* look_for_file() is quite noisy */ + if (entry.id <= WHATIS_MAN) + found = look_for_file (manpath, entry.ext, + entry.name ? entry.name + : nicekey, + 0, LFF_MATCHCASE); + else + found = look_for_file (catpath, entry.ext, + entry.name ? entry.name + : nicekey, + 1, LFF_MATCHCASE); + debug_level = save_debug; + + /* Now actually decide whether to purge, depending on the + * type of entry. + */ + if (entry.id == ULT_MAN || entry.id == SO_MAN || + entry.id == STRAY_CAT) + count += purge_normal (dbf, nicekey, &entry, found); + else if (entry.id == WHATIS_MAN) + count += purge_whatis (dbf, manpath, 0, nicekey, + &entry, found, db_mtime); + else /* entry.id == WHATIS_CAT */ + count += purge_whatis (dbf, catpath, 1, nicekey, + &entry, found, db_mtime); + + gl_list_free (found); + free (nicekey); + + free_mandata_elements (&entry); + nextkey = MYDBM_NEXTKEY (dbf, key); + MYDBM_FREE_DPTR (key); + key = nextkey; + } + + MYDBM_REORG (dbf); + if (will_run_mandb) + /* Reset mtime to avoid confusing mandb into not running. + * TODO: It would be better to avoid this by only opening + * the database once between here and mandb. + */ + MYDBM_SET_TIME (dbf, db_mtime); + MYDBM_CLOSE (dbf); + return count; +} diff --git a/src/check_mandirs.h b/src/check_mandirs.h new file mode 100644 index 0000000..4ed12b1 --- /dev/null +++ b/src/check_mandirs.h @@ -0,0 +1,35 @@ +/* + * check_mandirs.h: Interface to updating database caches + * + * Copyright (C) 2001, 2002 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "db_storage.h" + +/* check_mandirs.c */ +extern void test_manfile (MYDBM_FILE dbf, const char *file, const char *path); +extern void chown_if_possible (const char *path); +extern int create_db (const char *database, + const char *manpath, const char *catpath); +extern int update_db (const char *database, + const char *manpath, const char *catpath); +extern void purge_pointers (MYDBM_FILE dbf, const char *name); +extern int purge_missing (const char *database, + const char *manpath, const char *catpath, + int will_run_mandb); diff --git a/src/compression.c b/src/compression.c new file mode 100644 index 0000000..cd1028c --- /dev/null +++ b/src/compression.c @@ -0,0 +1,120 @@ +/* + * compression.c: code to find decompressor / compression extension + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Sat Aug 20 15:01:02 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "xvasprintf.h" + +#include "manconfig.h" + +#include "error.h" +#include "pipeline.h" + +/* Take filename as arg, return structure containing decompressor + and extension, or NULL if no comp extension found. + If want_stem, set comp->stem to the filename without extension, which + the caller should free. + + eg. + filename = /usr/man/man1/foo.1.gz + + comp->prog = "/usr/bin/gzip -dc"; + comp->ext = "gz"; + comp->stem = "/usr/man/man1/foo.1"; + */ +struct compression *comp_info (const char *filename, int want_stem) +{ + const char *ext; + static struct compression hpux_comp = {GUNZIP " -S \"\"", "", NULL}; + + ext = strrchr (filename, '.'); + + if (ext) { + struct compression *comp; + for (comp = comp_list; comp->ext; comp++) { + if (strcmp (comp->ext, ext + 1) == 0) { + if (want_stem) + comp->stem = xstrndup (filename, + ext - filename); + else + comp->stem = NULL; + return comp; + } + } + } + + if (*GUNZIP) { + ext = strstr (filename, ".Z/"); + if (ext) { + if (want_stem) + hpux_comp.stem = xstrndup (filename, + ext - filename); + else + hpux_comp.stem = NULL; + return &hpux_comp; + } + } + + return NULL; +} + +/* take filename w/o comp ext. as arg, return comp->stem as a relative + compressed file or NULL if none found */ +struct compression *comp_file (const char *filename) +{ + size_t len; + char *compfile; + struct compression *comp; + + compfile = xasprintf ("%s.", filename); + len = strlen (compfile); + + for (comp = comp_list; comp->ext; comp++) { + struct stat buf; + + compfile = appendstr (compfile, comp->ext, (void *) 0); + + if (stat (compfile, &buf) == 0) { + comp->stem = compfile; + return comp; + } + + *(compfile + len) = '\0'; + } + free (compfile); + return NULL; +} diff --git a/src/decompress.c b/src/decompress.c new file mode 100644 index 0000000..5cee5ff --- /dev/null +++ b/src/decompress.c @@ -0,0 +1,159 @@ +/* + * decompress.c: decompression abstraction layer + * + * Copyright (C) 2007, 2008 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +#ifdef HAVE_LIBZ +# include "zlib.h" +#endif /* HAVE_LIBZ */ + +#include "xvasprintf.h" + +#include "manconfig.h" +#include "comp_src.h" +#include "pipeline.h" +#include "decompress.h" +#include "sandbox.h" + +#ifdef HAVE_LIBZ + +static void decompress_zlib (void *data _GL_UNUSED) +{ + gzFile zlibfile; + int fd; + + fd = dup (STDIN_FILENO); + if (fd < 0) + return; + + zlibfile = gzdopen (fd, "r"); + if (!zlibfile) { + close (fd); + return; + } + + for (;;) { + char buffer[4096]; + int r = gzread (zlibfile, buffer, 4096); + if (r <= 0) + break; + if (fwrite (buffer, 1, (size_t) r, stdout) < (size_t) r) + break; + } + + gzclose (zlibfile); + return; +} + +#endif /* HAVE_LIBZ */ + +extern man_sandbox *sandbox; + +pipeline *decompress_open (const char *filename) +{ + pipecmd *cmd; + pipeline *p; + struct stat st; +#ifdef HAVE_LIBZ + size_t filename_len; +#endif /* HAVE_LIBZ */ + char *ext; + struct compression *comp; + + if (stat (filename, &st) < 0 || S_ISDIR (st.st_mode)) + return NULL; + +#ifdef HAVE_LIBZ + filename_len = strlen (filename); + if (filename_len > 3 && STREQ (filename + filename_len - 3, ".gz")) { + cmd = pipecmd_new_function ("zcat", &decompress_zlib, NULL, + NULL); + pipecmd_pre_exec (cmd, sandbox_load, sandbox_free, sandbox); + p = pipeline_new_commands (cmd, (void *) 0); + goto got_pipeline; + } +#endif /* HAVE_LIBZ */ + + ext = strrchr (filename, '.'); + if (ext) { + ++ext; + + for (comp = comp_list; comp->ext; ++comp) { + if (!STREQ (comp->ext, ext)) + continue; + + cmd = pipecmd_new_argstr (comp->prog); + pipecmd_pre_exec (cmd, sandbox_load, sandbox_free, + sandbox); + p = pipeline_new_commands (cmd, (void *) 0); + goto got_pipeline; + } + } + +#ifdef HAVE_GZIP + /* HP-UX */ + ext = strstr (filename, ".Z/"); + if (ext) { + cmd = pipecmd_new_argstr (GUNZIP); + pipecmd_pre_exec (cmd, sandbox_load, sandbox_free, sandbox); + p = pipeline_new_commands (cmd, (void *) 0); + goto got_pipeline; + } +#endif + + p = pipeline_new (); + +got_pipeline: + pipeline_want_infile (p, filename); + pipeline_want_out (p, -1); + return p; +} + +pipeline *decompress_fdopen (int fd) +{ + pipeline *p; +#ifdef HAVE_LIBZ + pipecmd *cmd; +#endif /* HAVE_LIBZ */ + +#ifdef HAVE_LIBZ + cmd = pipecmd_new_function ("zcat", &decompress_zlib, NULL, NULL); + pipecmd_pre_exec (cmd, sandbox_load, sandbox_free, sandbox); + p = pipeline_new_commands (cmd, (void *) 0); +#else /* HAVE_LIBZ */ + p = pipeline_new (); +#endif /* HAVE_LIBZ */ + + pipeline_want_in (p, fd); + pipeline_want_out (p, -1); + return p; +} diff --git a/src/decompress.h b/src/decompress.h new file mode 100644 index 0000000..35f7c51 --- /dev/null +++ b/src/decompress.h @@ -0,0 +1,40 @@ +/* + * decompress.h: interface to decompression abstraction layer + * + * Copyright (C) 2007 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAN_DECOMPRESS_H +#define MAN_DECOMPRESS_H + +#include "pipeline.h" + +struct decompress; + +/* Open a decompressor reading from FILENAME. The caller must start the + * resulting pipeline. + */ +pipeline *decompress_open (const char *filename); + +/* Open a decompressor reading from file descriptor FD. The caller must + * start the resulting pipeline. + */ +pipeline *decompress_fdopen (int fd); + +#endif /* MAN_DECOMPRESS_H */ diff --git a/src/descriptions.c b/src/descriptions.c new file mode 100644 index 0000000..5184611 --- /dev/null +++ b/src/descriptions.c @@ -0,0 +1,156 @@ +/* + * descriptions.c: manipulate man page descriptions + * + * Copyright (C) 2002, 2003, 2006, 2007, 2008, 2009, 2010, 2011 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <stdlib.h> + +#include "gl_array_list.h" +#include "gl_xlist.h" + +#include "manconfig.h" +#include "descriptions.h" + +/* Free a page description. */ +static void page_description_free (const void *value) +{ + struct page_description *desc = (struct page_description *) value; + + free (desc->name); + free (desc->whatis); + free (desc); +} + +/* Parse the description in a whatis line returned by find_name() into a + * list of names and whatis descriptions. + */ +gl_list_t parse_descriptions (const char *base, const char *whatis) +{ + const char *sep, *nextsep; + gl_list_t descs; + int seen_base = 0; + + descs = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL, + page_description_free, true); + + if (!whatis) + return descs; + + sep = whatis; + + while (sep) { + char *record; + size_t length; + const char *dash; + char *names; + const char *token; + + /* Use a while loop so that we skip over things like the + * result of double line breaks. + */ + while (*sep == 0x11 || *sep == ' ') + ++sep; + nextsep = strchr (sep, 0x11); + + /* Get this record as a null-terminated string. */ + if (nextsep) + length = (size_t) (nextsep - sep); + else + length = strlen (sep); + if (length == 0) + break; + + record = xstrndup (sep, length); + debug ("record = '%s'\n", record); + + /* Split the record into name and whatis description. */ + dash = strstr (record, " - "); + if (dash) + names = xstrndup (record, dash - record); + else if (!gl_list_size (descs)) + /* Some pages have a NAME section with just the page + * name and no whatis. We might as well include + * this. + */ + names = xstrdup (record); + else + /* Once at least one record has been seen, further + * cases where there is no whatis usually amount to + * garbage following the useful records, and can + * cause problems due to false WHATIS_MAN entries in + * the database. On the whole it seems best to + * ignore these. + */ + goto next; + + for (token = strtok (names, ","); token; + token = strtok (NULL, ",")) { + char *name = trim_spaces (token); + struct page_description *desc; + + /* Skip name tokens containing whitespace. They are + * almost never useful as manual page names. + */ + if (strpbrk (name, " \t") != NULL) { + free (name); + continue; + } + + /* Allocate new description node. */ + desc = xmalloc (sizeof *desc); + desc->name = name; /* steal memory */ + desc->whatis = dash ? trim_spaces (dash + 3) : NULL; + gl_list_add_last (descs, desc); + + if (base && STREQ (base, desc->name)) + seen_base = 1; + } + + free (names); +next: + free (record); + + sep = nextsep; + } + + /* If it isn't there already, add the base name onto the returned + * list. + */ + if (base && !seen_base) { + struct page_description *desc = xmalloc (sizeof *desc); + + desc->name = xstrdup (base); + desc->whatis = NULL; + if (gl_list_size (descs)) { + const struct page_description *first = + gl_list_get_at (descs, 0); + if (first->whatis) + desc->whatis = xstrdup (first->whatis); + } + gl_list_add_last (descs, desc); + } + + return descs; +} diff --git a/src/descriptions.h b/src/descriptions.h new file mode 100644 index 0000000..38ae412 --- /dev/null +++ b/src/descriptions.h @@ -0,0 +1,37 @@ +/* + * descriptions.h: Interface to manipulating man page descriptions + * + * Copyright (C) 2002, 2007, 2008, 2011 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gl_list.h" + +#include "db_storage.h" + +struct page_description { + char *name; + char *whatis; +}; + +/* Returns a list of struct page_description. */ +extern gl_list_t parse_descriptions (const char *base, const char *whatis); +extern void store_descriptions (MYDBM_FILE dbf, gl_list_t descs, + struct mandata *info, + const char *path, const char *base, + gl_list_t trace); diff --git a/src/descriptions_store.c b/src/descriptions_store.c new file mode 100644 index 0000000..94ab83e --- /dev/null +++ b/src/descriptions_store.c @@ -0,0 +1,151 @@ +/* + * descriptions_store.c: store man page descriptions in database + * + * Copyright (C) 2002, 2003, 2006, 2007, 2008, 2011 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "gettext.h" +#define _(String) gettext (String) + +#include "error.h" +#include "gl_list.h" + +#include "manconfig.h" + +#include "glcontainers.h" + +#include "db_storage.h" + +#include "filenames.h" +#include "ult_src.h" +#include "descriptions.h" + +static void gripe_bad_store (const char *name, const char *ext) +{ + if (quiet < 2) + error (0, 0, _("warning: failed to store entry for %s(%s)"), + name, ext); +} + +/* Is PATH a prefix of DIR, such that DIR is in the manual hierarchy PATH? + * This requires that the part of DIR following PATH start with "/man". + */ +static int is_prefix (const char *path, const char *dir) +{ + return (STRNEQ (dir, path, strlen (path)) && + STRNEQ (dir + strlen (path), "/man", 4)); +} + +/* Take a list of descriptions returned by parse_descriptions() and store + * it into the database. + */ +void store_descriptions (MYDBM_FILE dbf, gl_list_t descs, struct mandata *info, + const char *path, const char *base, gl_list_t trace) +{ + const struct page_description *desc; + char save_id = info->id; + const char *trace_name; + + if (gl_list_size (descs) && trace) { + GL_LIST_FOREACH_START (trace, trace_name) + debug ("trace: '%s'\n", trace_name); + GL_LIST_FOREACH_END (trace); + } + + GL_LIST_FOREACH_START (descs, desc) { + /* Either it's the real thing or merely a reference. Get the + * id and pointer right in either case. + */ + bool found_real_page = false; + bool found_external = false; + + if (STREQ (base, desc->name)) { + info->id = save_id; + info->pointer = NULL; + info->whatis = desc->whatis; + found_real_page = true; + } else if (trace) { + GL_LIST_FOREACH_START (trace, trace_name) { + struct mandata trace_info; + char *buf; + + buf = filename_info (trace_name, + &trace_info, ""); + if (trace_info.name && + STREQ (trace_info.name, desc->name)) { + if (path && !is_prefix (path, buf)) { + /* Link outside this manual + * hierarchy; skip this + * description. + */ + found_external = true; + free (trace_info.name); + free (buf); + break; + } + if (!gl_list_next_node (trace, + trace_node) && + save_id == SO_MAN) + info->id = ULT_MAN; + else + info->id = save_id; + info->pointer = NULL; + info->whatis = desc->whatis; + found_real_page = true; + } + + free (trace_info.name); + free (buf); + } GL_LIST_FOREACH_END (trace); + } + + if (found_external) { + debug ("skipping '%s'; link outside manual " + "hierarchy\n", desc->name); + continue; + } + + if (!found_real_page) { + if (save_id < STRAY_CAT) + info->id = WHATIS_MAN; + else + info->id = WHATIS_CAT; + info->pointer = xstrdup (base); + /* Don't waste space storing the whatis in the db + * more than once. + */ + info->whatis = NULL; + } + + debug ("name = '%s', id = %c\n", desc->name, info->id); + if (dbstore (dbf, info, desc->name) > 0) { + gripe_bad_store (base, info->ext); + break; + } + } GL_LIST_FOREACH_END (descs); +} diff --git a/src/filenames.c b/src/filenames.c new file mode 100644 index 0000000..a3ba7e5 --- /dev/null +++ b/src/filenames.c @@ -0,0 +1,151 @@ +/* + * filenames.c: compose and dissect man page file names + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "xvasprintf.h" + +#include "gettext.h" +#define _(String) gettext (String) + +#include "manconfig.h" + +#include "error.h" + +#include "db_storage.h" + +#include "filenames.h" + +static void gripe_bogus_manpage (const char *manpage) +{ + if (quiet < 2) + error (0, 0, _("warning: %s: ignoring bogus filename"), + manpage); +} + +char *make_filename (const char *path, const char *name, + struct mandata *in, const char *type) +{ + static char *file; + + if (!name) + name = in->name; /* comes from dblookup(), so non-NULL */ + + file = xasprintf ("%s/%s%s/%s.%s", path, type, in->sec, name, in->ext); + + if (in->comp && *in->comp != '-') /* Is there an extension? */ + file = appendstr (file, ".", in->comp, (void *) 0); + + debug ("Checking physical location: %s\n", file); + if (!CAN_ACCESS (file, R_OK)) { + free (file); + return NULL; + } + + return file; +} + +/* Fill in a mandata structure with information about a file name. + * file is the name to examine. info points to the structure to be filled + * in. req_name is the page name that was requested. + * + * Returns either a pointer to the buffer which the fields in info point + * into, to be freed by the caller, or NULL on error. The buffer will + * contain either three or four null-terminated strings: the directory name, + * the base of the file name in that directory, the section extension, and + * optionally the compression extension. + * + * Only the fields name, ext, sec, and comp are filled in by this function. + * name is only set if it differs from req_name; otherwise it remains at + * NULL. + */ +char *filename_info (const char *file, struct mandata *info, + const char *req_name) +{ + char *manpage = xstrdup (file); + char *slash = strrchr (manpage, '/'); + char *base_name; + struct compression *comp; + + memset (info, 0, sizeof (struct mandata)); + + if (slash) { + *slash = '\0'; /* strip '/base_name' */ + base_name = slash + 1; + } else + base_name = manpage; + + /* Bogus files either have (i) no period, ie no extension, (ii) + a compression extension, but no sectional extension, (iii) + a missmatch between the section they are under and the + sectional part of their extension. */ + + comp = comp_info (base_name, 1); + if (comp) { + info->comp = comp->ext; + *(base_name + strlen (comp->stem)) = '\0'; + free (comp->stem); + } else + info->comp = NULL; + + { + char *ext = strrchr (base_name, '.'); + if (!ext) { + /* no section extension */ + gripe_bogus_manpage (file); + free (manpage); + return NULL; + } + *ext++ = '\0'; /* set section ext */ + info->ext = ext; + if (!*info->ext) { + /* zero-length section extension */ + gripe_bogus_manpage (file); + free (manpage); + return NULL; + } + } + + info->sec = strrchr (manpage, '/') + 4; /* set section name */ + + if (strlen (info->sec) >= 1 && strlen (info->ext) >= 1 && + info->sec[0] != info->ext[0]) { + /* mismatch in section */ + gripe_bogus_manpage (file); + free (manpage); + return NULL; + } + + if (req_name && !STREQ (base_name, req_name)) + info->name = xstrdup (base_name); + else + info->name = NULL; + + return manpage; +} diff --git a/src/filenames.h b/src/filenames.h new file mode 100644 index 0000000..d7fd8a1 --- /dev/null +++ b/src/filenames.h @@ -0,0 +1,28 @@ +/* + * filenames.h: Interface to composing and dissecting man page file names + * + * Copyright (C) 2001, 2002 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "db_storage.h" + +extern char *make_filename (const char *path, const char *name, + struct mandata *in, const char *type); +extern char *filename_info (const char *file, struct mandata *info, + const char *req_name); diff --git a/src/globbing.c b/src/globbing.c new file mode 100644 index 0000000..017c361 --- /dev/null +++ b/src/globbing.c @@ -0,0 +1,405 @@ +/* + * globbing.c: interface to the POSIX glob routines + * + * Copyright (C) 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Mon Mar 13 20:27:36 GMT 1995 Wilf. (G.Wilford@ee.surrey.ac.uk) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <glob.h> +#include <sys/types.h> +#include <dirent.h> + +#include "fnmatch.h" +#include "gl_array_list.h" +#include "gl_hash_map.h" +#include "gl_xlist.h" +#include "gl_xmap.h" +#include "regex.h" +#include "xvasprintf.h" + +#include "manconfig.h" + +#include "error.h" +#include "glcontainers.h" +#include "cleanup.h" +#include "xregcomp.h" + +#include "globbing.h" + +const char *extension; +static const char *mandir_layout = MANDIR_LAYOUT; + +static char *make_pattern (const char *name, const char *sec, int opts) +{ + char *pattern; + + if (opts & LFF_REGEX) { + if (extension) { + char *esc_ext = escape_shell (extension); + pattern = xasprintf ("%s\\..*%s.*", name, esc_ext); + free (esc_ext); + } else { + char *esc_sec = escape_shell (sec); + pattern = xasprintf ("%s\\.%s.*", name, esc_sec); + free (esc_sec); + } + } else { + if (extension) + pattern = xasprintf ("%s.*%s*", name, extension); + else + pattern = xasprintf ("%s.%s*", name, sec); + } + + return pattern; +} + +#define LAYOUT_GNU 1 +#define LAYOUT_HPUX 2 +#define LAYOUT_IRIX 4 +#define LAYOUT_SOLARIS 8 +#define LAYOUT_BSD 16 + +static int parse_layout (const char *layout) +{ + if (!*layout) + return LAYOUT_GNU | LAYOUT_HPUX | LAYOUT_IRIX | + LAYOUT_SOLARIS | LAYOUT_BSD; + else { + int flags = 0; + + char *upper_layout = xstrdup (layout); + char *layoutp; + for (layoutp = upper_layout; *layoutp; layoutp++) + *layoutp = CTYPE (toupper, *layoutp); + + if (strstr (upper_layout, "GNU")) + flags |= LAYOUT_GNU; + if (strstr (upper_layout, "HPUX")) + flags |= LAYOUT_HPUX; + if (strstr (upper_layout, "IRIX")) + flags |= LAYOUT_IRIX; + if (strstr (upper_layout, "SOLARIS")) + flags |= LAYOUT_SOLARIS; + if (strstr (upper_layout, "BSD")) + flags |= LAYOUT_BSD; + + free (upper_layout); + return flags; + } +} + +struct dirent_names { + char **names; + size_t names_len, names_max; +}; + +static void dirent_names_free (const void *value) +{ + struct dirent_names *cache = (struct dirent_names *) value; + size_t i; + + for (i = 0; i < cache->names_len; ++i) + free (cache->names[i]); + free (cache->names); + free (cache); +} + +static gl_map_t dirent_map = NULL; + +static int cache_compare (const void *a, const void *b) +{ + const char *left = *(const char **) a; + const char *right = *(const char **) b; + return strcasecmp (left, right); +} + +static struct dirent_names *update_directory_cache (const char *path) +{ + struct dirent_names *cache; + DIR *dir; + struct dirent *entry; + + if (!dirent_map) { + dirent_map = new_string_map (GL_HASH_MAP, dirent_names_free); + push_cleanup ((cleanup_fun) gl_map_free, dirent_map, 0); + } + cache = (struct dirent_names *) gl_map_get (dirent_map, path); + + /* Check whether we've got this one already. */ + if (cache) { + debug ("update_directory_cache %s: hit\n", path); + return cache; + } + + debug ("update_directory_cache %s: miss\n", path); + + dir = opendir (path); + if (!dir) { + debug_error ("can't open directory %s", path); + return NULL; + } + + cache = XMALLOC (struct dirent_names); + cache->names_len = 0; + cache->names_max = 1024; + cache->names = XNMALLOC (cache->names_max, char *); + + /* Dump all the entries into cache->names, resizing if necessary. */ + for (entry = readdir (dir); entry; entry = readdir (dir)) { + if (cache->names_len >= cache->names_max) { + cache->names_max *= 2; + cache->names = + xnrealloc (cache->names, cache->names_max, + sizeof (char *)); + } + cache->names[cache->names_len++] = xstrdup (entry->d_name); + } + + qsort (cache->names, cache->names_len, sizeof *cache->names, + &cache_compare); + + gl_map_put (dirent_map, xstrdup (path), cache); + closedir (dir); + + return cache; +} + +struct pattern_bsearch { + char *pattern; + size_t len; +}; + +static int pattern_compare (const void *a, const void *b) +{ + const struct pattern_bsearch *key = a; + const char *memb = *(const char **) b; + return strncasecmp (key->pattern, memb, key->len); +} + +static void match_in_directory (const char *path, const char *pattern, + int opts, gl_list_t matched) +{ + struct dirent_names *cache; + int flags; + regex_t preg; + struct pattern_bsearch pattern_start = { NULL, -1 }; + char **bsearched; + size_t i; + + cache = update_directory_cache (path); + if (!cache) { + debug ("directory cache update failed\n"); + return; + } + + debug ("globbing pattern in %s: %s\n", path, pattern); + + if (opts & LFF_REGEX) + flags = REG_EXTENDED | REG_NOSUB | + ((opts & LFF_MATCHCASE) ? 0 : REG_ICASE); + else + flags = (opts & LFF_MATCHCASE) ? 0 : FNM_CASEFOLD; + + if (opts & LFF_REGEX) { + xregcomp (&preg, pattern, flags); + bsearched = cache->names; + } else { + pattern_start.pattern = xstrndup (pattern, + strcspn (pattern, "?*{}\\")); + pattern_start.len = strlen (pattern_start.pattern); + bsearched = bsearch (&pattern_start, cache->names, + cache->names_len, sizeof *cache->names, + &pattern_compare); + if (!bsearched) { + free (pattern_start.pattern); + return; + } + while (bsearched > cache->names && + !strncasecmp (pattern_start.pattern, *(bsearched - 1), + pattern_start.len)) + --bsearched; + } + + for (i = bsearched - cache->names; i < cache->names_len; ++i) { + if (opts & LFF_REGEX) { + if (regexec (&preg, cache->names[i], 0, NULL, 0) != 0) + continue; + } else { + if (strncasecmp (pattern_start.pattern, + cache->names[i], pattern_start.len)) + break; + + if (fnmatch (pattern, cache->names[i], flags) != 0) + continue; + } + + debug ("matched: %s/%s\n", path, cache->names[i]); + + gl_list_add_last (matched, + xasprintf ("%s/%s", path, cache->names[i])); + } + + if (opts & LFF_REGEX) + regfree (&preg); + else + free (pattern_start.pattern); +} + +gl_list_t look_for_file (const char *hier, const char *sec, + const char *unesc_name, int cat, int opts) +{ + gl_list_t matched; + char *pattern, *path = NULL; + static int layout = -1; + char *name; + + matched = new_string_list (GL_ARRAY_LIST, false); + + /* This routine only does a minimum amount of matching. It does not + find cat files in the alternate cat directory. */ + + if (layout == -1) { + layout = parse_layout (mandir_layout); + debug ("Layout is %s (%d)\n", mandir_layout, layout); + } + + if (opts & (LFF_REGEX | LFF_WILDCARD)) + name = xstrdup (unesc_name); + else + name = escape_shell (unesc_name); + + /* allow lookups like "3x foo" to match "../man3/foo.3x" */ + + if (layout & LAYOUT_GNU) { + gl_list_t dirs; + const char *dir; + + dirs = new_string_list (GL_ARRAY_LIST, false); + pattern = xasprintf ("%s\t*", cat ? "cat" : "man"); + *strrchr (pattern, '\t') = *sec; + match_in_directory (hier, pattern, LFF_MATCHCASE, dirs); + free (pattern); + + pattern = make_pattern (name, sec, opts); + GL_LIST_FOREACH_START (dirs, dir) { + if (path) + *path = '\0'; + match_in_directory (dir, pattern, opts, matched); + } GL_LIST_FOREACH_END (dirs); + free (pattern); + gl_list_free (dirs); + } + + /* Try HPUX style compressed man pages */ + if ((layout & LAYOUT_HPUX) && gl_list_size (matched) == 0) { + if (path) + *path = '\0'; + path = appendstr (path, hier, cat ? "/cat" : "/man", + sec, ".Z", (void *) 0); + pattern = make_pattern (name, sec, opts); + + match_in_directory (path, pattern, opts, matched); + free (pattern); + } + + /* Try man pages without the section extension --- IRIX man pages */ + if ((layout & LAYOUT_IRIX) && gl_list_size (matched) == 0) { + if (path) + *path = '\0'; + path = appendstr (path, hier, cat ? "/cat" : "/man", sec, + (void *) 0); + if (opts & LFF_REGEX) + pattern = xasprintf ("%s\\..*", name); + else + pattern = xasprintf ("%s.*", name); + + match_in_directory (path, pattern, opts, matched); + free (pattern); + } + + /* Try Solaris style man page directories */ + if ((layout & LAYOUT_SOLARIS) && gl_list_size (matched) == 0) { + if (path) + *path = '\0'; + /* TODO: This needs to be man/sec*, not just man/sec. */ + path = appendstr (path, hier, cat ? "/cat" : "/man", sec, + (void *) 0); + pattern = make_pattern (name, sec, opts); + + match_in_directory (path, pattern, opts, matched); + free (pattern); + } + + /* BSD cat pages take the extension .0 */ + if ((layout & LAYOUT_BSD) && gl_list_size (matched) == 0) { + if (path) + *path = '\0'; + if (cat) { + path = appendstr (path, hier, "/cat", sec, (void *) 0); + if (opts & LFF_REGEX) + pattern = xasprintf ("%s\\.0.*", name); + else + pattern = xasprintf ("%s.0*", name); + } else { + path = appendstr (path, hier, "/man", sec, (void *) 0); + pattern = make_pattern (name, sec, opts); + } + match_in_directory (path, pattern, opts, matched); + free (pattern); + } + + free (name); + free (path); + + return matched; +} + +gl_list_t expand_path (const char *path) +{ + int res = 0; + gl_list_t result; + glob_t globbuf; + + result = new_string_list (GL_ARRAY_LIST, false); + + res = glob (path, GLOB_NOCHECK, NULL, &globbuf); + /* if glob failed, return the given path */ + if (res != 0) + gl_list_add_last (result, xstrdup (path)); + else { + size_t i; + for (i = 0; i < globbuf.gl_pathc; ++i) + gl_list_add_last (result, + xstrdup (globbuf.gl_pathv[i])); + } + + globfree (&globbuf); + + return result; +} diff --git a/src/globbing.h b/src/globbing.h new file mode 100644 index 0000000..ea9ff00 --- /dev/null +++ b/src/globbing.h @@ -0,0 +1,36 @@ +/* + * globbing.h: Headers for glob routines + * + * Copyright (C) 2001, 2002, 2007, 2008 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gl_list.h" + +enum look_for_file_opts { + LFF_MATCHCASE = 1, + LFF_REGEX = 2, + LFF_WILDCARD = 4 +}; + +/* globbing.c */ +extern gl_list_t look_for_file (const char *hier, const char *sec, + const char *unesc_name, int cat, int opts); + +/* Expand path with wildcards into list of all existing directories. */ +extern gl_list_t expand_path (const char *path); diff --git a/src/globbing_test.c b/src/globbing_test.c new file mode 100644 index 0000000..7133c05 --- /dev/null +++ b/src/globbing_test.c @@ -0,0 +1,131 @@ +/* + * globbing_test.c: test program for file-finding functions + * + * Copyright (C) 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2006, 2007, 2008 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#include "argp.h" +#include "gl_list.h" +#include "progname.h" + +#include "gettext.h" +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "error.h" +#include "glcontainers.h" +#include "globbing.h" + +extern const char *extension; +static bool match_case = false; +static bool regex_opt = false; +static bool wildcard = false; +static char **remaining_args; + +const char *argp_program_version = "globbing " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("PATH SECTION NAME"); + +static struct argp_option options[] = { + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "extension", 'e', N_("EXTENSION"), 0, N_("limit search to extension type EXTENSION") }, + { "ignore-case", 'i', 0, 0, N_("look for pages case-insensitively (default)") }, + { "match-case", 'I', 0, 0, N_("look for pages case-sensitively") }, + { "regex", 'r', 0, 0, N_("interpret page name as a regex") }, + { "wildcard", 'w', 0, 0, N_("the page name contains wildcards") }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'd': + debug_level = true; + return 0; + case 'e': + extension = arg; + return 0; + case 'i': + match_case = false; + return 0; + case 'I': + match_case = true; + return 0; + case 'r': + regex_opt = true; + return 0; + case 'w': + wildcard = true; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP); + break; + case ARGP_KEY_ARGS: + if (state->argc - state->next != 3) + argp_usage (state); + remaining_args = state->argv + state->next; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp = { options, parse_opt, args_doc }; + +int main (int argc, char **argv) +{ + int i; + + set_program_name (argv[0]); + + init_debug (); + init_locale (); + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + + for (i = 0; i <= 1; i++) { + gl_list_t files; + const char *file; + + files = look_for_file (remaining_args[0], remaining_args[1], + remaining_args[2], i, + (match_case ? LFF_MATCHCASE : 0) | + (regex_opt ? LFF_REGEX : 0) | + (wildcard ? LFF_WILDCARD : 0)); + GL_LIST_FOREACH_START (files, file) + printf ("%s\n", file); + GL_LIST_FOREACH_END (files); + gl_list_free (files); + } + return 0; +} diff --git a/src/lexgrog.c b/src/lexgrog.c new file mode 100644 index 0000000..150cd26 --- /dev/null +++ b/src/lexgrog.c @@ -0,0 +1,5281 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "manconfig.h" + +/* Flex emits several functions which might reasonably have various + * attributes applied and many unused macros; none of these are our problem. + */ +#if GNUC_PREREQ(8,0) +# pragma GCC diagnostic ignored "-Wsuggest-attribute=malloc" +#endif +#pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" +#pragma GCC diagnostic ignored "-Wunused-macros" + +#line 17 "lexgrog.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = NULL; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_flush_buffer ( YY_BUFFER_STATE b ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state ( void ); + +static void yyensure_buffer_stack ( void ); +static void yy_load_buffer_state ( void ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); + +void *yyalloc ( yy_size_t ); +void *yyrealloc ( void *, yy_size_t ); +void yyfree ( void * ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap() (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +FILE *yyin = NULL, *yyout = NULL; + +typedef int yy_state_type; + +extern int yylineno; +int yylineno = 1; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yynoreturn yy_fatal_error ( const char* msg ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; +#define YY_NUM_RULES 141 +#define YY_END_OF_BUFFER 142 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[1494] = + { 0, + 0, 0, 0, 0, 67, 67, 67, 67, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 119, 0, 0, + 0, 0, 0, 0, 126, 126, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 142, 141, 29, 29, + 29, 140, 67, 67, 140, 140, 140, 138, 140, 136, + 83, 67, 67, 140, 83, 106, 106, 106, 101, 106, + 108, 108, 107, 113, 113, 113, 112, 118, 118, 118, + 118, 118, 119, 121, 121, 120, 123, 123, 122, 125, + 125, 124, 126, 126, 140, 137, 140, 140, 140, 7, + 10, 7, 7, 6, 6, 6, 4, 18, 18, 18, + + 19, 19, 0, 28, 0, 27, 27, 29, 67, 0, + 138, 0, 0, 139, 67, 67, 139, 135, 139, 139, + 135, 83, 67, 0, 0, 138, 0, 136, 70, 67, + 76, 0, 62, 0, 0, 64, 66, 0, 0, 61, + 63, 61, 0, 0, 0, 0, 0, 0, 67, 0, + 67, 67, 139, 135, 83, 67, 0, 64, 0, 106, + 0, 0, 101, 94, 95, 96, 97, 98, 99, 100, + 108, 107, 113, 0, 0, 0, 112, 112, 118, 0, + 0, 0, 119, 121, 120, 123, 122, 125, 124, 126, + 137, 0, 0, 126, 126, 139, 139, 139, 139, 126, + + 137, 54, 0, 126, 39, 7, 0, 9, 0, 10, + 7, 6, 0, 3, 4, 3, 4, 3, 18, 0, + 0, 0, 18, 19, 28, 0, 0, 0, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 57, 0, + 0, 67, 0, 0, 36, 36, 36, 36, 36, 36, + 36, 36, 0, 50, 0, 0, 0, 0, 0, 0, + 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 48, 0, 57, 0, 49, 0, 58, 76, + 72, 0, 0, 0, 0, 0, 0, 0, 0, 75, + 75, 75, 0, 0, 0, 0, 0, 0, 69, 0, + + 77, 78, 0, 79, 0, 0, 81, 0, 0, 67, + 0, 0, 36, 36, 36, 36, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 93, 102, 103, 104, 105, 101, 94, 95, 96, 97, + 98, 99, 100, 0, 0, 0, 0, 0, 0, 60, + 0, 126, 38, 38, 126, 38, 38, 38, 38, 37, + 54, 7, 0, 0, 0, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 5, 3, 3, 4, 4, 4, 3, + 0, 0, 0, 0, 0, 16, 0, 21, 27, 27, + + 27, 27, 20, 27, 27, 27, 57, 0, 0, 0, + 0, 0, 0, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 51, 41, 0, 0, 0, 0, 0, 128, + 59, 0, 130, 47, 129, 0, 0, 30, 0, 0, + 0, 127, 34, 53, 0, 35, 33, 52, 32, 0, + 0, 0, 49, 84, 73, 62, 55, 63, 68, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 85, 0, 87, 92, 88, 89, 90, 91, 0, 0, + 0, 64, 0, 93, 102, 103, 104, 105, 0, 0, + 0, 116, 114, 0, 38, 38, 38, 38, 37, 38, + 38, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 5, 3, + 3, 4, 4, 4, 0, 3, 3, 12, 14, 13, + 15, 11, 17, 21, 25, 26, 20, 22, 24, 23, + + 55, 0, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 30, 36, 36, 36, 36, 34, 36, 36, 35, + 33, 36, 32, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 40, 132, 131, 45, 128, 59, 46, 47, 129, + 134, 133, 30, 42, 44, 31, 127, 34, 34, 53, + 43, 35, 33, 52, 32, 0, 84, 55, 74, 0, + 71, 56, 80, 82, 36, 36, 36, 36, 36, 36, + + 36, 36, 36, 36, 36, 36, 36, 36, 85, 86, + 87, 92, 88, 89, 90, 91, 0, 65, 0, 110, + 0, 116, 117, 114, 115, 38, 38, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 3, 25, + + 26, 22, 24, 23, 56, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 30, 36, 36, 31, 36, 34, + 34, 36, 36, 35, 33, 36, 32, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 132, + 131, 45, 134, 133, 44, 43, 56, 56, 82, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 86, + 65, 109, 110, 111, 117, 115, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 7, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 109, 111, + 7, 0, 2, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 4, 1, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 36, 36, 36, 7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 1, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 1, 4, 1, 4, 4, 4, + + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 1, 1, 4, + 4, 4, 4, 4, 4, 1, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 3, 3, 4, + 4, 1, 0, 1, 0, 0, 0, 0, 0, 0, + + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8, 0, 0, 1, + 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + + 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5, 6, 7, 8, 9, 6, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 1, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 27, 1, 1, 1, + 1, 1, 1, 1, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 6, 57, 58, 59, 60, 61, 62, + + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 6, 85, 6, 86, 1, 1, 87, 1, + 1, 88, 89, 90, 1, 1, 91, 92, 1, 1, + 93, 1, 1, 94, 95, 96, 1, 1, 97, 1, + 98, 99, 1, 1, 1, 100, 101, 102, 1, 1, + 103, 1, 1, 104, 1, 105, 106, 107, 108, 109, + 1, 1, 110, 111, 112, 113, 114, 1, 1, 115, + 1, 1, 1, 1, 116, 1, 117, 1, 118, 119, + 1, 1, 1, 1, 120, 121, 1, 1, 1, 1, + + 1, 1, 1, 122, 1, 1, 1, 123, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 124, 1, 1, 1, 125, 126, 1, + 127, 1, 1, 1, 128, 129, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[130] = + { 0, + 1, 2, 3, 4, 2, 1, 5, 1, 1, 1, + 6, 7, 1, 1, 1, 1, 1, 1, 8, 9, + 9, 9, 9, 8, 8, 8, 8, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 1, 1, 1, 1, 1, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1 + } ; + +static const flex_int16_t yy_base[1677] = + { 0, + 0, 0, 0, 2, 6, 0, 134, 138, 141, 142, + 222, 0, 351, 0, 140, 477, 5447, 5446, 555, 0, + 684, 0, 813, 0, 145, 154, 149, 162, 164, 165, + 0, 0, 169, 174, 5444, 5443, 5446, 7367, 7367, 194, + 5442, 7367, 480, 487, 941, 5389, 5387, 179, 5423, 0, + 994, 952, 959, 5436, 476, 7367, 5371, 5352, 190, 486, + 7367, 5369, 0, 7367, 5368, 144, 212, 7367, 5367, 5302, + 5305, 5308, 0, 7367, 5363, 0, 7367, 5362, 0, 7367, + 5361, 0, 964, 970, 5359, 498, 204, 5355, 5302, 0, + 515, 5354, 5342, 7367, 5343, 5290, 982, 0, 510, 5341, + + 7367, 5340, 529, 533, 5339, 0, 1048, 1018, 1083, 1099, + 521, 5324, 1027, 7367, 1104, 1115, 1126, 5285, 5284, 5321, + 1180, 938, 1144, 5330, 5250, 536, 538, 0, 7367, 954, + 0, 1151, 7367, 1121, 475, 543, 7367, 974, 483, 5255, + 7367, 7367, 5320, 0, 5292, 5296, 1239, 982, 1134, 948, + 1162, 1200, 5299, 1239, 1028, 1271, 1225, 7367, 1071, 7367, + 5221, 1173, 1228, 945, 986, 1013, 1045, 1060, 1109, 1139, + 7367, 0, 7367, 5226, 5232, 5231, 1155, 1186, 7367, 5215, + 5220, 5225, 0, 7367, 0, 7367, 0, 7367, 0, 1290, + 1234, 1295, 5285, 1300, 1306, 5284, 978, 5281, 5228, 1311, + + 1316, 1320, 5281, 1325, 7367, 0, 1329, 1335, 5280, 1339, + 5277, 7367, 5274, 5273, 5272, 5271, 1321, 5270, 0, 1204, + 5273, 1315, 1210, 7367, 7367, 5272, 1275, 5271, 0, 1348, + 1372, 5202, 165, 1354, 5200, 1183, 5193, 1313, 1349, 1342, + 1344, 1422, 1430, 1438, 5267, 1448, 1464, 962, 980, 157, + 1518, 1593, 1383, 7367, 5266, 1409, 5225, 1444, 5224, 424, + 1417, 1442, 1461, 5194, 1349, 0, 5189, 5201, 472, 1027, + 5199, 1448, 7367, 5250, 1416, 1409, 0, 5248, 1474, 0, + 7367, 0, 5192, 148, 5190, 5164, 5179, 5166, 5174, 7367, + 0, 0, 1497, 0, 0, 923, 5160, 5175, 7367, 0, + + 7367, 7367, 0, 7367, 0, 1594, 7367, 1504, 0, 1537, + 1119, 5238, 1532, 5236, 1678, 1753, 5159, 1659, 5161, 5126, + 878, 5125, 5124, 1480, 1080, 5122, 5133, 1097, 5116, 5131, + 1524, 1528, 1543, 1547, 1553, 1566, 1570, 1572, 1574, 1579, + 1581, 1586, 1604, 5123, 5118, 5115, 5131, 5118, 5117, 1620, + 1624, 1629, 0, 1633, 1689, 5186, 1716, 5183, 5130, 7367, + 1637, 0, 1838, 5183, 1847, 1860, 5182, 462, 5144, 481, + 919, 1870, 5155, 5062, 5135, 5067, 5060, 87, 5067, 1241, + 5068, 5075, 5170, 0, 5167, 5166, 5165, 5164, 1695, 5163, + 5125, 5148, 5121, 5146, 5119, 7367, 5109, 0, 1628, 1680, + + 1684, 1685, 1703, 1710, 1726, 1732, 1747, 1320, 5070, 5084, + 1357, 5067, 5082, 5145, 1773, 1781, 1066, 1792, 1854, 1203, + 1160, 1786, 1798, 5144, 1890, 1198, 1948, 1427, 1784, 1914, + 1910, 1969, 1107, 1817, 2050, 975, 1095, 1665, 1857, 1221, + 1928, 5143, 2005, 0, 2003, 5142, 5140, 1978, 5129, 1851, + 5137, 2011, 1976, 1112, 5109, 5108, 2092, 2118, 2191, 1396, + 2047, 2029, 7367, 1923, 1749, 1764, 1800, 1816, 1843, 1856, + 1867, 1936, 1942, 1950, 1990, 1995, 2015, 2031, 2043, 2053, + 2054, 2103, 2274, 0, 2104, 2105, 2114, 0, 2170, 1420, + 5027, 5042, 0, 2171, 7367, 7367, 2202, 7367, 7367, 0, + + 0, 5093, 5047, 5046, 0, 0, 5094, 5089, 5096, 2277, + 1142, 2282, 1021, 1150, 1883, 1435, 1507, 2227, 5095, 2242, + 2203, 5013, 2204, 2208, 2209, 2210, 2244, 2276, 1426, 5011, + 5026, 7367, 5036, 2286, 2295, 2297, 2299, 2305, 5025, 5031, + 5026, 2307, 2309, 5025, 0, 2314, 2318, 5082, 0, 2324, + 0, 0, 2341, 5083, 2363, 1509, 5053, 1684, 1604, 2334, + 1673, 1753, 1828, 1947, 2358, 2245, 5035, 4991, 1395, 1378, + 4959, 4958, 4971, 4986, 4960, 4988, 4961, 5070, 0, 5067, + 5066, 5065, 5064, 2491, 2619, 5063, 5062, 7367, 7367, 7367, + 7367, 7367, 7367, 0, 0, 0, 2328, 0, 0, 0, + + 2332, 5003, 2286, 2353, 0, 1515, 2337, 2418, 2370, 2392, + 2410, 2429, 2434, 2438, 2442, 2446, 2450, 2454, 2469, 2473, + 2477, 2497, 2501, 2505, 2509, 2513, 2519, 0, 2538, 2542, + 2546, 0, 2550, 0, 5052, 0, 4995, 1440, 4993, 4968, + 4983, 4970, 4977, 5039, 0, 2554, 1266, 2355, 1441, 1483, + 5038, 2746, 5035, 5034, 2772, 5031, 1553, 2547, 5030, 2573, + 1633, 7367, 0, 0, 0, 2345, 2349, 2456, 2521, 2528, + 0, 0, 2558, 2559, 0, 2575, 2577, 2583, 2585, 0, + 0, 2587, 2589, 0, 2591, 4974, 2593, 2595, 7367, 4973, + 7367, 2597, 7367, 0, 2601, 1567, 2605, 2625, 2629, 2633, + + 2637, 2641, 1702, 4945, 4960, 2465, 1698, 1591, 2645, 2647, + 2649, 2656, 2662, 2663, 2667, 2668, 4970, 7367, 4966, 2673, + 4954, 2674, 2675, 2679, 2680, 2685, 2689, 0, 2184, 2693, + 2176, 5010, 1238, 2318, 2331, 2672, 2394, 2621, 2396, 2703, + 2414, 2855, 2456, 2630, 4993, 4979, 4978, 4904, 4925, 4909, + 4913, 4918, 4887, 2748, 4884, 4879, 4879, 5001, 0, 0, + 0, 0, 2927, 2869, 2667, 4965, 2661, 2671, 2840, 4975, + 4882, 1827, 4955, 4887, 4880, 114, 4887, 1815, 4887, 4893, + 2904, 2683, 4953, 2680, 2689, 2841, 4962, 4868, 1847, 4938, + 4866, 4858, 1383, 4854, 1981, 4854, 4858, 0, 0, 0, + + 0, 0, 0, 0, 2707, 2755, 1727, 1715, 2757, 1920, + 1777, 0, 2714, 4952, 0, 0, 0, 2720, 2724, 2734, + 2752, 2759, 0, 0, 2763, 2767, 0, 2859, 2876, 2888, + 2920, 0, 0, 2947, 2952, 0, 2970, 4950, 4921, 2974, + 4920, 4919, 0, 2314, 987, 3054, 3080, 2058, 2398, 0, + 0, 0, 0, 0, 0, 0, 2754, 2861, 0, 2978, + 2982, 2997, 3001, 3005, 3009, 3013, 3017, 4918, 1945, 2865, + 7367, 2890, 2933, 2937, 2941, 2960, 0, 2829, 3032, 2964, + 4917, 2829, 4908, 4869, 2852, 2841, 3132, 2895, 4886, 4823, + 4801, 4790, 4789, 4797, 1991, 1143, 2283, 4789, 4787, 4796, + + 4895, 0, 0, 3203, 3145, 2946, 4857, 2946, 2981, 3155, + 4842, 4749, 1974, 4822, 4753, 4745, 1835, 4749, 2616, 4747, + 4754, 2968, 4819, 3135, 2982, 3140, 2980, 2991, 2998, 2999, + 3223, 2955, 4800, 4755, 3321, 3264, 1891, 2862, 4723, 4720, + 4733, 4740, 4714, 4742, 4714, 2987, 4796, 3139, 3000, 3347, + 2994, 3005, 3007, 3098, 3348, 3122, 4778, 4715, 4794, 3289, + 2462, 2938, 4678, 4675, 4688, 4702, 4675, 4675, 4647, 0, + 3173, 2377, 3214, 0, 0, 0, 0, 0, 0, 0, + 3224, 4758, 4757, 3233, 4756, 3251, 3259, 4746, 3075, 3240, + 0, 3135, 3247, 3138, 3152, 3198, 4712, 3293, 2264, 2968, + + 4656, 4635, 3186, 4630, 4586, 4584, 4585, 4570, 4590, 4672, + 0, 0, 3431, 3408, 3267, 4624, 3265, 3285, 3350, 4634, + 4536, 2396, 4609, 4541, 4533, 2510, 4539, 3206, 4529, 4535, + 3293, 4602, 3349, 3338, 3424, 3331, 3343, 3344, 3345, 3462, + 3231, 4584, 4538, 3549, 3588, 2495, 3193, 4506, 4505, 4517, + 4502, 4476, 4504, 4477, 3357, 3388, 3350, 4575, 1552, 3352, + 3359, 3425, 3367, 3404, 3400, 3454, 3422, 3614, 3430, 3412, + 4558, 4544, 3660, 3593, 3659, 3689, 4537, 4459, 4477, 4435, + 4440, 4439, 4408, 3400, 4405, 4400, 4400, 3444, 3511, 3438, + 4515, 2434, 3449, 3452, 3596, 3453, 3464, 3462, 3618, 3481, + + 3726, 3487, 3467, 4495, 4481, 0, 3698, 3750, 3761, 4480, + 4405, 4426, 4408, 4413, 4417, 4377, 3536, 4374, 4368, 4368, + 0, 3335, 3560, 3571, 0, 3509, 4454, 3647, 3662, 4364, + 4361, 4358, 4367, 3744, 3757, 3780, 4337, 3789, 4449, 4442, + 4441, 3889, 3831, 3771, 1875, 3599, 3773, 3872, 3295, 1296, + 3540, 1390, 2211, 2378, 3651, 1024, 3473, 1601, 2389, 3747, + 2731, 3807, 3716, 3909, 3757, 3802, 3826, 3836, 3967, 3772, + 3172, 1971, 2721, 3979, 3551, 3549, 2331, 2502, 2528, 3328, + 2878, 2574, 2946, 3818, 3844, 3789, 2914, 2510, 3822, 3848, + 3906, 3870, 3941, 3862, 3851, 3914, 4023, 3908, 3956, 3396, + + 1525, 4435, 4017, 4068, 4030, 3499, 2717, 3230, 1264, 3066, + 1287, 3440, 3640, 2646, 2476, 2167, 3954, 3735, 3965, 3780, + 3966, 2494, 3216, 4020, 3921, 4027, 4037, 3226, 4091, 2998, + 3017, 3435, 3234, 2506, 3072, 3165, 3858, 3744, 3816, 3646, + 3061, 3321, 3590, 4099, 3630, 3567, 3860, 4407, 4363, 3882, + 3938, 4114, 3956, 4378, 4397, 4312, 4323, 4288, 4259, 4244, + 4242, 4251, 3714, 3391, 3741, 4242, 4239, 4217, 4311, 7367, + 3990, 4279, 3542, 4211, 4197, 4105, 4120, 4124, 4128, 4132, + 4145, 4158, 4162, 4168, 4172, 4213, 4295, 0, 0, 7367, + 4151, 3974, 4013, 4031, 4284, 4188, 4013, 4017, 4001, 4259, + + 4202, 4281, 4113, 3812, 3943, 4192, 4171, 4072, 4180, 4157, + 4154, 4163, 4138, 4158, 0, 0, 4137, 4205, 4081, 4074, + 4073, 4045, 4179, 4197, 4228, 4051, 4137, 4154, 0, 4188, + 4215, 4242, 4076, 4231, 4247, 0, 4262, 4257, 4258, 3940, + 3938, 3932, 3922, 4264, 4299, 4319, 3897, 4323, 4220, 3959, + 3888, 3879, 3867, 4244, 4311, 3869, 3853, 3869, 4315, 4006, + 3803, 3790, 4337, 4338, 4344, 4345, 4351, 4355, 4359, 4375, + 4396, 4400, 4406, 4414, 4415, 3758, 4252, 3799, 4392, 4421, + 3709, 4241, 4412, 3795, 3755, 3620, 3601, 3597, 3559, 4438, + 4514, 4483, 4439, 4568, 4620, 4457, 4674, 4646, 3568, 3553, + + 3526, 4269, 4711, 4445, 0, 4451, 3521, 3446, 3439, 3427, + 4737, 4469, 4789, 4470, 4843, 4476, 4798, 4484, 4897, 4569, + 4949, 4576, 3382, 3380, 4314, 4445, 3453, 4577, 4685, 3364, + 4637, 4686, 4903, 4662, 4955, 4959, 4731, 4976, 4980, 3347, + 3339, 3244, 3179, 5041, 5085, 5137, 5181, 5233, 5277, 3137, + 3125, 3108, 2936, 5329, 4797, 5357, 4852, 5356, 4856, 5384, + 4860, 5436, 4984, 5464, 4990, 2904, 2852, 2838, 2799, 4735, + 4814, 4818, 2771, 2650, 2644, 2624, 2548, 2545, 2510, 2431, + 2327, 2328, 2192, 2160, 2178, 2010, 1954, 1896, 1899, 1886, + 1747, 1709, 7367, 5517, 5528, 5539, 5550, 5561, 5572, 5583, + + 5594, 5605, 5616, 5627, 5631, 5642, 5653, 5664, 5675, 5686, + 5697, 5708, 5719, 5730, 5741, 5752, 5763, 5767, 5778, 5789, + 5800, 5811, 5816, 5817, 5822, 5833, 5844, 5855, 5866, 5877, + 5888, 5899, 5910, 5921, 5932, 5943, 5954, 5965, 1693, 5976, + 5987, 5998, 6009, 6020, 6031, 6042, 6053, 1627, 6055, 6066, + 6077, 6088, 6099, 6110, 6121, 6132, 6143, 6154, 6165, 6176, + 6187, 6198, 6209, 6220, 6231, 6242, 6253, 6264, 6275, 1130, + 6277, 6288, 6299, 6310, 6321, 6332, 6343, 6354, 6365, 6376, + 6387, 6398, 6409, 6420, 6431, 6442, 6453, 6464, 6475, 6486, + 6497, 6508, 6519, 6530, 6541, 6552, 6563, 6574, 6585, 6596, + + 6607, 6618, 6629, 6640, 6651, 6662, 6673, 6684, 6695, 6706, + 6717, 6728, 6739, 6750, 6761, 6772, 6783, 6794, 6805, 6816, + 6827, 6838, 6849, 6860, 6871, 6882, 6893, 6904, 6915, 6926, + 6937, 6948, 6959, 6970, 6981, 6992, 7003, 7014, 7025, 7036, + 7047, 7058, 7069, 7080, 7091, 7096, 7106, 7117, 7128, 7139, + 7150, 7161, 7172, 7183, 7194, 7205, 7216, 7227, 1114, 7238, + 7249, 7260, 7271, 7282, 7293, 7300, 7310, 7315, 7319, 517, + 505, 7329, 7340, 7351, 7356, 170 + } ; + +static const flex_int16_t yy_def[1677] = + { 0, + 1494, 1494, 1495, 1495, 1493, 5, 5, 5, 1496, 1496, + 1493, 11, 1493, 13, 1497, 1497, 1498, 1498, 1493, 19, + 1493, 21, 1493, 23, 1499, 1499, 1500, 1500, 1501, 1501, + 1494, 1494, 1502, 1502, 1503, 1503, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1504, 1493, 1493, 1493, 1493, 1493, 1505, + 1493, 1493, 1504, 1493, 51, 1493, 1493, 1493, 1506, 1493, + 1493, 1493, 1507, 1493, 1493, 1493, 1508, 1493, 1493, 1493, + 1493, 1493, 1509, 1493, 1493, 1510, 1493, 1493, 1511, 1493, + 1493, 1512, 1493, 1504, 1493, 1493, 1493, 1493, 1493, 1513, + 1493, 1513, 1513, 1493, 1493, 1493, 1514, 1515, 1493, 1515, + + 1493, 1493, 1493, 1493, 1493, 1516, 1516, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1517, 1493, 1493, 1493, 1493, + 1493, 51, 1504, 1493, 1493, 1493, 1493, 1518, 1493, 1493, + 1519, 1493, 1493, 1520, 1521, 1493, 1493, 1493, 1522, 1493, + 1493, 1493, 1523, 1524, 1525, 1493, 1493, 1493, 1493, 1493, + 1493, 1517, 1493, 121, 51, 1504, 1520, 1493, 1522, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1526, 1493, 1493, 1493, 1493, 1527, 1493, 1493, 1493, + 1493, 1493, 1528, 1493, 1529, 1493, 1530, 1493, 1531, 1493, + 1493, 1493, 1493, 1493, 1532, 1493, 1493, 1493, 1493, 1504, + + 1493, 1493, 1493, 1493, 1493, 1533, 1493, 1493, 1493, 1493, + 1533, 1493, 1493, 1534, 1535, 1534, 1535, 1534, 1536, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1537, 1537, + 1537, 1537, 1537, 1537, 1537, 1537, 1537, 1537, 1493, 1493, + 1493, 1493, 1538, 1493, 1538, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1539, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1540, 1493, 1493, 1541, 1493, 1493, 1542, + 1493, 1543, 1543, 1543, 1543, 1543, 1543, 1543, 1543, 1493, + 1544, 1545, 1493, 1546, 1547, 1547, 1547, 1547, 1493, 1548, + + 1493, 1493, 1549, 1493, 1550, 1493, 1493, 1493, 1551, 1493, + 1538, 1493, 1538, 1538, 1538, 1538, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1543, 1543, 1543, 1547, 1547, 1547, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1552, 1552, 1553, 1552, 1552, 1552, 1552, 1493, + 1493, 1554, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1554, 1555, 1556, 1556, 1557, 1557, 1557, 1558, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1559, 1560, 1560, + + 1560, 1560, 1560, 1560, 1560, 1560, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1538, 1538, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1538, 1561, 1538, 1538, 1538, 1562, 1563, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 435, 435, 435, 1538, + 1538, 1538, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1564, 1493, 1493, 1493, 1565, 1493, 1493, + 1493, 1493, 1566, 1493, 1493, 1493, 1493, 1493, 1493, 1567, + + 1568, 1569, 1493, 1493, 1570, 1571, 1572, 1573, 1538, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1574, 1538, 1538, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1575, 1575, 1575, 1575, 1575, 1575, + 1575, 1576, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1576, 1577, 1578, + 1578, 1579, 1579, 1579, 1493, 1580, 1580, 1493, 1493, 1493, + 1493, 1493, 1493, 1581, 1582, 1583, 1584, 1585, 1586, 1587, + + 1493, 1493, 1538, 1538, 1588, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1589, 1538, 1538, + 1538, 1590, 1538, 1591, 1538, 1592, 1592, 1592, 1592, 1592, + 1592, 1592, 1592, 1538, 1593, 1538, 1594, 1538, 1538, 1538, + 1538, 1538, 1538, 1538, 652, 1538, 1595, 1538, 1538, 1538, + 1596, 1493, 1597, 1598, 1599, 1493, 1493, 1493, 1493, 1493, + 1600, 1601, 1493, 1493, 1602, 1493, 1493, 1493, 1493, 1603, + 1604, 1493, 1493, 1605, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1606, 1538, 1538, 1538, 1538, 1538, 1538, + + 1538, 1538, 1607, 1607, 1607, 1538, 1538, 1538, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1608, 1608, 1609, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1609, 1610, 1611, + 1612, 1613, 1612, 1613, 1612, 1612, 1612, 1612, 1612, 1612, + 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1614, 1615, 1616, + + 1617, 1618, 1619, 1620, 1493, 1538, 1538, 1538, 1538, 1538, + 1538, 1621, 1538, 1538, 1622, 1623, 1624, 1538, 1538, 1538, + 1538, 1538, 1625, 1626, 1538, 1538, 1627, 1538, 1538, 1538, + 1538, 1628, 1629, 1538, 1538, 1630, 1538, 1538, 1538, 1538, + 1538, 1538, 1631, 1632, 1538, 1538, 846, 1633, 1634, 1635, + 1636, 1637, 1638, 1639, 1640, 1641, 1493, 1493, 1642, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1538, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1643, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + + 1643, 1644, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, + 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, + 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, + 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, + 1645, 1645, 1645, 1645, 1645, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1646, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1647, + 1538, 1538, 1538, 1648, 1649, 1650, 1651, 1652, 1653, 1654, + 1538, 1538, 1538, 1538, 1538, 1655, 1538, 1538, 1493, 1493, + 1656, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1656, + 1657, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, + 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, + 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, + 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, + 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, + 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, + 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, + 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + + 1493, 1493, 1493, 1493, 1493, 1659, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1660, 1538, 1538, 1655, 1661, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1662, 1663, 1664, 1493, 1493, 1661, 1665, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1666, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + + 1493, 1659, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1666, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1229, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1667, 1493, + 1493, 1493, 1493, 1493, 1493, 1663, 1662, 1662, 1662, 1663, + 1663, 1663, 1664, 1664, 1664, 1493, 1493, 1665, 1665, 1493, + 1493, 1493, 1493, 1493, 1668, 1493, 1493, 1493, 1493, 1493, + + 1493, 1669, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1667, 1667, 1493, 1493, 1493, 1493, + 1493, 1493, 1662, 1663, 1664, 1493, 1493, 1493, 1670, 1493, + 1493, 1493, 1493, 1493, 1493, 1671, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1672, 1673, 1674, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1668, 1493, 1493, 1493, 1669, 1493, + 1493, 1493, 1673, 1672, 1672, 1672, 1672, 1673, 1673, 1673, + 1673, 1674, 1674, 1674, 1674, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1675, 1493, 1493, 1493, 1493, 1493, 1672, + 1672, 1672, 1673, 1673, 1673, 1674, 1674, 1674, 1493, 1493, + + 1493, 1493, 1493, 1493, 1676, 1493, 1493, 1493, 1493, 1493, + 1672, 1672, 1672, 1672, 1673, 1673, 1673, 1673, 1674, 1674, + 1674, 1674, 1493, 1493, 1493, 1675, 1493, 1493, 1493, 1493, + 1391, 1672, 1672, 1394, 1673, 1673, 1397, 1674, 1674, 1493, + 1493, 1493, 1493, 1672, 1391, 1673, 1394, 1674, 1397, 1493, + 1493, 1493, 1493, 1672, 1672, 1411, 1672, 1673, 1673, 1415, + 1673, 1674, 1674, 1419, 1674, 1493, 1493, 1493, 1493, 1445, + 1447, 1449, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 0, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493 + } ; + +static const flex_int16_t yy_nxt[7497] = + { 0, + 1493, 1493, 40, 41, 40, 41, 42, 43, 44, 45, + 43, 42, 42, 42, 42, 42, 46, 47, 42, 42, + 42, 48, 49, 46, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 42, + 51, 42, 42, 42, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 52, 53, 54, 52, 52, + 53, 54, 52, 69, 57, 57, 83, 84, 85, 83, + 42, 91, 92, 93, 42, 83, 84, 85, 83, 1493, + 86, 87, 58, 58, 91, 92, 93, 95, 95, 86, + 87, 99, 100, 422, 96, 96, 99, 100, 70, 1426, + 126, 97, 97, 126, 71, 571, 72, 572, 55, 59, + 59, 163, 55, 399, 163, 103, 104, 105, 103, 88, + + 399, 89, 174, 175, 106, 176, 202, 203, 88, 70, + 89, 107, 939, 178, 940, 71, 178, 72, 497, 497, + 60, 60, 61, 61, 61, 62, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 61, 61, 61, 61, 61, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + + 63, 63, 63, 63, 63, 63, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 64, 64, 64, 65, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 66, 64, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + + 67, 67, 67, 67, 64, 64, 64, 64, 64, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 69, 109, 110, 110, 109, 471, 291, 157, 115, 116, + 117, 115, 158, 556, 472, 111, 112, 118, 119, 201, + + 201, 201, 201, 120, 121, 164, 165, 166, 167, 168, + 169, 170, 220, 221, 1359, 70, 207, 208, 209, 207, + 558, 71, 126, 72, 556, 126, 1355, 222, 292, 159, + 103, 225, 226, 103, 113, 227, 228, 126, 484, 279, + 126, 122, 279, 106, 279, 296, 70, 279, 297, 485, + 107, 558, 71, 298, 72, 74, 74, 74, 75, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + + 76, 76, 76, 76, 76, 76, 76, 76, 74, 74, + 74, 74, 74, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 77, 77, 77, 78, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + + 77, 77, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 77, 77, 77, + 77, 77, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 80, 80, 80, 81, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 80, 80, 80, 80, + 80, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 80, 80, 80, + + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 110, 123, 110, 110, 337, 273, 472, 337, + 559, 274, 130, 149, 275, 130, 149, 112, 130, 526, + 151, 152, 153, 151, 1493, 190, 130, 111, 190, 118, + 119, 194, 195, 196, 194, 293, 154, 1493, 293, 191, + 192, 559, 1493, 308, 294, 215, 308, 338, 216, 1493, + 338, 276, 309, 504, 504, 112, 129, 129, 130, 129, + + 131, 131, 132, 129, 133, 134, 150, 135, 150, 360, + 136, 137, 130, 155, 339, 197, 420, 339, 193, 103, + 104, 105, 103, 1493, 198, 130, 199, 217, 106, 360, + 1290, 130, 138, 130, 421, 107, 218, 273, 240, 157, + 360, 274, 984, 239, 158, 130, 340, 139, 140, 340, + 626, 141, 129, 138, 129, 129, 142, 143, 217, 138, + 360, 341, 144, 138, 341, 145, 146, 129, 1493, 129, + 147, 130, 129, 138, 148, 138, 230, 129, 129, 130, + 241, 324, 422, 231, 109, 110, 110, 109, 232, 486, + 486, 487, 233, 234, 235, 698, 236, 1493, 111, 112, + + 110, 110, 110, 110, 130, 242, 243, 244, 242, 1493, + 342, 237, 130, 342, 1493, 112, 246, 116, 247, 246, + 112, 311, 509, 1255, 238, 248, 249, 110, 116, 110, + 110, 250, 251, 328, 965, 149, 329, 113, 149, 301, + 343, 330, 112, 343, 1493, 115, 116, 117, 115, 111, + 532, 532, 1493, 112, 118, 119, 178, 627, 113, 178, + 120, 121, 1493, 310, 311, 312, 310, 533, 533, 252, + 281, 281, 281, 281, 281, 281, 281, 281, 623, 283, + 112, 253, 254, 284, 253, 285, 286, 178, 150, 651, + 178, 287, 332, 333, 334, 335, 288, 255, 122, 289, + + 1493, 313, 152, 314, 313, 1493, 220, 221, 256, 605, + 248, 249, 220, 221, 257, 258, 150, 315, 259, 695, + 260, 222, 261, 1493, 262, 263, 264, 222, 265, 336, + 266, 699, 336, 890, 124, 201, 201, 201, 201, 267, + 610, 268, 405, 269, 606, 405, 270, 332, 333, 334, + 335, 271, 1004, 306, 316, 306, 272, 307, 307, 307, + 307, 307, 307, 307, 307, 307, 317, 318, 502, 319, + 1290, 320, 151, 152, 153, 151, 414, 227, 228, 321, + 322, 118, 119, 283, 632, 106, 323, 325, 154, 285, + 326, 190, 107, 1290, 190, 327, 350, 350, 350, 350, + + 288, 352, 1290, 289, 352, 191, 192, 354, 355, 356, + 354, 351, 194, 195, 196, 194, 192, 201, 201, 201, + 201, 361, 361, 361, 361, 155, 204, 388, 731, 204, + 207, 363, 364, 207, 574, 575, 365, 366, 367, 365, + 207, 208, 209, 207, 193, 731, 391, 403, 392, 398, + 407, 357, 398, 407, 193, 389, 197, 393, 403, 394, + 358, 395, 359, 368, 403, 198, 389, 199, 396, 1259, + 369, 370, 389, 398, 371, 372, 398, 373, 403, 193, + 1261, 374, 399, 399, 253, 254, 389, 253, 403, 403, + 601, 601, 399, 399, 397, 403, 1290, 389, 1493, 403, + + 400, 375, 657, 389, 408, 403, 411, 409, 480, 412, + 464, 480, 410, 464, 413, 958, 400, 407, 473, 403, + 407, 473, 401, 242, 243, 244, 242, 602, 602, 1493, + 403, 415, 243, 416, 415, 961, 403, 1493, 112, 110, + 243, 110, 110, 1493, 465, 464, 417, 474, 464, 418, + 110, 415, 418, 465, 112, 376, 377, 378, 379, 475, + 380, 747, 381, 382, 417, 415, 116, 415, 415, 614, + 465, 490, 467, 476, 491, 279, 113, 465, 279, 492, + 417, 963, 478, 964, 417, 1493, 468, 477, 467, 465, + 686, 686, 112, 478, 469, 478, 717, 717, 293, 478, + + 465, 293, 419, 749, 750, 308, 478, 294, 308, 1493, + 840, 840, 478, 478, 309, 748, 701, 1493, 417, 423, + 254, 489, 423, 845, 478, 534, 478, 813, 534, 535, + 478, 1290, 535, 443, 1493, 424, 443, 478, 310, 311, + 312, 310, 529, 478, 536, 530, 425, 536, 537, 845, + 531, 537, 426, 427, 538, 507, 428, 538, 429, 414, + 430, 729, 431, 432, 433, 1099, 434, 336, 435, 1493, + 336, 337, 420, 338, 337, 339, 338, 436, 339, 437, + 340, 438, 341, 340, 439, 341, 510, 342, 702, 440, + 342, 150, 729, 1493, 441, 129, 442, 443, 442, 444, + + 444, 445, 446, 447, 448, 343, 449, 1290, 343, 450, + 451, 443, 307, 307, 307, 307, 307, 307, 307, 307, + 307, 350, 350, 350, 350, 350, 350, 350, 350, 398, + 352, 452, 398, 352, 546, 508, 505, 546, 361, 361, + 361, 361, 1186, 414, 734, 192, 453, 454, 861, 547, + 455, 442, 452, 442, 442, 456, 457, 869, 452, 1186, + 464, 458, 452, 464, 459, 460, 442, 1493, 442, 461, + 443, 442, 452, 462, 452, 734, 442, 442, 443, 423, + 254, 398, 423, 193, 398, 595, 596, 548, 595, 596, + 354, 355, 356, 354, 465, 424, 584, 585, 584, 584, + + 1493, 583, 481, 465, 597, 511, 512, 597, 513, 968, + 514, 598, 426, 427, 598, 731, 428, 1493, 515, 516, + 430, 740, 431, 432, 433, 517, 434, 599, 435, 1493, + 599, 628, 420, 600, 357, 522, 600, 436, 732, 437, + 523, 438, 629, 358, 439, 359, 731, 549, 407, 440, + 464, 407, 740, 464, 441, 129, 442, 443, 442, 444, + 444, 445, 446, 447, 518, 663, 449, 549, 663, 519, + 451, 443, 868, 868, 415, 110, 415, 415, 549, 1493, + 869, 971, 415, 243, 415, 415, 1493, 607, 1493, 417, + 607, 452, 741, 418, 110, 415, 418, 417, 549, 423, + + 254, 464, 423, 733, 464, 1339, 520, 454, 417, 971, + 455, 442, 452, 442, 442, 456, 457, 664, 452, 1493, + 664, 458, 452, 741, 459, 460, 442, 417, 442, 461, + 443, 442, 452, 462, 452, 417, 442, 442, 443, 365, + 553, 554, 365, 972, 665, 615, 419, 665, 365, 553, + 554, 365, 607, 1493, 616, 607, 1493, 666, 443, 1493, + 666, 555, 366, 367, 555, 603, 368, 742, 667, 1492, + 422, 667, 443, 369, 370, 368, 624, 371, 372, 624, + 373, 1290, 369, 370, 374, 1493, 371, 372, 368, 373, + 935, 608, 1493, 374, 608, 369, 370, 560, 742, 371, + + 372, 561, 373, 936, 375, 562, 374, 604, 942, 943, + 959, 563, 1493, 375, 947, 617, 1493, 564, 617, 630, + 630, 631, 1493, 960, 464, 609, 375, 464, 565, 662, + 1493, 443, 561, 1048, 609, 1049, 562, 668, 609, 443, + 668, 620, 563, 473, 618, 609, 473, 1493, 564, 608, + 1493, 669, 608, 616, 669, 621, 619, 1077, 376, 377, + 378, 379, 622, 380, 700, 381, 382, 376, 377, 378, + 379, 1493, 380, 622, 381, 382, 611, 1290, 1493, 622, + 376, 377, 378, 379, 1491, 380, 743, 381, 382, 566, + 612, 670, 611, 622, 670, 1129, 671, 609, 613, 671, + + 988, 633, 972, 622, 622, 1493, 443, 1493, 609, 443, + 622, 1078, 646, 1493, 622, 646, 672, 743, 1490, 672, + 622, 647, 635, 635, 635, 635, 635, 635, 635, 635, + 660, 1493, 673, 660, 622, 673, 637, 1044, 648, 661, + 638, 649, 639, 640, 674, 622, 650, 674, 641, 1493, + 1045, 622, 1493, 642, 675, 676, 643, 675, 676, 510, + 507, 658, 1105, 658, 985, 659, 659, 659, 659, 659, + 659, 659, 659, 659, 966, 967, 1489, 625, 625, 625, + 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, + 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, + + 625, 625, 625, 652, 677, 681, 682, 677, 681, 682, + 1488, 653, 653, 653, 653, 683, 1002, 1003, 683, 653, + 653, 653, 653, 653, 653, 653, 653, 653, 653, 653, + 653, 653, 653, 653, 653, 653, 653, 653, 653, 653, + 653, 653, 653, 653, 653, 654, 654, 654, 654, 654, + 654, 654, 654, 654, 654, 654, 654, 654, 654, 654, + 654, 654, 654, 654, 654, 654, 654, 654, 654, 654, + 654, 685, 687, 1290, 685, 687, 654, 654, 654, 654, + 654, 654, 654, 654, 654, 654, 654, 654, 654, 654, + 654, 654, 654, 654, 654, 654, 654, 654, 654, 654, + + 654, 654, 655, 688, 709, 711, 688, 709, 711, 712, + 713, 714, 712, 713, 714, 878, 882, 1290, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 1493, 715, 878, 882, 715, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 656, 678, 1487, 716, 678, 1493, + 716, 443, 1486, 608, 1493, 637, 608, 534, 1493, 703, + 534, 639, 704, 679, 1268, 443, 535, 705, 536, 535, + + 537, 536, 642, 537, 706, 643, 538, 707, 722, 538, + 724, 722, 708, 724, 1485, 546, 502, 609, 546, 726, + 350, 726, 726, 790, 983, 550, 609, 723, 550, 597, + 547, 744, 597, 688, 727, 561, 688, 1290, 607, 1493, + 679, 607, 555, 553, 554, 555, 666, 744, 806, 666, + 667, 807, 561, 667, 443, 1493, 808, 1493, 696, 740, + 1128, 735, 443, 697, 555, 553, 554, 555, 548, 368, + 740, 608, 1493, 736, 608, 1129, 369, 370, 548, 1493, + 371, 372, 737, 373, 1290, 735, 738, 374, 1005, 1006, + 740, 368, 735, 815, 1493, 1290, 815, 736, 369, 370, + + 508, 740, 371, 372, 736, 373, 737, 375, 986, 374, + 739, 608, 1493, 737, 608, 809, 735, 739, 810, 608, + 1493, 1484, 608, 811, 814, 845, 845, 1483, 736, 375, + 816, 1493, 1122, 816, 740, 817, 1493, 737, 817, 818, + 1493, 739, 818, 819, 1493, 885, 819, 820, 1493, 740, + 820, 617, 1493, 1114, 617, 821, 1493, 668, 821, 1173, + 668, 376, 377, 378, 379, 740, 380, 1493, 381, 382, + 822, 1493, 1174, 822, 823, 1493, 885, 823, 824, 1493, + 740, 824, 1290, 376, 377, 378, 379, 740, 380, 969, + 381, 382, 763, 585, 763, 763, 962, 764, 825, 1493, + + 1290, 825, 826, 1493, 1297, 826, 827, 1493, 1290, 827, + 828, 1493, 1290, 828, 829, 1493, 1290, 829, 740, 765, + 830, 1493, 669, 830, 1090, 669, 766, 767, 1110, 670, + 768, 769, 670, 770, 1290, 869, 869, 771, 831, 833, + 1493, 1090, 833, 834, 1493, 772, 834, 835, 1493, 1493, + 835, 837, 1493, 1482, 837, 646, 1493, 773, 646, 673, + 674, 1206, 673, 674, 647, 659, 659, 659, 659, 659, + 659, 659, 659, 659, 660, 1493, 676, 660, 677, 676, + 1290, 677, 1111, 661, 678, 831, 678, 678, 682, 678, + 683, 682, 685, 683, 687, 685, 688, 687, 858, 688, + + 1090, 858, 860, 1493, 1267, 860, 862, 1493, 1177, 862, + 1178, 774, 775, 776, 777, 1207, 778, 1090, 779, 780, + 585, 585, 585, 585, 1115, 781, 863, 1493, 1304, 863, + 864, 1493, 1481, 864, 865, 1493, 1116, 865, 866, 1493, + 1480, 866, 867, 1493, 1339, 867, 709, 782, 870, 709, + 711, 870, 1290, 711, 783, 784, 884, 712, 785, 786, + 712, 787, 1119, 713, 714, 788, 713, 714, 715, 716, + 885, 715, 716, 789, 873, 722, 875, 873, 722, 875, + 724, 876, 888, 724, 876, 790, 726, 350, 726, 726, + 726, 350, 726, 726, 879, 880, 881, 879, 922, 740, + + 924, 885, 925, 740, 879, 880, 881, 879, 858, 1051, + 1052, 858, 740, 888, 946, 973, 1493, 1479, 973, 948, + 949, 818, 1493, 1290, 818, 819, 1493, 1290, 819, 922, + 740, 924, 1106, 925, 740, 820, 1493, 1290, 820, 791, + 792, 793, 794, 740, 795, 946, 796, 797, 1493, 895, + 948, 949, 895, 821, 1493, 858, 821, 1493, 858, 1493, + 822, 1493, 1089, 822, 825, 1493, 1478, 825, 826, 1493, + 1266, 826, 1477, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 847, + + 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 847, 847, 847, 847, 847, 971, 971, 972, 972, 1110, + 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 847, 847, 847, 847, 847, 847, 879, 880, 881, 879, + 828, 1493, 858, 828, 992, 858, 870, 926, 950, 870, + 740, 927, 951, 896, 897, 928, 952, 829, 1493, 740, + 829, 929, 953, 886, 1290, 995, 887, 930, 954, 830, + 1493, 989, 830, 1476, 989, 992, 740, 765, 931, 955, + + 1475, 740, 927, 951, 766, 767, 928, 952, 768, 769, + 740, 770, 929, 953, 886, 771, 995, 887, 930, 954, + 1290, 830, 1493, 772, 830, 1248, 996, 740, 904, 585, + 904, 904, 782, 905, 873, 773, 1474, 873, 990, 783, + 784, 990, 875, 785, 786, 875, 787, 1473, 834, 1493, + 788, 834, 1290, 835, 1493, 906, 835, 996, 789, 932, + 956, 876, 907, 908, 876, 993, 909, 910, 993, 911, + 790, 837, 1493, 912, 837, 981, 1493, 1031, 981, 860, + 1493, 913, 860, 987, 1493, 1033, 987, 1079, 1080, 774, + 775, 776, 777, 914, 778, 1118, 779, 780, 862, 1493, + + 1129, 862, 863, 1493, 1290, 863, 864, 1493, 1031, 864, + 865, 1493, 1034, 865, 866, 1493, 1033, 866, 867, 1493, + 1055, 867, 1060, 1290, 791, 792, 793, 794, 1066, 795, + 1067, 796, 797, 879, 880, 881, 879, 1068, 1069, 1088, + 1093, 1070, 1099, 1034, 1100, 1036, 1101, 915, 916, 917, + 918, 1055, 919, 1060, 920, 921, 1493, 1070, 1469, 1066, + 1120, 1067, 1036, 1112, 1113, 1130, 1131, 1290, 1068, 1069, + 1088, 1093, 1290, 1099, 1301, 1100, 989, 1101, 1290, 989, + 1302, 653, 653, 653, 653, 653, 653, 653, 653, 653, + 653, 653, 653, 653, 653, 653, 653, 653, 653, 653, + + 653, 653, 653, 653, 653, 653, 653, 656, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 879, 880, 881, 879, 1102, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 1126, 1260, 1057, 1061, 1102, 731, + 1090, 1290, 740, 1015, 981, 1493, 1313, 981, 1290, 1062, + 1016, 1017, 1035, 740, 1018, 1019, 1036, 1020, 1063, 1058, + 1037, 1021, 1064, 1091, 1305, 1126, 1038, 1057, 1061, 1022, + + 731, 1090, 1039, 740, 1013, 585, 1013, 1013, 1103, 1014, + 1062, 1023, 951, 1040, 740, 973, 1493, 1036, 973, 1063, + 1104, 1037, 1290, 1065, 1103, 981, 1493, 1038, 981, 951, + 1468, 1015, 1290, 1039, 1123, 1493, 1290, 1123, 1016, 1017, + 1290, 990, 1018, 1019, 990, 1020, 740, 1467, 993, 1021, + 1061, 993, 414, 859, 1059, 414, 1300, 1022, 1092, 1466, + 987, 1493, 1062, 987, 1089, 1024, 1025, 1026, 1027, 1023, + 1028, 1063, 1029, 1030, 1041, 1065, 1306, 740, 1075, 1453, + 1075, 1061, 1076, 1076, 1076, 1076, 1076, 1076, 1076, 1076, + 1076, 1133, 1006, 1062, 879, 880, 881, 879, 1160, 1180, + + 1181, 1290, 1063, 1108, 1162, 1108, 1065, 1109, 1109, 1109, + 1109, 1109, 1109, 1109, 1109, 1109, 1163, 1199, 1208, 1209, + 1258, 1165, 957, 1024, 1025, 1026, 1027, 1290, 1028, 1160, + 1029, 1030, 1073, 1199, 1290, 1162, 1123, 1493, 1165, 1123, + 1074, 1074, 1074, 1074, 1452, 1184, 1303, 1163, 1074, 1074, + 1074, 1074, 1074, 1074, 1074, 1074, 1074, 1074, 1074, 1074, + 1074, 1074, 1074, 1074, 1074, 1074, 1074, 1074, 1074, 1074, + 1074, 1074, 1074, 1074, 1094, 1094, 1184, 1164, 1189, 1195, + 1186, 1165, 1196, 1197, 1198, 1166, 1095, 1095, 1217, 1218, + 1221, 1167, 1218, 1195, 1219, 1096, 1096, 1168, 1195, 1097, + + 1098, 1237, 1290, 1187, 1237, 1094, 1094, 1195, 1169, 1189, + 1195, 1186, 1165, 1196, 1197, 1198, 1166, 1095, 1095, 1217, + 1117, 1221, 1167, 1254, 1195, 1314, 1096, 1096, 1168, 1195, + 1098, 1098, 1142, 585, 1142, 1142, 1144, 1143, 1195, 1223, + 1451, 1290, 1220, 1145, 1146, 1450, 1290, 1147, 1148, 1224, + 1149, 1190, 1195, 1224, 1150, 1218, 1195, 1195, 1218, 1144, + 1219, 1195, 1151, 1191, 1227, 1195, 1145, 1146, 1188, 1170, + 1147, 1148, 1192, 1149, 1152, 1243, 1193, 1150, 1247, 1290, + 1224, 1258, 1190, 1195, 1224, 1151, 1443, 1195, 1195, 1190, + 1099, 1099, 1195, 1099, 1191, 1227, 1195, 1152, 1099, 1249, + + 1309, 1191, 1441, 1192, 1440, 1290, 1243, 1194, 1220, 1247, + 1192, 1250, 1244, 1250, 1194, 1244, 1099, 1245, 1099, 1253, + 1190, 1099, 1099, 1430, 1099, 1238, 1239, 1303, 1153, 1154, + 1155, 1156, 1191, 1157, 1429, 1158, 1159, 1263, 1099, 1428, + 1263, 1192, 1250, 1271, 1250, 1194, 1290, 1099, 1427, 1099, + 1253, 1153, 1154, 1155, 1156, 1290, 1157, 1290, 1158, 1159, + 1202, 1123, 1493, 1262, 1123, 1246, 966, 967, 1203, 1203, + 1203, 1203, 414, 859, 1271, 414, 1203, 1203, 1203, 1203, + 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, + 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203, + + 1203, 1203, 1204, 959, 1204, 1290, 1205, 1205, 1205, 1205, + 1205, 1205, 1205, 1205, 1205, 1218, 960, 1110, 1218, 1244, + 1219, 1144, 1244, 1099, 1245, 1293, 1424, 1099, 1145, 1146, + 1295, 1294, 1147, 1148, 1294, 1149, 1099, 1319, 948, 1150, + 1320, 1263, 1225, 1296, 1263, 1226, 1290, 1230, 879, 880, + 881, 879, 1290, 1423, 1099, 1195, 1293, 1290, 1099, 1152, + 1346, 1264, 1265, 879, 880, 881, 879, 1099, 1220, 948, + 1099, 1111, 1246, 1225, 1112, 1113, 1226, 1205, 1205, 1205, + 1205, 1205, 1205, 1205, 1205, 1205, 1195, 1229, 1229, 1229, + 1229, 1229, 1229, 1229, 1229, 1229, 1229, 1229, 1229, 1229, + + 1229, 1229, 1229, 1229, 1229, 1229, 1229, 1229, 1229, 1229, + 1229, 1229, 1229, 1153, 1154, 1155, 1156, 1144, 1157, 1410, + 1158, 1159, 1290, 1409, 1145, 1146, 782, 1244, 1147, 1148, + 1244, 1149, 1245, 783, 784, 1150, 1244, 785, 786, 1244, + 787, 1292, 1408, 1231, 788, 1278, 880, 1279, 1278, 963, + 1290, 964, 1256, 1290, 1251, 1152, 1093, 1252, 1281, 880, + 1282, 1281, 1312, 1290, 790, 1264, 1265, 1099, 1109, 1109, + 1109, 1109, 1109, 1109, 1109, 1109, 1109, 1290, 1290, 1290, + 1246, 1284, 880, 1285, 1284, 1251, 1290, 1093, 1252, 782, + 879, 880, 881, 879, 1407, 1290, 783, 784, 1099, 1088, + + 785, 786, 946, 787, 949, 1099, 1405, 788, 1290, 1153, + 1154, 1155, 1156, 1290, 1157, 1257, 1158, 1159, 791, 792, + 793, 794, 1290, 795, 1290, 796, 797, 790, 1290, 1247, + 1088, 1401, 1290, 946, 1258, 949, 1099, 1290, 1090, 1307, + 1308, 1100, 1290, 1295, 740, 1244, 1310, 1311, 1244, 1243, + 1292, 1399, 1244, 1309, 1290, 1244, 1296, 1292, 1103, 782, + 1247, 1091, 951, 1099, 1290, 1101, 783, 784, 1290, 1090, + 785, 786, 1100, 787, 1103, 1102, 1290, 788, 1290, 951, + 1243, 791, 792, 793, 794, 789, 795, 1099, 796, 797, + 585, 585, 585, 585, 1099, 1291, 1101, 790, 1246, 950, + + 1389, 1099, 1388, 951, 1385, 1246, 1102, 952, 1338, 1099, + 1099, 1250, 1290, 953, 1290, 1290, 1384, 782, 1099, 954, + 1290, 1310, 1311, 1339, 783, 784, 1092, 1290, 785, 786, + 955, 787, 1099, 1099, 951, 788, 1094, 1099, 952, 1099, + 1099, 1099, 1250, 789, 953, 1383, 1099, 1290, 1095, 1099, + 954, 791, 792, 793, 794, 790, 795, 1096, 796, 797, + 1290, 1097, 1290, 1381, 1099, 1298, 1294, 1094, 1099, 1294, + 1099, 1290, 1290, 1290, 1380, 1294, 1249, 1099, 1294, 1095, + 1099, 1379, 1298, 1307, 1308, 1290, 1378, 1299, 1096, 1293, + 1250, 956, 1098, 1108, 1094, 1108, 1298, 1109, 1109, 1109, + + 1109, 1109, 1109, 1109, 1109, 1109, 1095, 1099, 1253, 791, + 792, 793, 794, 1298, 795, 1096, 796, 797, 1299, 1098, + 1293, 1250, 1376, 1290, 1244, 1094, 1290, 1244, 1244, 1292, + 1317, 1244, 1294, 1292, 1363, 1294, 1290, 1095, 1099, 1253, + 1340, 1341, 1328, 1290, 1090, 782, 1096, 1099, 1099, 1099, + 1098, 1251, 783, 784, 1252, 1362, 785, 786, 782, 787, + 1361, 1317, 1360, 788, 1099, 783, 784, 1099, 1299, 785, + 786, 1256, 787, 1328, 1290, 1090, 788, 1246, 1099, 1099, + 1099, 1246, 1251, 790, 1257, 1252, 1109, 1109, 1109, 1109, + 1109, 1109, 1109, 1109, 1109, 1099, 790, 1290, 1099, 1299, + + 1244, 1386, 1493, 1244, 1387, 1245, 1281, 880, 1282, 1281, + 1493, 1493, 1493, 1493, 1244, 1244, 1358, 1244, 1244, 1245, + 1245, 1323, 880, 1279, 1323, 1323, 880, 1279, 1323, 1323, + 880, 1279, 1323, 1324, 880, 1282, 1324, 791, 792, 793, + 794, 1327, 795, 1136, 796, 797, 1324, 880, 1282, 1324, + 791, 792, 793, 794, 1099, 795, 740, 796, 797, 1324, + 880, 1282, 1324, 1325, 880, 1285, 1325, 1246, 1246, 1325, + 880, 1285, 1325, 1325, 880, 1285, 1325, 1343, 1311, 782, + 1323, 880, 1279, 1323, 1349, 1099, 783, 784, 1354, 1244, + 785, 786, 1244, 787, 1245, 1353, 1352, 788, 1324, 880, + + 1282, 1324, 1331, 1351, 1331, 789, 1332, 1332, 1332, 1332, + 1332, 1332, 1332, 1332, 1332, 1349, 1334, 790, 1334, 1354, + 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1325, + 880, 1285, 1325, 1332, 1332, 1332, 1332, 1332, 1332, 1332, + 1332, 1332, 1356, 1244, 1350, 1348, 1244, 1348, 1245, 1335, + 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1244, 1244, + 1377, 1244, 1244, 1245, 1245, 1365, 1347, 1346, 1365, 1345, + 1366, 791, 792, 793, 794, 782, 795, 1344, 796, 797, + 1309, 1342, 783, 784, 1382, 740, 785, 786, 1402, 787, + 782, 1377, 1336, 788, 1333, 1329, 1357, 783, 784, 1327, + + 1369, 785, 786, 1369, 787, 1370, 1326, 1322, 788, 1425, + 1321, 1246, 1246, 790, 1318, 1382, 740, 1316, 1367, 1402, + 1373, 1314, 1493, 1373, 1244, 1374, 1493, 1244, 790, 1245, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1369, 1390, + 1425, 1369, 1390, 1370, 1366, 1390, 1390, 1099, 1390, 1390, + 1366, 1366, 1390, 1371, 1313, 1390, 1393, 1366, 1312, 1393, + 1393, 1370, 1306, 1393, 1305, 1370, 1304, 791, 792, 793, + 794, 1303, 795, 1375, 796, 797, 1393, 1246, 1099, 1393, + 1303, 1370, 791, 792, 793, 794, 1302, 795, 1301, 796, + 797, 1371, 1367, 879, 880, 881, 879, 1393, 1367, 1367, + + 1393, 1396, 1370, 1493, 1396, 1367, 1374, 1396, 1300, 1371, + 1396, 1089, 1374, 1371, 1391, 1396, 1396, 1297, 1396, 1396, + 1374, 1374, 879, 880, 881, 879, 1403, 1392, 1403, 1371, + 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1390, + 1393, 1290, 1390, 1393, 1366, 1370, 1244, 1290, 1289, 1244, + 1371, 1245, 1244, 1287, 1375, 1244, 1493, 1245, 1396, 1394, + 1375, 1396, 1286, 1374, 1493, 1493, 1493, 1493, 1375, 1375, + 1390, 1390, 1395, 1390, 1390, 1366, 1366, 1393, 1397, 1276, + 1393, 1275, 1370, 1274, 1390, 1393, 1273, 1390, 1393, 1366, + 1370, 1398, 1367, 1371, 1272, 1268, 1267, 1413, 1266, 1413, + + 1262, 1414, 1414, 1414, 1414, 1414, 1414, 1414, 1414, 1414, + 1261, 1375, 1260, 1259, 1400, 1390, 1258, 1110, 1390, 1099, + 1366, 1099, 1254, 1432, 1433, 1411, 1248, 1242, 1241, 1240, + 1435, 1236, 1235, 1412, 1412, 1412, 1412, 1367, 1436, 1234, + 1233, 1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412, + 1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412, + 1412, 1412, 1412, 1412, 1412, 1412, 1412, 1232, 1367, 1393, + 1396, 1206, 1393, 1396, 1370, 1374, 1195, 1396, 1244, 1415, + 1396, 1244, 1374, 1245, 1195, 1228, 1222, 1416, 1416, 1416, + 1416, 1216, 1215, 1214, 1213, 1416, 1416, 1416, 1416, 1416, + + 1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416, + 1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416, + 1416, 1393, 1371, 1438, 1393, 1212, 1370, 1211, 1210, 1201, + 1439, 1246, 1200, 1185, 1417, 1183, 1417, 1182, 1418, 1418, + 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1396, 1364, 1179, + 1396, 1176, 1374, 1152, 1175, 1172, 1364, 1364, 1364, 1364, + 1421, 1171, 1421, 1161, 1422, 1422, 1422, 1422, 1422, 1422, + 1422, 1422, 1422, 1368, 1371, 1396, 1139, 1138, 1396, 1138, + 1374, 1368, 1368, 1368, 1368, 1419, 1244, 1390, 1137, 1244, + 1390, 1245, 1366, 1420, 1420, 1420, 1420, 1136, 1135, 1442, + + 1375, 1420, 1420, 1420, 1420, 1420, 1420, 1420, 1420, 1420, + 1420, 1420, 1420, 1420, 1420, 1420, 1420, 1420, 1420, 1420, + 1420, 1420, 1420, 1420, 1420, 1420, 1420, 1134, 1375, 1404, + 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1390, 1246, + 1367, 1390, 1372, 1366, 1004, 1132, 1364, 1127, 1493, 1391, + 1372, 1372, 1372, 1372, 1364, 1364, 1364, 1364, 1493, 1493, + 1493, 1120, 1444, 1119, 1431, 1431, 1431, 1431, 1431, 1431, + 1431, 1431, 1431, 1431, 1431, 1431, 1431, 1431, 1431, 1431, + 1431, 1431, 1431, 1431, 1431, 1431, 1431, 1431, 1431, 1431, + 1390, 1367, 1118, 1390, 1117, 1366, 1116, 1115, 1390, 1393, + + 1114, 1390, 1393, 1366, 1370, 1106, 1105, 1414, 1414, 1414, + 1414, 1414, 1414, 1414, 1414, 1414, 1418, 1418, 1418, 1418, + 1418, 1418, 1418, 1418, 1418, 1368, 1104, 1089, 1087, 1372, + 1086, 1085, 1084, 1368, 1368, 1368, 1368, 1372, 1372, 1372, + 1372, 1083, 1082, 1367, 1393, 1081, 1072, 1393, 1071, 1370, + 1056, 1433, 1371, 1390, 1054, 1053, 1390, 1393, 1366, 1050, + 1393, 1393, 1370, 1047, 1393, 1023, 1370, 1046, 1043, 1042, + 1434, 1434, 1434, 1434, 1434, 1434, 1434, 1434, 1434, 1434, + 1434, 1434, 1434, 1434, 1434, 1434, 1434, 1434, 1434, 1434, + 1434, 1434, 1434, 1434, 1434, 1434, 1032, 1371, 1396, 1010, + + 1009, 1396, 1008, 1374, 1390, 1007, 1432, 1390, 1001, 1366, + 1436, 1000, 999, 998, 1435, 998, 997, 730, 994, 880, + 1493, 1493, 1493, 1493, 1437, 1437, 1437, 1437, 1437, 1437, + 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, + 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, + 1396, 1375, 1493, 1396, 1493, 1374, 1393, 1367, 969, 1393, + 1393, 1370, 968, 1393, 965, 1370, 1445, 1422, 1422, 1422, + 1422, 1422, 1422, 1422, 1422, 1422, 962, 1396, 790, 1392, + 1396, 1396, 1374, 961, 1396, 1396, 1374, 958, 1396, 957, + 1374, 1396, 947, 945, 1396, 944, 1374, 941, 938, 914, + + 937, 934, 933, 1375, 923, 901, 900, 899, 898, 1371, + 894, 893, 892, 1371, 891, 890, 747, 740, 1394, 740, + 889, 883, 1447, 874, 872, 871, 868, 868, 689, 857, + 1375, 1446, 1493, 1493, 1375, 1395, 1493, 1493, 1439, 1397, + 1493, 1493, 1390, 1449, 1438, 1390, 842, 1366, 842, 840, + 840, 841, 1448, 839, 1493, 1454, 1398, 1454, 805, 1455, + 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 799, 799, + 762, 762, 760, 760, 758, 757, 756, 755, 754, 753, + 752, 751, 746, 745, 730, 553, 550, 725, 721, 720, + 719, 718, 717, 717, 710, 1367, 1456, 1493, 311, 694, + + 693, 692, 495, 691, 1457, 1457, 1457, 1457, 686, 686, + 1493, 1493, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, + 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, + 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1393, 1493, + 645, 1393, 1493, 1370, 1493, 1493, 463, 1493, 602, 602, + 601, 1458, 601, 1458, 593, 1459, 1459, 1459, 1459, 1459, + 1459, 1459, 1459, 1459, 592, 591, 590, 589, 588, 587, + 583, 583, 581, 581, 578, 577, 576, 573, 570, 375, + 569, 568, 567, 557, 366, 363, 551, 550, 355, 544, + 543, 1371, 1460, 542, 541, 540, 539, 533, 533, 532, + + 1461, 1461, 1461, 1461, 532, 528, 527, 525, 1461, 1461, + 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, + 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, + 1461, 1461, 1461, 1461, 1396, 524, 521, 1396, 152, 1374, + 311, 504, 504, 499, 499, 497, 497, 1462, 498, 1462, + 496, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, + 494, 291, 488, 483, 482, 479, 470, 466, 463, 1493, + 406, 404, 402, 227, 225, 220, 390, 386, 388, 386, + 384, 383, 208, 202, 205, 204, 195, 1375, 1464, 204, + 349, 348, 347, 346, 345, 344, 1465, 1465, 1465, 1465, + + 331, 152, 305, 303, 1465, 1465, 1465, 1465, 1465, 1465, + 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, + 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, + 1390, 300, 299, 1390, 278, 1366, 277, 239, 125, 124, + 239, 104, 224, 223, 213, 212, 211, 1455, 1455, 1455, + 1455, 1455, 1455, 1455, 1455, 1455, 210, 1393, 205, 204, + 1393, 200, 1370, 188, 186, 184, 182, 181, 180, 179, + 173, 171, 161, 160, 1459, 1459, 1459, 1459, 1459, 1459, + 1459, 1459, 1459, 1367, 1470, 1470, 1470, 1470, 1470, 1470, + 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, + + 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, + 1371, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, + 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, + 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1396, 156, 127, + 1396, 125, 1374, 124, 108, 1493, 102, 102, 38, 38, + 1493, 1493, 1493, 1493, 1463, 1463, 1463, 1463, 1463, 1463, + 1463, 1463, 1463, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1375, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, + + 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, + 1472, 1472, 1472, 1472, 1472, 1472, 1472, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 94, 94, 94, 94, 94, 94, + + 94, 94, 94, 94, 94, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 114, 114, 114, + 114, 114, 114, 114, 114, 114, 114, 114, 128, 128, + 128, 128, 162, 162, 1493, 162, 162, 162, 162, 162, + 162, 162, 162, 172, 1493, 1493, 1493, 172, 172, 172, + 172, 172, 172, 172, 177, 177, 1493, 1493, 177, 177, + 177, 177, 177, 177, 177, 183, 183, 1493, 183, 183, + 183, 183, 183, 183, 183, 183, 185, 1493, 1493, 1493, + 185, 185, 185, 185, 185, 185, 185, 187, 1493, 1493, + + 1493, 187, 187, 187, 187, 187, 187, 187, 189, 1493, + 1493, 1493, 189, 189, 189, 189, 189, 189, 189, 206, + 206, 1493, 206, 206, 206, 206, 206, 206, 206, 206, + 214, 214, 1493, 214, 214, 214, 214, 214, 214, 214, + 214, 219, 219, 1493, 219, 219, 219, 219, 219, 219, + 219, 219, 229, 229, 1493, 229, 229, 229, 229, 229, + 229, 229, 229, 245, 245, 245, 245, 245, 245, 245, + 245, 245, 245, 245, 128, 128, 128, 128, 280, 280, + 1493, 280, 280, 280, 280, 280, 280, 280, 280, 282, + 282, 1493, 282, 282, 282, 282, 282, 282, 282, 282, + + 290, 290, 1493, 290, 290, 290, 290, 290, 290, 290, + 290, 295, 295, 1493, 295, 295, 295, 295, 295, 295, + 295, 295, 301, 1493, 301, 301, 302, 302, 304, 1493, + 1493, 304, 304, 172, 1493, 1493, 1493, 172, 172, 172, + 172, 172, 172, 172, 177, 177, 1493, 1493, 177, 177, + 177, 177, 177, 177, 177, 183, 183, 1493, 183, 183, + 183, 183, 183, 183, 183, 183, 185, 1493, 1493, 1493, + 185, 185, 185, 185, 185, 185, 185, 187, 1493, 1493, + 1493, 187, 187, 187, 187, 187, 187, 187, 189, 1493, + 1493, 1493, 189, 189, 189, 189, 189, 189, 189, 353, + + 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, + 362, 362, 1493, 362, 362, 362, 362, 362, 362, 362, + 362, 385, 385, 1493, 385, 385, 385, 385, 385, 385, + 385, 385, 387, 387, 1493, 387, 387, 387, 387, 387, + 387, 387, 387, 219, 219, 1493, 219, 219, 219, 219, + 219, 219, 219, 219, 229, 229, 1493, 229, 229, 229, + 229, 229, 229, 229, 229, 414, 414, 414, 414, 414, + 414, 414, 414, 414, 414, 414, 290, 290, 1493, 290, + 290, 290, 290, 290, 290, 290, 290, 493, 493, 1493, + 493, 493, 493, 493, 493, 493, 493, 493, 280, 280, + + 1493, 280, 280, 280, 280, 280, 280, 280, 280, 495, + 495, 1493, 495, 495, 495, 495, 495, 495, 495, 495, + 500, 500, 1493, 500, 500, 500, 500, 500, 500, 500, + 500, 501, 501, 1493, 501, 501, 501, 501, 501, 501, + 501, 501, 502, 502, 502, 502, 502, 1493, 502, 502, + 502, 502, 502, 503, 503, 1493, 503, 503, 503, 503, + 503, 503, 503, 503, 506, 506, 507, 507, 507, 507, + 1493, 507, 507, 507, 507, 507, 507, 508, 508, 508, + 508, 508, 1493, 508, 508, 508, 508, 508, 545, 545, + 1493, 545, 545, 545, 545, 545, 545, 545, 545, 353, + + 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, + 552, 552, 1493, 552, 552, 552, 552, 552, 552, 552, + 552, 579, 579, 1493, 579, 579, 579, 579, 579, 579, + 579, 579, 580, 580, 1493, 580, 580, 580, 580, 580, + 580, 580, 580, 582, 582, 1493, 582, 582, 582, 582, + 582, 582, 582, 582, 586, 586, 1493, 586, 586, 586, + 586, 586, 586, 586, 586, 594, 594, 1493, 594, 594, + 594, 594, 594, 594, 594, 594, 229, 229, 1493, 229, + 229, 229, 229, 229, 229, 229, 229, 634, 634, 1493, + 634, 634, 634, 634, 634, 634, 634, 634, 636, 636, + + 1493, 636, 636, 636, 636, 636, 636, 636, 636, 644, + 644, 1493, 644, 644, 644, 644, 644, 644, 644, 644, + 680, 680, 1493, 680, 680, 680, 680, 680, 680, 680, + 680, 684, 684, 1493, 684, 684, 684, 684, 684, 684, + 684, 684, 493, 493, 1493, 493, 493, 493, 493, 493, + 493, 493, 493, 689, 689, 1493, 689, 689, 689, 689, + 689, 689, 689, 689, 690, 690, 1493, 690, 690, 690, + 690, 690, 690, 690, 690, 502, 502, 502, 502, 502, + 502, 502, 502, 502, 502, 502, 304, 304, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 508, + + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 636, 636, 1493, 636, 636, 636, 636, 636, 636, 636, + 636, 545, 545, 1493, 545, 545, 545, 545, 545, 545, + 545, 545, 728, 728, 1493, 728, 728, 728, 728, 728, + 728, 728, 728, 579, 579, 1493, 579, 579, 579, 579, + 579, 579, 579, 579, 759, 759, 1493, 759, 759, 759, + 759, 759, 759, 759, 759, 761, 761, 1493, 761, 761, + 761, 761, 761, 761, 761, 761, 798, 798, 1493, 798, + 798, 798, 798, 798, 798, 798, 798, 594, 594, 1493, + 594, 594, 594, 594, 594, 594, 594, 594, 800, 800, + + 1493, 800, 800, 800, 800, 800, 800, 800, 800, 801, + 801, 1493, 801, 801, 801, 801, 801, 801, 801, 801, + 229, 229, 1493, 229, 229, 229, 229, 229, 229, 229, + 229, 802, 802, 1493, 802, 802, 802, 802, 802, 802, + 802, 802, 803, 803, 1493, 803, 803, 803, 803, 803, + 803, 803, 803, 804, 804, 1493, 804, 804, 804, 804, + 804, 804, 804, 804, 812, 812, 1493, 812, 812, 812, + 812, 812, 812, 812, 812, 832, 832, 1493, 832, 832, + 832, 832, 832, 832, 832, 832, 836, 836, 1493, 836, + 836, 836, 836, 836, 836, 836, 836, 634, 634, 1493, + + 634, 634, 634, 634, 634, 634, 634, 634, 838, 838, + 1493, 838, 838, 838, 838, 838, 838, 838, 838, 843, + 843, 1493, 843, 843, 843, 843, 843, 843, 843, 843, + 844, 844, 844, 844, 844, 844, 844, 844, 844, 844, + 844, 848, 848, 848, 848, 848, 848, 848, 848, 848, + 848, 848, 849, 849, 849, 849, 849, 849, 849, 849, + 849, 849, 849, 850, 850, 1493, 850, 850, 850, 850, + 850, 850, 850, 850, 851, 851, 1493, 851, 851, 851, + 851, 851, 851, 851, 851, 852, 852, 1493, 852, 852, + 852, 852, 852, 852, 852, 852, 853, 853, 1493, 853, + + 853, 853, 853, 853, 853, 853, 853, 854, 854, 1493, + 854, 854, 854, 854, 854, 854, 854, 854, 855, 855, + 1493, 855, 855, 855, 855, 855, 855, 855, 855, 680, + 680, 1493, 680, 680, 680, 680, 680, 680, 680, 680, + 856, 856, 1493, 856, 856, 856, 856, 856, 856, 856, + 856, 684, 684, 1493, 684, 684, 684, 684, 684, 684, + 684, 684, 859, 1493, 859, 859, 859, 859, 859, 859, + 859, 859, 859, 838, 838, 1493, 838, 838, 838, 838, + 838, 838, 838, 838, 545, 545, 545, 545, 545, 545, + 545, 545, 545, 545, 545, 877, 877, 1493, 877, 877, + + 877, 877, 877, 877, 877, 877, 902, 902, 1493, 902, + 902, 902, 902, 902, 902, 902, 902, 759, 759, 1493, + 759, 759, 759, 759, 759, 759, 759, 759, 903, 903, + 1493, 903, 903, 903, 903, 903, 903, 903, 903, 761, + 761, 1493, 761, 761, 761, 761, 761, 761, 761, 761, + 970, 970, 1493, 970, 970, 970, 970, 970, 970, 970, + 970, 798, 798, 1493, 798, 798, 798, 798, 798, 798, + 798, 798, 800, 800, 1493, 800, 800, 800, 800, 800, + 800, 800, 800, 801, 801, 1493, 801, 801, 801, 801, + 801, 801, 801, 801, 802, 802, 1493, 802, 802, 802, + + 802, 802, 802, 802, 802, 803, 803, 1493, 803, 803, + 803, 803, 803, 803, 803, 803, 804, 804, 1493, 804, + 804, 804, 804, 804, 804, 804, 804, 812, 812, 1493, + 812, 812, 812, 812, 812, 812, 812, 812, 974, 974, + 1493, 974, 974, 974, 974, 974, 974, 974, 974, 975, + 975, 1493, 975, 975, 975, 975, 975, 975, 975, 975, + 976, 976, 1493, 976, 976, 976, 976, 976, 976, 976, + 976, 977, 977, 1493, 977, 977, 977, 977, 977, 977, + 977, 977, 978, 978, 1493, 978, 978, 978, 978, 978, + 978, 978, 978, 979, 979, 1493, 979, 979, 979, 979, + + 979, 979, 979, 979, 832, 832, 1493, 832, 832, 832, + 832, 832, 832, 832, 832, 980, 980, 1493, 980, 980, + 980, 980, 980, 980, 980, 980, 836, 836, 1493, 836, + 836, 836, 836, 836, 836, 836, 836, 982, 982, 1493, + 982, 982, 982, 982, 982, 982, 982, 982, 844, 844, + 844, 844, 844, 844, 844, 844, 844, 844, 844, 848, + 848, 848, 848, 848, 848, 848, 848, 848, 848, 848, + 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, + 849, 850, 850, 1493, 850, 850, 850, 850, 850, 850, + 850, 850, 851, 851, 1493, 851, 851, 851, 851, 851, + + 851, 851, 851, 852, 852, 1493, 852, 852, 852, 852, + 852, 852, 852, 852, 853, 853, 1493, 853, 853, 853, + 853, 853, 853, 853, 853, 854, 854, 1493, 854, 854, + 854, 854, 854, 854, 854, 854, 855, 855, 1493, 855, + 855, 855, 855, 855, 855, 855, 855, 856, 856, 1493, + 856, 856, 856, 856, 856, 856, 856, 856, 859, 1493, + 859, 859, 859, 859, 859, 859, 859, 859, 859, 991, + 991, 1493, 991, 991, 991, 991, 991, 991, 991, 991, + 1011, 1011, 1493, 1011, 1011, 1011, 1011, 1011, 1011, 1011, + 1011, 1012, 1012, 1493, 1012, 1012, 1012, 1012, 1012, 1012, + + 1012, 1012, 1107, 1493, 1107, 1107, 1121, 1121, 1493, 1121, + 1121, 1121, 1121, 1121, 1121, 1121, 1121, 974, 974, 1493, + 974, 974, 974, 974, 974, 974, 974, 974, 975, 975, + 1493, 975, 975, 975, 975, 975, 975, 975, 975, 976, + 976, 1493, 976, 976, 976, 976, 976, 976, 976, 976, + 977, 977, 1493, 977, 977, 977, 977, 977, 977, 977, + 977, 978, 978, 1493, 978, 978, 978, 978, 978, 978, + 978, 978, 979, 979, 1493, 979, 979, 979, 979, 979, + 979, 979, 979, 980, 980, 1493, 980, 980, 980, 980, + 980, 980, 980, 980, 1124, 1124, 1124, 1124, 1124, 1124, + + 1124, 1124, 1124, 1124, 1124, 1125, 1125, 1493, 1125, 1125, + 1125, 1125, 1125, 1125, 1125, 1125, 1140, 1140, 1493, 1140, + 1140, 1140, 1140, 1140, 1140, 1140, 1140, 1141, 1141, 1493, + 1141, 1141, 1141, 1141, 1141, 1141, 1141, 1141, 1269, 1269, + 1493, 1269, 1269, 1269, 1269, 1269, 1269, 1269, 1269, 1270, + 1270, 1493, 1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270, + 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, + 1277, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, + 1280, 1280, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, + 1283, 1283, 1283, 1288, 1288, 1493, 1288, 1288, 1288, 1288, + + 1288, 1288, 1288, 1288, 1107, 1493, 1107, 1493, 1107, 1107, + 1315, 1315, 1493, 1315, 1315, 1315, 1315, 1315, 1315, 1315, + 1315, 1330, 1493, 1330, 1330, 1337, 1493, 1337, 1337, 1364, + 1364, 1493, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1368, 1368, 1493, 1368, 1368, 1368, 1368, 1368, 1368, 1368, + 1368, 1372, 1372, 1493, 1372, 1372, 1372, 1372, 1372, 1372, + 1372, 1372, 1406, 1493, 1406, 1406, 37, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493 + + } ; + +static const flex_int16_t yy_chk[7497] = + { 0, + 0, 0, 3, 3, 4, 4, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 7, 7, 7, 7, 8, + 8, 8, 8, 15, 9, 10, 25, 25, 25, 25, + 7, 27, 27, 27, 8, 26, 26, 26, 26, 250, + 25, 25, 9, 10, 28, 28, 28, 29, 30, 26, + 26, 33, 33, 250, 29, 30, 34, 34, 15, 1676, + 48, 29, 30, 48, 15, 378, 15, 378, 7, 9, + 10, 59, 8, 233, 59, 40, 40, 40, 40, 25, + + 233, 25, 66, 66, 40, 66, 87, 87, 26, 15, + 26, 40, 776, 67, 776, 15, 67, 15, 284, 284, + 9, 10, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 16, 43, 43, 43, 43, 260, 135, 55, 44, 44, + 44, 44, 55, 368, 260, 43, 43, 44, 44, 86, + + 86, 86, 86, 44, 44, 60, 60, 60, 60, 60, + 60, 60, 99, 99, 1671, 16, 91, 91, 91, 91, + 370, 16, 111, 16, 368, 111, 1670, 99, 135, 55, + 103, 103, 103, 103, 43, 104, 104, 126, 269, 127, + 126, 44, 127, 104, 136, 139, 16, 136, 139, 269, + 104, 370, 16, 139, 16, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 45, 45, 45, 45, 164, 122, 321, 164, + 371, 122, 150, 52, 122, 130, 52, 45, 130, 321, + 53, 53, 53, 53, 248, 83, 150, 52, 83, 53, + 53, 84, 84, 84, 84, 138, 53, 436, 138, 83, + 83, 371, 249, 148, 138, 97, 148, 165, 97, 845, + 165, 122, 148, 296, 296, 45, 51, 51, 51, 51, + + 51, 51, 51, 51, 51, 51, 52, 51, 130, 197, + 51, 51, 51, 53, 166, 84, 248, 166, 83, 108, + 108, 108, 108, 513, 84, 150, 84, 97, 108, 197, + 1156, 113, 51, 150, 249, 108, 97, 155, 113, 155, + 197, 155, 845, 113, 155, 113, 167, 51, 51, 167, + 436, 51, 51, 51, 51, 51, 51, 51, 97, 51, + 197, 168, 51, 51, 168, 51, 51, 51, 417, 51, + 51, 51, 51, 51, 51, 51, 107, 51, 51, 51, + 113, 155, 417, 107, 109, 109, 109, 109, 107, 270, + 270, 270, 107, 107, 107, 513, 107, 437, 109, 109, + + 110, 110, 110, 110, 113, 115, 115, 115, 115, 433, + 169, 107, 113, 169, 454, 110, 116, 116, 116, 116, + 115, 311, 311, 1659, 107, 116, 116, 117, 117, 117, + 117, 116, 116, 159, 1156, 149, 159, 109, 149, 1570, + 170, 159, 117, 170, 511, 123, 123, 123, 123, 149, + 325, 325, 514, 110, 123, 123, 177, 437, 115, 177, + 123, 123, 421, 151, 151, 151, 151, 328, 328, 116, + 132, 132, 132, 132, 132, 132, 132, 132, 433, 134, + 117, 121, 121, 134, 121, 134, 134, 178, 149, 454, + 178, 134, 162, 162, 162, 162, 134, 121, 123, 134, + + 426, 152, 152, 152, 152, 420, 220, 220, 121, 420, + 152, 152, 223, 223, 121, 121, 151, 152, 121, 511, + 121, 220, 121, 440, 121, 121, 121, 223, 121, 163, + 121, 514, 163, 896, 121, 191, 191, 191, 191, 121, + 426, 121, 236, 121, 421, 236, 121, 163, 163, 163, + 163, 121, 896, 147, 152, 147, 121, 147, 147, 147, + 147, 147, 147, 147, 147, 147, 154, 154, 647, 154, + 1209, 154, 156, 156, 156, 156, 647, 227, 227, 154, + 154, 156, 156, 157, 440, 227, 154, 157, 156, 157, + 157, 190, 227, 1211, 190, 157, 192, 192, 192, 192, + + 157, 194, 1150, 157, 194, 190, 190, 195, 195, 195, + 195, 192, 200, 200, 200, 200, 194, 201, 201, 201, + 201, 202, 202, 202, 202, 156, 204, 217, 733, 204, + 207, 207, 207, 207, 380, 380, 208, 208, 208, 208, + 210, 210, 210, 210, 190, 733, 222, 238, 222, 230, + 239, 195, 230, 239, 194, 217, 200, 222, 238, 222, + 195, 222, 195, 208, 238, 200, 217, 200, 222, 1209, + 208, 208, 217, 231, 208, 208, 231, 208, 238, 204, + 1211, 208, 234, 230, 253, 253, 217, 253, 234, 238, + 408, 408, 230, 234, 222, 238, 1152, 217, 460, 234, + + 231, 208, 460, 217, 240, 234, 241, 240, 265, 241, + 256, 265, 240, 256, 241, 1150, 231, 275, 261, 234, + 275, 261, 231, 242, 242, 242, 242, 411, 411, 428, + 234, 243, 243, 243, 243, 1152, 234, 516, 242, 244, + 244, 244, 244, 649, 256, 258, 243, 261, 258, 246, + 246, 246, 246, 256, 244, 208, 208, 208, 208, 261, + 208, 569, 208, 208, 246, 247, 247, 247, 247, 428, + 262, 276, 258, 262, 276, 279, 242, 262, 279, 276, + 247, 793, 272, 793, 243, 650, 258, 262, 258, 263, + 490, 490, 244, 272, 258, 263, 529, 529, 293, 272, + + 263, 293, 246, 570, 570, 308, 263, 293, 308, 517, + 638, 638, 263, 272, 308, 569, 516, 606, 247, 251, + 251, 272, 251, 649, 272, 331, 263, 606, 331, 332, + 272, 1201, 332, 313, 313, 251, 313, 263, 310, 310, + 310, 310, 324, 263, 333, 324, 251, 333, 334, 650, + 324, 334, 251, 251, 335, 657, 251, 335, 251, 657, + 251, 556, 251, 251, 251, 1201, 251, 336, 251, 696, + 336, 337, 251, 338, 337, 339, 338, 251, 339, 251, + 340, 251, 341, 340, 251, 341, 313, 342, 517, 251, + 342, 310, 556, 708, 251, 252, 252, 252, 252, 252, + + 252, 252, 252, 252, 252, 343, 252, 1158, 343, 252, + 252, 252, 306, 306, 306, 306, 306, 306, 306, 306, + 306, 350, 350, 350, 350, 351, 351, 351, 351, 399, + 352, 252, 399, 352, 354, 661, 1548, 354, 361, 361, + 361, 361, 1059, 661, 559, 352, 252, 252, 696, 354, + 252, 252, 252, 252, 252, 252, 252, 708, 252, 1059, + 318, 252, 252, 318, 252, 252, 252, 438, 252, 252, + 252, 252, 252, 252, 252, 559, 252, 252, 252, 315, + 315, 400, 315, 352, 400, 401, 402, 354, 401, 402, + 355, 355, 355, 355, 318, 315, 389, 389, 389, 389, + + 707, 389, 1539, 318, 403, 315, 315, 403, 315, 1158, + 315, 404, 315, 315, 404, 558, 315, 808, 315, 315, + 315, 561, 315, 315, 315, 315, 315, 405, 315, 807, + 405, 438, 315, 406, 355, 318, 406, 315, 558, 315, + 318, 315, 438, 355, 315, 355, 558, 357, 407, 315, + 465, 407, 561, 465, 315, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 466, 316, 357, 466, 316, + 316, 316, 703, 703, 415, 415, 415, 415, 357, 811, + 707, 808, 416, 416, 416, 416, 429, 422, 422, 415, + 422, 316, 562, 418, 418, 418, 418, 416, 357, 423, + + 423, 467, 423, 558, 467, 1492, 316, 316, 418, 807, + 316, 316, 316, 316, 316, 316, 316, 468, 316, 434, + 468, 316, 316, 562, 316, 316, 316, 415, 316, 316, + 316, 316, 316, 316, 316, 416, 316, 316, 316, 363, + 363, 363, 363, 811, 469, 429, 418, 469, 365, 365, + 365, 365, 450, 450, 429, 450, 419, 470, 419, 439, + 470, 366, 366, 366, 366, 419, 363, 563, 471, 1491, + 419, 471, 419, 363, 363, 365, 434, 363, 363, 434, + 363, 1145, 365, 365, 363, 515, 365, 365, 366, 365, + 772, 425, 425, 365, 425, 366, 366, 372, 563, 366, + + 366, 372, 366, 772, 363, 372, 366, 419, 778, 778, + 789, 372, 431, 365, 1145, 430, 430, 372, 430, 439, + 439, 439, 810, 789, 464, 425, 366, 464, 372, 464, + 441, 419, 372, 917, 425, 917, 372, 472, 431, 419, + 472, 431, 372, 473, 430, 431, 473, 869, 372, 427, + 427, 474, 427, 515, 474, 431, 430, 937, 363, 363, + 363, 363, 441, 363, 515, 363, 363, 365, 365, 365, + 365, 432, 365, 441, 365, 365, 427, 1172, 453, 441, + 366, 366, 366, 366, 1490, 366, 564, 366, 366, 372, + 427, 475, 427, 441, 475, 1489, 476, 432, 427, 476, + + 869, 441, 810, 432, 441, 445, 443, 443, 432, 443, + 441, 937, 452, 452, 432, 452, 477, 564, 1488, 477, + 432, 452, 445, 445, 445, 445, 445, 445, 445, 445, + 462, 462, 478, 462, 432, 478, 448, 913, 453, 462, + 448, 453, 448, 448, 479, 432, 453, 479, 448, 461, + 913, 432, 435, 448, 480, 481, 448, 480, 481, 443, + 848, 461, 1172, 461, 848, 461, 461, 461, 461, 461, + 461, 461, 461, 461, 795, 795, 1487, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + + 435, 435, 435, 457, 482, 485, 486, 482, 485, 486, + 1486, 457, 457, 457, 457, 487, 895, 895, 487, 457, + 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, + 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, + 457, 457, 457, 457, 457, 458, 458, 458, 458, 458, + 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, + 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, + 458, 489, 494, 1216, 489, 494, 458, 458, 458, 458, + 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, + 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, + + 458, 458, 459, 497, 521, 523, 497, 521, 523, 524, + 525, 526, 524, 525, 526, 729, 731, 1153, 459, 459, + 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, + 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, + 459, 459, 459, 459, 520, 527, 729, 731, 527, 459, + 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, + 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, + 459, 459, 459, 459, 459, 483, 1485, 528, 483, 510, + 528, 510, 1484, 512, 512, 518, 512, 534, 603, 518, + 534, 518, 518, 483, 1216, 510, 535, 518, 536, 535, + + 537, 536, 518, 537, 520, 518, 538, 520, 542, 538, + 543, 542, 520, 543, 1483, 546, 844, 512, 546, 547, + 547, 547, 547, 1153, 844, 550, 512, 542, 550, 597, + 546, 566, 597, 601, 547, 566, 601, 1177, 607, 607, + 483, 607, 553, 553, 553, 553, 666, 566, 603, 666, + 667, 603, 566, 667, 510, 604, 603, 648, 512, 734, + 999, 560, 510, 512, 555, 555, 555, 555, 546, 553, + 735, 609, 609, 560, 609, 999, 553, 553, 550, 972, + 553, 553, 560, 553, 1154, 565, 560, 553, 897, 897, + 734, 555, 560, 610, 610, 1159, 610, 565, 555, 555, + + 849, 735, 555, 555, 560, 555, 565, 553, 849, 555, + 565, 611, 611, 560, 611, 604, 565, 560, 604, 608, + 608, 1482, 608, 604, 608, 648, 648, 1481, 565, 555, + 612, 612, 972, 612, 737, 613, 613, 565, 613, 614, + 614, 565, 614, 615, 615, 739, 615, 616, 616, 741, + 616, 617, 617, 1177, 617, 618, 618, 668, 618, 1022, + 668, 553, 553, 553, 553, 737, 553, 706, 553, 553, + 619, 619, 1022, 619, 620, 620, 739, 620, 621, 621, + 741, 621, 1215, 555, 555, 555, 555, 743, 555, 1159, + 555, 555, 584, 584, 584, 584, 1154, 584, 622, 622, + + 1222, 622, 623, 623, 1222, 623, 624, 624, 1178, 624, + 625, 625, 1234, 625, 626, 626, 1188, 626, 743, 584, + 627, 627, 669, 627, 1092, 669, 584, 584, 961, 670, + 584, 584, 670, 584, 1179, 706, 706, 584, 627, 629, + 629, 1092, 629, 630, 630, 584, 630, 631, 631, 658, + 631, 633, 633, 1480, 633, 646, 646, 584, 646, 673, + 674, 1046, 673, 674, 646, 658, 658, 658, 658, 658, + 658, 658, 658, 658, 660, 660, 676, 660, 677, 676, + 1182, 677, 961, 660, 678, 627, 679, 678, 682, 679, + 683, 682, 685, 683, 687, 685, 688, 687, 692, 688, + + 1188, 692, 695, 695, 1215, 695, 697, 697, 1026, 697, + 1026, 584, 584, 584, 584, 1046, 584, 1188, 584, 584, + 585, 585, 585, 585, 1178, 585, 698, 698, 1234, 698, + 699, 699, 1479, 699, 700, 700, 1179, 700, 701, 701, + 1478, 701, 702, 702, 1477, 702, 709, 585, 710, 709, + 711, 710, 1214, 711, 585, 585, 738, 712, 585, 585, + 712, 585, 1182, 713, 714, 585, 713, 714, 715, 716, + 738, 715, 716, 585, 720, 722, 723, 720, 722, 723, + 724, 725, 744, 724, 725, 585, 726, 726, 726, 726, + 727, 727, 727, 727, 730, 730, 730, 730, 765, 736, + + 767, 738, 768, 736, 740, 740, 740, 740, 805, 919, + 919, 805, 736, 744, 782, 813, 813, 1476, 813, 784, + 785, 818, 818, 1207, 818, 819, 819, 1173, 819, 765, + 736, 767, 1173, 768, 736, 820, 820, 1161, 820, 585, + 585, 585, 585, 736, 585, 782, 585, 585, 652, 754, + 784, 785, 754, 821, 821, 857, 821, 806, 857, 809, + 822, 822, 1161, 822, 825, 825, 1475, 825, 826, 826, + 1214, 826, 1474, 652, 652, 652, 652, 652, 652, 652, + 652, 652, 652, 652, 652, 652, 652, 652, 652, 652, + 652, 652, 652, 652, 652, 652, 652, 652, 652, 655, + + 655, 655, 655, 655, 655, 655, 655, 655, 655, 655, + 655, 655, 655, 655, 655, 655, 655, 655, 655, 655, + 655, 655, 655, 655, 655, 806, 806, 809, 809, 1207, + 655, 655, 655, 655, 655, 655, 655, 655, 655, 655, + 655, 655, 655, 655, 655, 655, 655, 655, 655, 655, + 655, 655, 655, 655, 655, 655, 742, 742, 742, 742, + 828, 828, 858, 828, 878, 858, 870, 769, 786, 870, + 882, 769, 786, 754, 754, 769, 786, 829, 829, 885, + 829, 769, 786, 742, 1181, 886, 742, 769, 786, 830, + 830, 872, 830, 1473, 872, 878, 742, 764, 769, 786, + + 1469, 882, 769, 786, 764, 764, 769, 786, 764, 764, + 885, 764, 769, 786, 742, 764, 886, 742, 769, 786, + 1187, 831, 831, 764, 831, 1187, 888, 742, 763, 763, + 763, 763, 781, 763, 873, 764, 1468, 873, 874, 781, + 781, 874, 875, 781, 781, 875, 781, 1467, 834, 834, + 781, 834, 1183, 835, 835, 763, 835, 888, 781, 769, + 786, 876, 763, 763, 876, 880, 763, 763, 880, 763, + 781, 837, 837, 763, 837, 840, 840, 906, 840, 860, + 860, 763, 860, 861, 861, 908, 861, 938, 938, 764, + 764, 764, 764, 763, 764, 1181, 764, 764, 862, 862, + + 1466, 862, 863, 863, 1230, 863, 864, 864, 906, 864, + 865, 865, 909, 865, 866, 866, 908, 866, 867, 867, + 922, 867, 925, 1231, 781, 781, 781, 781, 927, 781, + 928, 781, 781, 879, 879, 879, 879, 929, 930, 946, + 949, 932, 951, 909, 952, 932, 953, 763, 763, 763, + 763, 922, 763, 925, 763, 763, 846, 932, 1453, 927, + 1183, 928, 932, 962, 962, 1000, 1000, 1241, 929, 930, + 946, 949, 1210, 951, 1230, 952, 989, 953, 1235, 989, + 1231, 846, 846, 846, 846, 846, 846, 846, 846, 846, + 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, + + 846, 846, 846, 846, 846, 846, 846, 847, 847, 847, + 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 847, 847, 847, 887, 887, 887, 887, 954, 847, 847, + 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 847, 847, 847, 847, 847, 847, 847, 847, 847, 847, + 847, 847, 847, 847, 992, 1210, 924, 926, 954, 994, + 948, 1236, 887, 905, 971, 971, 1241, 971, 1171, 926, + 905, 905, 910, 995, 905, 905, 910, 905, 926, 924, + 910, 905, 926, 948, 1235, 992, 910, 924, 926, 905, + + 994, 948, 910, 887, 904, 904, 904, 904, 956, 904, + 926, 905, 956, 910, 995, 973, 973, 910, 973, 926, + 1171, 910, 1223, 926, 956, 981, 981, 910, 981, 956, + 1452, 904, 1228, 910, 984, 984, 1208, 984, 904, 904, + 1233, 990, 904, 904, 990, 904, 996, 1451, 993, 904, + 931, 993, 986, 986, 924, 986, 1228, 904, 948, 1450, + 987, 987, 931, 987, 1223, 905, 905, 905, 905, 904, + 905, 931, 905, 905, 910, 931, 1236, 996, 936, 1443, + 936, 931, 936, 936, 936, 936, 936, 936, 936, 936, + 936, 1003, 1003, 931, 998, 998, 998, 998, 1015, 1028, + + 1028, 1149, 931, 960, 1017, 960, 931, 960, 960, 960, + 960, 960, 960, 960, 960, 960, 1018, 1041, 1047, 1047, + 1208, 1041, 1149, 904, 904, 904, 904, 1242, 904, 1015, + 904, 904, 935, 1041, 1180, 1017, 1122, 1122, 1041, 1122, + 935, 935, 935, 935, 1442, 1031, 1233, 1018, 935, 935, + 935, 935, 935, 935, 935, 935, 935, 935, 935, 935, + 935, 935, 935, 935, 935, 935, 935, 935, 935, 935, + 935, 935, 935, 935, 950, 955, 1031, 1019, 1034, 1036, + 1033, 1019, 1037, 1038, 1039, 1019, 950, 955, 1055, 1056, + 1057, 1019, 1056, 1060, 1056, 950, 955, 1019, 1061, 950, + + 955, 1084, 1200, 1033, 1084, 950, 955, 1063, 1019, 1034, + 1036, 1033, 1019, 1037, 1038, 1039, 1019, 950, 955, 1055, + 1180, 1057, 1019, 1200, 1060, 1242, 950, 955, 1019, 1061, + 950, 955, 1013, 1013, 1013, 1013, 1014, 1013, 1063, 1064, + 1441, 1232, 1056, 1014, 1014, 1440, 1212, 1014, 1014, 1065, + 1014, 1035, 1062, 1064, 1014, 1066, 1062, 1067, 1066, 1013, + 1066, 1069, 1014, 1035, 1070, 1062, 1013, 1013, 1033, 1019, + 1013, 1013, 1035, 1013, 1014, 1088, 1035, 1013, 1090, 1157, + 1065, 1264, 1035, 1062, 1064, 1013, 1430, 1062, 1067, 1040, + 1093, 1094, 1069, 1096, 1035, 1070, 1062, 1013, 1427, 1097, + + 1264, 1040, 1424, 1035, 1423, 1206, 1088, 1035, 1066, 1090, + 1040, 1098, 1089, 1097, 1040, 1089, 1100, 1089, 1102, 1103, + 1040, 1093, 1094, 1410, 1096, 1084, 1084, 1232, 1014, 1014, + 1014, 1014, 1040, 1014, 1409, 1014, 1014, 1117, 1206, 1408, + 1117, 1040, 1098, 1126, 1097, 1040, 1151, 1100, 1407, 1102, + 1103, 1013, 1013, 1013, 1013, 1176, 1013, 1175, 1013, 1013, + 1044, 1123, 1123, 1212, 1123, 1089, 1157, 1157, 1044, 1044, + 1044, 1044, 1124, 1124, 1126, 1124, 1044, 1044, 1044, 1044, + 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, + 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, + + 1044, 1044, 1045, 1151, 1045, 1146, 1045, 1045, 1045, 1045, + 1045, 1045, 1045, 1045, 1045, 1068, 1151, 1175, 1068, 1099, + 1068, 1074, 1099, 1095, 1099, 1243, 1401, 1095, 1074, 1074, + 1246, 1245, 1074, 1074, 1245, 1074, 1095, 1273, 1146, 1074, + 1273, 1213, 1068, 1246, 1213, 1068, 1213, 1074, 1128, 1128, + 1128, 1128, 1240, 1400, 1095, 1068, 1243, 1155, 1095, 1074, + 1399, 1117, 1117, 1129, 1129, 1129, 1129, 1095, 1068, 1146, + 1389, 1175, 1099, 1068, 1176, 1176, 1068, 1075, 1075, 1075, + 1075, 1075, 1075, 1075, 1075, 1075, 1068, 1073, 1073, 1073, + 1073, 1073, 1073, 1073, 1073, 1073, 1073, 1073, 1073, 1073, + + 1073, 1073, 1073, 1073, 1073, 1073, 1073, 1073, 1073, 1073, + 1073, 1073, 1073, 1074, 1074, 1074, 1074, 1076, 1074, 1388, + 1074, 1074, 1163, 1387, 1076, 1076, 1107, 1101, 1076, 1076, + 1101, 1076, 1101, 1107, 1107, 1076, 1218, 1107, 1107, 1218, + 1107, 1218, 1386, 1076, 1107, 1134, 1134, 1134, 1134, 1155, + 1238, 1155, 1107, 1160, 1101, 1076, 1163, 1101, 1135, 1135, + 1135, 1135, 1240, 1165, 1107, 1213, 1213, 1101, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1144, 1170, 1147, + 1101, 1136, 1136, 1136, 1136, 1101, 1220, 1163, 1101, 1109, + 1138, 1138, 1138, 1138, 1385, 1186, 1109, 1109, 1101, 1160, + + 1109, 1109, 1144, 1109, 1147, 1165, 1384, 1109, 1166, 1076, + 1076, 1076, 1076, 1162, 1076, 1109, 1076, 1076, 1107, 1107, + 1107, 1107, 1239, 1107, 1184, 1107, 1107, 1109, 1189, 1186, + 1160, 1381, 1167, 1144, 1238, 1147, 1165, 1143, 1162, 1263, + 1263, 1166, 1168, 1220, 1378, 1185, 1265, 1265, 1185, 1184, + 1185, 1376, 1195, 1238, 1190, 1195, 1220, 1195, 1170, 1143, + 1186, 1162, 1170, 1189, 1237, 1167, 1143, 1143, 1194, 1162, + 1143, 1143, 1166, 1143, 1170, 1168, 1192, 1143, 1148, 1170, + 1184, 1109, 1109, 1109, 1109, 1143, 1109, 1190, 1109, 1109, + 1142, 1142, 1142, 1142, 1189, 1142, 1167, 1143, 1185, 1148, + + 1362, 1247, 1361, 1148, 1358, 1195, 1168, 1148, 1304, 1250, + 1192, 1194, 1191, 1148, 1198, 1164, 1357, 1142, 1190, 1148, + 1196, 1239, 1239, 1304, 1142, 1142, 1162, 1225, 1142, 1142, + 1148, 1142, 1247, 1191, 1148, 1142, 1164, 1191, 1148, 1198, + 1250, 1192, 1194, 1142, 1148, 1356, 1191, 1193, 1164, 1196, + 1148, 1143, 1143, 1143, 1143, 1142, 1143, 1164, 1143, 1143, + 1217, 1164, 1199, 1353, 1191, 1225, 1219, 1164, 1191, 1219, + 1198, 1219, 1221, 1169, 1352, 1292, 1193, 1191, 1292, 1164, + 1196, 1351, 1251, 1237, 1237, 1174, 1350, 1253, 1164, 1217, + 1193, 1148, 1164, 1174, 1169, 1174, 1225, 1174, 1174, 1174, + + 1174, 1174, 1174, 1174, 1174, 1174, 1169, 1221, 1199, 1142, + 1142, 1142, 1142, 1251, 1142, 1169, 1142, 1142, 1253, 1169, + 1217, 1193, 1347, 1203, 1197, 1169, 1224, 1197, 1226, 1197, + 1271, 1226, 1294, 1226, 1343, 1294, 1205, 1169, 1221, 1199, + 1305, 1305, 1293, 1227, 1297, 1203, 1169, 1224, 1298, 1299, + 1169, 1197, 1203, 1203, 1197, 1342, 1203, 1203, 1205, 1203, + 1341, 1271, 1340, 1203, 1197, 1205, 1205, 1226, 1227, 1205, + 1205, 1203, 1205, 1293, 1204, 1297, 1205, 1197, 1224, 1298, + 1299, 1226, 1197, 1203, 1205, 1197, 1204, 1204, 1204, 1204, + 1204, 1204, 1204, 1204, 1204, 1197, 1205, 1229, 1226, 1227, + + 1244, 1360, 1229, 1244, 1360, 1244, 1276, 1276, 1276, 1276, + 1229, 1229, 1229, 1229, 1303, 1252, 1333, 1303, 1252, 1303, + 1252, 1277, 1277, 1277, 1277, 1278, 1278, 1278, 1278, 1279, + 1279, 1279, 1279, 1280, 1280, 1280, 1280, 1203, 1203, 1203, + 1203, 1327, 1203, 1326, 1203, 1203, 1281, 1281, 1281, 1281, + 1205, 1205, 1205, 1205, 1252, 1205, 1322, 1205, 1205, 1282, + 1282, 1282, 1282, 1283, 1283, 1283, 1283, 1303, 1252, 1284, + 1284, 1284, 1284, 1285, 1285, 1285, 1285, 1308, 1308, 1291, + 1323, 1323, 1323, 1323, 1317, 1252, 1291, 1291, 1328, 1330, + 1291, 1291, 1330, 1291, 1330, 1321, 1320, 1291, 1324, 1324, + + 1324, 1324, 1296, 1319, 1296, 1291, 1296, 1296, 1296, 1296, + 1296, 1296, 1296, 1296, 1296, 1317, 1301, 1291, 1301, 1328, + 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1325, + 1325, 1325, 1325, 1331, 1331, 1331, 1331, 1331, 1331, 1331, + 1331, 1331, 1330, 1332, 1318, 1314, 1332, 1313, 1332, 1334, + 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1338, 1339, + 1349, 1338, 1339, 1338, 1339, 1344, 1312, 1311, 1344, 1310, + 1344, 1291, 1291, 1291, 1291, 1335, 1291, 1309, 1291, 1291, + 1307, 1306, 1335, 1335, 1354, 1377, 1335, 1335, 1382, 1335, + 1337, 1349, 1302, 1335, 1300, 1295, 1332, 1337, 1337, 1287, + + 1345, 1337, 1337, 1345, 1337, 1345, 1286, 1275, 1337, 1402, + 1274, 1338, 1339, 1335, 1272, 1354, 1377, 1269, 1344, 1382, + 1346, 1268, 1355, 1346, 1348, 1346, 1359, 1348, 1337, 1348, + 1355, 1355, 1355, 1355, 1359, 1359, 1359, 1359, 1363, 1364, + 1402, 1363, 1364, 1363, 1364, 1365, 1366, 1425, 1365, 1366, + 1365, 1366, 1367, 1345, 1267, 1367, 1368, 1367, 1266, 1368, + 1369, 1368, 1262, 1369, 1261, 1369, 1260, 1335, 1335, 1335, + 1335, 1259, 1335, 1346, 1335, 1335, 1370, 1348, 1425, 1370, + 1258, 1370, 1337, 1337, 1337, 1337, 1257, 1337, 1256, 1337, + 1337, 1363, 1364, 1379, 1379, 1379, 1379, 1371, 1365, 1366, + + 1371, 1372, 1371, 1255, 1372, 1367, 1372, 1373, 1254, 1368, + 1373, 1249, 1373, 1369, 1367, 1374, 1375, 1248, 1374, 1375, + 1374, 1375, 1380, 1380, 1380, 1380, 1383, 1367, 1383, 1370, + 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1390, + 1393, 1202, 1390, 1393, 1390, 1393, 1404, 1141, 1140, 1404, + 1371, 1404, 1406, 1139, 1372, 1406, 1426, 1406, 1396, 1371, + 1373, 1396, 1137, 1396, 1426, 1426, 1426, 1426, 1374, 1375, + 1412, 1414, 1371, 1412, 1414, 1412, 1414, 1416, 1375, 1133, + 1416, 1132, 1416, 1131, 1392, 1418, 1130, 1392, 1418, 1392, + 1418, 1375, 1390, 1393, 1127, 1120, 1119, 1392, 1118, 1392, + + 1116, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, + 1115, 1396, 1114, 1113, 1379, 1391, 1112, 1111, 1391, 1110, + 1391, 1105, 1104, 1412, 1414, 1391, 1091, 1087, 1086, 1085, + 1416, 1083, 1082, 1391, 1391, 1391, 1391, 1392, 1418, 1081, + 1080, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, + 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, + 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1079, 1391, 1394, + 1420, 1078, 1394, 1420, 1394, 1420, 1077, 1422, 1428, 1394, + 1422, 1428, 1422, 1428, 1072, 1071, 1058, 1394, 1394, 1394, + 1394, 1054, 1053, 1052, 1051, 1394, 1394, 1394, 1394, 1394, + + 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, + 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, + 1394, 1395, 1394, 1420, 1395, 1050, 1395, 1049, 1048, 1043, + 1422, 1428, 1042, 1032, 1395, 1030, 1395, 1029, 1395, 1395, + 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1398, 1431, 1027, + 1398, 1025, 1398, 1024, 1023, 1021, 1431, 1431, 1431, 1431, + 1398, 1020, 1398, 1016, 1398, 1398, 1398, 1398, 1398, 1398, + 1398, 1398, 1398, 1434, 1395, 1397, 1010, 1009, 1397, 1008, + 1397, 1434, 1434, 1434, 1434, 1397, 1429, 1432, 1007, 1429, + 1432, 1429, 1432, 1397, 1397, 1397, 1397, 1006, 1005, 1428, + + 1398, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, + 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, + 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1004, 1397, 1403, + 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1411, 1429, + 1432, 1411, 1437, 1411, 1002, 1001, 1470, 997, 988, 1432, + 1437, 1437, 1437, 1437, 1470, 1470, 1470, 1470, 985, 983, + 982, 969, 1432, 968, 1411, 1411, 1411, 1411, 1411, 1411, + 1411, 1411, 1411, 1411, 1411, 1411, 1411, 1411, 1411, 1411, + 1411, 1411, 1411, 1411, 1411, 1411, 1411, 1411, 1411, 1411, + 1413, 1411, 967, 1413, 966, 1413, 965, 964, 1455, 1417, + + 963, 1455, 1417, 1455, 1417, 959, 958, 1413, 1413, 1413, + 1413, 1413, 1413, 1413, 1413, 1413, 1417, 1417, 1417, 1417, + 1417, 1417, 1417, 1417, 1417, 1471, 957, 947, 945, 1472, + 944, 943, 942, 1471, 1471, 1471, 1471, 1472, 1472, 1472, + 1472, 941, 940, 1413, 1415, 939, 934, 1415, 933, 1415, + 923, 1455, 1417, 1457, 921, 920, 1457, 1459, 1457, 918, + 1459, 1461, 1459, 916, 1461, 915, 1461, 914, 912, 911, + 1415, 1415, 1415, 1415, 1415, 1415, 1415, 1415, 1415, 1415, + 1415, 1415, 1415, 1415, 1415, 1415, 1415, 1415, 1415, 1415, + 1415, 1415, 1415, 1415, 1415, 1415, 907, 1415, 1419, 901, + + 900, 1419, 899, 1419, 1433, 898, 1457, 1433, 894, 1433, + 1459, 893, 892, 891, 1461, 890, 889, 884, 883, 881, + 868, 842, 841, 839, 1419, 1419, 1419, 1419, 1419, 1419, + 1419, 1419, 1419, 1419, 1419, 1419, 1419, 1419, 1419, 1419, + 1419, 1419, 1419, 1419, 1419, 1419, 1419, 1419, 1419, 1419, + 1421, 1419, 838, 1421, 814, 1421, 1435, 1433, 797, 1435, + 1436, 1435, 796, 1436, 794, 1436, 1433, 1421, 1421, 1421, + 1421, 1421, 1421, 1421, 1421, 1421, 792, 1438, 791, 1433, + 1438, 1439, 1438, 790, 1439, 1463, 1439, 788, 1463, 787, + 1463, 1465, 783, 780, 1465, 779, 1465, 777, 775, 774, + + 773, 771, 770, 1421, 766, 758, 757, 756, 755, 1435, + 753, 752, 751, 1436, 750, 749, 748, 747, 1435, 746, + 745, 732, 1436, 721, 719, 717, 705, 704, 690, 686, + 1438, 1435, 659, 656, 1439, 1436, 654, 653, 1463, 1438, + 651, 644, 1444, 1439, 1465, 1444, 643, 1444, 642, 641, + 640, 639, 1438, 637, 635, 1444, 1439, 1444, 602, 1444, + 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 587, 586, + 583, 582, 581, 580, 578, 577, 576, 575, 574, 573, + 572, 571, 568, 567, 557, 554, 548, 544, 541, 540, + 539, 533, 531, 530, 522, 1444, 1445, 519, 509, 508, + + 507, 504, 503, 502, 1445, 1445, 1445, 1445, 492, 491, + 456, 455, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, + 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, + 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1446, 451, + 449, 1446, 447, 1446, 446, 442, 424, 414, 413, 412, + 410, 1446, 409, 1446, 397, 1446, 1446, 1446, 1446, 1446, + 1446, 1446, 1446, 1446, 395, 394, 393, 392, 391, 390, + 388, 387, 386, 385, 383, 382, 381, 379, 377, 376, + 375, 374, 373, 369, 367, 364, 359, 358, 356, 349, + 348, 1446, 1447, 347, 346, 345, 344, 330, 329, 327, + + 1447, 1447, 1447, 1447, 326, 323, 322, 320, 1447, 1447, + 1447, 1447, 1447, 1447, 1447, 1447, 1447, 1447, 1447, 1447, + 1447, 1447, 1447, 1447, 1447, 1447, 1447, 1447, 1447, 1447, + 1447, 1447, 1447, 1447, 1448, 319, 317, 1448, 314, 1448, + 312, 298, 297, 289, 288, 287, 286, 1448, 285, 1448, + 283, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, + 278, 274, 271, 268, 267, 264, 259, 257, 255, 245, + 237, 235, 232, 228, 226, 221, 218, 216, 215, 214, + 213, 211, 209, 203, 199, 198, 196, 1448, 1449, 193, + 182, 181, 180, 176, 175, 174, 1449, 1449, 1449, 1449, + + 161, 153, 146, 145, 1449, 1449, 1449, 1449, 1449, 1449, + 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, + 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, + 1454, 143, 140, 1454, 125, 1454, 124, 120, 119, 118, + 112, 105, 102, 100, 96, 95, 93, 1454, 1454, 1454, + 1454, 1454, 1454, 1454, 1454, 1454, 92, 1458, 89, 88, + 1458, 85, 1458, 81, 78, 75, 72, 71, 70, 69, + 65, 62, 58, 57, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1454, 1456, 1456, 1456, 1456, 1456, 1456, + 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, + + 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, + 1458, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, + 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, + 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1462, 54, 49, + 1462, 47, 1462, 46, 41, 37, 36, 35, 18, 17, + 0, 0, 0, 0, 1462, 1462, 1462, 1462, 1462, 1462, + 1462, 1462, 1462, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1462, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, + + 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, + 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1494, 1494, 1494, + 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1495, 1495, + 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1496, + 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, + 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, + 1497, 1498, 1498, 1498, 1498, 1498, 1498, 1498, 1498, 1498, + 1498, 1498, 1499, 1499, 1499, 1499, 1499, 1499, 1499, 1499, + 1499, 1499, 1499, 1500, 1500, 1500, 1500, 1500, 1500, 1500, + 1500, 1500, 1500, 1500, 1501, 1501, 1501, 1501, 1501, 1501, + + 1501, 1501, 1501, 1501, 1501, 1502, 1502, 1502, 1502, 1502, + 1502, 1502, 1502, 1502, 1502, 1502, 1503, 1503, 1503, 1503, + 1503, 1503, 1503, 1503, 1503, 1503, 1503, 1504, 1504, 1504, + 1504, 1504, 1504, 1504, 1504, 1504, 1504, 1504, 1505, 1505, + 1505, 1505, 1506, 1506, 0, 1506, 1506, 1506, 1506, 1506, + 1506, 1506, 1506, 1507, 0, 0, 0, 1507, 1507, 1507, + 1507, 1507, 1507, 1507, 1508, 1508, 0, 0, 1508, 1508, + 1508, 1508, 1508, 1508, 1508, 1509, 1509, 0, 1509, 1509, + 1509, 1509, 1509, 1509, 1509, 1509, 1510, 0, 0, 0, + 1510, 1510, 1510, 1510, 1510, 1510, 1510, 1511, 0, 0, + + 0, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1512, 0, + 0, 0, 1512, 1512, 1512, 1512, 1512, 1512, 1512, 1513, + 1513, 0, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, + 1514, 1514, 0, 1514, 1514, 1514, 1514, 1514, 1514, 1514, + 1514, 1515, 1515, 0, 1515, 1515, 1515, 1515, 1515, 1515, + 1515, 1515, 1516, 1516, 0, 1516, 1516, 1516, 1516, 1516, + 1516, 1516, 1516, 1517, 1517, 1517, 1517, 1517, 1517, 1517, + 1517, 1517, 1517, 1517, 1518, 1518, 1518, 1518, 1519, 1519, + 0, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1520, + 1520, 0, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, + + 1521, 1521, 0, 1521, 1521, 1521, 1521, 1521, 1521, 1521, + 1521, 1522, 1522, 0, 1522, 1522, 1522, 1522, 1522, 1522, + 1522, 1522, 1523, 0, 1523, 1523, 1524, 1524, 1525, 0, + 0, 1525, 1525, 1526, 0, 0, 0, 1526, 1526, 1526, + 1526, 1526, 1526, 1526, 1527, 1527, 0, 0, 1527, 1527, + 1527, 1527, 1527, 1527, 1527, 1528, 1528, 0, 1528, 1528, + 1528, 1528, 1528, 1528, 1528, 1528, 1529, 0, 0, 0, + 1529, 1529, 1529, 1529, 1529, 1529, 1529, 1530, 0, 0, + 0, 1530, 1530, 1530, 1530, 1530, 1530, 1530, 1531, 0, + 0, 0, 1531, 1531, 1531, 1531, 1531, 1531, 1531, 1532, + + 1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532, + 1533, 1533, 0, 1533, 1533, 1533, 1533, 1533, 1533, 1533, + 1533, 1534, 1534, 0, 1534, 1534, 1534, 1534, 1534, 1534, + 1534, 1534, 1535, 1535, 0, 1535, 1535, 1535, 1535, 1535, + 1535, 1535, 1535, 1536, 1536, 0, 1536, 1536, 1536, 1536, + 1536, 1536, 1536, 1536, 1537, 1537, 0, 1537, 1537, 1537, + 1537, 1537, 1537, 1537, 1537, 1538, 1538, 1538, 1538, 1538, + 1538, 1538, 1538, 1538, 1538, 1538, 1540, 1540, 0, 1540, + 1540, 1540, 1540, 1540, 1540, 1540, 1540, 1541, 1541, 0, + 1541, 1541, 1541, 1541, 1541, 1541, 1541, 1541, 1542, 1542, + + 0, 1542, 1542, 1542, 1542, 1542, 1542, 1542, 1542, 1543, + 1543, 0, 1543, 1543, 1543, 1543, 1543, 1543, 1543, 1543, + 1544, 1544, 0, 1544, 1544, 1544, 1544, 1544, 1544, 1544, + 1544, 1545, 1545, 0, 1545, 1545, 1545, 1545, 1545, 1545, + 1545, 1545, 1546, 1546, 1546, 1546, 1546, 0, 1546, 1546, + 1546, 1546, 1546, 1547, 1547, 0, 1547, 1547, 1547, 1547, + 1547, 1547, 1547, 1547, 1549, 1549, 1550, 1550, 1550, 1550, + 0, 1550, 1550, 1550, 1550, 1550, 1550, 1551, 1551, 1551, + 1551, 1551, 0, 1551, 1551, 1551, 1551, 1551, 1552, 1552, + 0, 1552, 1552, 1552, 1552, 1552, 1552, 1552, 1552, 1553, + + 1553, 1553, 1553, 1553, 1553, 1553, 1553, 1553, 1553, 1553, + 1554, 1554, 0, 1554, 1554, 1554, 1554, 1554, 1554, 1554, + 1554, 1555, 1555, 0, 1555, 1555, 1555, 1555, 1555, 1555, + 1555, 1555, 1556, 1556, 0, 1556, 1556, 1556, 1556, 1556, + 1556, 1556, 1556, 1557, 1557, 0, 1557, 1557, 1557, 1557, + 1557, 1557, 1557, 1557, 1558, 1558, 0, 1558, 1558, 1558, + 1558, 1558, 1558, 1558, 1558, 1559, 1559, 0, 1559, 1559, + 1559, 1559, 1559, 1559, 1559, 1559, 1560, 1560, 0, 1560, + 1560, 1560, 1560, 1560, 1560, 1560, 1560, 1561, 1561, 0, + 1561, 1561, 1561, 1561, 1561, 1561, 1561, 1561, 1562, 1562, + + 0, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1563, + 1563, 0, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, + 1564, 1564, 0, 1564, 1564, 1564, 1564, 1564, 1564, 1564, + 1564, 1565, 1565, 0, 1565, 1565, 1565, 1565, 1565, 1565, + 1565, 1565, 1566, 1566, 0, 1566, 1566, 1566, 1566, 1566, + 1566, 1566, 1566, 1567, 1567, 0, 1567, 1567, 1567, 1567, + 1567, 1567, 1567, 1567, 1568, 1568, 0, 1568, 1568, 1568, + 1568, 1568, 1568, 1568, 1568, 1569, 1569, 1569, 1569, 1569, + 1569, 1569, 1569, 1569, 1569, 1569, 1571, 1571, 1572, 1572, + 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1573, + + 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, + 1574, 1574, 0, 1574, 1574, 1574, 1574, 1574, 1574, 1574, + 1574, 1575, 1575, 0, 1575, 1575, 1575, 1575, 1575, 1575, + 1575, 1575, 1576, 1576, 0, 1576, 1576, 1576, 1576, 1576, + 1576, 1576, 1576, 1577, 1577, 0, 1577, 1577, 1577, 1577, + 1577, 1577, 1577, 1577, 1578, 1578, 0, 1578, 1578, 1578, + 1578, 1578, 1578, 1578, 1578, 1579, 1579, 0, 1579, 1579, + 1579, 1579, 1579, 1579, 1579, 1579, 1580, 1580, 0, 1580, + 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1581, 1581, 0, + 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1582, 1582, + + 0, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1583, + 1583, 0, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, + 1584, 1584, 0, 1584, 1584, 1584, 1584, 1584, 1584, 1584, + 1584, 1585, 1585, 0, 1585, 1585, 1585, 1585, 1585, 1585, + 1585, 1585, 1586, 1586, 0, 1586, 1586, 1586, 1586, 1586, + 1586, 1586, 1586, 1587, 1587, 0, 1587, 1587, 1587, 1587, + 1587, 1587, 1587, 1587, 1588, 1588, 0, 1588, 1588, 1588, + 1588, 1588, 1588, 1588, 1588, 1589, 1589, 0, 1589, 1589, + 1589, 1589, 1589, 1589, 1589, 1589, 1590, 1590, 0, 1590, + 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1591, 1591, 0, + + 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1592, 1592, + 0, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1593, + 1593, 0, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, + 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, + 1594, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, + 1595, 1595, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, + 1596, 1596, 1596, 1597, 1597, 0, 1597, 1597, 1597, 1597, + 1597, 1597, 1597, 1597, 1598, 1598, 0, 1598, 1598, 1598, + 1598, 1598, 1598, 1598, 1598, 1599, 1599, 0, 1599, 1599, + 1599, 1599, 1599, 1599, 1599, 1599, 1600, 1600, 0, 1600, + + 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1601, 1601, 0, + 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1602, 1602, + 0, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1603, + 1603, 0, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, + 1604, 1604, 0, 1604, 1604, 1604, 1604, 1604, 1604, 1604, + 1604, 1605, 1605, 0, 1605, 1605, 1605, 1605, 1605, 1605, + 1605, 1605, 1606, 0, 1606, 1606, 1606, 1606, 1606, 1606, + 1606, 1606, 1606, 1607, 1607, 0, 1607, 1607, 1607, 1607, + 1607, 1607, 1607, 1607, 1608, 1608, 1608, 1608, 1608, 1608, + 1608, 1608, 1608, 1608, 1608, 1609, 1609, 0, 1609, 1609, + + 1609, 1609, 1609, 1609, 1609, 1609, 1610, 1610, 0, 1610, + 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1611, 1611, 0, + 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1612, 1612, + 0, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1613, + 1613, 0, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, + 1614, 1614, 0, 1614, 1614, 1614, 1614, 1614, 1614, 1614, + 1614, 1615, 1615, 0, 1615, 1615, 1615, 1615, 1615, 1615, + 1615, 1615, 1616, 1616, 0, 1616, 1616, 1616, 1616, 1616, + 1616, 1616, 1616, 1617, 1617, 0, 1617, 1617, 1617, 1617, + 1617, 1617, 1617, 1617, 1618, 1618, 0, 1618, 1618, 1618, + + 1618, 1618, 1618, 1618, 1618, 1619, 1619, 0, 1619, 1619, + 1619, 1619, 1619, 1619, 1619, 1619, 1620, 1620, 0, 1620, + 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1621, 1621, 0, + 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1622, 1622, + 0, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1623, + 1623, 0, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, + 1624, 1624, 0, 1624, 1624, 1624, 1624, 1624, 1624, 1624, + 1624, 1625, 1625, 0, 1625, 1625, 1625, 1625, 1625, 1625, + 1625, 1625, 1626, 1626, 0, 1626, 1626, 1626, 1626, 1626, + 1626, 1626, 1626, 1627, 1627, 0, 1627, 1627, 1627, 1627, + + 1627, 1627, 1627, 1627, 1628, 1628, 0, 1628, 1628, 1628, + 1628, 1628, 1628, 1628, 1628, 1629, 1629, 0, 1629, 1629, + 1629, 1629, 1629, 1629, 1629, 1629, 1630, 1630, 0, 1630, + 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1631, 1631, 0, + 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1632, 1632, + 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1633, + 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, + 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, + 1634, 1635, 1635, 0, 1635, 1635, 1635, 1635, 1635, 1635, + 1635, 1635, 1636, 1636, 0, 1636, 1636, 1636, 1636, 1636, + + 1636, 1636, 1636, 1637, 1637, 0, 1637, 1637, 1637, 1637, + 1637, 1637, 1637, 1637, 1638, 1638, 0, 1638, 1638, 1638, + 1638, 1638, 1638, 1638, 1638, 1639, 1639, 0, 1639, 1639, + 1639, 1639, 1639, 1639, 1639, 1639, 1640, 1640, 0, 1640, + 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1641, 1641, 0, + 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1642, 0, + 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1643, + 1643, 0, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, + 1644, 1644, 0, 1644, 1644, 1644, 1644, 1644, 1644, 1644, + 1644, 1645, 1645, 0, 1645, 1645, 1645, 1645, 1645, 1645, + + 1645, 1645, 1646, 0, 1646, 1646, 1647, 1647, 0, 1647, + 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1648, 1648, 0, + 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1649, 1649, + 0, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1650, + 1650, 0, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, + 1651, 1651, 0, 1651, 1651, 1651, 1651, 1651, 1651, 1651, + 1651, 1652, 1652, 0, 1652, 1652, 1652, 1652, 1652, 1652, + 1652, 1652, 1653, 1653, 0, 1653, 1653, 1653, 1653, 1653, + 1653, 1653, 1653, 1654, 1654, 0, 1654, 1654, 1654, 1654, + 1654, 1654, 1654, 1654, 1655, 1655, 1655, 1655, 1655, 1655, + + 1655, 1655, 1655, 1655, 1655, 1656, 1656, 0, 1656, 1656, + 1656, 1656, 1656, 1656, 1656, 1656, 1657, 1657, 0, 1657, + 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1658, 1658, 0, + 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1660, 1660, + 0, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1661, + 1661, 0, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, + 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, + 1662, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, + 1663, 1663, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, + 1664, 1664, 1664, 1665, 1665, 0, 1665, 1665, 1665, 1665, + + 1665, 1665, 1665, 1665, 1666, 0, 1666, 0, 1666, 1666, + 1667, 1667, 0, 1667, 1667, 1667, 1667, 1667, 1667, 1667, + 1667, 1668, 0, 1668, 1668, 1669, 0, 1669, 1669, 1672, + 1672, 0, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, + 1673, 1673, 0, 1673, 1673, 1673, 1673, 1673, 1673, 1673, + 1673, 1674, 1674, 0, 1674, 1674, 1674, 1674, 1674, 1674, + 1674, 1674, 1675, 0, 1675, 1675, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, + 1493, 1493, 1493, 1493, 1493, 1493 + + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "lexgrog.l" + +#line 19 "lexgrog.l" + +/* + * lexgrog.l: extract 'whatis' info from nroff man / formatted cat pages. + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + * 2011, 2012 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Wed Oct 12 18:46:11 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + * + * CJW: Detect grap and vgrind. Understand fill requests. Other improvements + * in the syntax accepted. + */ + +#include <sys/stat.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "gettext.h" +#define _(String) gettext (String) + +#include "error.h" +#include "pipeline.h" +#include "decompress.h" +#include "security.h" +#include "encodings.h" +#include "sandbox.h" + +#include "manconv_client.h" + +#define YY_READ_BUF_SIZE 1024 +#define MAX_NAME 8192 + +#define ARRAY_SIZE(array) (sizeof (array) / sizeof ((array)[0])) + +extern man_sandbox *sandbox; + +struct macro { + const char *name; + const char *value; +}; + +static const struct macro glyphs[] = { + /* It is vital to keep these in strcmp order (sort -t\" -k2)! They + * will be searched using bsearch. + * Data from groff_char(7), although I have omitted some that are + * particularly unlikely to be used in NAME sections. + */ + { "'A", "Á" }, + { "'C", "Ć" }, + { "'E", "É" }, + { "'I", "Í" }, + { "'O", "Ó" }, + { "'U", "Ú" }, + { "'Y", "Ý" }, + { "'a", "á" }, + { "'c", "ć" }, + { "'e", "é" }, + { "'i", "í" }, + { "'o", "ó" }, + { "'u", "ú" }, + { "'y", "ý" }, + { ",C", "Ç" }, + { ",c", "ç" }, + { "-D", "Ð" }, + { ".i", "ı" }, + { "/L", "Ł" }, + { "/O", "Ø" }, + { "/l", "ł" }, + { "/o", "ø" }, + { ":A", "Ä" }, + { ":E", "Ë" }, + { ":I", "Ï" }, + { ":O", "Ö" }, + { ":U", "Ü" }, + { ":Y", "Ÿ" }, + { ":a", "ä" }, + { ":e", "ë" }, + { ":i", "ï" }, + { ":o", "ö" }, + { ":u", "ü" }, + { ":y", "ÿ" }, + { "AE", "Æ" }, + { "Bq", "„" }, + { "Fc", "»" }, + { "Fi", "ffi" }, + { "Fl", "ffl" }, + { "Fo", "«" }, + { "IJ", "IJ" }, + { "OE", "Œ" }, + { "Sd", "ð" }, + { "TP", "Þ" }, + { "Tp", "þ" }, + { "^A", "Â" }, + { "^E", "Ê" }, + { "^I", "Î" }, + { "^O", "Ô" }, + { "^U", "Û" }, + { "^a", "â" }, + { "^e", "ê" }, + { "^i", "î" }, + { "^o", "ô" }, + { "^u", "û" }, + { "`A", "À" }, + { "`E", "È" }, + { "`I", "Ì" }, + { "`O", "Ò" }, + { "`U", "Ù" }, + { "`a", "à" }, + { "`e", "è" }, + { "`i", "ì" }, + { "`o", "ò" }, + { "`u", "ù" }, + { "a\"", "˝" }, + { "a-", "¯" }, + { "a.", "˙" }, + { "a^", "^" }, + { "aa", "´" }, + { "ab", "˘" }, + { "ac", "¸" }, + { "ad", "¨" }, + { "ae", "æ" }, + { "ah", "ˇ" }, + { "ao", "˚" }, + { "aq", "'" }, + { "a~", "~" }, + { "bq", "‚" }, + { "cq", "’" }, + { "dq", "\"" }, + { "em", "—" }, + { "en", "–" }, + { "fc", "›" }, + { "ff", "ff" }, + { "fi", "fi" }, + { "fl", "fl" }, + { "fo", "‹" }, + { "ga", "`" }, + { "ha", "^" }, + { "ho", "˛" }, + { "hy", "‐" }, + { "ij", "ij" }, + { "lq", "“" }, + { "oA", "Å" }, + { "oa", "å" }, + { "oe", "œ" }, + { "oq", "‘" }, + { "r!", "¡" }, + { "r?", "¿" }, + { "rq", "”" }, + { "ss", "ß" }, + { "ti", "~" }, + { "vS", "Š" }, + { "vZ", "Ž" }, + { "vs", "š" }, + { "vz", "ž" }, + { "~A", "Ã" }, + { "~N", "Ñ" }, + { "~O", "Õ" }, + { "~a", "ã" }, + { "~n", "ñ" }, + { "~o", "õ" } +}; + +static const struct macro perldocs[] = { + /* It is vital to keep these in strcmp order (sort -t\" -k2)! They + * will be searched using bsearch. + * Data from Pod/Man.pm. + */ + { "--", "-" }, + { "Aq", "'" }, + { "C'", "'" }, + { "C+", "C++" }, + { "C`", "`" }, + { "L\"", "\"" }, + { "PI", "π" }, + { "R\"", "\"" } +}; + +static void add_str_to_whatis (const char *string, size_t length); +static void add_char_to_whatis (unsigned char c); +static void add_separator_to_whatis (void); +static void add_wordn_to_whatis (const char *string, size_t length); +static void add_word_to_whatis (const char *string); +static void add_glyph_to_whatis (const char *string, size_t length); +static void add_perldoc_to_whatis (const char *string, size_t length); +static void mdoc_text (const char *string); +static void newline_found (void); + +static char newname[MAX_NAME]; +static char *p_name; +static const char *fname; +static char filters[MAX_FILTERS]; + +static int fill_mode; +static int waiting_for_quote; + +static pipeline *decomp; + +#define YY_INPUT(buf,result,max_size) { \ + size_t size = max_size; \ + const char *block = pipeline_read (decomp, &size); \ + if (block && size != 0) { \ + memcpy (buf, block, size); \ + buf[size] = '\0'; \ + result = size; \ + } else \ + result = YY_NULL; \ +} +#define YY_NO_INPUT +#line 2878 "lexgrog.c" + +#line 292 "lexgrog.l" + /* Please add to this list if you know how. */ + /* Note that, since flex only supports UTF-8 by accident, character classes + * including non-ASCII characters must be written out as (a|b|c|d) rather + * than [abcd]. + */ + /* NOME also works for gl, pt */ + /* eptgrv : eqn, pic, tbl, grap, refer, vgrind */ +#line 2888 "lexgrog.c" + +#define INITIAL 0 +#define MAN_PRENAME 1 +#define MAN_NAME 2 +#define MAN_DESC 3 +#define MAN_DESC_AT 4 +#define MAN_DESC_BSX 5 +#define MAN_DESC_BX 6 +#define MAN_DESC_BX_RELEASE 7 +#define MAN_DESC_DQ 8 +#define MAN_DESC_FX 9 +#define MAN_DESC_NX 10 +#define MAN_DESC_OX 11 +#define CAT_NAME 12 +#define CAT_FILE 13 +#define MAN_FILE 14 +#define CAT_REST 15 +#define MAN_REST 16 +#define FORCE_EXIT 17 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals ( void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( void ); + +int yyget_debug ( void ); + +void yyset_debug ( int debug_flag ); + +YY_EXTRA_TYPE yyget_extra ( void ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in ( void ); + +void yyset_in ( FILE * _in_str ); + +FILE *yyget_out ( void ); + +void yyset_out ( FILE * _out_str ); + + int yyget_leng ( void ); + +char *yyget_text ( void ); + +int yyget_lineno ( void ); + +void yyset_lineno ( int _line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( void ); +#else +extern int yywrap ( void ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * ); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + { +#line 340 "lexgrog.l" + + + /* begin NAME section processing */ +#line 3124 "lexgrog.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 1494 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_current_state != 1493 ); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 343 "lexgrog.l" +BEGIN (MAN_PRENAME); + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 344 "lexgrog.l" +BEGIN (CAT_NAME); + YY_BREAK +/* general text matching */ + +case 3: +#line 349 "lexgrog.l" +case 4: +#line 350 "lexgrog.l" +case 5: +#line 351 "lexgrog.l" +case 6: +/* rule 6 can match eol */ +YY_RULE_SETUP +#line 351 "lexgrog.l" + + YY_BREAK + + +case 7: +#line 356 "lexgrog.l" +case 8: +#line 357 "lexgrog.l" +case 9: +/* rule 9 can match eol */ +#line 358 "lexgrog.l" +case 10: +/* rule 10 can match eol */ +YY_RULE_SETUP +#line 358 "lexgrog.l" + + YY_BREAK + + +case 11: +/* rule 11 can match eol */ +YY_RULE_SETUP +#line 362 "lexgrog.l" +filters[TBL_FILTER] = 't'; + YY_BREAK +case 12: +/* rule 12 can match eol */ +YY_RULE_SETUP +#line 363 "lexgrog.l" +filters[EQN_FILTER] = 'e'; + YY_BREAK +case 13: +/* rule 13 can match eol */ +YY_RULE_SETUP +#line 364 "lexgrog.l" +filters[PIC_FILTER] = 'p'; + YY_BREAK +case 14: +/* rule 14 can match eol */ +YY_RULE_SETUP +#line 365 "lexgrog.l" +filters[GRAP_FILTER] = 'g'; + YY_BREAK +case 15: +/* rule 15 can match eol */ +#line 367 "lexgrog.l" +case 16: +/* rule 16 can match eol */ +YY_RULE_SETUP +#line 367 "lexgrog.l" +filters[REF_FILTER] = 'r'; + YY_BREAK +case 17: +/* rule 17 can match eol */ +YY_RULE_SETUP +#line 368 "lexgrog.l" +filters[VGRIND_FILTER] = 'v'; + YY_BREAK + +case YY_STATE_EOF(MAN_REST): +#line 370 "lexgrog.l" +{ /* exit */ + *p_name = '\0'; /* terminate the string */ + yyterminate (); +} + YY_BREAK +case 18: +/* rule 18 can match eol */ +YY_RULE_SETUP +#line 374 "lexgrog.l" + + YY_BREAK +/* rules to end NAME section processing */ +case 19: +/* rule 19 can match eol */ +YY_RULE_SETUP +#line 377 "lexgrog.l" +{ /* forced exit */ + *p_name = '\0'; /* terminate the string */ + yyterminate (); +} + YY_BREAK +case 20: +/* rule 20 can match eol */ +#line 383 "lexgrog.l" +YY_RULE_SETUP +case YY_STATE_EOF(MAN_PRENAME): +#line 383 "lexgrog.l" +{ /* no NAME at all */ + *p_name = '\0'; + BEGIN (MAN_REST); +} + YY_BREAK +/* need to match whole string so that we beat the following roff catch-all, + so use yyless to push back the name */ + +case 21: +/* rule 21 can match eol */ +#line 392 "lexgrog.l" +case 22: +/* rule 22 can match eol */ +#line 393 "lexgrog.l" +case 23: +/* rule 23 can match eol */ +#line 394 "lexgrog.l" +case 24: +/* rule 24 can match eol */ +#line 395 "lexgrog.l" +case 25: +/* rule 25 can match eol */ +#line 396 "lexgrog.l" +case 26: +/* rule 26 can match eol */ +YY_RULE_SETUP +#line 396 "lexgrog.l" +{ + yyless (0); + BEGIN (MAN_NAME); + } + YY_BREAK + +/* Skip over initial roff requests in NAME section. The use of yyless here + is evil. */ +case 27: +/* rule 27 can match eol */ +YY_RULE_SETUP +#line 404 "lexgrog.l" + + YY_BREAK +case 28: +/* rule 28 can match eol */ +YY_RULE_SETUP +#line 406 "lexgrog.l" +yyless (1); + YY_BREAK +case 29: +/* rule 29 can match eol */ +YY_RULE_SETUP +#line 408 "lexgrog.l" +{ + yyless (0); + BEGIN (MAN_NAME); +} + YY_BREAK + +case 30: +/* rule 30 can match eol */ +#line 415 "lexgrog.l" +case 31: +/* rule 31 can match eol */ +#line 416 "lexgrog.l" +case 32: +/* rule 32 can match eol */ +#line 417 "lexgrog.l" +case 33: +/* rule 33 can match eol */ +#line 418 "lexgrog.l" +case 34: +/* rule 34 can match eol */ +#line 419 "lexgrog.l" +case 35: +/* rule 35 can match eol */ +#line 420 "lexgrog.l" +case 36: +/* rule 36 can match eol */ +#line 421 "lexgrog.l" +YY_RULE_SETUP +case YY_STATE_EOF(MAN_NAME): +YY_RULE_SETUP +case YY_STATE_EOF(MAN_DESC): +#line 421 "lexgrog.l" +{ /* terminate the string */ + *p_name = '\0'; + BEGIN (MAN_REST); + } + YY_BREAK + + +case 37: +/* rule 37 can match eol */ +#line 429 "lexgrog.l" +case 38: +/* rule 38 can match eol */ +#line 430 "lexgrog.l" +case 39: +/* rule 39 can match eol */ +YY_RULE_SETUP +#line 430 "lexgrog.l" +{ /* terminate the string */ + *p_name = '\0'; + BEGIN (CAT_REST); + yyterminate (); + } + YY_BREAK + +/* ROFF request removal */ + +/* some include quoting; dealing with this is unpleasant */ +case 40: +/* rule 40 can match eol */ +YY_RULE_SETUP +#line 440 "lexgrog.l" +{ + newline_found (); + waiting_for_quote = 1; + } + YY_BREAK +case 41: +/* rule 41 can match eol */ +#line 446 "lexgrog.l" +case 42: +/* rule 42 can match eol */ +#line 447 "lexgrog.l" +case 43: +/* rule 43 can match eol */ +#line 448 "lexgrog.l" +case 44: +/* rule 44 can match eol */ +#line 449 "lexgrog.l" +case 45: +/* rule 45 can match eol */ +#line 450 "lexgrog.l" +case 46: +/* rule 46 can match eol */ +#line 451 "lexgrog.l" +case 47: +/* rule 47 can match eol */ +#line 452 "lexgrog.l" +case 48: +/* rule 48 can match eol */ +#line 453 "lexgrog.l" +case 49: +/* rule 49 can match eol */ +YY_RULE_SETUP +#line 453 "lexgrog.l" +{ /* per line comments */ + newline_found (); + } + YY_BREAK + +/* No-op requests */ + +case 50: +/* rule 50 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_cp - 1); +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 460 "lexgrog.l" +newline_found (); + YY_BREAK +case 51: +/* rule 51 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_cp - 1); +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 461 "lexgrog.l" +newline_found (); + YY_BREAK + +/* Toggle fill mode */ + +case 52: +/* rule 52 can match eol */ +YY_RULE_SETUP +#line 466 "lexgrog.l" +fill_mode = 0; + YY_BREAK +case 53: +/* rule 53 can match eol */ +YY_RULE_SETUP +#line 467 "lexgrog.l" +fill_mode = 1; + YY_BREAK + +case 54: +/* rule 54 can match eol */ +YY_RULE_SETUP +#line 470 "lexgrog.l" +/* strip continuations */ + YY_BREAK +/* convert to DASH */ + +case 55: +/* rule 55 can match eol */ +#line 475 "lexgrog.l" +case 56: +/* rule 56 can match eol */ +#line 476 "lexgrog.l" +case 57: +/* rule 57 can match eol */ +#line 477 "lexgrog.l" +case 58: +/* rule 58 can match eol */ +#line 478 "lexgrog.l" +case 59: +/* rule 59 can match eol */ +YY_RULE_SETUP +#line 478 "lexgrog.l" +{ + add_separator_to_whatis (); + BEGIN (MAN_DESC); + } + YY_BREAK + +case 60: +/* rule 60 can match eol */ +YY_RULE_SETUP +#line 483 "lexgrog.l" +add_separator_to_whatis (); + YY_BREAK +/* escape sequences and special characters */ + +case 61: +/* rule 61 can match eol */ +YY_RULE_SETUP +#line 487 "lexgrog.l" +add_char_to_whatis ('\\'); + YY_BREAK +case 62: +/* rule 62 can match eol */ +YY_RULE_SETUP +#line 488 "lexgrog.l" +add_char_to_whatis ('\''); + YY_BREAK +case 63: +/* rule 63 can match eol */ +YY_RULE_SETUP +#line 489 "lexgrog.l" +add_char_to_whatis ('`'); + YY_BREAK +case 64: +/* rule 64 can match eol */ +YY_RULE_SETUP +#line 490 "lexgrog.l" +add_char_to_whatis ('-'); + YY_BREAK +case 65: +/* rule 65 can match eol */ +YY_RULE_SETUP +#line 491 "lexgrog.l" +add_char_to_whatis ('-'); + YY_BREAK +case 66: +/* rule 66 can match eol */ +YY_RULE_SETUP +#line 492 "lexgrog.l" +add_char_to_whatis ('.'); + YY_BREAK +case 67: +/* rule 67 can match eol */ +YY_RULE_SETUP +#line 493 "lexgrog.l" +add_char_to_whatis (' '); + YY_BREAK +case 68: +/* rule 68 can match eol */ +YY_RULE_SETUP +#line 494 "lexgrog.l" +add_char_to_whatis ('_'); + YY_BREAK +case 69: +/* rule 69 can match eol */ +YY_RULE_SETUP +#line 495 "lexgrog.l" +add_char_to_whatis ('\t'); + YY_BREAK +case 70: +/* rule 70 can match eol */ +YY_RULE_SETUP +#line 497 "lexgrog.l" +/* various useless control chars */ + YY_BREAK +case 71: +/* rule 71 can match eol */ +YY_RULE_SETUP +#line 498 "lexgrog.l" +/* various inline functions */ + YY_BREAK +case 72: +/* rule 72 can match eol */ +YY_RULE_SETUP +#line 500 "lexgrog.l" +/* interpolate arg */ + YY_BREAK +/* roff named glyphs */ +case 73: +/* rule 73 can match eol */ +YY_RULE_SETUP +#line 503 "lexgrog.l" +add_glyph_to_whatis (yytext + 2, 2); + YY_BREAK +/* perldoc strings */ +case 74: +/* rule 74 can match eol */ +YY_RULE_SETUP +#line 505 "lexgrog.l" +add_perldoc_to_whatis (yytext + 3, 2); + YY_BREAK +case 75: +/* rule 75 can match eol */ +YY_RULE_SETUP +#line 506 "lexgrog.l" +add_perldoc_to_whatis (yytext + 2, 1); + YY_BREAK +case 76: +/* rule 76 can match eol */ +YY_RULE_SETUP +#line 508 "lexgrog.l" +/* comment */ + YY_BREAK +case 77: +/* rule 77 can match eol */ +YY_RULE_SETUP +#line 510 "lexgrog.l" +/* font changes */ + YY_BREAK +case 78: +/* rule 78 can match eol */ +YY_RULE_SETUP +#line 511 "lexgrog.l" +/* mark input place in register */ + YY_BREAK +case 79: +/* rule 79 can match eol */ +YY_RULE_SETUP +#line 513 "lexgrog.l" +/* interpolate number register */ + YY_BREAK +case 80: +/* rule 80 can match eol */ +YY_RULE_SETUP +#line 514 "lexgrog.l" +/* overstrike chars */ + YY_BREAK +case 81: +/* rule 81 can match eol */ +YY_RULE_SETUP +#line 516 "lexgrog.l" +/* size changes */ + YY_BREAK +case 82: +/* rule 82 can match eol */ +YY_RULE_SETUP +#line 517 "lexgrog.l" +/* width of string */ + YY_BREAK +case 83: +/* rule 83 can match eol */ +YY_RULE_SETUP +#line 519 "lexgrog.l" +/* catch all */ + YY_BREAK +case 84: +/* rule 84 can match eol */ +YY_RULE_SETUP +#line 521 "lexgrog.l" +/* function() in hpux */ + YY_BREAK + +/* some people rather ambitiously use non-trivial mdoc macros in NAME + sections; cope with those that have been seen in the wild, and a few + more */ + +case 85: +/* rule 85 can match eol */ +YY_RULE_SETUP +#line 528 "lexgrog.l" +BEGIN (MAN_DESC_AT); + YY_BREAK +case 86: +/* rule 86 can match eol */ +YY_RULE_SETUP +#line 529 "lexgrog.l" +BEGIN (MAN_DESC_BSX); + YY_BREAK +case 87: +/* rule 87 can match eol */ +YY_RULE_SETUP +#line 530 "lexgrog.l" +BEGIN (MAN_DESC_BX); + YY_BREAK +case 88: +/* rule 88 can match eol */ +YY_RULE_SETUP +#line 531 "lexgrog.l" +BEGIN (MAN_DESC_FX); + YY_BREAK +case 89: +/* rule 89 can match eol */ +YY_RULE_SETUP +#line 532 "lexgrog.l" +BEGIN (MAN_DESC_NX); + YY_BREAK +case 90: +/* rule 90 can match eol */ +YY_RULE_SETUP +#line 533 "lexgrog.l" +BEGIN (MAN_DESC_OX); + YY_BREAK +case 91: +/* rule 91 can match eol */ +YY_RULE_SETUP +#line 534 "lexgrog.l" +add_word_to_whatis ("UNIX"); + YY_BREAK +case 92: +/* rule 92 can match eol */ +YY_RULE_SETUP +#line 536 "lexgrog.l" +{ + add_word_to_whatis ("\""); + BEGIN (MAN_DESC_DQ); + } + YY_BREAK + + +case 93: +YY_RULE_SETUP +#line 543 "lexgrog.l" +mdoc_text ("Version 32V AT&T UNIX"); + YY_BREAK +case 94: +YY_RULE_SETUP +#line 544 "lexgrog.l" +mdoc_text ("Version 1 AT&T UNIX"); + YY_BREAK +case 95: +YY_RULE_SETUP +#line 545 "lexgrog.l" +mdoc_text ("Version 2 AT&T UNIX"); + YY_BREAK +case 96: +YY_RULE_SETUP +#line 546 "lexgrog.l" +mdoc_text ("Version 3 AT&T UNIX"); + YY_BREAK +case 97: +YY_RULE_SETUP +#line 547 "lexgrog.l" +mdoc_text ("Version 4 AT&T UNIX"); + YY_BREAK +case 98: +YY_RULE_SETUP +#line 548 "lexgrog.l" +mdoc_text ("Version 5 AT&T UNIX"); + YY_BREAK +case 99: +YY_RULE_SETUP +#line 549 "lexgrog.l" +mdoc_text ("Version 6 AT&T UNIX"); + YY_BREAK +case 100: +YY_RULE_SETUP +#line 550 "lexgrog.l" +mdoc_text ("Version 7 AT&T UNIX"); + YY_BREAK +case 101: +YY_RULE_SETUP +#line 551 "lexgrog.l" +mdoc_text ("AT&T System V UNIX"); + YY_BREAK +case 102: +YY_RULE_SETUP +#line 552 "lexgrog.l" +mdoc_text ("AT&T System V.1 UNIX"); + YY_BREAK +case 103: +YY_RULE_SETUP +#line 553 "lexgrog.l" +mdoc_text ("AT&T System V.2 UNIX"); + YY_BREAK +case 104: +YY_RULE_SETUP +#line 554 "lexgrog.l" +mdoc_text ("AT&T System V.3 UNIX"); + YY_BREAK +case 105: +YY_RULE_SETUP +#line 555 "lexgrog.l" +mdoc_text ("AT&T System V.4 UNIX"); + YY_BREAK +case 106: +/* rule 106 can match eol */ +YY_RULE_SETUP +#line 556 "lexgrog.l" +{ + yyless (0); + mdoc_text ("AT&T UNIX"); + } + YY_BREAK + + +case 107: +YY_RULE_SETUP +#line 563 "lexgrog.l" +{ + add_word_to_whatis ("BSD/OS"); + add_wordn_to_whatis (yytext, yyleng); + BEGIN (MAN_DESC); + } + YY_BREAK +case 108: +/* rule 108 can match eol */ +YY_RULE_SETUP +#line 568 "lexgrog.l" +{ + yyless (0); + mdoc_text ("BSD/OS"); + } + YY_BREAK + + +case 109: +YY_RULE_SETUP +#line 575 "lexgrog.l" +mdoc_text ("BSD (currently in alpha test)"); + YY_BREAK +case 110: +YY_RULE_SETUP +#line 576 "lexgrog.l" +mdoc_text ("BSD (currently in beta test)"); + YY_BREAK +case 111: +YY_RULE_SETUP +#line 577 "lexgrog.l" +mdoc_text ("BSD (currently under development"); + YY_BREAK +case 112: +YY_RULE_SETUP +#line 578 "lexgrog.l" +{ + add_wordn_to_whatis (yytext, yyleng); + add_str_to_whatis ("BSD", 3); + BEGIN (MAN_DESC_BX_RELEASE); + } + YY_BREAK +case 113: +/* rule 113 can match eol */ +YY_RULE_SETUP +#line 583 "lexgrog.l" +{ + yyless (0); + mdoc_text ("BSD"); + } + YY_BREAK + + +case 114: +YY_RULE_SETUP +#line 590 "lexgrog.l" +{ + add_str_to_whatis ("-Reno", 5); + BEGIN (MAN_DESC); + } + YY_BREAK +case 115: +YY_RULE_SETUP +#line 594 "lexgrog.l" +{ + add_str_to_whatis ("-Tahoe", 6); + BEGIN (MAN_DESC); + } + YY_BREAK +case 116: +YY_RULE_SETUP +#line 598 "lexgrog.l" +{ + add_str_to_whatis ("-Lite", 5); + BEGIN (MAN_DESC); + } + YY_BREAK +case 117: +YY_RULE_SETUP +#line 602 "lexgrog.l" +{ + add_str_to_whatis ("-Lite2", 6); + BEGIN (MAN_DESC); + } + YY_BREAK +case 118: +/* rule 118 can match eol */ +YY_RULE_SETUP +#line 606 "lexgrog.l" +{ + yyless (0); + BEGIN (MAN_DESC); + } + YY_BREAK + +case 119: +YY_RULE_SETUP +#line 612 "lexgrog.l" +{ + add_str_to_whatis (yytext, yyleng); + add_char_to_whatis ('"'); + BEGIN (MAN_DESC); +} + YY_BREAK + +case 120: +YY_RULE_SETUP +#line 619 "lexgrog.l" +{ + add_word_to_whatis ("FreeBSD"); + add_wordn_to_whatis (yytext, yyleng); + BEGIN (MAN_DESC); + } + YY_BREAK +case 121: +/* rule 121 can match eol */ +YY_RULE_SETUP +#line 624 "lexgrog.l" +{ + yyless (0); + mdoc_text ("FreeBSD"); + } + YY_BREAK + + +case 122: +YY_RULE_SETUP +#line 631 "lexgrog.l" +{ + add_word_to_whatis ("NetBSD"); + add_wordn_to_whatis (yytext, yyleng); + BEGIN (MAN_DESC); + } + YY_BREAK +case 123: +/* rule 123 can match eol */ +YY_RULE_SETUP +#line 636 "lexgrog.l" +{ + yyless (0); + mdoc_text ("NetBSD"); + } + YY_BREAK + + +case 124: +YY_RULE_SETUP +#line 643 "lexgrog.l" +{ + add_word_to_whatis ("OpenBSD"); + add_wordn_to_whatis (yytext, yyleng); + BEGIN (MAN_DESC); + } + YY_BREAK +case 125: +/* rule 125 can match eol */ +YY_RULE_SETUP +#line 648 "lexgrog.l" +{ + yyless (0); + mdoc_text ("OpenBSD"); + } + YY_BREAK + +/* collapse spaces, escaped spaces, tabs, newlines to a single space */ +case 126: +/* rule 126 can match eol */ +YY_RULE_SETUP +#line 655 "lexgrog.l" +add_char_to_whatis (' '); + YY_BREAK +/* a ROFF break request, a paragraph request, or an indentation change + usually means we have multiple whatis definitions, provide a separator + for later processing */ + +case 127: +/* rule 127 can match eol */ +#line 662 "lexgrog.l" +case 128: +/* rule 128 can match eol */ +#line 663 "lexgrog.l" +case 129: +/* rule 129 can match eol */ +#line 664 "lexgrog.l" +case 130: +/* rule 130 can match eol */ +#line 665 "lexgrog.l" +case 131: +/* rule 131 can match eol */ +#line 666 "lexgrog.l" +case 132: +/* rule 132 can match eol */ +#line 667 "lexgrog.l" +case 133: +/* rule 133 can match eol */ +#line 668 "lexgrog.l" +case 134: +/* rule 134 can match eol */ +YY_RULE_SETUP +#line 668 "lexgrog.l" +{ + add_char_to_whatis ((char) 0x11); + BEGIN (MAN_NAME); + } + YY_BREAK + +/* any other roff request we don't recognise terminates definitions */ +case 135: +/* rule 135 can match eol */ +YY_RULE_SETUP +#line 675 "lexgrog.l" +{ + *p_name = '\0'; + BEGIN (MAN_REST); +} + YY_BREAK +/* pass words as a chunk. speed optimization */ +case 136: +YY_RULE_SETUP +#line 681 "lexgrog.l" +add_str_to_whatis (yytext, yyleng); + YY_BREAK +/* normalise the comma (,) separators */ +case 137: +/* rule 137 can match eol */ +#line 685 "lexgrog.l" +case 138: +/* rule 138 can match eol */ +YY_RULE_SETUP +#line 685 "lexgrog.l" +add_str_to_whatis (", ", 2); + YY_BREAK +case 139: +/* rule 139 can match eol */ +YY_RULE_SETUP +#line 687 "lexgrog.l" +{ + newline_found (); + add_char_to_whatis (yytext[yyleng - 1]); +} + YY_BREAK +case 140: +YY_RULE_SETUP +#line 692 "lexgrog.l" +add_char_to_whatis (*yytext); + YY_BREAK +/* default EOF rule */ +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(MAN_DESC_AT): +case YY_STATE_EOF(MAN_DESC_BSX): +case YY_STATE_EOF(MAN_DESC_BX): +case YY_STATE_EOF(MAN_DESC_BX_RELEASE): +case YY_STATE_EOF(MAN_DESC_DQ): +case YY_STATE_EOF(MAN_DESC_FX): +case YY_STATE_EOF(MAN_DESC_NX): +case YY_STATE_EOF(MAN_DESC_OX): +case YY_STATE_EOF(CAT_NAME): +case YY_STATE_EOF(CAT_FILE): +case YY_STATE_EOF(MAN_FILE): +case YY_STATE_EOF(CAT_REST): +case YY_STATE_EOF(FORCE_EXIT): +#line 695 "lexgrog.l" +return 1; + YY_BREAK +case 141: +YY_RULE_SETUP +#line 697 "lexgrog.l" +ECHO; + YY_BREAK +#line 4068 "lexgrog.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + yy_state_type yy_current_state; + char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 1494 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + int yy_is_jam; + char *yy_cp = (yy_c_buf_p); + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 1494 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 1493); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf ); + + yyfree( (void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + yy_size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr ) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg ) +{ + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param _line_number line number + * + */ +void yyset_lineno (int _line_number ) +{ + + yylineno = _line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str ) +{ + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str ) +{ + yyout = _out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int _bdebug ) +{ + yy_flex_debug = _bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = NULL; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = NULL; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n ) +{ + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s ) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 697 "lexgrog.l" + + +/* print warning and force scanner to terminate */ +static void too_big (void) +{ + /* Even though MAX_NAME is a macro expanding to a constant, we + * translate it using ngettext anyway because that will make it + * easier to change the macro later. + */ + error (0, 0, + ngettext ("warning: whatis for %s exceeds %d byte, " + "truncating.", + "warning: whatis for %s exceeds %d bytes, " + "truncating.", MAX_NAME), + fname, MAX_NAME); + + BEGIN (FORCE_EXIT); +} + +/* append a string to newname if enough room */ +static void add_str_to_whatis (const char *string, size_t length) +{ + if (p_name - newname + length >= MAX_NAME) + too_big (); + else { + (void) strncpy (p_name, string, length); + p_name += length; + } +} + +/* append a char to newname if enough room */ +static void add_char_to_whatis (unsigned char c) +{ + if (p_name - newname + 1 >= MAX_NAME) + too_big (); + else if (waiting_for_quote && c == '"') + waiting_for_quote = 0; + else + *p_name++ = c; +} + +/* append the " - " separator to newname, trimming the first space if one's + * already there + */ +static void add_separator_to_whatis (void) +{ + if (p_name != newname && *(p_name - 1) != ' ') + add_char_to_whatis (' '); + add_str_to_whatis ("- ", 2); +} + +/* append a word to newname if enough room, ensuring only necessary + surrounding space */ +static void add_wordn_to_whatis (const char *string, size_t length) +{ + if (p_name != newname && *(p_name - 1) != ' ') + add_char_to_whatis (' '); + while (length && string[length - 1] == ' ') + --length; + if (length) + add_str_to_whatis (string, length); +} + +static void add_word_to_whatis (const char *string) +{ + add_wordn_to_whatis (string, strlen (string)); +} + +struct compare_macro_key { + const char *string; + size_t length; +}; + +static int compare_macro (const void *left, const void *right) +{ + const struct compare_macro_key *key = left; + const struct macro *value = right; + int cmp; + + cmp = strncmp (key->string, value->name, key->length); + if (cmp) + return cmp; + /* equal up to key->length, so value->name must be at least size + * key->length + 1 + */ + else if (value->name[key->length]) + return -1; + else + return 0; +} + +static void add_macro_to_whatis (const struct macro *macros, size_t n_macros, + const char *string, size_t length) +{ + struct compare_macro_key key; + const struct macro *macro; + + key.string = string; + key.length = length; + macro = bsearch (&key, macros, n_macros, sizeof (struct macro), + compare_macro); + if (macro) + add_str_to_whatis (macro->value, strlen (macro->value)); +} + +static void add_glyph_to_whatis (const char *string, size_t length) +{ + add_macro_to_whatis (glyphs, ARRAY_SIZE (glyphs), string, length); +} + +static void add_perldoc_to_whatis (const char *string, size_t length) +{ + add_macro_to_whatis (perldocs, ARRAY_SIZE (perldocs), string, length); +} + +static void mdoc_text (const char *string) +{ + add_word_to_whatis (string); + BEGIN (MAN_DESC); +} + +static void newline_found (void) +{ + /* If we are mid p_name and the last added char was not a space, + * best add one. + */ + if (p_name != newname && *(p_name - 1) != ' ') { + if (fill_mode) + add_char_to_whatis (' '); + else { + add_char_to_whatis ((char) 0x11); + BEGIN (MAN_NAME); + } + } + waiting_for_quote = 0; +} + +int find_name (const char *file, const char *filename, lexgrog *p_lg, + const char *encoding) +{ + int ret; + pipeline *p; + char *page_encoding = NULL; + + if (strcmp (file, "-") == 0) { + p = decompress_fdopen (dup (STDIN_FILENO)); + } else { + struct stat st; + char *lang; + + if (stat (file, &st)) { + error (0, errno, "%s", file); + return 0; + } + + if (S_ISDIR (st.st_mode)) { + error (0, EISDIR, "%s", file); + return 0; + } + + drop_effective_privs (); + p = decompress_open (file); + if (!p) { + error (0, errno, _("can't open %s"), file); + regain_effective_privs (); + return 0; + } + regain_effective_privs (); + + if (!encoding) { + lang = lang_dir (file); + page_encoding = get_page_encoding (lang); + free (lang); + } + } + if (!page_encoding && encoding) + page_encoding = xstrdup (encoding); + if (page_encoding) + add_manconv (p, page_encoding, "UTF-8"); + free (page_encoding); + if (p_lg->type && *COL) { + pipecmd *col_cmd; + col_cmd = pipecmd_new_args (COL, "-b", "-p", "-x", (void *) 0); + pipecmd_pre_exec (col_cmd, sandbox_load, sandbox_free, + sandbox); + pipeline_command (p, col_cmd); + } + pipeline_start (p); + + ret = find_name_decompressed (p, filename, p_lg); + pipeline_free (p); + return ret; +} + +int find_name_decompressed (pipeline *p, const char *filename, lexgrog *p_lg) +{ + int ret; + + decomp = p; + + fname = filename; + *(p_name = newname) = '\0'; + memset (filters, '_', sizeof (filters)); + + fill_mode = 1; + waiting_for_quote = 0; + + if (p_lg->type) + BEGIN (CAT_FILE); + else + BEGIN (MAN_FILE); + + drop_effective_privs (); + + yyrestart (NULL); + ret = yylex (); + + regain_effective_privs (); + + pipeline_wait (decomp); + + if (ret) + return 0; + else { + char f_tmp[MAX_FILTERS]; + int j, k; + + /* wipe out any leading or trailing spaces */ + if (*newname) { + for (p_name = strchr (newname, '\0'); + *(p_name - 1) == ' '; + p_name--); + if (*p_name == ' ') + *p_name = '\0'; + } + for (p_name = newname; *p_name == ' '; p_name++); + p_lg->whatis = xstrdup (p_name); + memset (f_tmp, '\0', MAX_FILTERS); + f_tmp[0] = '-'; + for (j = k = 0; j < MAX_FILTERS; j++) + if (filters[j] != '_') + f_tmp[k++] = filters[j]; + p_lg->filters = xstrdup (f_tmp); + return p_name[0]; + } +} + diff --git a/src/lexgrog.l b/src/lexgrog.l new file mode 100644 index 0000000..cc31a70 --- /dev/null +++ b/src/lexgrog.l @@ -0,0 +1,941 @@ +%top{ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "manconfig.h" + +/* Flex emits several functions which might reasonably have various + * attributes applied and many unused macros; none of these are our problem. + */ +#if GNUC_PREREQ(8,0) +# pragma GCC diagnostic ignored "-Wsuggest-attribute=malloc" +#endif +#pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" +#pragma GCC diagnostic ignored "-Wunused-macros" +} + +%{ + +/* + * lexgrog.l: extract 'whatis' info from nroff man / formatted cat pages. + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + * 2011, 2012 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Wed Oct 12 18:46:11 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + * + * CJW: Detect grap and vgrind. Understand fill requests. Other improvements + * in the syntax accepted. + */ + +#include <sys/stat.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "gettext.h" +#define _(String) gettext (String) + +#include "error.h" +#include "pipeline.h" +#include "decompress.h" +#include "security.h" +#include "encodings.h" +#include "sandbox.h" + +#include "manconv_client.h" + +#define YY_READ_BUF_SIZE 1024 +#define MAX_NAME 8192 + +#define ARRAY_SIZE(array) (sizeof (array) / sizeof ((array)[0])) + +extern man_sandbox *sandbox; + +struct macro { + const char *name; + const char *value; +}; + +static const struct macro glyphs[] = { + /* It is vital to keep these in strcmp order (sort -t\" -k2)! They + * will be searched using bsearch. + * Data from groff_char(7), although I have omitted some that are + * particularly unlikely to be used in NAME sections. + */ + { "'A", "Á" }, + { "'C", "Ć" }, + { "'E", "É" }, + { "'I", "Í" }, + { "'O", "Ó" }, + { "'U", "Ú" }, + { "'Y", "Ý" }, + { "'a", "á" }, + { "'c", "ć" }, + { "'e", "é" }, + { "'i", "í" }, + { "'o", "ó" }, + { "'u", "ú" }, + { "'y", "ý" }, + { ",C", "Ç" }, + { ",c", "ç" }, + { "-D", "Ð" }, + { ".i", "ı" }, + { "/L", "Ł" }, + { "/O", "Ø" }, + { "/l", "ł" }, + { "/o", "ø" }, + { ":A", "Ä" }, + { ":E", "Ë" }, + { ":I", "Ï" }, + { ":O", "Ö" }, + { ":U", "Ü" }, + { ":Y", "Ÿ" }, + { ":a", "ä" }, + { ":e", "ë" }, + { ":i", "ï" }, + { ":o", "ö" }, + { ":u", "ü" }, + { ":y", "ÿ" }, + { "AE", "Æ" }, + { "Bq", "„" }, + { "Fc", "»" }, + { "Fi", "ffi" }, + { "Fl", "ffl" }, + { "Fo", "«" }, + { "IJ", "IJ" }, + { "OE", "Œ" }, + { "Sd", "ð" }, + { "TP", "Þ" }, + { "Tp", "þ" }, + { "^A", "Â" }, + { "^E", "Ê" }, + { "^I", "Î" }, + { "^O", "Ô" }, + { "^U", "Û" }, + { "^a", "â" }, + { "^e", "ê" }, + { "^i", "î" }, + { "^o", "ô" }, + { "^u", "û" }, + { "`A", "À" }, + { "`E", "È" }, + { "`I", "Ì" }, + { "`O", "Ò" }, + { "`U", "Ù" }, + { "`a", "à" }, + { "`e", "è" }, + { "`i", "ì" }, + { "`o", "ò" }, + { "`u", "ù" }, + { "a\"", "˝" }, + { "a-", "¯" }, + { "a.", "˙" }, + { "a^", "^" }, + { "aa", "´" }, + { "ab", "˘" }, + { "ac", "¸" }, + { "ad", "¨" }, + { "ae", "æ" }, + { "ah", "ˇ" }, + { "ao", "˚" }, + { "aq", "'" }, + { "a~", "~" }, + { "bq", "‚" }, + { "cq", "’" }, + { "dq", "\"" }, + { "em", "—" }, + { "en", "–" }, + { "fc", "›" }, + { "ff", "ff" }, + { "fi", "fi" }, + { "fl", "fl" }, + { "fo", "‹" }, + { "ga", "`" }, + { "ha", "^" }, + { "ho", "˛" }, + { "hy", "‐" }, + { "ij", "ij" }, + { "lq", "“" }, + { "oA", "Å" }, + { "oa", "å" }, + { "oe", "œ" }, + { "oq", "‘" }, + { "r!", "¡" }, + { "r?", "¿" }, + { "rq", "”" }, + { "ss", "ß" }, + { "ti", "~" }, + { "vS", "Š" }, + { "vZ", "Ž" }, + { "vs", "š" }, + { "vz", "ž" }, + { "~A", "Ã" }, + { "~N", "Ñ" }, + { "~O", "Õ" }, + { "~a", "ã" }, + { "~n", "ñ" }, + { "~o", "õ" } +}; + +static const struct macro perldocs[] = { + /* It is vital to keep these in strcmp order (sort -t\" -k2)! They + * will be searched using bsearch. + * Data from Pod/Man.pm. + */ + { "--", "-" }, + { "Aq", "'" }, + { "C'", "'" }, + { "C+", "C++" }, + { "C`", "`" }, + { "L\"", "\"" }, + { "PI", "π" }, + { "R\"", "\"" } +}; + +static void add_str_to_whatis (const char *string, size_t length); +static void add_char_to_whatis (unsigned char c); +static void add_separator_to_whatis (void); +static void add_wordn_to_whatis (const char *string, size_t length); +static void add_word_to_whatis (const char *string); +static void add_glyph_to_whatis (const char *string, size_t length); +static void add_perldoc_to_whatis (const char *string, size_t length); +static void mdoc_text (const char *string); +static void newline_found (void); + +static char newname[MAX_NAME]; +static char *p_name; +static const char *fname; +static char filters[MAX_FILTERS]; + +static int fill_mode; +static int waiting_for_quote; + +static pipeline *decomp; + +#define YY_INPUT(buf,result,max_size) { \ + size_t size = max_size; \ + const char *block = pipeline_read (decomp, &size); \ + if (block && size != 0) { \ + memcpy (buf, block, size); \ + buf[size] = '\0'; \ + result = size; \ + } else \ + result = YY_NULL; \ +} +#define YY_NO_INPUT +%} + +%option ecs meta-ecs +%option 8bit batch caseful never-interactive +%option nostdinit +%option warn +%option noyywrap nounput + +%x MAN_PRENAME +%x MAN_NAME +%x MAN_DESC +%x MAN_DESC_AT +%x MAN_DESC_BSX +%x MAN_DESC_BX +%x MAN_DESC_BX_RELEASE +%x MAN_DESC_DQ +%x MAN_DESC_FX +%x MAN_DESC_NX +%x MAN_DESC_OX +%x CAT_NAME +%x CAT_FILE +%x MAN_FILE +%x CAT_REST +%x MAN_REST +%x FORCE_EXIT + +digit [[:digit:]] +upper [[:upper:]] +alpha [[:alpha:]] +blank [[:blank:]] +blank_eol [[:blank:]\r\n] +word [[:alnum:]][^[:blank:]\r\n]* +eol \r?\n +bol {eol}+ +next {eol}* +empty {eol}{blank}* +indent {eol}{blank}+ +dbl_quote \" +font_change \\f([[:upper:]1-4]|\({upper}{2}) +size_change \\s[+-]?{digit} +style_change ({font_change}{size_change}?|{size_change}{font_change}?) +typeface \.(B[IR]?|I[BR]?|R[BI]|S[BM]) +sec_request \.[Ss][HhYySs] +comment ['.]\\{dbl_quote} + + /* Please add to this list if you know how. */ + /* Note that, since flex only supports UTF-8 by accident, character classes + * including non-ASCII characters must be written out as (a|b|c|d) rather + * than [abcd]. + */ +bg_name ИМЕ +cs_name (J[Mm](É|é|\\\('[Ee]|E|e)[Nn][Oo]|N(Á|á)[Zz][Ee][Vv]) +da_name N[Aa][Vv][Nn] +de_name B[Ee][Zz][Ee][Ii][Cc][Hh][Nn][Uu][Nn][Gg] +en_name N[Aa][Mm][Ee] +eo_name N[Oo][Mm][Oo] +es_name N[Oo][Mm][Bb][Rr][Ee] +fi_name N[Ii][Mm][Ii] +fr_name N[Oo][Mm] +hu_name N(É|é|E|e)[Vv] +id_name N[Aa][Mm][Aa] + /* NOME also works for gl, pt */ +it_name N[Oo][Mm][Ee] +ja_name (名|̾)(前|称) +ko_name (이름|명칭) +latin_name N[Oo][Mm][Ee][Nn] +lt_name PAVADINIMAS +nl_name N[Aa][Aa][Mm] +pl_name N[Aa][Zz][Ww][Aa] +ro_name N[Uu][Mm][Ee] +ru_name (ИМЯ|НАЗВАНИЕ|НАИМЕНОВАНИЕ) +sk_name M[Ee][Nn][Oo] +sr_name (ИМЕ|НАЗИВ) +srlatin_name (IME|NAZIV) +sv_name N[Aa][Mm][Nn] +ta_name பெய +tr_name (İ|i)S(İ|i)M +uk_name НАЗВА +vi_name TÊN +zh_CN_name 名{blank}?(称|字){blank}?.* +zh_TW_name (名{blank}?(稱|字)|命令名){blank}?.* +name ({bg_name}|{cs_name}|{da_name}|{de_name}|{en_name}|{eo_name}|{es_name}|{fi_name}|{fr_name}|{hu_name}|{id_name}|{it_name}|{ja_name}|{ko_name}|{latin_name}|{lt_name}|{nl_name}|{pl_name}|{ro_name}|{ru_name}|{sk_name}|{sr_name}|{srlatin_name}|{sv_name}|{ta_name}|{tr_name}|{uk_name}|{vi_name}|{zh_CN_name}|{zh_TW_name}) +name_sec {dbl_quote}?{style_change}?{name}{style_change}?({blank}*{dbl_quote})? + + /* eptgrv : eqn, pic, tbl, grap, refer, vgrind */ +tbl_request \.TS +eqn_request \.EQ +pic_request \.PS +grap_request \.G1 +ref1_request \.R1 +ref2_request \.\[ +vgrind_request \.vS + +%% + + /* begin NAME section processing */ +<MAN_FILE>{sec_request}{blank_eol}+{name_sec}{blank}* BEGIN (MAN_PRENAME); +<CAT_FILE>{empty}{2,}{name}{blank}*{indent} BEGIN (CAT_NAME); + + /* general text matching */ +<MAN_FILE>{ + \.[^Ss\r\n].* | + \..{0,3}{dbl_quote}?.{0,4}{dbl_quote}? | + {comment}.* | + .|{eol} +} + +<CAT_FILE>{ + .{1,9} | + [ ]* | + {eol}{2,} | + .|{eol} +} + +<MAN_REST>{ + {bol}{tbl_request} filters[TBL_FILTER] = 't'; + {bol}{eqn_request} filters[EQN_FILTER] = 'e'; + {bol}{pic_request} filters[PIC_FILTER] = 'p'; + {bol}{grap_request} filters[GRAP_FILTER] = 'g'; + {bol}{ref1_request} | + {bol}{ref2_request} filters[REF_FILTER] = 'r'; + {bol}{vgrind_request} filters[VGRIND_FILTER] = 'v'; +} +<MAN_REST><<EOF>> { /* exit */ + *p_name = '\0'; /* terminate the string */ + yyterminate (); +} +<MAN_REST>.+|{eol} + + /* rules to end NAME section processing */ +<FORCE_EXIT>.|{eol} { /* forced exit */ + *p_name = '\0'; /* terminate the string */ + yyterminate (); +} + +<MAN_PRENAME>{bol}{sec_request}{blank}* | +<MAN_PRENAME><<EOF>> { /* no NAME at all */ + *p_name = '\0'; + BEGIN (MAN_REST); +} + + /* need to match whole string so that we beat the following roff catch-all, + so use yyless to push back the name */ +<MAN_PRENAME>{ + {bol}{typeface}{blank}.* | + {bol}\.Tn{blank}.* | + {bol}\.ft{blank}.* | + {bol}\.V[be]{blank}.* | + {bol}\.IX{blank}.* | + {bol}\.Nm{blank}.* { + yyless (0); + BEGIN (MAN_NAME); + } +} + + /* Skip over initial roff requests in NAME section. The use of yyless here + is evil. */ +<MAN_PRENAME>{bol}['.].* + +<MAN_PRENAME>{empty}{eol} yyless (1); + +<MAN_PRENAME>.|{eol} { + yyless (0); + BEGIN (MAN_NAME); +} + +<MAN_NAME,MAN_DESC>{ + {bol}{sec_request}{blank}* | /* Another section */ + {bol}\.X{upper}{blank}+ | /* special - hpux */ + {bol}\.sp{blank}* | /* vertical spacing */ + {bol}\.ig{blank}* | /* block comment */ + {bol}\.de[1i]?{blank}* | /* macro definition */ + {bol}\.i[ef]{blank}* | /* conditional */ + {empty}{bol}.+ | + <<EOF>> { /* terminate the string */ + *p_name = '\0'; + BEGIN (MAN_REST); + } +} + +<CAT_NAME>{ + {bol}S[yYeE] | + {eol}{2,}.+ | + {next}__ { /* terminate the string */ + *p_name = '\0'; + BEGIN (CAT_REST); + yyterminate (); + } +} + + /* ROFF request removal */ +<MAN_NAME,MAN_DESC>{ + /* some include quoting; dealing with this is unpleasant */ + {bol}{typeface}{blank}+\" { + newline_found (); + waiting_for_quote = 1; + } + + {bol}{typeface}{blank}+ | /* type face commands */ + {bol}\.Tn{blank}+ | /* mdoc trade name */ + {bol}\.ft{blank}.* | /* font change */ + {bol}\.V[be]{blank}.* | /* pod2man, verbatim mode */ + {bol}\.IX{blank}.* | /* .IX line */ + {bol}\.Nm{blank}+ | /* mdoc name */ + {bol}\.PD{blank}* | /* paragraph spacing */ + {bol}\\& | /* non-breaking space */ + {next}{comment}.* { /* per line comments */ + newline_found (); + } +} + + /* No-op requests */ +<MAN_NAME,MAN_DESC>{ + {bol}\.{blank}*$ newline_found (); + {bol}\.\.$ newline_found (); +} + + /* Toggle fill mode */ +<MAN_NAME,MAN_DESC>{ + {bol}\.nf.* fill_mode = 0; + {bol}\.fi.* fill_mode = 1; +} + +<CAT_NAME>-{eol}{blank_eol}* /* strip continuations */ + + /* convert to DASH */ +<MAN_NAME>{ + {next}{blank}*\\\((mi|hy|em|en){blank}* | + {next}{blank}*\\\[(mi|hy|em|en)\]{blank}* | + {next}{blank_eol}+[-\\]-{blank}* | + {next}{blank_eol}*[-\\]-{blank}+ | + {bol}\.Nd{blank}* { + add_separator_to_whatis (); + BEGIN (MAN_DESC); + } +} +<CAT_NAME>{next}{blank}+-{1,2}{blank_eol}+ add_separator_to_whatis (); + + /* escape sequences and special characters */ +<MAN_NAME,MAN_DESC>{ + {next}\\[\\e] add_char_to_whatis ('\\'); + {next}\\('|\(aa) add_char_to_whatis ('\''); + {next}\\(`|\(ga) add_char_to_whatis ('`'); + {next}\\(-|\((mi|hy|em|en)) add_char_to_whatis ('-'); + {next}\\\[(mi|hy|em|en)\] add_char_to_whatis ('-'); + {next}\\\. add_char_to_whatis ('.'); + {next}((\\[ 0t~])|[ ]|\t)* add_char_to_whatis (' '); + {next}\\\((ru|ul) add_char_to_whatis ('_'); + {next}\\\\t add_char_to_whatis ('\t'); + + {next}\\[|^&!%acdpruz{}\r\n] /* various useless control chars */ + {next}\\[bhlLvx]{blank}*'[^']+' /* various inline functions */ + + {next}\\\$[1-9] /* interpolate arg */ + + /* roff named glyphs */ + {next}\\\(..|\\\[..\] add_glyph_to_whatis (yytext + 2, 2); + /* perldoc strings */ + {next}\\\*\(..|\\\*\[..\] add_perldoc_to_whatis (yytext + 3, 2); + {next}\\\*. add_perldoc_to_whatis (yytext + 2, 1); + + {next}\\["#].* /* comment */ + + {next}{font_change} /* font changes */ + {next}\\k{alpha} /* mark input place in register */ + + {next}\\n(\({alpha})?{alpha} /* interpolate number register */ + {next}\\o\"[^"]+\" /* overstrike chars */ + + {next}{size_change} /* size changes */ + {next}\\w{blank}*'[^']+'[^ \t]* /* width of string */ + + {next}\\ /* catch all */ + + {next}\(\\\|\){blank}* /* function() in hpux */ +} + + /* some people rather ambitiously use non-trivial mdoc macros in NAME + sections; cope with those that have been seen in the wild, and a few + more */ +<MAN_DESC>{ + {bol}\.At{blank}* BEGIN (MAN_DESC_AT); + {bol}\.Bsx{blank}* BEGIN (MAN_DESC_BSX); + {bol}\.Bx{blank}* BEGIN (MAN_DESC_BX); + {bol}\.Fx{blank}* BEGIN (MAN_DESC_FX); + {bol}\.Nx{blank}* BEGIN (MAN_DESC_NX); + {bol}\.Ox{blank}* BEGIN (MAN_DESC_OX); + {bol}\.Ux{blank}* add_word_to_whatis ("UNIX"); + + {bol}\.Dq{blank}* { + add_word_to_whatis ("\""); + BEGIN (MAN_DESC_DQ); + } +} + +<MAN_DESC_AT>{ + 32v{blank}* mdoc_text ("Version 32V AT&T UNIX"); + v1{blank}* mdoc_text ("Version 1 AT&T UNIX"); + v2{blank}* mdoc_text ("Version 2 AT&T UNIX"); + v3{blank}* mdoc_text ("Version 3 AT&T UNIX"); + v4{blank}* mdoc_text ("Version 4 AT&T UNIX"); + v5{blank}* mdoc_text ("Version 5 AT&T UNIX"); + v6{blank}* mdoc_text ("Version 6 AT&T UNIX"); + v7{blank}* mdoc_text ("Version 7 AT&T UNIX"); + V{blank}* mdoc_text ("AT&T System V UNIX"); + V.1{blank}* mdoc_text ("AT&T System V.1 UNIX"); + V.2{blank}* mdoc_text ("AT&T System V.2 UNIX"); + V.3{blank}* mdoc_text ("AT&T System V.3 UNIX"); + V.4{blank}* mdoc_text ("AT&T System V.4 UNIX"); + .|{eol} { + yyless (0); + mdoc_text ("AT&T UNIX"); + } +} + +<MAN_DESC_BSX>{ + {word} { + add_word_to_whatis ("BSD/OS"); + add_wordn_to_whatis (yytext, yyleng); + BEGIN (MAN_DESC); + } + .|{eol} { + yyless (0); + mdoc_text ("BSD/OS"); + } +} + +<MAN_DESC_BX>{ + -alpha{blank}* mdoc_text ("BSD (currently in alpha test)"); + -beta{blank}* mdoc_text ("BSD (currently in beta test)"); + -devel{blank}* mdoc_text ("BSD (currently under development"); + {word}{blank}* { + add_wordn_to_whatis (yytext, yyleng); + add_str_to_whatis ("BSD", 3); + BEGIN (MAN_DESC_BX_RELEASE); + } + .|{eol} { + yyless (0); + mdoc_text ("BSD"); + } +} + +<MAN_DESC_BX_RELEASE>{ + [Rr]eno{blank}* { + add_str_to_whatis ("-Reno", 5); + BEGIN (MAN_DESC); + } + [Tt]ahoe{blank}* { + add_str_to_whatis ("-Tahoe", 6); + BEGIN (MAN_DESC); + } + [Ll]ite{blank}* { + add_str_to_whatis ("-Lite", 5); + BEGIN (MAN_DESC); + } + [Ll]ite2{blank}* { + add_str_to_whatis ("-Lite2", 6); + BEGIN (MAN_DESC); + } + .|{eol} { + yyless (0); + BEGIN (MAN_DESC); + } +} + +<MAN_DESC_DQ>.* { + add_str_to_whatis (yytext, yyleng); + add_char_to_whatis ('"'); + BEGIN (MAN_DESC); +} + +<MAN_DESC_FX>{ + {word} { + add_word_to_whatis ("FreeBSD"); + add_wordn_to_whatis (yytext, yyleng); + BEGIN (MAN_DESC); + } + .|{eol} { + yyless (0); + mdoc_text ("FreeBSD"); + } +} + +<MAN_DESC_NX>{ + {word} { + add_word_to_whatis ("NetBSD"); + add_wordn_to_whatis (yytext, yyleng); + BEGIN (MAN_DESC); + } + .|{eol} { + yyless (0); + mdoc_text ("NetBSD"); + } +} + +<MAN_DESC_OX>{ + {word} { + add_word_to_whatis ("OpenBSD"); + add_wordn_to_whatis (yytext, yyleng); + BEGIN (MAN_DESC); + } + .|{eol} { + yyless (0); + mdoc_text ("OpenBSD"); + } +} + + /* collapse spaces, escaped spaces, tabs, newlines to a single space */ +<CAT_NAME>{next}((\\[ ])|{blank})* add_char_to_whatis (' '); + + /* a ROFF break request, a paragraph request, or an indentation change + usually means we have multiple whatis definitions, provide a separator + for later processing */ +<MAN_NAME,MAN_DESC>{ + {bol}\.br{blank}* | + {bol}\.LP{blank}* | + {bol}\.PP{blank}* | + {bol}\.P{blank}* | + {bol}\.IP{blank}.* | + {bol}\.HP{blank}.* | + {bol}\.RS{blank}.* | + {bol}\.RE{blank}.* { + add_char_to_whatis ((char) 0x11); + BEGIN (MAN_NAME); + } +} + + /* any other roff request we don't recognise terminates definitions */ +<MAN_NAME,MAN_DESC>{bol}['.] { + *p_name = '\0'; + BEGIN (MAN_REST); +} + + /* pass words as a chunk. speed optimization */ +<MAN_NAME,MAN_DESC>[[:alnum:]]* add_str_to_whatis (yytext, yyleng); + + /* normalise the comma (,) separators */ +<CAT_NAME>{blank}*,[ \t\r\n]* | +<MAN_NAME,MAN_DESC>{blank}*,{blank}* add_str_to_whatis (", ", 2); + +<CAT_NAME,MAN_NAME,MAN_DESC>{bol}. { + newline_found (); + add_char_to_whatis (yytext[yyleng - 1]); +} + +<CAT_NAME,MAN_NAME,MAN_DESC>. add_char_to_whatis (*yytext); + + /* default EOF rule */ +<<EOF>> return 1; + +%% + +/* print warning and force scanner to terminate */ +static void too_big (void) +{ + /* Even though MAX_NAME is a macro expanding to a constant, we + * translate it using ngettext anyway because that will make it + * easier to change the macro later. + */ + error (0, 0, + ngettext ("warning: whatis for %s exceeds %d byte, " + "truncating.", + "warning: whatis for %s exceeds %d bytes, " + "truncating.", MAX_NAME), + fname, MAX_NAME); + + BEGIN (FORCE_EXIT); +} + +/* append a string to newname if enough room */ +static void add_str_to_whatis (const char *string, size_t length) +{ + if (p_name - newname + length >= MAX_NAME) + too_big (); + else { + (void) strncpy (p_name, string, length); + p_name += length; + } +} + +/* append a char to newname if enough room */ +static void add_char_to_whatis (unsigned char c) +{ + if (p_name - newname + 1 >= MAX_NAME) + too_big (); + else if (waiting_for_quote && c == '"') + waiting_for_quote = 0; + else + *p_name++ = c; +} + +/* append the " - " separator to newname, trimming the first space if one's + * already there + */ +static void add_separator_to_whatis (void) +{ + if (p_name != newname && *(p_name - 1) != ' ') + add_char_to_whatis (' '); + add_str_to_whatis ("- ", 2); +} + +/* append a word to newname if enough room, ensuring only necessary + surrounding space */ +static void add_wordn_to_whatis (const char *string, size_t length) +{ + if (p_name != newname && *(p_name - 1) != ' ') + add_char_to_whatis (' '); + while (length && string[length - 1] == ' ') + --length; + if (length) + add_str_to_whatis (string, length); +} + +static void add_word_to_whatis (const char *string) +{ + add_wordn_to_whatis (string, strlen (string)); +} + +struct compare_macro_key { + const char *string; + size_t length; +}; + +static int compare_macro (const void *left, const void *right) +{ + const struct compare_macro_key *key = left; + const struct macro *value = right; + int cmp; + + cmp = strncmp (key->string, value->name, key->length); + if (cmp) + return cmp; + /* equal up to key->length, so value->name must be at least size + * key->length + 1 + */ + else if (value->name[key->length]) + return -1; + else + return 0; +} + +static void add_macro_to_whatis (const struct macro *macros, size_t n_macros, + const char *string, size_t length) +{ + struct compare_macro_key key; + const struct macro *macro; + + key.string = string; + key.length = length; + macro = bsearch (&key, macros, n_macros, sizeof (struct macro), + compare_macro); + if (macro) + add_str_to_whatis (macro->value, strlen (macro->value)); +} + +static void add_glyph_to_whatis (const char *string, size_t length) +{ + add_macro_to_whatis (glyphs, ARRAY_SIZE (glyphs), string, length); +} + +static void add_perldoc_to_whatis (const char *string, size_t length) +{ + add_macro_to_whatis (perldocs, ARRAY_SIZE (perldocs), string, length); +} + +static void mdoc_text (const char *string) +{ + add_word_to_whatis (string); + BEGIN (MAN_DESC); +} + +static void newline_found (void) +{ + /* If we are mid p_name and the last added char was not a space, + * best add one. + */ + if (p_name != newname && *(p_name - 1) != ' ') { + if (fill_mode) + add_char_to_whatis (' '); + else { + add_char_to_whatis ((char) 0x11); + BEGIN (MAN_NAME); + } + } + waiting_for_quote = 0; +} + +int find_name (const char *file, const char *filename, lexgrog *p_lg, + const char *encoding) +{ + int ret; + pipeline *p; + char *page_encoding = NULL; + + if (strcmp (file, "-") == 0) { + p = decompress_fdopen (dup (STDIN_FILENO)); + } else { + struct stat st; + char *lang; + + if (stat (file, &st)) { + error (0, errno, "%s", file); + return 0; + } + + if (S_ISDIR (st.st_mode)) { + error (0, EISDIR, "%s", file); + return 0; + } + + drop_effective_privs (); + p = decompress_open (file); + if (!p) { + error (0, errno, _("can't open %s"), file); + regain_effective_privs (); + return 0; + } + regain_effective_privs (); + + if (!encoding) { + lang = lang_dir (file); + page_encoding = get_page_encoding (lang); + free (lang); + } + } + if (!page_encoding && encoding) + page_encoding = xstrdup (encoding); + if (page_encoding) + add_manconv (p, page_encoding, "UTF-8"); + free (page_encoding); + if (p_lg->type && *COL) { + pipecmd *col_cmd; + col_cmd = pipecmd_new_args (COL, "-b", "-p", "-x", (void *) 0); + pipecmd_pre_exec (col_cmd, sandbox_load, sandbox_free, + sandbox); + pipeline_command (p, col_cmd); + } + pipeline_start (p); + + ret = find_name_decompressed (p, filename, p_lg); + pipeline_free (p); + return ret; +} + +int find_name_decompressed (pipeline *p, const char *filename, lexgrog *p_lg) +{ + int ret; + + decomp = p; + + fname = filename; + *(p_name = newname) = '\0'; + memset (filters, '_', sizeof (filters)); + + fill_mode = 1; + waiting_for_quote = 0; + + if (p_lg->type) + BEGIN (CAT_FILE); + else + BEGIN (MAN_FILE); + + drop_effective_privs (); + + yyrestart (NULL); + ret = yylex (); + + regain_effective_privs (); + + pipeline_wait (decomp); + + if (ret) + return 0; + else { + char f_tmp[MAX_FILTERS]; + int j, k; + + /* wipe out any leading or trailing spaces */ + if (*newname) { + for (p_name = strchr (newname, '\0'); + *(p_name - 1) == ' '; + p_name--); + if (*p_name == ' ') + *p_name = '\0'; + } + for (p_name = newname; *p_name == ' '; p_name++); + p_lg->whatis = xstrdup (p_name); + memset (f_tmp, '\0', MAX_FILTERS); + f_tmp[0] = '-'; + for (j = k = 0; j < MAX_FILTERS; j++) + if (filters[j] != '_') + f_tmp[k++] = filters[j]; + p_lg->filters = xstrdup (f_tmp); + return p_name[0]; + } +} diff --git a/src/lexgrog_test.c b/src/lexgrog_test.c new file mode 100644 index 0000000..189da43 --- /dev/null +++ b/src/lexgrog_test.c @@ -0,0 +1,242 @@ +/* + * lexgrog_test.c: test whatis extraction from man/cat pages + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + * 2011 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <sys/stat.h> + +#include "argp.h" +#include "error.h" +#include "gl_list.h" +#include "progname.h" + +#include "gettext.h" +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "cleanup.h" +#include "glcontainers.h" +#include "pipeline.h" +#include "sandbox.h" +#include "security.h" + +#include "descriptions.h" +#include "ult_src.h" + +int quiet = 1; +man_sandbox *sandbox; + +static bool parse_man = false, parse_cat = false; +static bool show_whatis = false, show_filters = false; +static const char *encoding = NULL; +static char **files; +static int num_files; + +const char *argp_program_version = "lexgrog " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("FILE..."); +static const char doc[] = "\v" N_("The defaults are --man and --whatis."); + +static struct argp_option options[] = { + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "man", 'm', 0, 0, N_("parse as man page"), 1 }, + { "cat", 'c', 0, 0, N_("parse as cat page") }, + { "whatis", 'w', 0, 0, N_("show whatis information"), 2 }, + { "filters", 'f', 0, 0, N_("show guessed series of preprocessing filters") }, + { "encoding", 'E', N_("ENCODING"), 0, N_("use selected output encoding"), 3 }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'd': + debug_level = true; + return 0; + case 'm': + parse_man = true; + return 0; + case 'c': + parse_cat = true; + return 0; + case 'w': + show_whatis = true; + return 0; + case 'f': + show_filters = true; + return 0; + case 'E': + encoding = arg; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP & + ~ARGP_HELP_PRE_DOC); + break; + case ARGP_KEY_ARGS: + files = state->argv + state->next; + num_files = state->argc - state->next; + return 0; + case ARGP_KEY_NO_ARGS: + argp_usage (state); + break; + case ARGP_KEY_SUCCESS: + if (parse_man && parse_cat) + /* This slightly odd construction allows us + * to reuse a translation. + */ + argp_error (state, + _("%s: incompatible options"), + "-m -c"); + /* defaults: --man, --whatis */ + if (!parse_man && !parse_cat) + parse_man = true; + if (!show_whatis && !show_filters) + show_whatis = true; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static char *help_filter (int key, const char *text, void *input _GL_UNUSED) +{ + switch (key) { + case ARGP_KEY_HELP_PRE_DOC: + /* We have no pre-options help text, but the input + * text may contain header junk due to gettext (""). + */ + return NULL; + default: + return (char *) text; + } +} + +static struct argp argp = { options, parse_opt, args_doc, doc, 0, + help_filter }; + +int main (int argc, char **argv) +{ + int type = 0; + int i; + bool some_failed = false; + + set_program_name (argv[0]); + + init_debug (); + pipeline_install_post_fork (pop_all_cleanups); + sandbox = sandbox_init (); + init_locale (); + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + + /* We aren't setuid, but this allows generic code in lexgrog.l to + * use drop_effective_privs/regain_effective_privs. + */ + init_security (); + + if (parse_man) + type = 0; + else + type = 1; + + for (i = 0; i < num_files; ++i) { + lexgrog lg; + const char *file; + bool found = false; + + lg.type = type; + + if (STREQ (files[i], "-")) + file = files[i]; + else { + char *path, *pathend; + struct stat statbuf; + + path = xstrdup (files[i]); + pathend = strrchr (path, '/'); + if (pathend) { + *pathend = '\0'; + pathend = strrchr (path, '/'); + if (pathend && STRNEQ (pathend + 1, "man", 3)) + *pathend = '\0'; + else { + free (path); + path = NULL; + } + } else { + free (path); + path = NULL; + } + + file = ult_src (files[i], path ? path : ".", + &statbuf, SO_LINK, NULL); + free (path); + } + + if (file && find_name (file, "-", &lg, encoding)) { + gl_list_t descs = parse_descriptions (NULL, lg.whatis); + const struct page_description *desc; + GL_LIST_FOREACH_START (descs, desc) { + if (!desc->name || !desc->whatis) + continue; + found = true; + printf ("%s", files[i]); + if (show_filters) + printf (" (%s)", lg.filters); + if (show_whatis) + printf (": \"%s - %s\"", + desc->name, desc->whatis); + printf ("\n"); + } GL_LIST_FOREACH_END (descs); + gl_list_free (descs); + free (lg.filters); + free (lg.whatis); + } + + if (!found) { + printf ("%s: parse failed\n", files[i]); + some_failed = true; + } + } + + sandbox_free (sandbox); + + if (some_failed) + return FATAL; + else + return OK; +} diff --git a/src/man-recode.c b/src/man-recode.c new file mode 100644 index 0000000..39d0493 --- /dev/null +++ b/src/man-recode.c @@ -0,0 +1,290 @@ +/* + * man-recode.c: convert manual pages to another encoding + * + * Copyright (C) 2019 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "argp.h" +#include "dirname.h" +#include "gl_array_list.h" +#include "gl_xlist.h" +#include "progname.h" +#include "tempname.h" + +#include "gettext.h" +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "pipeline.h" + +#include "cleanup.h" +#include "encodings.h" +#include "error.h" +#include "glcontainers.h" +#include "sandbox.h" + +#include "decompress.h" +#include "manconv_client.h" + +int quiet = 0; +man_sandbox *sandbox; + +static char *to_code; +static gl_list_t filenames; +static const char *suffix; +static bool in_place; + +struct try_file_at_args { + int dir_fd; + int flags; +}; + +static int +try_file_at (char *tmpl, void *flags) +{ + struct try_file_at_args *args = flags; + return openat (args->dir_fd, tmpl, + (args->flags & ~O_ACCMODE) | O_RDWR | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR); +} + +static int +mkstempat (int dir_fd, char *xtemplate) +{ + struct try_file_at_args args; + + args.dir_fd = dir_fd; + args.flags = 0; + return try_tempname (xtemplate, 0, &args, try_file_at); +} + +enum opts { + OPT_SUFFIX = 256, + OPT_IN_PLACE = 257, + OPT_MAX +}; + +const char *argp_program_version = "man-recode " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = + N_("-t CODE {--suffix SUFFIX | --in-place} FILENAME..."); + +static struct argp_option options[] = { + { "to-code", 't', N_("CODE"), 0, N_("encoding for output") }, + { "suffix", OPT_SUFFIX, + N_("SUFFIX"), 0, N_("suffix to append to output file name") }, + { "in-place", OPT_IN_PLACE, + 0, 0, N_("overwrite input files in place") }, + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "quiet", 'q', 0, 0, N_("produce fewer warnings") }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 't': + to_code = xstrdup (arg); + return 0; + case OPT_SUFFIX: + suffix = arg; + return 0; + case OPT_IN_PLACE: + in_place = true; + return 0; + case 'd': + debug_level = true; + return 0; + case 'q': + quiet = 1; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP); + break; + case ARGP_KEY_ARG: + gl_list_add_last (filenames, xstrdup (arg)); + return 0; + case ARGP_KEY_NO_ARGS: + argp_usage (state); + break; + case ARGP_KEY_SUCCESS: + if (!to_code) + argp_error (state, + _("must specify an output " + "encoding")); + if (!suffix && !in_place) + argp_error (state, + _("must use either --suffix or " + "--in-place")); + if (suffix && in_place) + argp_error (state, + _("--suffix and --in-place are " + "mutually exclusive")); + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp = { options, parse_opt, args_doc }; + +static void recode (const char *filename) +{ + pipeline *decomp, *convert; + struct compression *comp; + int dir_fd = -1; + char *dirname, *basename, *stem, *outfilename; + char *page_encoding; + int status; + + decomp = decompress_open (filename); + if (!decomp) + error (FAIL, 0, _("can't open %s"), filename); + + dirname = dir_name (filename); + basename = base_name (filename); + comp = comp_info (basename, 1); + if (comp) + stem = comp->stem; /* steal memory */ + else + stem = xstrdup (basename); + + convert = pipeline_new (); + if (suffix) { + outfilename = xasprintf ("%s/%s%s", dirname, stem, suffix); + pipeline_want_outfile (convert, outfilename); + } else { + int dir_fd_open_flags; + char *template_path; + int outfd; + + dir_fd_open_flags = O_SEARCH | O_DIRECTORY; +#ifdef O_PATH + dir_fd_open_flags |= O_PATH; +#endif + dir_fd = open (dirname, dir_fd_open_flags); + if (dir_fd < 0) + error (FATAL, errno, _("can't open %s"), dirname); + + outfilename = xasprintf ("%s.XXXXXX", stem); + /* For error messages. */ + template_path = xasprintf ("%s/%s", dirname, outfilename); + outfd = mkstempat (dir_fd, outfilename); + if (outfd == -1) { + error (FATAL, errno, + _("can't open temporary file %s"), + template_path); + } + free (template_path); + pipeline_want_out (convert, outfd); + } + + pipeline_start (decomp); + page_encoding = check_preprocessor_encoding (decomp, NULL, NULL); + if (!page_encoding) { + char *lang = lang_dir (filename); + page_encoding = get_page_encoding (lang); + free (lang); + } + debug ("guessed input encoding %s for %s\n", page_encoding, filename); + add_manconv (convert, page_encoding, to_code); + + if (!pipeline_get_ncommands (convert)) + pipeline_command (convert, pipecmd_new_passthrough ()); + + pipeline_connect (decomp, convert, (void *) 0); + pipeline_pump (decomp, convert, (void *) 0); + pipeline_wait (decomp); + status = pipeline_wait (convert); + if (status != 0) + error (CHILD_FAIL, 0, _("command exited with status %d: %s"), + status, pipeline_tostring (convert)); + + if (in_place) { + assert (dir_fd != -1); + if (renameat (dir_fd, outfilename, dir_fd, stem) == -1) { + char *outfilepath = xasprintf + ("%s/%s", dirname, outfilename); + unlink (outfilename); + error (FATAL, errno, _("can't rename %s to %s"), + outfilepath, filename); + } + debug ("stem: %s, basename: %s\n", stem, basename); + if (!STREQ (stem, basename)) { + if (unlinkat (dir_fd, basename, 0) == -1) + error (FATAL, errno, _("can't remove %s"), + filename); + } + } + + free (page_encoding); + free (outfilename); + free (stem); + free (basename); + free (dirname); + if (dir_fd) + close (dir_fd); + pipeline_free (convert); + pipeline_free (decomp); +} + +int main (int argc, char *argv[]) +{ + const char *filename; + + set_program_name (argv[0]); + + init_debug (); + pipeline_install_post_fork (pop_all_cleanups); + sandbox = sandbox_init (); + init_locale (); + filenames = new_string_list (GL_ARRAY_LIST, true); + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + + GL_LIST_FOREACH_START (filenames, filename) + recode (filename); + GL_LIST_FOREACH_END (filenames); + + free (to_code); + + gl_list_free (filenames); + sandbox_free (sandbox); + + return 0; +} diff --git a/src/man.c b/src/man.c new file mode 100644 index 0000000..6d1cba7 --- /dev/null +++ b/src/man.c @@ -0,0 +1,4386 @@ +/* + * man.c: The manual pager + * + * Copyright (C) 1990, 1991 John W. Eaton. + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + * 2011, 2012 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * John W. Eaton + * jwe@che.utexas.edu + * Department of Chemical Engineering + * The University of Texas at Austin + * Austin, Texas 78712 + * + * Mostly written/re-written by Wilf, some routines by Markus Armbruster. + * + * CJW: Various robustness, security, and internationalization fixes. + * Improved HTML support (originally written by Fabrizio Polacco). + * Rewrite of page location routines for improved maintainability and + * accuracy. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <errno.h> +#include <termios.h> +#include <unistd.h> +#include <limits.h> +#include <fcntl.h> +#include <ctype.h> +#include <signal.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "argp.h" +#include "dirname.h" +#include "gl_array_list.h" +#include "gl_hash_map.h" +#include "gl_list.h" +#include "gl_xlist.h" +#include "gl_xmap.h" +#include "minmax.h" +#include "progname.h" +#include "regex.h" +#include "stat-time.h" +#include "utimens.h" +#include "xgetcwd.h" +#include "xvasprintf.h" +#include "xstdopen.h" + +#include "gettext.h" +#include <locale.h> +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "error.h" +#include "cleanup.h" +#include "glcontainers.h" +#include "pipeline.h" +#include "pathsearch.h" +#include "linelength.h" +#include "decompress.h" +#include "xregcomp.h" +#include "security.h" +#include "encodings.h" +#include "orderfiles.h" +#include "sandbox.h" + +#include "mydbm.h" +#include "db_storage.h" + +#include "filenames.h" +#include "globbing.h" +#include "ult_src.h" +#include "manp.h" +#include "zsoelim.h" +#include "manconv_client.h" + +#ifdef MAN_OWNER +extern uid_t ruid; +extern uid_t euid; +#endif /* MAN_OWNER */ + +/* the default preprocessor sequence */ +#ifndef DEFAULT_MANROFFSEQ +# define DEFAULT_MANROFFSEQ "" +#endif + +/* placeholder for the manual page name in the less prompt string */ +#define MAN_PN "$MAN_PN" + +/* Some systems lack these */ +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +char *lang; + +/* external formatter programs, one for use without -t, and one with -t */ +#define NFMT_PROG "mandb_nfmt" +#define TFMT_PROG "mandb_tfmt" +#undef ALT_EXT_FORMAT /* allow external formatters located in cat hierarchy */ + +static bool global_manpath; /* global or user manual page hierarchy? */ +static int skip; /* page exists but has been skipped */ + +#if defined _AIX || defined __sgi +char **global_argv; +#endif + +struct candidate { + const char *req_name; + char from_db; + char cat; + const char *path; + char *ult; + struct mandata *source; + int add_index; /* for sort stabilisation */ + struct candidate *next; +}; + +#define CANDIDATE_FILESYSTEM 0 +#define CANDIDATE_DATABASE 1 + +static void gripe_system (pipeline *p, int status) +{ + error (CHILD_FAIL, 0, _("command exited with status %d: %s"), + status, pipeline_tostring (p)); +} + +enum opts { + OPT_WARNINGS = 256, + OPT_REGEX, + OPT_WILDCARD, + OPT_NAMES, + OPT_NO_HYPHENATION, + OPT_NO_JUSTIFICATION, + OPT_NO_SUBPAGES, + OPT_MAX +}; + +static gl_list_t manpathlist; + +/* globals */ +int quiet = 1; +char *database = NULL; +extern const char *extension; /* for globbing.c */ +extern char *user_config_file; /* defined in manp.c */ +extern bool disable_cache; +extern int min_cat_width, max_cat_width, cat_width; +man_sandbox *sandbox; + +/* locals */ +static const char *alt_system_name; +static gl_list_t section_list; +static const char *section; +static char *colon_sep_section_list; +static const char *preprocessors; +static const char *pager; +static const char *locale; +static char *internal_locale, *multiple_locale; +static const char *prompt_string; +static char *less; +static const char *std_sections[] = STD_SECTIONS; +static char *manp; +static const char *external; +static gl_map_t db_map = NULL; + +static bool troff; +static const char *roff_device = NULL; +static const char *want_encoding = NULL; +#ifdef NROFF_WARNINGS +static const char default_roff_warnings[] = "mac"; +static gl_list_t roff_warnings; +#endif /* NROFF_WARNINGS */ +static bool global_apropos; +static bool print_where, print_where_cat; +static bool catman; +static bool local_man_file; +static bool findall; +static bool update; +static bool match_case; +static bool regex_opt; +static bool wildcard; +static bool names_only; +static int ult_flags = SO_LINK | SOFT_LINK | HARD_LINK; +static const char *recode = NULL; +static bool no_hyphenation; +static bool no_justification; +static bool subpages = true; + +static bool ascii; /* insert tr in the output pipe */ +static bool save_cat; /* security breach? Can we save the cat? */ + +static int first_arg; + +static int found_a_stray; /* found a straycat */ + +#ifdef MAN_CATS +static char *tmp_cat_file; /* for open_cat_stream(), close_cat_stream() */ +static int created_tmp_cat; /* dto. */ +#endif +static int tmp_cat_fd; +static struct timespec man_modtime; /* modtime of man page, for + * commit_tmp_cat() */ + +# ifdef TROFF_IS_GROFF +static bool ditroff; +static const char *gxditview; +static bool htmlout; +static const char *html_pager; +# endif /* TROFF_IS_GROFF */ + +const char *argp_program_version = "man " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("[SECTION] PAGE..."); + +# ifdef NROFF_WARNINGS +# define ONLY_NROFF_WARNINGS 0 +# else +# define ONLY_NROFF_WARNINGS OPTION_HIDDEN +# endif + +# ifdef TROFF_IS_GROFF +# define ONLY_TROFF_IS_GROFF 0 +# else +# define ONLY_TROFF_IS_GROFF OPTION_HIDDEN +# endif + +/* Please keep these options in the same order as in parse_opt below. */ +static struct argp_option options[] = { + { "config-file", 'C', N_("FILE"), 0, N_("use this user configuration file") }, + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "default", 'D', 0, 0, N_("reset all options to their default values") }, + { "warnings", OPT_WARNINGS, N_("WARNINGS"), ONLY_NROFF_WARNINGS | OPTION_ARG_OPTIONAL, + N_("enable warnings from groff") }, + + { 0, 0, 0, 0, N_("Main modes of operation:"), 10 }, + { "whatis", 'f', 0, 0, N_("equivalent to whatis") }, + { "apropos", 'k', 0, 0, N_("equivalent to apropos") }, + { "global-apropos", 'K', 0, 0, N_("search for text in all pages") }, + { "where", 'w', 0, 0, N_("print physical location of man page(s)") }, + { "path", 0, 0, OPTION_ALIAS }, + { "location", 0, 0, OPTION_ALIAS }, + { "where-cat", 'W', 0, 0, N_("print physical location of cat file(s)") }, + { "location-cat", 0, 0, OPTION_ALIAS }, + { "local-file", 'l', 0, 0, N_("interpret PAGE argument(s) as local filename(s)") }, + { "catman", 'c', 0, 0, N_("used by catman to reformat out of date cat pages"), 11 }, + { "recode", 'R', N_("ENCODING"), 0, N_("output source page encoded in ENCODING") }, + + { 0, 0, 0, 0, N_("Finding manual pages:"), 20 }, + { "locale", 'L', N_("LOCALE"), 0, N_("define the locale for this particular man search") }, + { "systems", 'm', N_("SYSTEM"), 0, N_("use manual pages from other systems") }, + { "manpath", 'M', N_("PATH"), 0, N_("set search path for manual pages to PATH") }, + { "sections", 'S', N_("LIST"), 0, N_("use colon separated section list"), 21 }, + { 0, 's', 0, OPTION_ALIAS }, + { "extension", 'e', N_("EXTENSION"), + 0, N_("limit search to extension type EXTENSION"), 22 }, + { "ignore-case", 'i', 0, 0, N_("look for pages case-insensitively (default)"), 23 }, + { "match-case", 'I', 0, 0, N_("look for pages case-sensitively") }, + { "regex", OPT_REGEX, 0, 0, N_("show all pages matching regex"), 24 }, + { "wildcard", OPT_WILDCARD, 0, 0, N_("show all pages matching wildcard") }, + { "names-only", OPT_NAMES, 0, 0, N_("make --regex and --wildcard match page names only, not " + "descriptions"), 25 }, + { "all", 'a', 0, 0, N_("find all matching manual pages"), 26 }, + { "update", 'u', 0, 0, N_("force a cache consistency check") }, + { "no-subpages", + OPT_NO_SUBPAGES, 0, 0, N_("don't try subpages, e.g. 'man foo bar' => 'man foo-bar'"), 27 }, + + { 0, 0, 0, 0, N_("Controlling formatted output:"), 30 }, + { "pager", 'P', N_("PAGER"), 0, N_("use program PAGER to display output") }, + { "prompt", 'r', N_("STRING"), 0, N_("provide the `less' pager with a prompt") }, + { "ascii", '7', 0, 0, N_("display ASCII translation of certain latin1 chars"), 31 }, + { "encoding", 'E', N_("ENCODING"), 0, N_("use selected output encoding") }, + { "no-hyphenation", + OPT_NO_HYPHENATION, 0, 0, N_("turn off hyphenation") }, + { "nh", 0, 0, OPTION_ALIAS }, + { "no-justification", + OPT_NO_JUSTIFICATION, 0, 0, N_("turn off justification") }, + { "nj", 0, 0, OPTION_ALIAS }, + { "preprocessor", 'p', N_("STRING"), 0, N_("STRING indicates which preprocessors to run:\n" + "e - [n]eqn, p - pic, t - tbl,\n" + "g - grap, r - refer, v - vgrind") }, +#ifdef HAS_TROFF + { "troff", 't', 0, 0, N_("use %s to format pages"), 32 }, + { "troff-device", 'T', N_("DEVICE"), OPTION_ARG_OPTIONAL, + N_("use %s with selected device") }, + { "html", 'H', N_("BROWSER"), ONLY_TROFF_IS_GROFF | OPTION_ARG_OPTIONAL, + N_("use %s or BROWSER to display HTML output"), 33 }, + { "gxditview", 'X', N_("RESOLUTION"), + ONLY_TROFF_IS_GROFF | OPTION_ARG_OPTIONAL, + N_("use groff and display through gxditview (X11):\n" + "-X = -TX75, -X100 = -TX100, -X100-12 = -TX100-12") }, + { "ditroff", 'Z', 0, ONLY_TROFF_IS_GROFF, N_("use groff and force it to produce ditroff") }, +#endif /* HAS_TROFF */ + + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static void init_html_pager (void) +{ + html_pager = getenv ("BROWSER"); + if (!html_pager) + html_pager = WEB_BROWSER; +} + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + static bool apropos, whatis; /* retain values between calls */ + + /* Please keep these keys in the same order as in options above. */ + switch (key) { + case 'C': + user_config_file = arg; + return 0; + case 'd': + debug_level = true; + return 0; + case 'D': + /* discard all preset options */ + local_man_file = findall = update = catman = + debug_level = troff = global_apropos = + print_where = print_where_cat = + ascii = match_case = + regex_opt = wildcard = names_only = + no_hyphenation = no_justification = false; +#ifdef TROFF_IS_GROFF + ditroff = false; + gxditview = NULL; + htmlout = false; + init_html_pager (); +#endif + roff_device = want_encoding = extension = pager = + locale = alt_system_name = external = + preprocessors = NULL; + colon_sep_section_list = manp = NULL; + return 0; + + case OPT_WARNINGS: +#ifdef NROFF_WARNINGS + { + char *s = xstrdup + (arg ? arg : default_roff_warnings); + const char *warning; + + for (warning = strtok (s, ","); warning; + warning = strtok (NULL, ",")) + gl_list_add_last (roff_warnings, + xstrdup (warning)); + + free (s); + } +#endif /* NROFF_WARNINGS */ + return 0; + + case 'f': + external = WHATIS; + whatis = true; + return 0; + case 'k': + external = APROPOS; + apropos = true; + return 0; + case 'K': + global_apropos = true; + return 0; + case 'w': + print_where = true; + return 0; + case 'W': + print_where_cat = true; + return 0; + case 'l': + local_man_file = true; + return 0; + case 'c': + catman = true; + return 0; + case 'R': + recode = arg; + ult_flags &= ~SO_LINK; + return 0; + + case 'L': + locale = arg; + return 0; + case 'm': + alt_system_name = arg; + return 0; + case 'M': + manp = arg; + return 0; + case 'S': + case 's': + if (*arg) + colon_sep_section_list = arg; + return 0; + case 'e': + extension = arg; + return 0; + case 'i': + match_case = false; + return 0; + case 'I': + match_case = true; + return 0; + case OPT_REGEX: + regex_opt = true; + findall = true; + return 0; + case OPT_WILDCARD: + wildcard = true; + findall = true; + return 0; + case OPT_NAMES: + names_only = true; + return 0; + case 'a': + findall = true; + return 0; + case 'u': + update = true; + return 0; + case OPT_NO_SUBPAGES: + subpages = false; + return 0; + + case 'P': + pager = arg; + return 0; + case 'r': + prompt_string = arg; + return 0; + case '7': + ascii = true; + return 0; + case 'E': + want_encoding = arg; + if (is_roff_device (want_encoding)) + roff_device = want_encoding; + return 0; + case OPT_NO_HYPHENATION: + no_hyphenation = true; + return 0; + case OPT_NO_JUSTIFICATION: + no_justification = true; + return 0; + case 'p': + preprocessors = arg; + return 0; +#ifdef HAS_TROFF + case 't': + troff = true; + return 0; + case 'T': + /* Traditional nroff knows -T; troff does not (gets + * ignored). All incarnations of groff know it. Why + * does -T imply -t? + */ + roff_device = (arg ? arg : "ps"); + troff = true; + return 0; + case 'H': +# ifdef TROFF_IS_GROFF + if (arg) + html_pager = arg; + htmlout = true; + troff = true; + roff_device = "html"; +# endif /* TROFF_IS_GROFF */ + return 0; + case 'X': +# ifdef TROFF_IS_GROFF + troff = true; + gxditview = (arg ? arg : "75"); +# endif /* TROFF_IS_GROFF */ + return 0; + case 'Z': +# ifdef TROFF_IS_GROFF + ditroff = true; + troff = true; +# endif /* TROFF_IS_GROFF */ + return 0; +#endif /* HAS_TROFF */ + + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP); + break; + case ARGP_KEY_SUCCESS: + /* check for incompatible options */ + if ((int) troff + (int) whatis + (int) apropos + + (int) catman + + (int) (print_where || print_where_cat) > 1) { + char *badopts = xasprintf + ("%s%s%s%s%s%s", + troff ? "-[tTZH] " : "", + whatis ? "-f " : "", + apropos ? "-k " : "", + catman ? "-c " : "", + print_where ? "-w " : "", + print_where_cat ? "-W " : ""); + argp_error (state, + _("%s: incompatible options"), + badopts); + } + if ((int) regex_opt + (int) wildcard > 1) { + char *badopts = xasprintf + ("%s%s", + regex_opt ? "--regex " : "", + wildcard ? "--wildcard " : ""); + argp_error (state, + _("%s: incompatible options"), + badopts); + } + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static char *help_filter (int key, const char *text, void *input _GL_UNUSED) +{ +#ifdef HAS_TROFF +# ifdef TROFF_IS_GROFF + static const char formatter[] = "groff"; + const char *browser; +# else + static const char formatter[] = "troff"; +# endif /* TROFF_IS_GROFF */ +#endif /* HAS_TROFF */ + + switch (key) { +#ifdef HAS_TROFF + case 't': + case 'T': + return xasprintf (text, formatter); +# ifdef TROFF_IS_GROFF + case 'H': + browser = html_pager; + assert (browser); + if (STRNEQ (browser, "exec ", 5)) + browser += 5; + return xasprintf (text, browser); +# endif /* TROFF_IS_GROFF */ +#endif /* HAS_TROFF */ + default: + return (char *) text; + } +} +#pragma GCC diagnostic pop + +static struct argp argp = { options, parse_opt, args_doc, 0, 0, help_filter }; + +/* + * changed these messages from stdout to stderr, + * (Fabrizio Polacco) Fri, 14 Feb 1997 01:30:07 +0200 + */ +static void gripe_no_name (const char *sect) +{ + if (sect) { + fprintf (stderr, _("No manual entry for %s\n"), sect); + fprintf (stderr, + _("(Alternatively, what manual page do you want from " + "section %s?)\n"), + sect); + } else + fputs (_("What manual page do you want?\n"), stderr); + fputs (_("For example, try 'man man'.\n"), stderr); + + exit (FAIL); +} + +static struct termios tms; +static int tms_set = 0; +static pid_t tms_pid = 0; + +static void set_term (void) +{ + if (tms_set && getpid () == tms_pid) + tcsetattr (STDIN_FILENO, TCSANOW, &tms); +} + +static void get_term (void) +{ + if (isatty (STDOUT_FILENO)) { + debug ("is a tty\n"); + tcgetattr (STDIN_FILENO, &tms); + if (!tms_set++) { + /* Work around pipecmd_exec calling exit(3) rather + * than _exit(2), which means our atexit-registered + * functions are called at the end of each child + * process created using pipecmd_new_function and + * friends. It would probably be good to fix this + * in libpipeline at some point, but it would + * require care to avoid breaking compatibility. + */ + tms_pid = getpid (); + atexit (set_term); + } + } +} + +#if defined(TROFF_IS_GROFF) || defined(HEIRLOOM_NROFF) +static int get_roff_line_length (void) +{ + int line_length = cat_width ? cat_width : get_line_length (); + + /* groff >= 1.18 defaults to 78. */ + if ((!troff || ditroff) && line_length != 80) { + int length = line_length * 39 / 40; + if (length > line_length - 2) + return line_length - 2; + else + return length; + } else + return 0; +} + +#ifdef HEIRLOOM_NROFF +static void heirloom_line_length (void *data) +{ + printf (".ll %sn\n", (const char *) data); + /* TODO: This fails to do anything useful. Why? */ + printf (".lt %sn\n", (const char *) data); + printf (".lf 1\n"); +} +#endif /* HEIRLOOM_NROFF */ + +static pipecmd *add_roff_line_length (pipecmd *cmd, bool *save_cat_p) +{ + int length; + pipecmd *ret = NULL; + + if (!catman) { + int line_length = get_line_length (); + debug ("Terminal width %d\n", line_length); + if (line_length >= min_cat_width && + line_length <= max_cat_width) + debug ("Terminal width %d within cat page range " + "[%d, %d]\n", + line_length, min_cat_width, max_cat_width); + else { + debug ("Terminal width %d not within cat page range " + "[%d, %d]\n", + line_length, min_cat_width, max_cat_width); + *save_cat_p = false; + } + } + + length = get_roff_line_length (); + if (length) { +#ifdef HEIRLOOM_NROFF + char *name; + char *lldata; + pipecmd *llcmd; +#endif /* HEIRLOOM_NROFF */ + + debug ("Using %d-character lines\n", length); +#if defined(TROFF_IS_GROFF) + pipecmd_argf (cmd, "-rLL=%dn", length); + pipecmd_argf (cmd, "-rLT=%dn", length); +#elif defined(HEIRLOOM_NROFF) + name = xasprintf ("echo .ll %dn && echo .lt %dn", + length, length); + lldata = xasprintf ("%d", length); + llcmd = pipecmd_new_function (name, heirloom_line_length, free, + lldata); + ret = pipecmd_new_sequence ("line-length", llcmd, + pipecmd_new_passthrough (), NULL); + free (name); +#endif /* HEIRLOOM_NROFF */ + } + + return ret; +} +#endif /* TROFF_IS_GROFF || HEIRLOOM_NROFF */ + +static void gripe_no_man (const char *name, const char *sec) +{ + /* On AIX and IRIX, fall back to the vendor supplied browser. */ +#if defined _AIX || defined __sgi + if (!troff) { + pipecmd *vendor_man; + int i; + + vendor_man = pipecmd_new ("/usr/bin/man"); + for (i = 1; i < argc; ++i) + pipecmd_arg (vendor_man, global_argv[i]); + pipecmd_unsetenv (vendor_man, "MANPATH"); + pipecmd_exec (vendor_man); + } +#endif + + if (sec) + fprintf (stderr, _("No manual entry for %s in section %s\n"), + name, sec); + else + fprintf (stderr, _("No manual entry for %s\n"), name); + +#ifdef UNDOC_COMMAND + if (getenv ("MAN_TEST_DISABLE_UNDOCUMENTED") == NULL && + pathsearch_executable (name)) + fprintf (stderr, + _("See '%s' for help when manual pages are not " + "available.\n"), UNDOC_COMMAND); +#endif +} + +/* fire up the appropriate external program */ +static void do_extern (int argc, char *argv[]) +{ + pipeline *p; + pipecmd *cmd; + + cmd = pipecmd_new (external); + /* Please keep these in the same order as they are in whatis.c. */ + if (debug_level) + pipecmd_arg (cmd, "-d"); + if (local_man_file) /* actually apropos/whatis --long */ + pipecmd_arg (cmd, "-l"); + if (colon_sep_section_list) + pipecmd_args (cmd, "-s", colon_sep_section_list, (void *) 0); + if (alt_system_name) + pipecmd_args (cmd, "-m", alt_system_name, (void *) 0); + if (manp) + pipecmd_args (cmd, "-M", manp, (void *) 0); + if (locale) + pipecmd_args (cmd, "-L", locale, (void *) 0); + if (user_config_file) + pipecmd_args (cmd, "-C", user_config_file, (void *) 0); + while (first_arg < argc) + pipecmd_arg (cmd, argv[first_arg++]); + p = pipeline_new_commands (cmd, (void *) 0); + + /* privs are already dropped */ + exit (pipeline_run (p)); +} + +/* lookup $MANOPT and if available, put in *argv[] format for argp */ +static char **manopt_to_env (int *argc) +{ + char *manopt, *manopt_copy, *opt_start, **argv; + + manopt = getenv ("MANOPT"); + if (manopt == NULL || *manopt == '\0') + return NULL; + + opt_start = manopt = manopt_copy = xstrdup (manopt); + + /* allocate space for the program name */ + *argc = 0; + argv = XNMALLOC (*argc + 3, char *); + argv[(*argc)++] = base_name (program_name); + + /* for each [ \t]+ delimited string, allocate an array space and fill + it in. An escaped space is treated specially */ + while (*manopt) { + switch (*manopt) { + case ' ': + case '\t': + if (manopt != opt_start) { + *manopt = '\0'; + argv = xnrealloc (argv, *argc + 3, + sizeof (char *)); + argv[(*argc)++] = xstrdup (opt_start); + } + while (CTYPE (isspace, *(manopt + 1))) + *++manopt = '\0'; + opt_start = manopt + 1; + break; + case '\\': + if (*(manopt + 1) == ' ') + manopt++; + break; + default: + break; + } + manopt++; + } + + if (*opt_start) + argv[(*argc)++] = xstrdup (opt_start); + argv[*argc] = NULL; + + free (manopt_copy); + return argv; +} + +/* Return char array with 'less' special chars escaped. Uses static storage. */ +static const char *escape_less (const char *string) +{ + static char *escaped_string; + char *ptr; + + /* 2*strlen will always be long enough to hold the escaped string */ + ptr = escaped_string = xrealloc (escaped_string, + 2 * strlen (string) + 1); + + while (*string) { + if (*string == '?' || + *string == ':' || + *string == '.' || + *string == '%' || + *string == '\\') + *ptr++ = '\\'; + + *ptr++ = *string++; + } + + *ptr = *string; + return escaped_string; +} + +#if defined(MAN_DB_CREATES) || defined(MAN_DB_UPDATES) +/* Run mandb to ensure databases are up to date. Only used with -u. + * Returns the exit status of mandb. + * + * If filename is non-NULL, uses mandb's -f option to update a single file. + */ +static int run_mandb (int create, const char *manpath, const char *filename) +{ + pipeline *mandb_pl = pipeline_new (); + pipecmd *mandb_cmd = pipecmd_new ("mandb"); + + if (debug_level) + pipecmd_arg (mandb_cmd, "-d"); + else + pipecmd_arg (mandb_cmd, "-q"); + + if (user_config_file) + pipecmd_args (mandb_cmd, "-C", user_config_file, (void *) 0); + + if (filename) + pipecmd_args (mandb_cmd, "-f", filename, (void *) 0); + else if (create) { + pipecmd_arg (mandb_cmd, "-c"); + pipecmd_setenv (mandb_cmd, "MAN_MUST_CREATE", "1"); + } else + pipecmd_arg (mandb_cmd, "-p"); + + if (manpath) + pipecmd_arg (mandb_cmd, manpath); + + pipeline_command (mandb_pl, mandb_cmd); + + if (debug_level) { + debug ("running mandb: "); + pipeline_dump (mandb_pl, stderr); + } + + return pipeline_run (mandb_pl); +} +#endif /* MAN_DB_CREATES || MAN_DB_UPDATES */ + + +static char *locale_manpath (const char *manpath) +{ + char *all_locales; + char *new_manpath; + + if (multiple_locale && *multiple_locale) { + if (internal_locale && *internal_locale) + all_locales = xasprintf ("%s:%s", multiple_locale, + internal_locale); + else + all_locales = xstrdup (multiple_locale); + } else { + if (internal_locale && *internal_locale) + all_locales = xstrdup (internal_locale); + else + all_locales = NULL; + } + + new_manpath = add_nls_manpaths (manpath, all_locales); + free (all_locales); + + return new_manpath; +} + +/* + * Check to see if the argument is a valid section number. + * If the name matches one of + * the sections listed in section_list, we'll assume that it's a section. + * The list of sections in config.h simply allows us to specify oddly + * named directories like .../man3f. Yuk. + */ +static const char *is_section (const char *name) +{ + const char *vs; + + GL_LIST_FOREACH_START (section_list, vs) { + if (STREQ (vs, name)) + return name; + /* allow e.g. 3perl but disallow 8139too and libfoo */ + if (strlen (vs) == 1 && CTYPE (isdigit, *vs) && + strlen (name) > 1 && !CTYPE (isdigit, name[1]) && + STRNEQ (vs, name, 1)) + return name; + } GL_LIST_FOREACH_END (section_list); + return NULL; +} + +/* Snarf pre-processors from file, return string or NULL on failure */ +static char *get_preprocessors_from_file (pipeline *decomp, int prefixes) +{ + const size_t block = 4096; + int i; + char *line = NULL; + size_t previous_len = 0; + + if (!decomp) + return NULL; + + /* Prefixes are inserted into the stream by man itself, and we must + * skip over them to find any preprocessors line that exists. Each + * one ends with an .lf macro. + */ + for (i = 0; ; ++i) { + size_t len = block * (i + 1); + const char *buffer, *scan, *end; + int j; + + scan = buffer = pipeline_peek (decomp, &len); + if (!buffer || len == 0) + return NULL; + + for (j = 0; j < prefixes; ++j) { + scan = memmem (scan, len - (scan - buffer), + "\n.lf ", strlen ("\n.lf ")); + if (!scan) + break; + ++scan; + scan = memchr (scan, '\n', len - (scan - buffer)); + if (!scan) + break; + ++scan; + } + if (!scan) + continue; + + end = memchr (scan, '\n', len - (scan - buffer)); + if (!end && len == previous_len) + /* end of file, no newline found */ + end = buffer + len - 1; + if (end) { + line = xstrndup (scan, end - scan + 1); + break; + } + previous_len = len; + } + if (!line) + return NULL; + + if (!strncmp (line, PP_COOKIE, 4)) { + const char *newline = strchr (line, '\n'); + if (newline) + return xstrndup (line + 4, newline - (line + 4)); + else + return xstrdup (line + 4); + } + return NULL; +} + + +/* Determine pre-processors, set save_cat and return string */ +static char *get_preprocessors (pipeline *decomp, const char *dbfilters, + int prefixes) +{ + char *pp_string; + const char *pp_source; + const char *env; + + /* try in order: database, command line, file, environment, default */ + /* command line overrides the database, but database empty overrides default */ + if (dbfilters && (dbfilters[0] != '-') && !preprocessors) { + pp_string = xstrdup (dbfilters); + pp_source = "database"; + save_cat = true; + } else if (preprocessors) { + pp_string = xstrdup (preprocessors); + pp_source = "command line"; + save_cat = false; + } else if ((pp_string = get_preprocessors_from_file (decomp, + prefixes))) { + pp_source = "file"; + save_cat = true; + } else if ((env = getenv ("MANROFFSEQ"))) { + pp_string = xstrdup (env); + pp_source = "environment"; + save_cat = false; + } else if (!dbfilters) { + pp_string = xstrdup (DEFAULT_MANROFFSEQ); + pp_source = "default"; + save_cat = true; + } else { + pp_string = xstrdup (""); + pp_source = "no filters"; + save_cat = true; + } + + debug ("pre-processors `%s' from %s\n", pp_string, pp_source); + return pp_string; +} + +static const char *my_locale_charset (void) +{ + if (want_encoding && !is_roff_device (want_encoding)) + return want_encoding; + else + return get_locale_charset (); +} + +static void add_col (pipeline *p, const char *locale_charset, ...) +{ + pipecmd *cmd; + va_list argv; + char *col_locale = NULL; + + cmd = pipecmd_new (COL); + va_start (argv, locale_charset); + pipecmd_argv (cmd, argv); + va_end (argv); + pipecmd_pre_exec (cmd, sandbox_load, sandbox_free, sandbox); + + if (locale_charset) + col_locale = find_charset_locale (locale_charset); + if (col_locale) { + pipecmd_setenv (cmd, "LC_CTYPE", col_locale); + free (col_locale); + } + + pipeline_command (p, cmd); +} + +/* Return pipeline to format file to stdout. */ +static pipeline *make_roff_command (const char *dir, const char *file, + pipeline *decomp, const char *pp_string, + char **result_encoding) +{ + const char *roff_opt; + char *fmt_prog = NULL; + pipeline *p = pipeline_new (); + pipecmd *cmd; + char *page_encoding = NULL; + const char *output_encoding = NULL; + const char *locale_charset = NULL; + + *result_encoding = xstrdup ("UTF-8"); /* optimistic default */ + + roff_opt = getenv ("MANROFFOPT"); + if (!roff_opt) + roff_opt = ""; + + if (dir && !recode) { +#ifdef ALT_EXT_FORMAT + char *catpath = get_catpath + (dir, global_manpath ? SYSTEM_CAT : USER_CAT); + + /* If we have an alternate catpath, look for an external + * formatter there. + */ + if (catpath) { + fmt_prog = appendstr (catpath, "/", + troff ? TFMT_PROG : NFMT_PROG, + (void *) 0); + if (!CAN_ACCESS (fmt_prog, X_OK)) { + free (fmt_prog); + fmt_prog = NULL; + } + } +#endif /* ALT_EXT_FORMAT */ + + /* If the page is in a proper manual page hierarchy (as + * opposed to being read using --local-file or similar), + * look for an external formatter there. + */ + if (!fmt_prog) { + fmt_prog = appendstr (NULL, dir, "/", + troff ? TFMT_PROG : NFMT_PROG, + (void *) 0); + if (!CAN_ACCESS (fmt_prog, X_OK)) { + free (fmt_prog); + fmt_prog = NULL; + } + } + } + + if (fmt_prog) + debug ("External formatter %s\n", fmt_prog); + + if (!fmt_prog) { + /* we don't have an external formatter script */ + const char *source_encoding, *roff_encoding; + const char *groff_preconv; + + if (!recode) { + struct zsoelim_stdin_data *zsoelim_data; + + zsoelim_data = zsoelim_stdin_data_new (dir, + manpathlist); + cmd = pipecmd_new_function (ZSOELIM, &zsoelim_stdin, + zsoelim_stdin_data_free, + zsoelim_data); + pipecmd_pre_exec (cmd, sandbox_load, sandbox_free, + sandbox); + pipeline_command (p, cmd); + } + + page_encoding = check_preprocessor_encoding + (decomp, NULL, NULL); + if (!page_encoding) + page_encoding = get_page_encoding (lang); + if (page_encoding && !STREQ (page_encoding, "UTF-8")) + source_encoding = page_encoding; + else + source_encoding = get_source_encoding (lang); + debug ("page_encoding = %s\n", page_encoding); + debug ("source_encoding = %s\n", source_encoding); + + /* Load the roff_device value dependent on the language dir + * in the path. + */ + if (!troff) { +#define STRC(s, otherwise) ((s) ? (s) : (otherwise)) + + locale_charset = my_locale_charset (); + debug ("locale_charset = %s\n", + STRC (locale_charset, "NULL")); + + /* Pick the default device for this locale if there + * wasn't one selected explicitly. + */ + if (!roff_device) { + roff_device = + get_default_device (locale_charset, + source_encoding); +#ifdef HEIRLOOM_NROFF + /* In Heirloom, if LC_CTYPE is a UTF-8 + * locale, then -Tlocale will be equivalent + * to -Tutf8 except that it will do a + * slightly better job of rendering some + * special characters. + */ + if (STREQ (roff_device, "utf8")) { + const char *real_locale_charset = + get_locale_charset (); + if (real_locale_charset && + STREQ (real_locale_charset, + "UTF-8")) + roff_device = "locale"; + } +#endif /* HEIRLOOM_NROFF */ + debug ("roff_device (locale) = %s\n", + STRC (roff_device, "NULL")); + } + } + + roff_encoding = get_roff_encoding (roff_device, + source_encoding); + debug ("roff_encoding = %s\n", roff_encoding); + + /* We may need to recode: + * from page_encoding to roff_encoding on input; + * from output_encoding to locale_charset on output + * (if not troff). + * If we have preconv, then use it to recode the + * input to a safe escaped form. + * The --recode option overrides everything else. + */ + groff_preconv = get_groff_preconv (); + if (recode) + add_manconv (p, page_encoding, recode); + else if (groff_preconv) { + pipecmd *preconv_cmd; + add_manconv (p, page_encoding, "UTF-8"); + preconv_cmd = pipecmd_new_args + (groff_preconv, "-e", "UTF-8", (void *) 0); + pipecmd_pre_exec (preconv_cmd, sandbox_load, + sandbox_free, sandbox); + pipeline_command (p, preconv_cmd); + } else if (roff_encoding) + add_manconv (p, page_encoding, roff_encoding); + else + add_manconv (p, page_encoding, page_encoding); + + if (!troff && !recode) { + output_encoding = get_output_encoding (roff_device); + if (!output_encoding) + output_encoding = source_encoding; + debug ("output_encoding = %s\n", output_encoding); + free (*result_encoding); + *result_encoding = xstrdup (output_encoding); + + if (!getenv ("LESSCHARSET")) { + const char *less_charset = + get_less_charset (locale_charset); + debug ("less_charset = %s\n", less_charset); + setenv ("LESSCHARSET", less_charset, 1); + } + + if (!getenv ("JLESSCHARSET")) { + const char *jless_charset = + get_jless_charset (locale_charset); + if (jless_charset) { + debug ("jless_charset = %s\n", + jless_charset); + setenv ("JLESSCHARSET", + jless_charset, 1); + } + } + } + } + + if (recode) + ; + else if (!fmt_prog) { +#ifndef GNU_NROFF + int using_tbl = 0; +#endif /* GNU_NROFF */ + + do { +#ifdef NROFF_WARNINGS + const char *warning; +#endif /* NROFF_WARNINGS */ + int wants_dev = 0; /* filter wants a dev argument */ + int wants_post = 0; /* postprocessor arguments */ + + cmd = NULL; + /* set cmd according to *pp_string, on + errors leave cmd as NULL */ + switch (*pp_string) { + case 'e': + if (troff) + cmd = pipecmd_new_argstr + (get_def ("eqn", EQN)); + else + cmd = pipecmd_new_argstr + (get_def ("neqn", NEQN)); + wants_dev = 1; + break; + case 'g': + cmd = pipecmd_new_argstr + (get_def ("grap", GRAP)); + break; + case 'p': + cmd = pipecmd_new_argstr + (get_def ("pic", PIC)); + break; + case 't': + cmd = pipecmd_new_argstr + (get_def ("tbl", TBL)); +#ifndef GNU_NROFF + using_tbl = 1; +#endif /* GNU_NROFF */ + break; + case 'v': + cmd = pipecmd_new_argstr + (get_def ("vgrind", VGRIND)); + break; + case 'r': + cmd = pipecmd_new_argstr + (get_def ("refer", REFER)); + break; + case ' ': + case '-': + case 0: + /* done with preprocessors, now add roff */ + if (troff) { + cmd = pipecmd_new_argstr + (get_def ("troff", TROFF)); + save_cat = false; + } else + cmd = pipecmd_new_argstr + (get_def ("nroff", NROFF)); + +#ifdef TROFF_IS_GROFF + if (troff && ditroff) + pipecmd_arg (cmd, "-Z"); +#endif /* TROFF_IS_GROFF */ + +#if defined(TROFF_IS_GROFF) || defined(HEIRLOOM_NROFF) + { + pipecmd *seq = add_roff_line_length + (cmd, &save_cat); + if (seq) + pipeline_command (p, seq); + } +#endif /* TROFF_IS_GROFF || HEIRLOOM_NROFF */ + +#ifdef NROFF_WARNINGS + GL_LIST_FOREACH_START (roff_warnings, warning) + pipecmd_argf (cmd, "-w%s", warning); + GL_LIST_FOREACH_END (roff_warnings); +#endif /* NROFF_WARNINGS */ + +#ifdef HEIRLOOM_NROFF + if (running_setuid ()) + pipecmd_unsetenv (cmd, "TROFFMACS"); +#endif /* HEIRLOOM_NROFF */ + + pipecmd_argstr (cmd, roff_opt); + + wants_dev = 1; + wants_post = 1; + break; + } + + if (!cmd) { + assert (*pp_string); /* didn't fail on roff */ + error (0, 0, + _("ignoring unknown preprocessor `%c'"), + *pp_string); + continue; + } + + if (wants_dev) { + if (roff_device) + pipecmd_argf (cmd, + "-T%s", roff_device); +#ifdef TROFF_IS_GROFF + else if (gxditview) { + pipecmd_argf (cmd, "-TX%s", gxditview); + if (strstr (gxditview, "-12")) + pipecmd_argf (cmd, "-rS12"); + } +#endif /* TROFF_IS_GROFF */ + } + + if (wants_post) { +#ifdef TROFF_IS_GROFF + if (gxditview) + /* -X arranges for the correct + * options to be passed to troff. + * Normally it would run gxditview + * as well, but we suppress that + * with -Z so that we can do it + * ourselves; this lets us set a + * better window title, and means + * that we don't have to worry about + * sandboxing text processing and an + * X program in the same way. + */ + pipecmd_args (cmd, "-X", "-Z", + (void *) 0); +#endif /* TROFF_IS_GROFF */ + + if (roff_device && STREQ (roff_device, "ps")) + /* Tell grops to guess the page + * size. + */ + pipecmd_arg (cmd, "-P-g"); + } + + pipecmd_pre_exec (cmd, sandbox_load_permissive, + sandbox_free, sandbox); + pipeline_command (p, cmd); + + if (*pp_string == ' ' || *pp_string == '-') + break; + } while (*pp_string++); + + if (!troff && *COL) { + const char *man_keep_formatting = + getenv ("MAN_KEEP_FORMATTING"); + if ((!man_keep_formatting || !*man_keep_formatting) && + !isatty (STDOUT_FILENO)) + /* we'll run col later, but prepare for it */ + setenv ("GROFF_NO_SGR", "1", 1); +#ifndef GNU_NROFF + /* tbl needs col */ + else if (using_tbl && !troff && *COL) + add_col (p, locale_charset, (void *) 0); +#endif /* GNU_NROFF */ + } + } else { + /* use external formatter script, it takes arguments + input file, preprocessor string, and (optional) + output device */ + cmd = pipecmd_new_args (fmt_prog, file, pp_string, (void *) 0); + if (roff_device) + pipecmd_arg (cmd, roff_device); + pipeline_command (p, cmd); + } + + free (fmt_prog); + free (page_encoding); + return p; +} + +#ifdef TROFF_IS_GROFF +/* Return pipeline to run a browser on a given file, observing + * http://www.tuxedo.org/~esr/BROWSER/. + * + * (Actually, I really implement + * https://www.dwheeler.com/browse/secure_browser.html, but it's + * backward-compatible.) + * + * TODO: Is there any way to use the pipeline library better here? + */ +static pipeline *make_browser (const char *pattern, const char *file) +{ + pipeline *p; + pipecmd *cmd; + char *browser = xmalloc (1); + bool found_percent_s = false; + char *percent; + char *esc_file; + + *browser = '\0'; + + percent = strchr (pattern, '%'); + while (percent) { + size_t len = strlen (browser); + browser = xrealloc (browser, len + 1 + (percent - pattern)); + strncat (browser, pattern, percent - pattern); + switch (*(percent + 1)) { + case '\0': + case '%': + browser = appendstr (browser, "%", (void *) 0); + break; + case 'c': + browser = appendstr (browser, ":", (void *) 0); + break; + case 's': + esc_file = escape_shell (file); + browser = appendstr (browser, esc_file, + (void *) 0); + free (esc_file); + found_percent_s = true; + break; + default: + len = strlen (browser); /* cannot be NULL */ + browser = xrealloc (browser, len + 3); + strncat (browser, percent, 2); + break; + } + if (*(percent + 1)) + pattern = percent + 2; + else + pattern = percent + 1; + percent = strchr (pattern, '%'); + } + browser = appendstr (browser, pattern, (void *) 0); + if (!found_percent_s) { + esc_file = escape_shell (file); + browser = appendstr (browser, " ", esc_file, (void *) 0); + free (esc_file); + } + + cmd = pipecmd_new_args ("/bin/sh", "-c", browser, (void *) 0); + pipecmd_pre_exec (cmd, drop_privs, NULL, NULL); + p = pipeline_new_commands (cmd, (void *) 0); + pipeline_ignore_signals (p, 1); + free (browser); + + return p; +} +#endif /* TROFF_IS_GROFF */ + +static void setenv_less (pipecmd *cmd, const char *title) +{ + const char *esc_title; + char *less_opts, *man_pn; + + esc_title = escape_less (title); + less_opts = xasprintf (LESS_OPTS, prompt_string, prompt_string); + less_opts = appendstr (less_opts, less, (void *) 0); + man_pn = strstr (less_opts, MAN_PN); + while (man_pn) { + char *subst_opts = + xmalloc (strlen (less_opts) - strlen (MAN_PN) + + strlen (esc_title) + 1); + strncpy (subst_opts, less_opts, man_pn - less_opts); + subst_opts[man_pn - less_opts] = '\0'; + strcat (subst_opts, esc_title); + strcat (subst_opts, man_pn + strlen (MAN_PN)); + free (less_opts); + less_opts = subst_opts; + man_pn = strstr (less_opts, MAN_PN); + } + + debug ("Setting LESS to %s\n", less_opts); + pipecmd_setenv (cmd, "LESS", less_opts); + + debug ("Setting MAN_PN to %s\n", esc_title); + pipecmd_setenv (cmd, "MAN_PN", esc_title); + + free (less_opts); +} + +static void add_output_iconv (pipeline *p, + const char *source, const char *target) +{ + debug ("add_output_iconv: source %s, target %s\n", source, target); + if (source && target && !STREQ (source, target)) { + char *target_translit = xasprintf ("%s//TRANSLIT", target); + pipecmd *iconv_cmd; + iconv_cmd = pipecmd_new_args + ("iconv", "-c", "-f", source, "-t", target_translit, + (void *) 0); + pipecmd_pre_exec (iconv_cmd, sandbox_load, sandbox_free, + sandbox); + pipeline_command (p, iconv_cmd); + free (target_translit); + } +} + +/* Pipeline command to squeeze multiple blank lines into one. + * + */ +static void squeeze_blank_lines (void *data _GL_UNUSED) +{ + char *line = NULL; + size_t len = 0; + + while (getline (&line, &len, stdin) != -1) { + int in_blank_line = 1; + int got_blank_line = 0; + + while (in_blank_line) { + char *p; + for (p = line; *p; ++p) { + if (!CTYPE (isspace, *p)) { + in_blank_line = 0; + break; + } + } + + if (in_blank_line) { + got_blank_line = 1; + free (line); + line = NULL; + len = 0; + if (getline (&line, &len, stdin) == -1) + break; + } + } + + if (got_blank_line && putchar ('\n') < 0) + break; + + if (!in_blank_line && fputs (line, stdout) < 0) + break; + + free (line); + line = NULL; + len = 0; + } + + free (line); + return; +} + +/* Return pipeline to display file provided on stdin. + * + * TODO: htmlout case is pretty weird now. I'd like the intelligence to be + * somewhere other than format_display. + */ +static pipeline *make_display_command (const char *encoding, const char *title) +{ + pipeline *p = pipeline_new (); + const char *locale_charset = NULL; + pipecmd *pager_cmd = NULL; + + locale_charset = my_locale_charset (); + + if (!troff && (!want_encoding || !is_roff_device (want_encoding))) + add_output_iconv (p, encoding, locale_charset); + + if (!troff && *COL) { + /* get rid of special characters if not writing to a + * terminal + */ + const char *man_keep_formatting = + getenv ("MAN_KEEP_FORMATTING"); + if ((!man_keep_formatting || !*man_keep_formatting) && + !isatty (STDOUT_FILENO)) + add_col (p, locale_charset, "-b", "-p", "-x", + (void *) 0); + } + +#ifdef TROFF_IS_GROFF + if (gxditview) { + char *x_resource = xasprintf ("*iconName:%s", title); + pipeline_command_args + (p, "gxditview", + "-title", title, "-xrm", x_resource, "-", + (void *) 0); + free (x_resource); + return p; + } +#endif /* TROFF_IS_GROFF */ + + /* emulate pager -s, the sed code is just for information */ + { + pipecmd *cmd; + const char *name = "sed -e '/^[[:space:]]*$/{ N; /^[[:space:]]*\\n[[:space:]]*$/D; }'"; + cmd = pipecmd_new_function (name, &squeeze_blank_lines, NULL, NULL); + pipeline_command (p, cmd); + } + + if (isatty (STDOUT_FILENO)) { + if (ascii) { + pipecmd *tr_cmd; + tr_cmd = pipecmd_new_argstr + (get_def_user ("tr", TR TR_SET1 TR_SET2)); + pipecmd_pre_exec (tr_cmd, sandbox_load, sandbox_free, + sandbox); + pipeline_command (p, tr_cmd); + pager_cmd = pipecmd_new_argstr (pager); + } else +#ifdef TROFF_IS_GROFF + if (!htmlout) + /* format_display deals with html_pager */ +#endif + pager_cmd = pipecmd_new_argstr (pager); + } + + if (pager_cmd) { + setenv_less (pager_cmd, title); + pipeline_command (p, pager_cmd); + } + pipeline_ignore_signals (p, 1); + + if (!pipeline_get_ncommands (p)) + /* Always return at least a dummy pipeline. */ + pipeline_command (p, pipecmd_new_passthrough ()); + return p; +} + + +/* return a (malloced) temporary name in cat_file's directory */ +static char *tmp_cat_filename (const char *cat_file) +{ + char *name; + + if (debug_level) { + name = xstrdup ("/dev/null"); + tmp_cat_fd = open (name, O_WRONLY); + } else { + char *slash; + name = xstrdup (cat_file); + slash = strrchr (name, '/'); + if (slash) + *(slash + 1) = '\0'; + else + *name = '\0'; + name = appendstr (name, "catXXXXXX", (void *) 0); + tmp_cat_fd = mkstemp (name); + } + + if (tmp_cat_fd == -1) { + free (name); + return NULL; + } else + return name; +} + + +/* If delete unlink tmp_cat, else commit tmp_cat to cat_file. + Return non-zero on error. + */ +static int commit_tmp_cat (const char *cat_file, const char *tmp_cat, + int delete) +{ + int status = 0; + +#ifdef MAN_OWNER + if (!delete && global_manpath && euid == 0) { + if (debug_level) { + debug ("fixing temporary cat's ownership\n"); + status = 0; + } else { + struct passwd *man_owner = get_man_owner (); + status = chown (tmp_cat, man_owner->pw_uid, + man_owner->pw_gid); + if (status) + error (0, errno, _("can't chown %s"), tmp_cat); + } + } +#endif /* MAN_OWNER */ + + if (!delete && !status) { + if (debug_level) { + debug ("fixing temporary cat's mode\n"); + status = 0; + } else { + status = chmod (tmp_cat, CATMODE); + if (status) + error (0, errno, _("can't chmod %s"), tmp_cat); + } + } + + if (!delete && !status) { + if (debug_level) { + debug ("renaming temporary cat to %s\n", cat_file); + status = 0; + } else { + status = rename (tmp_cat, cat_file); + if (status) + error (0, errno, _("can't rename %s to %s"), + tmp_cat, cat_file); + } + } + + if (!delete && !status) { + if (debug_level) { + debug ("setting modtime on cat file %s\n", cat_file); + status = 0; + } else { + struct timespec times[2]; + + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_NOW; + times[1] = man_modtime; + status = utimens (cat_file, times); + if (status) + error (0, errno, _("can't set times on %s"), + cat_file); + } + } + + if (delete || status) { + if (debug_level) + debug ("unlinking temporary cat\n"); + else if (unlink (tmp_cat)) + error (0, errno, _("can't unlink %s"), tmp_cat); + } + + return status; +} + +/* TODO: This should all be refactored after work on the decompression + * library is complete. + */ +static void discard_stderr (pipeline *p) +{ + int i; + + for (i = 0; i < pipeline_get_ncommands (p); ++i) + pipecmd_discard_err (pipeline_get_command (p, i), 1); +} + +static void maybe_discard_stderr (pipeline *p) +{ + const char *man_keep_stderr = getenv ("MAN_KEEP_STDERR"); + if ((!man_keep_stderr || !*man_keep_stderr) && isatty (STDOUT_FILENO)) + discard_stderr (p); +} + +static void chdir_commands (pipeline *p, const char *dir) +{ + int i; + + for (i = 0; i < pipeline_get_ncommands (p); ++i) + pipecmd_chdir (pipeline_get_command (p, i), dir); +} + +static void cleanup_unlink (void *arg) +{ + const char *path = arg; + + if (unlink (path)) + error (0, errno, _("can't unlink %s"), path); +} + +#ifdef MAN_CATS + +/* Return pipeline to write formatted manual page to for saving as cat file. */ +static pipeline *open_cat_stream (const char *cat_file, const char *encoding) +{ + pipeline *cat_p; +# ifdef COMP_CAT + pipecmd *comp_cmd; +# endif + + created_tmp_cat = 0; + + debug ("creating temporary cat for %s\n", cat_file); + + tmp_cat_file = tmp_cat_filename (cat_file); + if (tmp_cat_file) + created_tmp_cat = 1; + else { + if (!debug_level && (errno == EACCES || errno == EROFS)) { + /* No permission to write to the cat file. Oh well, + * return NULL and let the caller sort it out. + */ + debug ("can't write to temporary cat for %s\n", + cat_file); + return NULL; + } else + error (FATAL, errno, + _("can't create temporary cat for %s"), + cat_file); + } + + if (!debug_level) + push_cleanup (cleanup_unlink, tmp_cat_file, 1); + + cat_p = pipeline_new (); + add_output_iconv (cat_p, encoding, "UTF-8"); +# ifdef COMP_CAT + /* fork the compressor */ + comp_cmd = pipecmd_new_argstr (get_def ("compressor", COMPRESSOR)); + pipecmd_nice (comp_cmd, 10); + pipecmd_pre_exec (comp_cmd, sandbox_load, sandbox_free, sandbox); + pipeline_command (cat_p, comp_cmd); +# endif + /* pipeline_start will close tmp_cat_fd */ + pipeline_want_out (cat_p, tmp_cat_fd); + + return cat_p; +} + +/* Close the cat page stream, return non-zero on error. + If delete don't update the cat file. + */ +static int close_cat_stream (pipeline *cat_p, const char *cat_file, + int delete) +{ + int status; + + status = pipeline_wait (cat_p); + debug ("cat-saver exited with status %d\n", status); + + pipeline_free (cat_p); + + if (created_tmp_cat) { + status |= commit_tmp_cat (cat_file, tmp_cat_file, + delete || status); + if (!debug_level) + pop_cleanup (cleanup_unlink, tmp_cat_file); + } + free (tmp_cat_file); + return status; +} + +/* + * format a manual page with format_cmd, display it with disp_cmd, and + * save it to cat_file + */ +static int format_display_and_save (pipeline *decomp, + pipeline *format_cmd, + pipeline *disp_cmd, + const char *cat_file, const char *encoding) +{ + pipeline *sav_p = open_cat_stream (cat_file, encoding); + int instat; + + if (global_manpath) + drop_effective_privs (); + + maybe_discard_stderr (format_cmd); + + pipeline_connect (decomp, format_cmd, (void *) 0); + if (sav_p) { + pipeline_connect (format_cmd, disp_cmd, sav_p, (void *) 0); + pipeline_pump (decomp, format_cmd, disp_cmd, sav_p, + (void *) 0); + } else { + pipeline_connect (format_cmd, disp_cmd, (void *) 0); + pipeline_pump (decomp, format_cmd, disp_cmd, (void *) 0); + } + + if (global_manpath) + regain_effective_privs (); + + pipeline_wait (decomp); + instat = pipeline_wait (format_cmd); + if (sav_p) + close_cat_stream (sav_p, cat_file, instat); + pipeline_wait (disp_cmd); + return instat; +} +#endif /* MAN_CATS */ + +/* Format a manual page with format_cmd and display it with disp_cmd. + * Handle temporary file creation if necessary. + * TODO: merge with format_display_and_save + */ +static void format_display (pipeline *decomp, + pipeline *format_cmd, pipeline *disp_cmd, + const char *man_file) +{ + int format_status = 0, disp_status = 0; +#ifdef TROFF_IS_GROFF + char *htmldir = NULL, *htmlfile = NULL; +#endif /* TROFF_IS_GROFF */ + + if (format_cmd) + maybe_discard_stderr (format_cmd); + + drop_effective_privs (); + +#ifdef TROFF_IS_GROFF + if (format_cmd && htmlout) { + char *man_base, *man_ext; + int htmlfd; + + htmldir = create_tempdir ("hman"); + if (!htmldir) + error (FATAL, errno, + _("can't create temporary directory")); + chdir_commands (format_cmd, htmldir); + chdir_commands (disp_cmd, htmldir); + man_base = base_name (man_file); + man_ext = strchr (man_base, '.'); + if (man_ext) + *man_ext = '\0'; + htmlfile = xasprintf ("%s/%s.html", htmldir, man_base); + free (man_base); + htmlfd = open (htmlfile, O_CREAT | O_EXCL | O_WRONLY, 0644); + if (htmlfd == -1) + error (FATAL, errno, _("can't open temporary file %s"), + htmlfile); + pipeline_want_out (format_cmd, htmlfd); + pipeline_connect (decomp, format_cmd, (void *) 0); + pipeline_pump (decomp, format_cmd, (void *) 0); + pipeline_wait (decomp); + format_status = pipeline_wait (format_cmd); + } else +#endif /* TROFF_IS_GROFF */ + if (format_cmd) { + pipeline_connect (decomp, format_cmd, (void *) 0); + pipeline_connect (format_cmd, disp_cmd, (void *) 0); + pipeline_pump (decomp, format_cmd, disp_cmd, (void *) 0); + pipeline_wait (decomp); + format_status = pipeline_wait (format_cmd); + disp_status = pipeline_wait (disp_cmd); + } else { + pipeline_connect (decomp, disp_cmd, (void *) 0); + pipeline_pump (decomp, disp_cmd, (void *) 0); + pipeline_wait (decomp); + disp_status = pipeline_wait (disp_cmd); + } + +#ifdef TROFF_IS_GROFF + if (format_cmd && htmlout) { + char *browser_list, *candidate; + + if (format_status) { + if (remove_directory (htmldir, 0) == -1) + error (0, errno, + _("can't remove directory %s"), + htmldir); + free (htmlfile); + free (htmldir); + gripe_system (format_cmd, format_status); + } + + browser_list = xstrdup (html_pager); + for (candidate = strtok (browser_list, ":"); candidate; + candidate = strtok (NULL, ":")) { + pipeline *browser; + debug ("Trying browser: %s\n", candidate); + browser = make_browser (candidate, htmlfile); + disp_status = pipeline_run (browser); + if (!disp_status) + break; + } + if (!candidate) { + if (html_pager && *html_pager) + error (CHILD_FAIL, 0, + "couldn't execute any browser from %s", + html_pager); + else + error (CHILD_FAIL, 0, + "no browser configured, so cannot show " + "HTML output"); + } + free (browser_list); + if (remove_directory (htmldir, 0) == -1) + error (0, errno, _("can't remove directory %s"), + htmldir); + free (htmlfile); + free (htmldir); + } else +#endif /* TROFF_IS_GROFF */ + { + if (format_status && format_status != (SIGPIPE + 0x80) * 256) + gripe_system (format_cmd, format_status); + if (disp_status && disp_status != (SIGPIPE + 0x80) * 256) + gripe_system (disp_cmd, disp_status); + } + + regain_effective_privs (); +} + +/* "Display" a page in catman mode, which amounts to saving it. */ +/* TODO: merge with format_display_and_save? */ +static void display_catman (const char *cat_file, pipeline *decomp, + pipeline *format_cmd, const char *encoding) +{ + char *tmpcat = tmp_cat_filename (cat_file); +#ifdef COMP_CAT + pipecmd *comp_cmd; +#endif /* COMP_CAT */ + int status; + + add_output_iconv (format_cmd, encoding, "UTF-8"); + +#ifdef COMP_CAT + comp_cmd = pipecmd_new_argstr (get_def ("compressor", COMPRESSOR)); + pipecmd_pre_exec (comp_cmd, sandbox_load, sandbox_free, sandbox); + pipeline_command (format_cmd, comp_cmd); +#endif /* COMP_CAT */ + + maybe_discard_stderr (format_cmd); + pipeline_want_out (format_cmd, tmp_cat_fd); + + push_cleanup (cleanup_unlink, tmpcat, 1); + + /* save the cat as real user + * (1) required for user man hierarchy + * (2) else depending on ruid's privs is ok, effectively disables + * catman for non-root. + */ + drop_effective_privs (); + pipeline_connect (decomp, format_cmd, (void *) 0); + pipeline_pump (decomp, format_cmd, (void *) 0); + pipeline_wait (decomp); + status = pipeline_wait (format_cmd); + regain_effective_privs (); + if (status) + gripe_system (format_cmd, status); + + close (tmp_cat_fd); + commit_tmp_cat (cat_file, tmpcat, status); + pop_cleanup (cleanup_unlink, tmpcat); + free (tmpcat); +} + +static void disable_hyphenation (void *data _GL_UNUSED) +{ + fputs (".nh\n" + ".de hy\n" + "..\n" + ".lf 1\n", stdout); +} + +static void disable_justification (void *data _GL_UNUSED) +{ + fputs (".na\n" + ".de ad\n" + "..\n" + ".lf 1\n", stdout); +} + +#ifdef TROFF_IS_GROFF +static void locale_macros (void *data) +{ + const char *macro_lang = data; + const char *hyphen_lang = STREQ (lang, "en") ? "us" : macro_lang; + + debug ("Macro language %s; hyphenation language %s\n", + macro_lang, hyphen_lang); + + printf ( + /* If we're using groff >= 1.20.2 (for the 'file' warning + * category): + */ + ".if \\n[.g] \\{\\\n" + ". ds Ystring \\n[.Y]\n" + ". while (\\B'\\*[Ystring]' = 0) .chop Ystring\n" + ". if ((\\n[.x] > 1) :" + " ((\\n[.x] == 1) & (\\n[.y] > 20)) :" + " ((\\n[.x] == 1) & (\\n[.y] == 20) & (\\*[Ystring] >= 2))) " + "\\{\\\n" + /* disable warnings of category 'file' */ + ". warn (\\n[.warn] -" + " (\\n[.warn] / 1048576 %% 2 * 1048576))\n" + /* and load the appropriate per-locale macros */ + ". mso %s.tmac\n" + ". \\}\n" + ". rm Ystring\n" + ".\\}\n" + /* set the hyphenation language anyway, to make sure groff + * only hyphenates languages it knows about + */ + ".hla %s\n" + ".lf 1\n", macro_lang, hyphen_lang); +} +#endif /* TROFF_IS_GROFF */ + +/* allow user to skip a page or quit after viewing desired page + return 1 to skip + return 0 to view + */ +static int do_prompt (const char *name) +{ + int ch; + FILE *tty = NULL; + + skip = 0; + if (!isatty (STDOUT_FILENO) || !isatty (STDIN_FILENO)) + return 0; /* noninteractive */ + tty = fopen ("/dev/tty", "r+"); + if (!tty) + return 0; + + fprintf (tty, _( + "--Man-- next: %s " + "[ view (return) | skip (Ctrl-D) | quit (Ctrl-C) ]\n"), + name); + fflush (tty); + + do { + ch = getc (tty); + switch (ch) { + case '\n': + fclose (tty); + return 0; + case EOF: + skip = 1; + fclose (tty); + return 1; + default: + break; + } + } while (1); + + fclose (tty); + return 0; +} + +/* + * optionally chdir to dir, if necessary update cat_file from man_file + * and display it. if man_file is NULL cat_file is a stray cat. If + * !save_cat or cat_file is NULL we must not save the formatted cat. + * If man_file is "" this is a special case -- we expect the man page + * on standard input. + */ +static int display (const char *dir, const char *man_file, + const char *cat_file, const char *title, + const char *dbfilters) +{ + int found; + static int prompt; + int prefixes = 0; + pipeline *format_cmd; /* command to format man_file to stdout */ + char *formatted_encoding = NULL; + bool display_to_stdout; + pipeline *decomp = NULL; + int decomp_errno = 0; + + /* define format_cmd */ + if (man_file) { + pipecmd *seq = pipecmd_new_sequence ("decompressor", + (void *) 0); + + if (*man_file) + decomp = decompress_open (man_file); + else + decomp = decompress_fdopen (dup (STDIN_FILENO)); + + if (!recode && no_hyphenation) { + pipecmd *hcmd = pipecmd_new_function ( + "echo .nh && echo .de hy && echo ..", + disable_hyphenation, NULL, NULL); + pipecmd_sequence_command (seq, hcmd); + ++prefixes; + } + + if (!recode && no_justification) { + pipecmd *jcmd = pipecmd_new_function ( + "echo .na && echo .de ad && echo ..", + disable_justification, NULL, NULL); + pipecmd_sequence_command (seq, jcmd); + ++prefixes; + } + +#ifdef TROFF_IS_GROFF + /* This only works with preconv, since the per-locale macros + * may change the assumed input encoding. + */ + if (!recode && *man_file && get_groff_preconv ()) { + char *page_lang = lang_dir (man_file); + + if (page_lang && *page_lang && + !STREQ (page_lang, "C")) { + struct locale_bits bits; + char *name; + pipecmd *lcmd; + + unpack_locale_bits (page_lang, &bits); + name = xasprintf ("echo .mso %s.tmac", + bits.language); + lcmd = pipecmd_new_function ( + name, locale_macros, free, + xstrdup (bits.language)); + pipecmd_sequence_command (seq, lcmd); + ++prefixes; + free (name); + free_locale_bits (&bits); + } + free (page_lang); + } +#endif /* TROFF_IS_GROFF */ + + if (prefixes) { + assert (pipeline_get_ncommands (decomp) <= 1); + if (pipeline_get_ncommands (decomp)) { + pipecmd_sequence_command + (seq, + pipeline_get_command (decomp, 0)); + pipeline_set_command (decomp, 0, seq); + } else { + pipecmd_sequence_command + (seq, pipecmd_new_passthrough ()); + pipeline_command (decomp, seq); + } + } else + pipecmd_free (seq); + } + + if (decomp) { + char *pp_string; + + pipeline_start (decomp); + pp_string = get_preprocessors (decomp, dbfilters, prefixes); + format_cmd = make_roff_command (dir, man_file, decomp, + pp_string, + &formatted_encoding); + if (dir) + chdir_commands (format_cmd, dir); + debug ("formatted_encoding = %s\n", formatted_encoding); + free (pp_string); + } else { + format_cmd = NULL; + decomp_errno = errno; + } + + /* Get modification time, for commit_tmp_cat(). */ + if (man_file && *man_file) { + struct stat stb; + if (stat (man_file, &stb)) { + man_modtime.tv_sec = 0; + man_modtime.tv_nsec = 0; + } else + man_modtime = get_stat_mtime (&stb); + } + + display_to_stdout = troff; +#ifdef TROFF_IS_GROFF + if (htmlout || gxditview) + display_to_stdout = false; +#endif + if (recode) + display_to_stdout = true; + + if (display_to_stdout) { + /* If we're reading stdin via '-l -', man_file is "". See + * below. + */ + assert (man_file); + if (!decomp) { + assert (!format_cmd); /* no need to free it */ + error (0, decomp_errno, _("can't open %s"), man_file); + return 0; + } + if (*man_file == '\0') + found = 1; + else + found = CAN_ACCESS (man_file, R_OK); + if (found) { + int status; + if (prompt && do_prompt (title)) { + pipeline_free (format_cmd); + pipeline_free (decomp); + free (formatted_encoding); + return 0; + } + drop_effective_privs (); + pipeline_connect (decomp, format_cmd, (void *) 0); + pipeline_pump (decomp, format_cmd, (void *) 0); + pipeline_wait (decomp); + status = pipeline_wait (format_cmd); + regain_effective_privs (); + if (status != 0) + gripe_system (format_cmd, status); + } + } else { + int format = 1; + int status; + + /* The caller should already have checked for any + * FSSTND-style (same hierarchy) cat page that may be + * present, and we don't expect to have to update the cat + * page in that case. If by some chance we do have to update + * it, then there's no harm trying; open_cat_stream() will + * refuse gracefully if the file isn't writeable. + */ + + /* In theory we might be able to get away with saving cats + * for want_encoding, but it does change the roff device so + * perhaps that's best avoided. + */ + if (want_encoding +#ifdef TROFF_IS_GROFF + || htmlout + || gxditview +#endif + || local_man_file + || recode + || disable_cache + || no_hyphenation + || no_justification) + save_cat = false; + + if (!man_file) { + /* Stray cat. */ + assert (cat_file); + format = 0; + } else if (!cat_file) { + assert (man_file); + save_cat = false; + format = 1; + } else if (format && save_cat) { + char *cat_dir; + char *tmp; + + status = is_changed (man_file, cat_file); + format = (status == -2) || ((status & 1) == 1); + + /* don't save if we haven't a cat directory */ + cat_dir = xstrdup (cat_file); + tmp = strrchr (cat_dir, '/'); + if (tmp) + *tmp = 0; + save_cat = is_directory (cat_dir) == 1; + if (!save_cat) + debug ("cat dir %s does not exist\n", cat_dir); + free (cat_dir); + } + + if (format && (!format_cmd || !decomp)) { + assert (man_file); + /* format_cmd is NULL iff decomp is NULL; no need to + * free either of them. + */ + assert (!format_cmd); + assert (!decomp); + error (0, decomp_errno, _("can't open %s"), man_file); + return 0; + } + + /* if we're trying to read stdin via '-l -' then man_file + * will be "" which access() obviously barfs on, but all is + * well because the format_cmd will have been created to + * expect input via stdin. So we special-case this to avoid + * the bogus access() check. + */ + if (format == 1 && *man_file == '\0') + found = 1; + else + found = CAN_ACCESS + (format ? man_file : cat_file, R_OK); + + debug ("format: %d, save_cat: %d, found: %d\n", + format, (int) save_cat, found); + + if (!found) { + pipeline_free (format_cmd); + pipeline_free (decomp); + return found; + } + + if (print_where || print_where_cat) { + int printed = 0; + if (print_where && man_file) { + printf ("%s", man_file); + printed = 1; + } + if (print_where_cat && cat_file && !format) { + if (printed) + putchar (' '); + printf ("%s", cat_file); + printed = 1; + } + if (printed) + putchar ('\n'); + } else if (catman) { + if (format) { + if (!save_cat) + error (0, 0, + _("\ncannot write to " + "%s in catman mode"), + cat_file); + else + display_catman (cat_file, decomp, + format_cmd, + formatted_encoding); + } + } else if (format) { + /* no cat or out of date */ + pipeline *disp_cmd; + + if (prompt && do_prompt (title)) { + pipeline_free (format_cmd); + pipeline_free (decomp); + free (formatted_encoding); + if (local_man_file) + return 1; + else + return 0; + } + + disp_cmd = make_display_command (formatted_encoding, + title); + +#ifdef MAN_CATS + if (save_cat) { + /* save cat */ + assert (disp_cmd); /* not htmlout for now */ + format_display_and_save (decomp, + format_cmd, + disp_cmd, + cat_file, + formatted_encoding); + } else +#endif /* MAN_CATS */ + /* don't save cat */ + format_display (decomp, format_cmd, disp_cmd, + man_file); + + pipeline_free (disp_cmd); + + } else { + /* display preformatted cat */ + pipeline *disp_cmd; + pipeline *decomp_cat; + + if (prompt && do_prompt (title)) { + pipeline_free (format_cmd); + pipeline_free (decomp); + return 0; + } + + decomp_cat = decompress_open (cat_file); + if (!decomp_cat) { + error (0, errno, _("can't open %s"), cat_file); + pipeline_free (format_cmd); + pipeline_free (decomp); + return 0; + } + disp_cmd = make_display_command ("UTF-8", title); + format_display (decomp_cat, NULL, disp_cmd, man_file); + pipeline_free (disp_cmd); + pipeline_free (decomp_cat); + } + } + + free (formatted_encoding); + + pipeline_free (format_cmd); + pipeline_free (decomp); + + if (!prompt) + prompt = found; + + return found; +} + +static _Noreturn void gripe_converting_name (const char *name) +{ + error (FATAL, 0, _("Can't convert %s to cat name"), name); + abort (); /* error should have exited; help compilers prove noreturn */ +} + +/* Convert the trailing part of 'name' to be a cat page path by altering its + * extension appropriately. If fsstnd is set, also try converting the + * containing directory name from "man1" to "cat1" etc., returning NULL if + * that doesn't work. + * + * fsstnd should only be set if name is the original path of a man page + * found in a man hierarchy, not something like a symlink target or a file + * named with 'man -l'. Otherwise, a symlink to "/home/manuel/foo.1.gz" + * would be converted to "/home/catuel/foo.1.gz", which would be bad. + */ +static char *convert_name (const char *name, int fsstnd) +{ + char *to_name, *t1 = NULL; + char *t2 = NULL; + struct compression *comp; + char *namestem; + + comp = comp_info (name, 1); + if (comp) + namestem = comp->stem; + else + namestem = xstrdup (name); + +#ifdef COMP_CAT + /* TODO: BSD layout requires .0. */ + to_name = xasprintf ("%s.%s", namestem, COMPRESS_EXT); +#else /* !COMP_CAT */ + to_name = xstrdup (namestem); +#endif /* COMP_CAT */ + free (namestem); + + if (fsstnd) { + t1 = strrchr (to_name, '/'); + if (!t1) + gripe_converting_name (name); + *t1 = '\0'; + + t2 = strrchr (to_name, '/'); + if (!t2) + gripe_converting_name (name); + ++t2; + *t1 = '/'; + + if (STRNEQ (t2, "man", 3)) { + /* If the second-last component starts with "man", + * replace "man" with "cat". + */ + *t2 = 'c'; + *(t2 + 2) = 't'; + } else { + free (to_name); + debug ("couldn't convert %s to FSSTND cat file\n", + name); + return NULL; + } + } + + debug ("converted %s to %s\n", name, to_name); + + return to_name; +} + +static char *find_cat_file (const char *path, const char *original, + const char *man_file) +{ + size_t path_len = strlen (path); + char *cat_file, *cat_path; + int status; + + /* Try the FSSTND way first, namely a cat page in the same hierarchy + * as the original path to the man page. We don't create these + * unless no alternate cat hierarchy is available, but will use them + * if they happen to exist already and have the same timestamp as + * the corresponding man page. (In practice I'm betting that this + * means we'll hardly ever use them at all except for user + * hierarchies; but compatibility, eh?) + */ + cat_file = convert_name (original, 1); + if (cat_file) { + status = is_changed (original, cat_file); + if (status != -2 && (!(status & 1)) == 1) { + debug ("found valid FSSTND cat file %s\n", cat_file); + return cat_file; + } + free (cat_file); + } + + /* Otherwise, find the cat page we actually want to use or create, + * taking any alternate cat hierarchy into account. If the original + * path and man_file differ (i.e. original was a symlink or .so + * link), try the link target and then the source. + */ + if (!STREQ (man_file, original)) { + global_manpath = is_global_mandir (man_file); + cat_path = get_catpath + (man_file, global_manpath ? SYSTEM_CAT : USER_CAT); + + if (cat_path) { + cat_file = convert_name (cat_path, 0); + free (cat_path); + } else if (STRNEQ (man_file, path, path_len) && + man_file[path_len] == '/') + cat_file = convert_name (man_file, 1); + else + cat_file = NULL; + + if (cat_file) { + char *cat_dir = xstrdup (cat_file); + char *tmp = strrchr (cat_dir, '/'); + if (tmp) + *tmp = 0; + if (is_directory (cat_dir)) { + debug ("will try cat file %s\n", cat_file); + free (cat_dir); + return cat_file; + } else + debug ("cat dir %s does not exist\n", cat_dir); + free (cat_dir); + } else + debug ("no cat path for %s\n", man_file); + } + + global_manpath = is_global_mandir (original); + cat_path = get_catpath + (original, global_manpath ? SYSTEM_CAT : USER_CAT); + + if (cat_path) { + cat_file = convert_name (cat_path, 0); + free (cat_path); + } else + cat_file = convert_name (original, 1); + + if (cat_file) + debug ("will try cat file %s\n", cat_file); + else + debug ("no cat path for %s\n", original); + + return cat_file; +} + +static int get_ult_flags (char from_db, char id) +{ + if (!from_db) + return ult_flags; + else if (id == ULT_MAN) + /* Checking .so links is expensive, as we have to open the + * file. Therefore, if the database lists it as ULT_MAN, + * that's good enough for us and we won't recheck that. This + * does mean that if a page changes from ULT_MAN to SO_MAN + * then you might get duplicates until mandb is next run, + * but that isn't a big deal. + */ + return ult_flags & ~SO_LINK; + else + return ult_flags; +} + +/* Is this candidate substantially a duplicate of a previous one? + * Returns true if so, otherwise false. + */ +static bool duplicate_candidates (struct candidate *left, + struct candidate *right) +{ + const char *slash1, *slash2; + struct locale_bits bits1, bits2; + bool ret; + + if (left->ult && right->ult && STREQ (left->ult, right->ult)) + return true; /* same ultimate source file */ + + if (!STREQ (left->source->name, right->source->name) || + !STREQ (left->source->sec, right->source->sec) || + !STREQ (left->source->ext, right->source->ext)) + return false; /* different name/section/extension */ + + if (STREQ (left->path, right->path)) + return true; /* same path */ + + /* Figure out if we've had a sufficiently similar candidate for this + * language already. + */ + slash1 = strrchr (left->path, '/'); + slash2 = strrchr (right->path, '/'); + if (!slash1 || !slash2 || + !STRNEQ (left->path, right->path, + MAX (slash1 - left->path, slash2 - right->path))) + return false; /* different path base */ + + unpack_locale_bits (++slash1, &bits1); + unpack_locale_bits (++slash2, &bits2); + + if (!STREQ (bits1.language, bits2.language) || + !STREQ (bits1.territory, bits2.territory) || + !STREQ (bits1.modifier, bits2.modifier)) + ret = false; /* different language/territory/modifier */ + else + /* Everything seems to be the same; we can find nothing to + * choose between them. + */ + ret = true; + + free_locale_bits (&bits1); + free_locale_bits (&bits2); + return ret; +} + +static int compare_candidates (const struct candidate *left, + const struct candidate *right) +{ + const struct mandata *lsource = left->source, *rsource = right->source; + int cmp; + const char *slash1, *slash2; + + /* If one candidate matches the requested name exactly, sort it + * first. This makes --ignore-case behave more sensibly. + */ + /* name is never NULL here, see add_candidate() */ + if (STREQ (lsource->name, left->req_name)) { + if (!STREQ (rsource->name, right->req_name)) + return -1; + } else { + if (STREQ (rsource->name, right->req_name)) + return 1; + } + + /* Compare pure sections first, then ids, then extensions. + * Rationale: whatis refs get the same section and extension as + * their source, but may be supplanted by a real page with a + * slightly different extension, possibly in another hierarchy (!); + * see Debian bug #204249 for the gory details. + * + * Any extension spelt out in full in section_list effectively + * becomes a pure section; this allows extensions to be selectively + * moved out of order with respect to their parent sections. + */ + if (strcmp (lsource->ext, rsource->ext)) { + size_t index_left, index_right; + + /* If the user asked for an explicit section, sort exact + * matches first. + */ + if (section) { + if (STREQ (lsource->ext, section)) { + if (!STREQ (rsource->ext, section)) + return -1; + } else { + if (STREQ (rsource->ext, section)) + return 1; + } + } + + /* Find out whether lsource->ext is ahead of rsource->ext in + * section_list. Sections missing from section_list are + * sorted to the end. + */ + index_left = gl_list_indexof (section_list, lsource->ext); + if (index_left == (size_t) -1 && strlen (lsource->ext) > 1) { + char *sec_left = xstrndup (lsource->ext, 1); + index_left = gl_list_indexof (section_list, sec_left); + free (sec_left); + if (index_left == (size_t) -1) + index_left = gl_list_size (section_list); + } + index_right = gl_list_indexof (section_list, rsource->ext); + if (index_right == (size_t) -1 && strlen (rsource->ext) > 1) { + char *sec_right = xstrndup (rsource->ext, 1); + index_right = gl_list_indexof (section_list, + sec_right); + free (sec_right); + if (index_right == (size_t) -1) + index_right = gl_list_size (section_list); + } + if (index_left < index_right) + return -1; + else if (index_left > index_right) + return 1; + + cmp = strcmp (lsource->sec, rsource->sec); + if (cmp) + return cmp; + } + + /* ULT_MAN comes first, etc. Consider SO_MAN equivalent to ULT_MAN. */ + cmp = compare_ids (lsource->id, rsource->id, 1); + if (cmp) + return cmp; + + /* The order in section_list has already been compared above. For + * everything not mentioned explicitly there, we just compare + * lexically. + */ + cmp = strcmp (lsource->ext, rsource->ext); + if (cmp) + return cmp; + + /* Try comparing based on language. We used to prefer to display a + * page in the user's preferred language than a page from a better + * section, but that attracted objections, so now we prefer to get + * the section right. See Debian bug #519547. + */ + slash1 = strrchr (left->path, '/'); + slash2 = strrchr (right->path, '/'); + if (slash1 && slash2) { + char *locale_copy, *p; + struct locale_bits bits1, bits2, lbits; + const char *codeset1, *codeset2; + + unpack_locale_bits (++slash1, &bits1); + unpack_locale_bits (++slash2, &bits2); + + /* We need the current locale as well. */ + locale_copy = xstrdup (internal_locale); + p = strchr (locale_copy, ':'); + if (p) + *p = '\0'; + unpack_locale_bits (locale_copy, &lbits); + free (locale_copy); + +#define COMPARE_LOCALE_ELEMENTS(elt) do { \ + /* For different elements, prefer one that matches the locale if + * possible. + */ \ + if (*lbits.elt) { \ + if (STREQ (lbits.elt, bits1.elt)) { \ + if (!STREQ (lbits.elt, bits2.elt)) { \ + cmp = -1; \ + goto out; \ + } \ + } else { \ + if (STREQ (lbits.elt, bits2.elt)) { \ + cmp = 1; \ + goto out; \ + } \ + } \ + } \ + cmp = strcmp (bits1.elt, bits2.elt); \ + if (cmp) \ + /* No help from locale; might as well sort lexically. */ \ + goto out; \ +} while (0) + + COMPARE_LOCALE_ELEMENTS (language); + COMPARE_LOCALE_ELEMENTS (territory); + COMPARE_LOCALE_ELEMENTS (modifier); + +#undef COMPARE_LOCALE_ELEMENTS + + /* Prefer UTF-8 if available. Otherwise, consider them + * equal. + */ + codeset1 = get_canonical_charset_name (bits1.codeset); + codeset2 = get_canonical_charset_name (bits2.codeset); + if (STREQ (codeset1, "UTF-8")) { + if (!STREQ (codeset2, "UTF-8")) { + cmp = -1; + goto out; + } + } else { + if (STREQ (codeset2, "UTF-8")) { + cmp = 1; + goto out; + } + } + +out: + free_locale_bits (&lbits); + free_locale_bits (&bits1); + free_locale_bits (&bits2); + if (cmp) + return cmp; + } + + /* Explicitly stabilise the sort as a last resort, so that manpath + * ordering (e.g. language-specific hierarchies) works. + */ + if (left->add_index < right->add_index) + return -1; + else if (left->add_index > right->add_index) + return 1; + else + return 0; + + return 0; +} + +static int compare_candidates_qsort (const void *l, const void *r) +{ + const struct candidate *left = *(const struct candidate **)l; + const struct candidate *right = *(const struct candidate **)r; + + return compare_candidates (left, right); +} + +static void free_candidate (struct candidate *candidate) +{ + if (candidate) + free (candidate->ult); + free (candidate); +} + +/* Add an entry to the list of candidates. */ +static int add_candidate (struct candidate **head, char from_db, char cat, + const char *req_name, const char *path, + const char *ult, struct mandata *source) +{ + struct candidate *search, *prev, *insert, *candp; + static int add_index = 0; + + if (!ult) { + const char *name; + char *filename; + + if (*source->pointer != '-') + name = source->pointer; + else if (source->name) + name = source->name; + else + name = req_name; + + filename = make_filename (path, name, source, cat ? "cat" : "man"); + if (!filename) + return 0; + ult = ult_src (filename, path, NULL, + get_ult_flags (from_db, source->id), NULL); + free (filename); + } + + debug ("candidate: %d %d %s %s %s %c %s %s %s\n", + from_db, cat, req_name, path, ult, + source->id, source->name ? source->name : "-", + source->sec, source->ext); + + if (!source->name) + source->name = xstrdup (req_name); + + candp = XMALLOC (struct candidate); + candp->req_name = req_name; + candp->from_db = from_db; + candp->cat = cat; + candp->path = path; + candp->ult = ult ? xstrdup (ult) : NULL; + candp->source = source; + candp->add_index = add_index++; + candp->next = NULL; + + /* insert will be NULL (insert at start) or a pointer to the element + * after which this element should be inserted. + */ + insert = NULL; + search = *head; + prev = NULL; + /* This search produces quadratic-time behaviour, although in + * practice it doesn't seem to be too bad at the moment since the + * run-time is dominated by calls to ult_src. In future it might be + * worth optimising this; the reason I haven't done this yet is that + * it involves quite a bit of tedious bookkeeping. A practical + * approach would be to keep two hashes, one that's just a set to + * keep track of whether candp->ult has been seen already, and one + * that keeps a list of candidates for each candp->name that could + * then be quickly checked by brute force. + */ + while (search) { + int dupcand = duplicate_candidates (candp, search); + + debug ("search: %d %d %s %s %s %c %s %s %s " + "(dup: %d)\n", + search->from_db, search->cat, search->req_name, + search->path, search->ult, search->source->id, + search->source->name ? search->source->name : "-", + search->source->sec, search->source->ext, dupcand); + + /* Check for duplicates. */ + if (dupcand) { + int cmp = compare_candidates (candp, search); + + if (cmp >= 0) { + debug ("other duplicate is at least as " + "good\n"); + free_candidate (candp); + return 0; + } else { + debug ("this duplicate is better; removing " + "old one\n"); + if (prev) { + prev->next = search->next; + free_candidate (search); + search = prev->next; + } else { + *head = search->next; + free_candidate (search); + search = *head; + } + continue; + } + } + + prev = search; + if (search->next) + search = search->next; + else + break; + } + /* Insert the new candidate at the end of the list (having had to go + * through them all looking for duplicates anyway); we'll sort it + * into place later. + */ + insert = prev; + + candp->next = insert ? insert->next : *head; + if (insert) + insert->next = candp; + else + *head = candp; + + return 1; +} + +/* Sort the entire list of candidates. */ +static void sort_candidates (struct candidate **candidates) +{ + struct candidate *cand, **allcands; + size_t count = 0, i; + + for (cand = *candidates; cand; cand = cand->next) + ++count; + + if (count == 0) + return; + + allcands = XNMALLOC (count, struct candidate *); + i = 0; + for (cand = *candidates; cand; cand = cand->next) { + assert (i < count); + allcands[i++] = cand; + } + assert (i == count); + + qsort (allcands, count, sizeof *allcands, compare_candidates_qsort); + + *candidates = cand = allcands[0]; + for (i = 1; i < count; ++i) { + cand->next = allcands[i]; + cand = cand->next; + } + cand->next = NULL; + + free (allcands); +} + +/* + * See if the preformatted man page or the source exists in the given + * section. + */ +static int try_section (const char *path, const char *sec, const char *name, + struct candidate **cand_head) +{ + int found = 0; + gl_list_t names = NULL; + const char *found_name; + char cat = 0; + int lff_opts = (match_case ? LFF_MATCHCASE : 0) | + (regex_opt ? LFF_REGEX : 0) | + (wildcard ? LFF_WILDCARD : 0); + + debug ("trying section %s with globbing\n", sec); + +#ifndef NROFF_MISSING /* #ifdef NROFF */ + /* + * Look for man page source files. + */ + + names = look_for_file (path, sec, name, 0, lff_opts); + if (!gl_list_size (names)) + /* + * No files match. + * See if there's a preformatted page around that + * we can display. + */ +#endif /* NROFF_MISSING */ + { + if (catman) + return 1; + + if (!troff && !want_encoding && !recode) { + gl_list_free (names); + names = look_for_file (path, sec, name, 1, lff_opts); + cat = 1; + } + } + + order_files (path, &names); + + GL_LIST_FOREACH_START (names, found_name) { + struct mandata *info = infoalloc (); + char *info_buffer = filename_info (found_name, info, name); + const char *ult; + int f; + + if (!info_buffer) { + free_mandata_struct (info); + continue; + } + info->addr = info_buffer; + + /* What kind of page is this? Since it's a real file, it + * must be either ULT_MAN or SO_MAN. ult_src() can tell us + * which. + */ + ult = ult_src (found_name, path, NULL, ult_flags, NULL); + if (!ult) { + /* already warned */ + debug ("try_section(): bad link %s\n", found_name); + free (info_buffer); + info->addr = NULL; + free_mandata_struct (info); + continue; + } + if (STREQ (ult, found_name)) + info->id = ULT_MAN; + else + info->id = SO_MAN; + + f = add_candidate (cand_head, CANDIDATE_FILESYSTEM, + cat, name, path, ult, info); + found += f; + /* Free info and info_buffer if they weren't added to the + * candidates. + */ + if (f == 0) { + free (info_buffer); + info->addr = NULL; + free_mandata_struct (info); + } + /* Don't free info and info_buffer here. */ + } GL_LIST_FOREACH_END (names); + + gl_list_free (names); + return found; +} + +static int display_filesystem (struct candidate *candp) +{ + char *filename = make_filename (candp->path, NULL, candp->source, + candp->cat ? "cat" : "man"); + char *title; + int found = 0; + + if (!filename) + return 0; + /* source->name is never NULL thanks to add_candidate() */ + title = xasprintf ("%s(%s)", candp->source->name, candp->source->ext); + + if (candp->cat) { + if (troff || want_encoding || recode) + goto out; + found = display (candp->path, NULL, filename, title, NULL); + } else { + const char *man_file; + char *cat_file; + + man_file = ult_src (filename, candp->path, NULL, ult_flags, + NULL); + if (man_file == NULL) + goto out; + + debug ("found ultimate source file %s\n", man_file); + lang = lang_dir (man_file); + + cat_file = find_cat_file (candp->path, filename, man_file); + found = display (candp->path, man_file, cat_file, title, NULL); + free (cat_file); + free (lang); + lang = NULL; + } + +out: + free (title); + free (filename); + return found; +} + +#ifdef MAN_DB_UPDATES +/* wrapper to dbdelete which deals with opening/closing the db */ +static void dbdelete_wrapper (const char *page, struct mandata *info) +{ + if (!catman) { + MYDBM_FILE dbf; + + dbf = MYDBM_RWOPEN (database); + if (dbf) { + if (dbdelete (dbf, page, info) == 1) + debug ("%s(%s) not in db!\n", page, info->ext); + MYDBM_CLOSE (dbf); + } + } +} +#endif /* MAN_DB_UPDATES */ + +/* This started out life as try_section, but a lot of that routine is + redundant wrt the db cache. */ +static int display_database (struct candidate *candp) +{ + int found = 0; + char *file; + const char *name; + char *title; + struct mandata *in = candp->source; + + debug ("trying a db located file.\n"); + dbprintf (in); + + /* if the pointer holds some data, this is a reference to the + real page, use that instead. */ + if (*in->pointer != '-') + name = in->pointer; + else if (in->name) + name = in->name; + else + name = candp->req_name; + + if (in->id == WHATIS_MAN || in->id == WHATIS_CAT) + debug (_("%s: relying on whatis refs is deprecated\n"), name); + + title = xasprintf ("%s(%s)", + in->name ? in->name : candp->req_name, in->ext); + +#ifndef NROFF_MISSING /* #ifdef NROFF */ + /* + * Look for man page source files. + */ + + if (in->id < STRAY_CAT) { /* There should be a src page */ + file = make_filename (candp->path, name, in, "man"); + if (file) { + const char *man_file; + char *cat_file; + + man_file = ult_src (file, candp->path, NULL, + get_ult_flags (1, in->id), NULL); + if (man_file == NULL) { + free (title); + return found; /* zero */ + } + + debug ("found ultimate source file %s\n", man_file); + lang = lang_dir (man_file); + + cat_file = find_cat_file (candp->path, file, man_file); + found += display (candp->path, man_file, cat_file, + title, in->filter); + free (cat_file); + free (lang); + lang = NULL; + free (file); + } /* else {drop through to the bottom and return 0 anyway} */ + } else + +#endif /* NROFF_MISSING */ + + if (in->id <= WHATIS_CAT) { + /* The db says we have a stray cat or whatis ref */ + + if (catman) { + free (title); + return ++found; + } + + /* show this page but force an update later to make sure + we haven't just added the new page */ + found_a_stray = 1; + + /* If explicitly asked for troff or a different encoding, + * don't show a stray cat. + */ + if (troff || want_encoding || recode) { + free (title); + return found; + } + + file = make_filename (candp->path, name, in, "cat"); + if (!file) { + char *catpath; + catpath = get_catpath (candp->path, + global_manpath ? SYSTEM_CAT + : USER_CAT); + + if (catpath && strcmp (catpath, candp->path) != 0) { + file = make_filename (catpath, name, + in, "cat"); + free (catpath); + if (!file) { + /* don't delete here, + return==0 will do that */ + free (title); + return found; /* zero */ + } + } else { + free (catpath); + free (title); + return found; /* zero */ + } + } + + found += display (candp->path, NULL, file, title, in->filter); + free (file); + } + free (title); + return found; +} + +/* test for existence, if fail: call dbdelete_wrapper, else return amount */ +static int display_database_check (struct candidate *candp) +{ + int exists = display_database (candp); + +#ifdef MAN_DB_UPDATES + if (!exists && !skip) { + debug ("dbdelete_wrapper (%s, %p)\n", + candp->req_name, candp->source); + dbdelete_wrapper (candp->req_name, candp->source); + } +#endif /* MAN_DB_UPDATES */ + + return exists; +} + +#ifdef MAN_DB_UPDATES +static int maybe_update_file (const char *manpath, const char *name, + struct mandata *info) +{ + const char *real_name; + char *file; + struct stat buf; + struct timespec file_mtime; + int status; + + if (!update) + return 0; + + /* If the pointer holds some data, then we need to look at that + * name in the filesystem instead. + */ + if (!STRNEQ (info->pointer, "-", 1)) + real_name = info->pointer; + else if (info->name) + real_name = info->name; + else + real_name = name; + + file = make_filename (manpath, real_name, info, "man"); + if (!file) + return 0; + if (lstat (file, &buf) != 0) + return 0; + file_mtime = get_stat_mtime (&buf); + if (timespec_cmp (file_mtime, info->mtime) == 0) + return 0; + + debug ("%s needs to be recached: %ld.%09ld %ld.%09ld\n", + file, + (long) info->mtime.tv_sec, (long) info->mtime.tv_nsec, + (long) file_mtime.tv_sec, (long) file_mtime.tv_nsec); + status = run_mandb (0, manpath, file); + if (status) + error (0, 0, _("mandb command failed with exit status %d"), + status); + free (file); + + return 1; +} +#endif /* MAN_DB_UPDATES */ + +/* Special return values from try_db(). */ + +#define TRY_DATABASE_OPEN_FAILED -1 + +#ifdef MAN_DB_CREATES +#define TRY_DATABASE_CREATED -2 +#endif /* MAN_DB_CREATES */ + +#ifdef MAN_DB_UPDATES +#define TRY_DATABASE_UPDATED -3 +#endif /* MAN_DB_UPDATES */ + +static void db_map_value_free (const void *value) +{ + /* The value may be NULL to indicate that opening the database at + * this location already failed. + */ + if (value) + gl_list_free ((gl_list_t) value); +} + +/* Look for a page in the database. If db not accessible, return -1, + otherwise return number of pages found. */ +static int try_db (const char *manpath, const char *sec, const char *name, + struct candidate **cand_head) +{ + gl_list_t matches; + struct mandata *loc; + char *catpath; + int found = 0; +#ifdef MAN_DB_UPDATES + bool found_stale = false; +#endif /* MAN_DB_UPDATES */ + + /* find out where our db for this manpath should be */ + + catpath = get_catpath (manpath, global_manpath ? SYSTEM_CAT : USER_CAT); + free (database); + if (catpath) { + database = mkdbname (catpath); + free (catpath); + } else + database = mkdbname (manpath); + + if (!db_map) + db_map = new_string_map (GL_HASH_MAP, db_map_value_free); + + /* If we haven't looked here already, do so now. */ + if (!gl_map_search (db_map, manpath, (const void **) &matches)) { + MYDBM_FILE dbf; + + dbf = MYDBM_RDOPEN (database); + if (dbf && dbver_rd (dbf)) { + MYDBM_CLOSE (dbf); + dbf = NULL; + } + if (dbf) { + debug ("Succeeded in opening %s O_RDONLY\n", database); + + /* if section is set, only return those that match, + otherwise NULL retrieves all available */ + if (regex_opt || wildcard) + matches = dblookup_pattern + (dbf, name, section, match_case, + regex_opt, !names_only); + else + matches = dblookup_all (dbf, name, section, + match_case); + gl_map_put (db_map, xstrdup (manpath), matches); + MYDBM_CLOSE (dbf); + dbf = NULL; +#ifdef MAN_DB_CREATES + } else if (!global_manpath) { + /* create one */ + debug ("Failed to open %s O_RDONLY\n", database); + if (run_mandb (1, manpath, NULL)) { + gl_map_put (db_map, xstrdup (manpath), NULL); + return TRY_DATABASE_OPEN_FAILED; + } + return TRY_DATABASE_CREATED; +#endif /* MAN_DB_CREATES */ + } else { + debug ("Failed to open %s O_RDONLY\n", database); + gl_map_put (db_map, xstrdup (manpath), NULL); + return TRY_DATABASE_OPEN_FAILED; + } + assert (matches != NULL); + } + + /* We already tried (and failed) to open this db before. */ + if (!matches) + return TRY_DATABASE_OPEN_FAILED; + +#ifdef MAN_DB_UPDATES + /* Check that all the entries found are up to date. If not, the + * caller should try again. + */ + GL_LIST_FOREACH_START (matches, loc) + if (STREQ (sec, loc->sec) && + (!extension || STREQ (extension, loc->ext) + || STREQ (extension, loc->ext + strlen (sec)))) + if (maybe_update_file (manpath, name, loc)) + found_stale = true; + GL_LIST_FOREACH_END (matches); + + if (found_stale) { + gl_map_remove (db_map, manpath); + return TRY_DATABASE_UPDATED; + } +#endif /* MAN_DB_UPDATES */ + + /* cycle through the mandata structures (there's usually only + 1 or 2) and see what we have w.r.t. the current section */ + GL_LIST_FOREACH_START (matches, loc) + if (STREQ (sec, loc->sec) && + (!extension || STREQ (extension, loc->ext) + || STREQ (extension, loc->ext + strlen (sec)))) + found += add_candidate (cand_head, CANDIDATE_DATABASE, + 0, name, manpath, NULL, loc); + GL_LIST_FOREACH_END (matches); + + return found; +} + +/* Try to locate the page under the specified manpath, in the desired section, + * with the supplied name. Glob if necessary. Initially search the filesystem; + * if that fails, try finding it via a db cache access. */ +static int locate_page (const char *manpath, const char *sec, const char *name, + struct candidate **candidates) +{ + int found, db_ok; + + /* sort out whether we want to treat this hierarchy as + global or user. Differences: + + global: if setuid, use privs; don't create db. + user : if setuid, drop privs; allow db creation. */ + + global_manpath = is_global_mandir (manpath); + if (!global_manpath) + drop_effective_privs (); + + debug ("searching in %s, section %s\n", manpath, sec); + + found = try_section (manpath, sec, name, candidates); + + if ((!found || findall) && !global_apropos) { + db_ok = try_db (manpath, sec, name, candidates); + +#ifdef MAN_DB_CREATES + if (db_ok == TRY_DATABASE_CREATED) + /* we created a db in the last call */ + db_ok = try_db (manpath, sec, name, candidates); +#endif /* MAN_DB_CREATES */ + +#ifdef MAN_DB_UPDATES + if (db_ok == TRY_DATABASE_UPDATED) + /* We found some outdated entries and rebuilt the + * database in the last call. If this keeps + * happening, though, give up and punt to the + * filesystem. + */ + db_ok = try_db (manpath, sec, name, candidates); +#endif /* MAN_DB_UPDATES */ + + if (db_ok > 0) /* we found/opened a db and found something */ + found += db_ok; + } + + if (!global_manpath) + regain_effective_privs (); + + return found; +} + +static int display_pages (struct candidate *candidates) +{ + struct candidate *candp; + int found = 0; + + for (candp = candidates; candp; candp = candp->next) { + global_manpath = is_global_mandir (candp->path); + if (!global_manpath) + drop_effective_privs (); + + switch (candp->from_db) { + case CANDIDATE_FILESYSTEM: + found += display_filesystem (candp); + break; + case CANDIDATE_DATABASE: + found += display_database_check (candp); + break; + default: + error (0, 0, + _("internal error: candidate type %d " + "out of range"), candp->from_db); + } + + if (!global_manpath) + regain_effective_privs (); + + if (found && !findall) + return found; + } + + return found; +} + +/* + * Search for text in all manual pages. + * + * This is not a real full-text search, but a brute-force on-demand search. + * The idea, name, and approach originate in the 'man' package, added (I + * believe) by Andries Brouwer, although the implementation is new for + * man-db and much faster due to running in-process. + * + * Conceptually, this really belongs in whatis.c, as part of apropos. + * However, the implementation in 'man' offers pages for immediate display + * on request rather than simply listing them, which is currently awkward to + * do in apropos. If we ever add support to apropos/whatis for either + * calling back to man or displaying pages directly, we should revisit this. + */ +static int grep (const char *file, const char *string, const regex_t *search) +{ + struct stat st; + pipeline *decomp; + const char *line; + int ret = 0; + + /* pipeline_start makes file open failures unconditionally fatal. + * Here, we'd rather just ignore any such files. + */ + if (stat (file, &st) < 0) + return 0; + + decomp = decompress_open (file); + if (!decomp) + return 0; + pipeline_start (decomp); + while ((line = pipeline_readline (decomp)) != NULL) { + if (regex_opt) { + if (regexec (search, line, + 0, (regmatch_t *) 0, 0) == 0) { + ret = 1; + break; + } + } else { + if (match_case ? + strstr (line, string) : + strcasestr (line, string)) { + ret = 1; + break; + } + } + } + + pipeline_free (decomp); + return ret; +} + +static int do_global_apropos_section (const char *path, const char *sec, + const char *name) +{ + int found = 0; + gl_list_t names; + const char *found_name; + regex_t search; + + global_manpath = is_global_mandir (path); + if (!global_manpath) + drop_effective_privs (); + + debug ("searching in %s, section %s\n", path, sec); + + names = look_for_file (path, sec, "*", 0, LFF_WILDCARD); + + if (regex_opt) + xregcomp (&search, name, + REG_EXTENDED | REG_NOSUB | + (match_case ? 0 : REG_ICASE)); + else + memset (&search, 0, sizeof search); + + order_files (path, &names); + + GL_LIST_FOREACH_START (names, found_name) { + struct mandata *info; + char *info_buffer; + char *title = NULL; + const char *man_file; + char *cat_file = NULL; + + if (!grep (found_name, name, &search)) + continue; + + info = infoalloc (); + info_buffer = filename_info (found_name, info, NULL); + if (!info_buffer) + goto next; + info->addr = info_buffer; + + title = xasprintf ("%s(%s)", strchr (info_buffer, '\0') + 1, + info->ext); + man_file = ult_src (found_name, path, NULL, ult_flags, NULL); + if (!man_file) + goto next; + lang = lang_dir (man_file); + cat_file = find_cat_file (path, found_name, man_file); + if (display (path, man_file, cat_file, title, NULL)) + found = 1; + free (lang); + lang = NULL; + +next: + free (cat_file); + free (title); + free_mandata_struct (info); + } GL_LIST_FOREACH_END (names); + + gl_list_free (names); + + if (regex_opt) + regfree (&search); + + if (!global_manpath) + regain_effective_privs (); + + return found; +} + +static int do_global_apropos (const char *name, int *found) +{ + gl_list_t my_section_list; + const char *sec; + + if (section) { + my_section_list = gl_list_create_empty (GL_ARRAY_LIST, NULL, + NULL, NULL, false); + gl_list_add_last (my_section_list, section); + } else + my_section_list = section_list; + + GL_LIST_FOREACH_START (my_section_list, sec) { + char *mp; + + GL_LIST_FOREACH_START (manpathlist, mp) + *found += do_global_apropos_section (mp, sec, name); + GL_LIST_FOREACH_END (manpathlist); + } GL_LIST_FOREACH_END (my_section_list); + + if (section) + gl_list_free (my_section_list); + + return *found ? OK : NOT_FOUND; +} + +/* Each of local_man_loop and man sometimes calls the other. */ +static int man (const char *name, int *found); + +/* man issued with `-l' option */ +static int local_man_loop (const char *argv) +{ + int exit_status = OK; + bool local_mf = local_man_file; + + drop_effective_privs (); + local_man_file = true; + if (strcmp (argv, "-") == 0) + display (NULL, "", NULL, "(stdin)", NULL); + else { + struct stat st; + + /* Check that the file exists and isn't e.g. a directory */ + if (stat (argv, &st)) { + error (0, errno, "%s", argv); + return NOT_FOUND; + } + + if (S_ISDIR (st.st_mode)) { + error (0, EISDIR, "%s", argv); + return NOT_FOUND; + } + + if (S_ISCHR (st.st_mode) || S_ISBLK (st.st_mode)) { + /* EINVAL is about the best I can do. */ + error (0, EINVAL, "%s", argv); + return NOT_FOUND; + } + + if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + /* Perhaps an executable. If its directory is on + * $PATH, then we want to look up the corresponding + * manual page in the appropriate hierarchy rather + * than displaying the executable. + */ + char *argv_dir = dir_name (argv); + int found = 0; + + if (directory_on_path (argv_dir)) { + char *argv_base = base_name (argv); + char *new_manp, *nm; + gl_list_t old_manpathlist; + + debug ("recalculating manpath for executable " + "in %s\n", argv_dir); + + new_manp = get_manpath_from_path (argv_dir, 0); + if (!new_manp || !*new_manp) { + debug ("no useful manpath for " + "executable\n"); + goto executable_out; + } + nm = locale_manpath (new_manp); + free (new_manp); + new_manp = nm; + + old_manpathlist = manpathlist; + manpathlist = create_pathlist (new_manp); + + man (argv_base, &found); + + free_pathlist (manpathlist); + manpathlist = old_manpathlist; +executable_out: + free (new_manp); + free (argv_base); + } + free (argv_dir); + + if (found) + return OK; + } + + if (exit_status == OK) { + char *argv_base = base_name (argv); + char *argv_abs; + if (argv[0] == '/') + argv_abs = xstrdup (argv); + else { + argv_abs = xgetcwd (); + if (argv_abs) + argv_abs = appendstr (argv_abs, "/", + argv, + (void *) 0); + else + argv_abs = xstrdup (argv); + } + lang = lang_dir (argv_abs); + free (argv_abs); + if (!display (NULL, argv, NULL, argv_base, NULL)) { + if (local_mf) + error (0, errno, "%s", argv); + exit_status = NOT_FOUND; + } + free (lang); + lang = NULL; + free (argv_base); + } + } + local_man_file = local_mf; + regain_effective_privs (); + return exit_status; +} + +/* + * Splits a "name[.section]" or "name(section)" into { "name", "section" }. + * Section would be NULL if not present. + * The caller is responsible for freeing *ret_name and *ret_section. + * */ +static void split_page_name (const char *page_name, + char **ret_name, + char **ret_section) +{ + char *dot, *lparen, *rparen; + + dot = strrchr (page_name, '.'); + if (dot && is_section (dot + 1)) { + *ret_name = xstrndup (page_name, dot - page_name); + *ret_section = xstrdup (dot + 1); + return; + } + + lparen = strrchr (page_name, '('); + rparen = strrchr (page_name, ')'); + if (lparen && rparen && rparen > lparen) { + char *paren_section = xstrndup + (lparen + 1, rparen - lparen - 1); + if (is_section (paren_section)) { + *ret_name = xstrndup (page_name, lparen - page_name); + *ret_section = paren_section; /* steal memory */ + return; + } + free (paren_section); + } + + *ret_name = xstrdup (page_name); + *ret_section = NULL; +} + +static void locate_page_in_manpath (const char *page_section, + const char *page_name, + struct candidate **candidates, + int *found) +{ + char *mp; + + GL_LIST_FOREACH_START (manpathlist, mp) + *found += locate_page (mp, page_section, page_name, + candidates); + GL_LIST_FOREACH_END (manpathlist); +} + +/* + * Search for manual pages. + * + * If preformatted manual pages are supported, look for the formatted + * file first, then the man page source file. If they both exist and + * the man page source file is newer, or only the source file exists, + * try to reformat it and write the results in the cat directory. If + * it is not possible to write the cat file, simply format and display + * the man file. + * + * If preformatted pages are not supported, or the troff option is + * being used, only look for the man page source file. + * + */ +static int man (const char *name, int *found) +{ + char *page_name, *page_section; + struct candidate *candidates = NULL, *cand, *candnext; + + *found = 0; + fflush (stdout); + + if (strchr (name, '/')) { + int status = local_man_loop (name); + if (status == OK) + *found = 1; + return status; + } + + if (section) + locate_page_in_manpath (section, name, &candidates, found); + else { + const char *sec; + + GL_LIST_FOREACH_START (section_list, sec) + locate_page_in_manpath (sec, name, &candidates, found); + GL_LIST_FOREACH_END (section_list); + } + + split_page_name (name, &page_name, &page_section); + + if (!*found && page_section) + locate_page_in_manpath (page_section, page_name, &candidates, + found); + + free (page_name); + free (page_section); + + sort_candidates (&candidates); + + if (*found) + *found = display_pages (candidates); + + for (cand = candidates; cand; cand = candnext) { + candnext = cand->next; + free_candidate (cand); + } + + return *found ? OK : NOT_FOUND; +} + + +static gl_list_t get_section_list (void) +{ + gl_list_t config_sections, sections; + const char *sec; + + /* Section list from configuration file, or STD_SECTIONS if it's + * empty. + */ + config_sections = get_sections (); + if (!gl_list_size (config_sections)) { + int i; + for (i = 0; std_sections[i]; ++i) + gl_list_add_last (config_sections, + xstrdup (std_sections[i])); + } + + if (colon_sep_section_list == NULL) + colon_sep_section_list = getenv ("MANSECT"); + if (colon_sep_section_list == NULL || *colon_sep_section_list == '\0') + return config_sections; + + /* Although this is documented as colon-separated, at least Solaris + * man's -s option takes a comma-separated list, so we accept that + * too for compatibility. + */ + sections = new_string_list (GL_ARRAY_LIST, true); + for (sec = strtok (colon_sep_section_list, ":,"); sec; + sec = strtok (NULL, ":,")) + gl_list_add_last (sections, xstrdup (sec)); + + if (gl_list_size (sections)) { + gl_list_free (config_sections); + return sections; + } else { + gl_list_free (sections); + return config_sections; + } +} + +/* + * Returns the first token of a libpipeline/sh-style command. See SUSv4TC2: + * 2.2 Shell Command Language: Quoting. + * + * Free the returned value. + * + * Examples: + * sh_lang_first_word ("echo 3") returns "echo" + * sh_lang_first_word ("'e ho' 3") returns "e ho" + * sh_lang_first_word ("e\\cho 3") returns "echo" + * sh_lang_first_word ("e\\\ncho 3") returns "echo" + * sh_lang_first_word ("\"echo t\" 3") returns "echo t" + * sh_lang_first_word ("\"ech\\o t\" 3") returns "ech\\o t" + * sh_lang_first_word ("\"ech\\\\o t\" 3") returns "ech\\o t" + * sh_lang_first_word ("\"ech\\\no t\" 3") returns "echo t" + * sh_lang_first_word ("\"ech\\$ t\" 3") returns "ech$ t" + * sh_lang_first_word ("\"ech\\` t\" 3") returns "ech` t" + * sh_lang_first_word ("e\"ch\"o 3") returns "echo" + * sh_lang_first_word ("e'ch'o 3") returns "echo" + */ +static char *sh_lang_first_word (const char *cmd) +{ + int i, o = 0; + char *ret = xmalloc (strlen (cmd) + 1); + + for (i = 0; cmd[i] != '\0'; i++) { + if (cmd[i] == '\\') { + /* Escape Character (Backslash) */ + i++; + if (cmd[i] == '\0') + break; + if (cmd[i] != '\n') + ret[o++] = cmd[i]; + } else if (cmd[i] == '\'') { + /* Single-Quotes */ + i++; + while (cmd[i] != '\0' && cmd[i] != '\'') + ret[o++] = cmd[i++]; + } else if (cmd[i] == '"') { + /* Double-Quotes */ + i++; + while (cmd[i] != '\0' && cmd[i] != '"') { + if (cmd[i] == '\\') { + if (cmd[i + 1] == '$' || + cmd[i + 1] == '`' || + cmd[i + 1] == '"' || + cmd[i + 1] == '\\') + ret[o++] = cmd[++i]; + else if (cmd[i + 1] == '\n') + i++; + else + ret[o++] = cmd[i]; + } else + ret[o++] = cmd[i]; + + i++; + } + } else if (cmd[i] == '\t' || cmd[i] == ' ' || cmd[i] == '\n' || + cmd[i] == '#') + break; + else + ret[o++] = cmd[i]; + } + + ret[o] = '\0'; + + return ret; +} + +int main (int argc, char *argv[]) +{ + int argc_env, exit_status = OK; + char **argv_env; + const char *tmp; + + set_program_name (argv[0]); + + init_debug (); + pipeline_install_post_fork (pop_all_cleanups); + sandbox = sandbox_init (); + + umask (022); + init_locale (); + + internal_locale = setlocale (LC_MESSAGES, NULL); + /* Use LANGUAGE only when LC_MESSAGES locale category is + * neither "C" nor "POSIX". */ + if (internal_locale && strcmp (internal_locale, "C") && + strcmp (internal_locale, "POSIX")) + multiple_locale = getenv ("LANGUAGE"); + internal_locale = xstrdup (internal_locale ? internal_locale : "C"); + + xstdopen (); + +/* export argv, it might be needed when invoking the vendor supplied browser */ +#if defined _AIX || defined __sgi + global_argv = argv; +#endif + +#ifdef TROFF_IS_GROFF + /* used in --help, so initialise early */ + if (!html_pager) + init_html_pager (); +#endif /* TROFF_IS_GROFF */ + +#ifdef NROFF_WARNINGS + roff_warnings = new_string_list (GL_ARRAY_LIST, true); +#endif /* NROFF_WARNINGS */ + + /* First of all, find out if $MANOPT is set. If so, put it in + *argv[] format for argp to play with. */ + argv_env = manopt_to_env (&argc_env); + if (argv_env) + if (argp_parse (&argp, argc_env, argv_env, ARGP_NO_ARGS, 0, 0)) + exit (FAIL); + + /* parse the actual program args */ + if (argp_parse (&argp, argc, argv, ARGP_NO_ARGS, &first_arg, 0)) + exit (FAIL); + + /* record who we are and drop effective privs for later use */ + init_security (); + + read_config_file (local_man_file || user_config_file); + + /* if the user wants whatis or apropos, give it to them... */ + if (external) + do_extern (argc, argv); + + get_term (); /* stores terminal settings */ + + /* close this locale and reinitialise if a new locale was + issued as an argument or in $MANOPT */ + if (locale) { + free (internal_locale); + internal_locale = setlocale (LC_ALL, locale); + if (internal_locale) + internal_locale = xstrdup (internal_locale); + else + internal_locale = xstrdup (locale); + + debug ("main(): locale = %s, internal_locale = %s\n", + locale, internal_locale); + if (internal_locale) { + setenv ("LANGUAGE", internal_locale, 1); + locale_changed (); + multiple_locale = NULL; + } + } + +#ifdef TROFF_IS_GROFF + if (htmlout) + pager = html_pager; +#endif /* TROFF_IS_GROFF */ + + if (pager == NULL) + pager = getenv ("MANPAGER"); + if (pager == NULL) + pager = getenv ("PAGER"); + if (pager == NULL) + pager = get_def_user ("pager", NULL); + if (pager == NULL) { + char *pager_program = sh_lang_first_word (PAGER); + if (pathsearch_executable (pager_program)) + pager = PAGER; + else + pager = ""; + free (pager_program); + } + if (*pager == '\0') + pager = get_def_user ("cat", CAT); + + if (prompt_string == NULL) + prompt_string = getenv ("MANLESS"); + + if (prompt_string == NULL) +#ifdef LESS_PROMPT + prompt_string = LESS_PROMPT; +#else + prompt_string = _( + " Manual page " MAN_PN + " ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):" + "?pB %pB\\%.. " + "(press h for help or q to quit)"); +#endif + + /* Restore and save $LESS in $MAN_ORIG_LESS so that recursive uses + * of man work as expected. + */ + less = getenv ("MAN_ORIG_LESS"); + if (less == NULL) + less = getenv ("LESS"); + setenv ("MAN_ORIG_LESS", less ? less : "", 1); + + debug ("using %s as pager\n", pager); + + if (first_arg == argc) { + if (print_where) { + manp = get_manpath (""); + printf ("%s\n", manp); + exit (OK); + } else { + free (internal_locale); + gripe_no_name (NULL); + } + } + + section_list = get_section_list (); + + if (manp == NULL) { + char *mp = get_manpath (alt_system_name); + manp = locale_manpath (mp); + free (mp); + } else + free (get_manpath (NULL)); + + manpathlist = create_pathlist (manp); + + /* man issued with `-l' option */ + if (local_man_file) { + while (first_arg < argc) { + exit_status = local_man_loop (argv[first_arg]); + ++first_arg; + } + free (internal_locale); + exit (exit_status); + } + + /* finished manpath processing, regain privs */ + regain_effective_privs (); + +#ifdef MAN_DB_UPDATES + /* If `-u', do it now. */ + if (update) { + int status = run_mandb (0, NULL, NULL); + if (status) + error (0, 0, + _("mandb command failed with exit status %d"), + status); + } +#endif /* MAN_DB_UPDATES */ + + while (first_arg < argc) { + int status = OK; + int found = 0; + static int maybe_section = 0; + const char *nextarg = argv[first_arg++]; + + /* + * See if this argument is a valid section name. If not, + * is_section returns NULL. + */ + if (!catman) { + tmp = is_section (nextarg); + if (tmp) { + section = tmp; + debug ("\nsection: %s\n", section); + maybe_section = 1; + } + } + + if (maybe_section) { + if (first_arg < argc) + /* e.g. 'man 3perl Shell' */ + nextarg = argv[first_arg++]; + else + /* e.g. 'man 9wm' */ + section = NULL; + /* ... but leave maybe_section set so we can + * tell later that this happened. + */ + } + + /* this is where we actually start looking for the man page */ + skip = 0; + if (global_apropos) + status = do_global_apropos (nextarg, &found); + else { + bool found_subpage = false; + if (subpages && first_arg < argc) { + char *subname = xasprintf ( + "%s-%s", nextarg, argv[first_arg]); + status = man (subname, &found); + free (subname); + if (status == OK) { + found_subpage = true; + ++first_arg; + } + } + if (!found_subpage && subpages && first_arg < argc) { + char *subname = xasprintf ( + "%s_%s", nextarg, argv[first_arg]); + status = man (subname, &found); + free (subname); + if (status == OK) { + found_subpage = true; + ++first_arg; + } + } + if (!found_subpage) + status = man (nextarg, &found); + } + + /* clean out the cache of database lookups for each man page */ + if (db_map) { + gl_map_free (db_map); + db_map = NULL; + } + + if (section && maybe_section) { + if (status != OK && !catman) { + /* Maybe the section wasn't a section after + * all? e.g. 'man 9wm fvwm'. + */ + bool found_subpage = false; + debug ("\nRetrying section %s as name\n", + section); + tmp = section; + section = NULL; + if (subpages) { + char *subname = xasprintf ( + "%s-%s", tmp, nextarg); + status = man (subname, &found); + free (subname); + if (status == OK) { + found_subpage = true; + ++first_arg; + } + } + if (!found_subpage) + status = man (tmp, &found); + if (db_map) { + gl_map_free (db_map); + db_map = NULL; + } + /* ... but don't gripe about it if it doesn't + * work! + */ + if (status == OK) { + /* It was a name after all, so arrange + * to try the next page again with a + * null section. + */ + nextarg = tmp; + --first_arg; + } else + /* No go, it really was a section. */ + section = tmp; + } + } + + if (status != OK && !catman) { + if (!skip) { + exit_status = status; + if (exit_status == NOT_FOUND) { + if (!section && maybe_section && + CTYPE (isdigit, nextarg[0])) + gripe_no_name (nextarg); + else + gripe_no_man (nextarg, section); + } + } + } else { + debug ("\nFound %d man pages\n", found); + if (catman) { + printf ("%s", nextarg); + if (section) + printf ("(%s)", section); + if (first_arg != argc) + fputs (", ", stdout); + else + fputs (".\n", stdout); + } + } + + maybe_section = 0; + + chkr_garbage_detector (); + } + if (db_map) { + gl_map_free (db_map); + db_map = NULL; + } + + drop_effective_privs (); + + free (database); + gl_list_free (section_list); + free_pathlist (manpathlist); + free (internal_locale); + sandbox_free (sandbox); + exit (exit_status); +} diff --git a/src/man_db.conf.in b/src/man_db.conf.in new file mode 100644 index 0000000..2942000 --- /dev/null +++ b/src/man_db.conf.in @@ -0,0 +1,132 @@ +# @config_file_basename@ +# +# This file is used by the man-db package to configure the man and cat paths. +# It is also used to provide a manpath for those without one by examining +# their PATH environment variable. For details see the manpath(5) man page. +# +# Lines beginning with `#' are comments and are ignored. Any combination of +# tabs or spaces may be used as `whitespace' separators. +# +# There are three mappings allowed in this file: +# -------------------------------------------------------- +# MANDATORY_MANPATH manpath_element +# MANPATH_MAP path_element manpath_element +# MANDB_MAP global_manpath [relative_catpath] +#--------------------------------------------------------- +# every automatically generated MANPATH includes these fields +# +#MANDATORY_MANPATH /usr/src/pvm3/man +# +MANDATORY_MANPATH /usr/man +MANDATORY_MANPATH /usr/share/man +MANDATORY_MANPATH /usr/local/share/man +#--------------------------------------------------------- +# set up PATH to MANPATH mapping +# ie. what man tree holds man pages for what binary directory. +# +# *PATH* -> *MANPATH* +# +MANPATH_MAP /bin /usr/share/man +MANPATH_MAP /usr/bin /usr/share/man +MANPATH_MAP /sbin /usr/share/man +MANPATH_MAP /usr/sbin /usr/share/man +MANPATH_MAP /usr/local/bin /usr/local/man +MANPATH_MAP /usr/local/bin /usr/local/share/man +MANPATH_MAP /usr/local/sbin /usr/local/man +MANPATH_MAP /usr/local/sbin /usr/local/share/man +MANPATH_MAP /usr/X11R6/bin /usr/X11R6/man +MANPATH_MAP /usr/bin/X11 /usr/X11R6/man +MANPATH_MAP /usr/games /usr/share/man +MANPATH_MAP /opt/bin /opt/man +MANPATH_MAP /opt/sbin /opt/man +#--------------------------------------------------------- +# For a manpath element to be treated as a system manpath (as most of those +# above should normally be), it must be mentioned below. Each line may have +# an optional extra string indicating the catpath associated with the +# manpath. If no catpath string is used, the catpath will default to the +# given manpath. +# +# You *must* provide all system manpaths, including manpaths for alternate +# operating systems, locale specific manpaths, and combinations of both, if +# they exist, otherwise the permissions of the user running man/mandb will +# be used to manipulate the manual pages. Also, mandb will not initialise +# the database cache for any manpaths not mentioned below unless explicitly +# requested to do so. +# +# In a per-user configuration file, this directive only controls the +# location of catpaths and the creation of database caches; it has no effect +# on privileges. +# +# Any manpaths that are subdirectories of other manpaths must be mentioned +# *before* the containing manpath. E.g. /usr/man/preformat must be listed +# before /usr/man. +# +# *MANPATH* -> *CATPATH* +# +MANDB_MAP /usr/man /var/cache/man/fsstnd +MANDB_MAP /usr/share/man /var/cache/man +MANDB_MAP /usr/local/man /var/cache/man/oldlocal +MANDB_MAP /usr/local/share/man /var/cache/man/local +MANDB_MAP /usr/X11R6/man /var/cache/man/X11R6 +MANDB_MAP /opt/man /var/cache/man/opt +MANDB_MAP /snap/man /var/cache/man/snap +# +#--------------------------------------------------------- +# Program definitions. These are commented out by default as the value +# of the definition is already the default. To change: uncomment a +# definition and modify it. +# +#DEFINE pager @pager@ +#DEFINE cat @cat@ +#DEFINE tr @tr@ '\255\267\264\327' '\055\157\047\170' +#DEFINE grep @grep@ +#DEFINE troff @troff@ +#DEFINE nroff @nroff@ +#DEFINE eqn @eqn@ +#DEFINE neqn @neqn@ +#DEFINE tbl @tbl@ +#DEFINE col @col@ +#DEFINE vgrind @vgrind@ +#DEFINE refer @refer@ +#DEFINE grap @grap@ +#DEFINE pic @pic@ +# +#DEFINE compressor @compressor@ +#--------------------------------------------------------- +# Misc definitions: same as program definitions above. +# +#DEFINE whatis_grep_flags -i +#DEFINE apropos_grep_flags -iEw +#DEFINE apropos_regex_grep_flags -iE +#--------------------------------------------------------- +# Section names. Manual sections will be searched in the order listed here; +# the default is 1, n, l, 8, 3, 0, 2, 5, 4, 9, 6, 7. Multiple SECTION +# directives may be given for clarity, and will be concatenated together in +# the expected way. +# If a particular extension is not in this list (say, 1mh), it will be +# displayed with the rest of the section it belongs to. The effect of this +# is that you only need to explicitly list extensions if you want to force a +# particular order. Sections with extensions should usually be adjacent to +# their main section (e.g. "1 1mh 8 ..."). +# +SECTION @sections@ +# +#--------------------------------------------------------- +# Range of terminal widths permitted when displaying cat pages. If the +# terminal falls outside this range, cat pages will not be created (if +# missing) or displayed. +# +#MINCATWIDTH 80 +#MAXCATWIDTH 80 +# +# If CATWIDTH is set to a non-zero number, cat pages will always be +# formatted for a terminal of the given width, regardless of the width of +# the terminal actually being used. This should generally be within the +# range set by MINCATWIDTH and MAXCATWIDTH. +# +#CATWIDTH 0 +# +#--------------------------------------------------------- +# Flags. +# NOCACHE keeps man from creating cat pages. +#NOCACHE diff --git a/src/manconv.c b/src/manconv.c new file mode 100644 index 0000000..b9ac8d3 --- /dev/null +++ b/src/manconv.c @@ -0,0 +1,370 @@ +/* + * manconv.c: convert manual page from one encoding to another + * + * Copyright (C) 2007, 2008, 2009, 2010, 2012 Colin Watson. + * Based loosely on parts of glibc's iconv_prog.c, which is: + * Copyright (C) 1998-2004, 2005, 2006, 2007 Free Software Foundation, Inc. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This program arose during a discussion with Adam Borowski. See: + * https://lists.debian.org/debian-mentors/2007/09/msg00245.html + * It behaves like iconv, but allows multiple source encodings and + * attempts to guess the first one that works. An Emacs-style + * "-*- coding:" declaration overrides this. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <stdint.h> +#include <unistd.h> + +#ifdef HAVE_ICONV +# include <iconv.h> +#endif /* HAVE_ICONV */ + +#include "argp.h" +#include "gl_list.h" + +#include "gettext.h" +#include <locale.h> +#define _(String) gettext (String) + +#include "manconfig.h" + +#include "error.h" +#include "pipeline.h" +#include "encodings.h" +#include "glcontainers.h" + +#include "manconv.h" + +#ifdef HAVE_ICONV + +/* When converting text containing an invalid multibyte sequence to + * UTF-8//IGNORE, GNU libc's iconv returns EILSEQ but sets *inbuf to the end + * of the input buffer. I'm not sure whether this is a bug or not (it seems + * to contradict the documentation), but work around it anyway by recoding + * to UTF-8 so that we can accurately position the error. + */ +static off_t locate_error (const char *try_from_code, + const char *input, size_t input_size, + char *utf8, size_t utf8_size) +{ + iconv_t cd_utf8_strict; + char *inptr = (char *) input, *utf8ptr = utf8; + size_t inleft = input_size, utf8left = utf8_size; + size_t n; + off_t ret; + + cd_utf8_strict = iconv_open ("UTF-8", try_from_code); + if (cd_utf8_strict == (iconv_t) -1) { + error (0, errno, "iconv_open (\"UTF-8\", \"%s\")", + try_from_code); + return 0; + } + + n = iconv (cd_utf8_strict, (ICONV_CONST char **) &inptr, &inleft, + &utf8ptr, &utf8left); + if (n == (size_t) -1) + ret = inptr - input; + else + ret = 0; + + iconv_close (cd_utf8_strict); + + return ret; +} + +static int try_iconv (pipeline *p, const char *try_from_code, const char *to, + bool last) +{ + char *try_to_code = xstrdup (to); + static const size_t buf_size = 65536; + size_t input_size = buf_size; + off_t input_pos = 0; + const char *input; + static char *utf8 = NULL, *output = NULL; + size_t utf8left = 0; + iconv_t cd_utf8, cd = NULL; + bool to_utf8 = STREQ (try_to_code, "UTF-8") || + STRNEQ (try_to_code, "UTF-8//", 7); + const char *utf8_target = last ? "UTF-8//IGNORE" : "UTF-8"; + bool ignore_errors = (strstr (try_to_code, "//IGNORE") != NULL); + int ret = 0; + + debug ("trying encoding %s -> %s\n", try_from_code, try_to_code); + + cd_utf8 = iconv_open (utf8_target, try_from_code); + if (cd_utf8 == (iconv_t) -1) { + error (0, errno, "iconv_open (\"%s\", \"%s\")", + utf8_target, try_from_code); + free (try_to_code); + return -1; + } + + if (!to_utf8) { + cd = iconv_open (try_to_code, "UTF-8"); + if (cd == (iconv_t) -1) { + error (0, errno, "iconv_open (\"%s\", \"UTF-8\")", + try_to_code); + free (try_to_code); + return -1; + } + } + + input = pipeline_peek (p, &input_size); + if (input_size < buf_size) { + /* End of file, error, or just a short read? Repeat until we + * have either a full buffer or EOF/error. + */ + while (input_size < buf_size) { + size_t old_input_size = input_size; + input_size = buf_size; + input = pipeline_peek (p, &input_size); + if (input_size == old_input_size) + break; + } + } + + if (!utf8) + utf8 = xmalloc (buf_size); + if (!output) + output = xmalloc (buf_size); + + while (input_size || utf8left) { + int handle_iconv_errors = 0; + char *inptr = (char *) input, *utf8ptr = utf8; + char *outptr = output; + size_t inleft = input_size, outleft; + size_t n, n2 = -1; + + if (!utf8left) { + /* First, convert the text to UTF-8. By assumption, + * all validly-encoded text can be converted to + * UTF-8 assuming that we picked the correct + * encoding. Any errors at this stage are due to + * selecting an incorrect encoding, or due to + * misencoded source text. + */ + utf8left = buf_size; + n = iconv (cd_utf8, (ICONV_CONST char **) &inptr, + &inleft, &utf8ptr, &utf8left); + utf8left = buf_size - utf8left; + + /* If we need to try the next encoding, do that + * before writing anything. + */ + if (!last && n == (size_t) -1 && + (errno == EILSEQ || + (errno == EINVAL && input_size < buf_size))) { + ret = -1; + break; + } else if (n == (size_t) -1) + handle_iconv_errors = errno; + } + + /* If the target encoding is UTF-8 (the common case), then + * we can just write out what we've got. Otherwise, we need + * to convert to the target encoding. Any errors at this + * stage are due to characters that are not representable in + * the target encoding. + */ + if (handle_iconv_errors) + /* Fall back to error handling below. If we have + * anything to write out, we'll do it next time + * round the loop. + */ + ; + else if (to_utf8) { + memcpy (output, utf8, utf8left); + outptr += utf8left; + outleft = utf8left; + utf8left = 0; + } else if (utf8left) { + outptr = output; + outleft = buf_size; + utf8ptr = utf8; + n2 = iconv ( + cd, (ICONV_CONST char **) &utf8ptr, &utf8left, + &outptr, &outleft); + outleft = buf_size - outleft; + if (n2 == (size_t) -1) + handle_iconv_errors = errno; + + if (n2 == (size_t) -1 && + errno == EILSEQ && ignore_errors) + errno = 0; + } else + /* We appear to have converted some input text, but + * not actually ended up with any UTF-8 text. This + * is odd. However, we can at least continue round + * the loop, skip the input text we converted, and + * then we should get a different result next time. + */ + outptr = output; + + if (outptr != output) { + /* We have something to write out. */ + int errno_save = errno; + size_t w; + w = fwrite (output, 1, outleft, stdout); + if (w < (size_t) outleft || ferror (stdout)) + error (FATAL, 0, _("can't write to " + "standard output")); + errno = errno_save; + } + + if (!to_utf8 && n2 != (size_t) -1) { + /* All the UTF-8 text we have so far was processed. + * For state-dependent character sets we have to + * flush the state now. + */ + outptr = output; + outleft = buf_size; + iconv (cd, NULL, NULL, &outptr, &outleft); + outleft = buf_size - outleft; + + if (outptr != output) { + /* We have something to write out. */ + int errno_save = errno; + size_t w; + w = fwrite (output, 1, outleft, stdout); + if (w < (size_t) outleft || ferror (stdout)) + error (FATAL, 0, _("can't write to " + "standard output")); + errno = errno_save; + } + } else if (handle_iconv_errors) { + intmax_t error_pos; + + if (handle_iconv_errors == EILSEQ && !ignore_errors) { + if (!quiet) { + error_pos = input_pos + locate_error ( + try_from_code, + input, input_size, + utf8, buf_size); + error (0, handle_iconv_errors, + "byte %jd: iconv", error_pos); + } + exit (FATAL); + } else if (handle_iconv_errors == EINVAL && + input_size < buf_size) { + if (!quiet) { + error_pos = input_pos + locate_error ( + try_from_code, + input, input_size, + utf8, buf_size); + error (FATAL, 0, "byte %jd: %s", + error_pos, + _("iconv: incomplete character " + "at end of buffer")); + } + exit (FATAL); + } + } + + if (inptr != input) { + pipeline_peek_skip (p, input_size - inleft); + input_pos += input_size - inleft; + } + + /* Unless we have some UTF-8 text left (which will only + * happen if the output encoding is more verbose than UTF-8, + * so is unlikely for legacy encodings), we need to fetch + * more input text now. + */ + if (!utf8left) { + input_size = buf_size; + input = pipeline_peek (p, &input_size); + while (input_size < buf_size) { + size_t old_input_size = input_size; + input_size = buf_size; + input = pipeline_peek (p, &input_size); + if (input_size == old_input_size) + break; + } + } + } + + if (!to_utf8) + iconv_close (cd); + iconv_close (cd_utf8); + free (try_to_code); + + return ret; +} + +void manconv (pipeline *p, gl_list_t from, const char *to) +{ + char *pp_encoding; + const char *try_from_code; + char *plain_to, *modified_pp_line = NULL; + + plain_to = xstrndup (to, strcspn (to, "/")); + pp_encoding = check_preprocessor_encoding + (p, plain_to, &modified_pp_line); + if (pp_encoding) { + if (modified_pp_line) { + size_t len = strlen (modified_pp_line); + pipeline_readline (p); + if (fwrite (modified_pp_line, 1, len, stdout) < len || + ferror (stdout)) + error (FATAL, 0, + _("can't write to standard output")); + free (modified_pp_line); + } + try_iconv (p, pp_encoding, to, 1); + free (pp_encoding); + } else { + GL_LIST_FOREACH_START (from, try_from_code) { + bool last = !gl_list_next_node (from, from_node); + if (try_iconv (p, try_from_code, to, last) == 0) + break; + } GL_LIST_FOREACH_END (from); + } + + free (plain_to); +} + +#else /* !HAVE_ICONV */ + +/* If we don't have iconv, there isn't much we can do; just pass everything + * through unchanged. + */ +void manconv (pipeline *p, gl_list_t from _GL_UNUSED, + const char *to _GL_UNUSED) +{ + for (;;) { + size_t len = 4096; + const char *buffer = pipeline_read (p, &len); + if (len == 0) + break; + if (fwrite (buffer, 1, len, stdout) < len || ferror (stdout)) + error (FATAL, 0, _("can't write to standard output")); + } +} + +#endif /* HAVE_ICONV */ diff --git a/src/manconv.h b/src/manconv.h new file mode 100644 index 0000000..d5dbd5e --- /dev/null +++ b/src/manconv.h @@ -0,0 +1,25 @@ +/* + * manconv.h: interface to converting manual page from one encoding to another + * + * Copyright (C) 2008 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gl_list.h" + +void manconv (struct pipeline *p, gl_list_t from, const char *to); diff --git a/src/manconv_client.c b/src/manconv_client.c new file mode 100644 index 0000000..0c41bec --- /dev/null +++ b/src/manconv_client.c @@ -0,0 +1,145 @@ +/* + * manconv_client.c: use manconv in a pipeline + * + * Copyright (C) 2007, 2008, 2010 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "gl_array_list.h" +#include "gl_xlist.h" +#include "idpriv.h" +#include "xvasprintf.h" + +#include "manconfig.h" + +#include "pipeline.h" +#include "decompress.h" +#include "glcontainers.h" +#include "sandbox.h" +#include "security.h" + +#include "manconv.h" +#include "manconv_client.h" + +extern man_sandbox *sandbox; + +struct manconv_codes { + gl_list_t from; + char *to; +}; + +static void manconv_stdin (void *data) +{ + struct manconv_codes *codes = data; + pipeline *p; + + p = decompress_fdopen (dup (STDIN_FILENO)); + pipeline_start (p); + manconv (p, codes->from, codes->to); + pipeline_wait (p); + pipeline_free (p); +} + +static void manconv_pre_exec (void *data) +{ + /* We must drop privileges before loading the sandbox, since our + * seccomp filter doesn't allow setresuid and friends. + */ + drop_privs (NULL); + sandbox_load (data); +} + +static void free_manconv_codes (void *data) +{ + struct manconv_codes *codes = data; + + gl_list_free (codes->from); + free (codes->to); + free (codes); +} + +void add_manconv (pipeline *p, const char *source, const char *target) +{ + struct manconv_codes *codes; + char *name; + pipecmd *cmd; + + if (STREQ (source, "UTF-8") && STREQ (target, "UTF-8")) + return; + + codes = xmalloc (sizeof *codes); + /* informational only; no shell quoting concerns */ + name = xasprintf ("%s -f ", MANCONV); + codes->from = new_string_list (GL_ARRAY_LIST, true); + if (STREQ (source, "UTF-8")) { + gl_list_add_last (codes->from, xstrdup (source)); + name = appendstr (name, source, (void *) 0); + } else { + gl_list_add_last (codes->from, xstrdup ("UTF-8")); + gl_list_add_last (codes->from, xstrdup (source)); + name = appendstr (name, "UTF-8:", source, (void *) 0); + } + codes->to = xasprintf ("%s//IGNORE", target); + /* informational only; no shell quoting concerns */ + name = appendstr (name, " -t ", codes->to, (void *) 0); + if (quiet >= 2) + name = appendstr (name, " -q", (void *) 0); + + /* iconv_open may not work correctly in setuid processes; in GNU + * libc, gconv modules may be linked against other gconv modules and + * rely on RPATH $ORIGIN to load those modules from the correct + * path, but $ORIGIN is disabled in setuid processes. It is + * impossible to reset libc's idea of setuidness without creating a + * whole new process image. Therefore, if the calling process is + * setuid, we must drop privileges and execute manconv. + */ + if (running_setuid ()) { + gl_list_t from = codes->from; + const char *from_code; + char *sources = NULL; + + cmd = pipecmd_new_args (MANCONV, "-f", (void *) 0); + GL_LIST_FOREACH_START (from, from_code) { + sources = appendstr (sources, from_code, (void *) 0); + if (gl_list_next_node (from, from_node)) + sources = appendstr (sources, ":", (void *) 0); + } GL_LIST_FOREACH_END (from); + pipecmd_arg (cmd, sources); + free (sources); + pipecmd_args (cmd, "-t", codes->to, (void *) 0); + if (quiet >= 2) + pipecmd_arg (cmd, "-q"); + pipecmd_pre_exec (cmd, manconv_pre_exec, sandbox_free, + sandbox); + free_manconv_codes (codes); + } else { + cmd = pipecmd_new_function (name, &manconv_stdin, + &free_manconv_codes, codes); + pipecmd_pre_exec (cmd, sandbox_load, sandbox_free, sandbox); + } + free (name); + pipeline_command (p, cmd); +} diff --git a/src/manconv_client.h b/src/manconv_client.h new file mode 100644 index 0000000..5380eef --- /dev/null +++ b/src/manconv_client.h @@ -0,0 +1,23 @@ +/* + * manconv_client.h: interface to using manconv in a pipeline + * + * Copyright (C) 2007 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +void add_manconv (struct pipeline *p, const char *source, const char *target); diff --git a/src/manconv_main.c b/src/manconv_main.c new file mode 100644 index 0000000..da27373 --- /dev/null +++ b/src/manconv_main.c @@ -0,0 +1,199 @@ +/* + * manconv_main.c: convert manual page from one encoding to another + * + * Copyright (C) 2007, 2008 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "argp.h" +#include "gl_array_list.h" +#include "gl_xlist.h" +#include "progname.h" + +#include "gettext.h" +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "cleanup.h" +#include "encodings.h" +#include "error.h" +#include "pipeline.h" +#include "decompress.h" +#include "glcontainers.h" +#include "sandbox.h" + +#include "manconv.h" + +int quiet = 0; +man_sandbox *sandbox; + +static const char *from_codes; +static char *to_code; +static gl_list_t from_code; +static const char *filename; + +static gl_list_t split_codes (const char *codestr) +{ + char *codestrtok, *codestrtok_ptr; + char *tok; + gl_list_t codelist = new_string_list (GL_ARRAY_LIST, true); + + if (!codestr) + return codelist; + + codestrtok = xstrdup (codestr); + codestrtok_ptr = codestrtok; + + for (tok = strsep (&codestrtok_ptr, ":"); tok; + tok = strsep (&codestrtok_ptr, ":")) { + if (!*tok) + continue; /* ignore empty fields */ + gl_list_add_last (codelist, xstrdup (tok)); + } + + free (codestrtok); + + return codelist; +} + +const char *argp_program_version = "manconv " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("[-f CODE[:...]] -t CODE [FILENAME]"); + +static struct argp_option options[] = { + { "from-code", 'f', N_("CODE[:...]"), + 0, N_("possible encodings of original text") }, + { "to-code", 't', N_("CODE"), 0, N_("encoding for output") }, + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "quiet", 'q', 0, 0, N_("produce fewer warnings") }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'f': + from_codes = arg; + return 0; + case 't': + to_code = xstrdup (arg); + if (!strstr (to_code, "//")) + to_code = appendstr (to_code, "//TRANSLIT", + (void *) 0); + return 0; + case 'd': + debug_level = true; + return 0; + case 'q': + quiet = 1; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP); + break; + case ARGP_KEY_ARG: + if (filename) + argp_usage (state); + filename = arg; + return 0; + case ARGP_KEY_SUCCESS: + if (!to_code) + argp_error (state, + _("must specify an output " + "encoding")); + from_code = split_codes (from_codes); + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp = { options, parse_opt, args_doc }; + +int main (int argc, char *argv[]) +{ + pipeline *p; + + set_program_name (argv[0]); + + init_debug (); + pipeline_install_post_fork (pop_all_cleanups); + sandbox = sandbox_init (); + init_locale (); + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + + if (filename) { + p = decompress_open (filename); + if (!p) + error (FAIL, 0, _("can't open %s"), filename); + } else + p = decompress_fdopen (dup (STDIN_FILENO)); + pipeline_start (p); + + if (!gl_list_size (from_code)) { + char *lang, *page_encoding; + + /* Note that we don't need to explicitly check the page's + * preprocessor encoding here, as the manconv function will + * do that itself and override the requested input encoding + * with it if it finds one. + */ + lang = lang_dir (filename); + page_encoding = get_page_encoding (lang); + if (STREQ (page_encoding, "UTF-8")) { + /* Steal memory. */ + gl_list_add_last (from_code, page_encoding); + debug ("guessed input encoding %s for %s\n", + page_encoding, filename); + } else { + gl_list_add_last (from_code, xstrdup ("UTF-8")); + /* Steal memory. */ + gl_list_add_last (from_code, page_encoding); + debug ("guessed input encodings UTF-8:%s for %s\n", + page_encoding, filename); + } + + free (lang); + } + + manconv (p, from_code, to_code); + + free (to_code); + gl_list_free (from_code); + + pipeline_wait (p); + + sandbox_free (sandbox); + + return 0; +} diff --git a/src/mandb.c b/src/mandb.c new file mode 100644 index 0000000..7a63d8d --- /dev/null +++ b/src/mandb.c @@ -0,0 +1,951 @@ +/* + * mandb.c: used to create and initialise global man database. + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011, + * 2012 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Tue Apr 26 12:56:44 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + * + * CJW: Security fixes. Make --test work. Purge old database entries. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> /* for chmod() */ +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <signal.h> + +#ifdef MAN_OWNER +# include <pwd.h> +#endif /* MAN_OWNER */ + +#include "argp.h" +#include "dirname.h" +#include "gl_hash_map.h" +#include "gl_list.h" +#include "gl_xmap.h" +#include "progname.h" +#include "stat-time.h" +#include "timespec.h" +#include "utimens.h" +#include "xgetcwd.h" +#include "xvasprintf.h" + +#include "gettext.h" +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "error.h" +#include "cleanup.h" +#include "glcontainers.h" +#include "pipeline.h" +#include "sandbox.h" +#include "security.h" + +#include "mydbm.h" + +#include "check_mandirs.h" +#include "filenames.h" +#include "manp.h" + +int quiet = 1; +extern bool opt_test; /* don't update db */ +char *manp; +extern char *extension; /* for globbing.c */ +extern bool force_rescan; /* for check_mandirs.c */ +static char *single_filename = NULL; +extern char *user_config_file; /* for manp.c */ +#ifdef MAN_OWNER +struct passwd *man_owner; +#endif +man_sandbox *sandbox; + +static int purged = 0; +static int strays = 0; + +static bool check_for_strays = true; +static bool purge = true; +static bool user; +static bool create; +static const char *arg_manp; + +struct tried_catdirs_entry { + char *manpath; + int seen; +}; + +const char *argp_program_version = "mandb " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("[MANPATH]"); + +static struct argp_option options[] = { + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "quiet", 'q', 0, 0, N_("work quietly, except for 'bogus' warning") }, + { "no-straycats", 's', 0, 0, N_("don't look for or add stray cats to the dbs") }, + { "no-purge", 'p', 0, 0, N_("don't purge obsolete entries from the dbs") }, + { "user-db", 'u', 0, 0, N_("produce user databases only") }, + { "create", 'c', 0, 0, N_("create dbs from scratch, rather than updating") }, + { "test", 't', 0, 0, N_("check manual pages for correctness") }, + { "filename", 'f', N_("FILENAME"), 0, N_("update just the entry for this filename") }, + { "config-file", 'C', N_("FILE"), 0, N_("use this user configuration file") }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + static int quiet_temp = 0; + + switch (key) { + case 'd': + debug_level = true; + return 0; + case 'q': + ++quiet_temp; + return 0; + case 's': + check_for_strays = false; + return 0; + case 'p': + purge = false; + return 0; + case 'u': + user = true; + return 0; + case 'c': + create = true; + purge = false; + return 0; + case 't': + opt_test = true; + return 0; + case 'f': + single_filename = arg; + create = false; + purge = false; + check_for_strays = false; + return 0; + case 'C': + user_config_file = arg; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP); + break; + case ARGP_KEY_ARG: + if (arg_manp) + argp_usage (state); + arg_manp = arg; + return 0; + case ARGP_KEY_SUCCESS: + if (opt_test && !debug_level) + quiet = 1; + else if (quiet_temp == 1) + quiet = 2; + else + quiet = quiet_temp; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp = { options, parse_opt, args_doc }; + +struct dbpaths { +#ifdef NDBM +# ifdef BERKELEY_DB + char *dbfile; + char *tmpdbfile; +# else /* !BERKELEY_DB NDBM */ + char *dirfile; + char *pagfile; + char *tmpdirfile; + char *tmppagfile; +# endif /* BERKELEY_DB */ +#else /* !NDBM */ + char *xfile; + char *xtmpfile; +#endif /* NDBM */ +}; + +#ifdef MAN_OWNER +extern uid_t ruid; +extern uid_t euid; +#endif /* MAN_OWNER */ + +static gl_list_t manpathlist; + +extern int pages; + +/* remove() with error checking */ +static void check_remove (const char *path) +{ + if (remove (path) == -1 && errno != ENOENT) + error (0, errno, _("can't remove %s"), path); +} + +/* rename() with error checking */ +static void check_rename (const char *from, const char *to) +{ + if (rename (from, to) == -1 && errno != ENOENT) { + error (0, errno, _("can't rename %s to %s"), from, to); + check_remove (from); + } +} + +/* chmod() with error checking */ +static void check_chmod (const char *path, mode_t mode) +{ + if (chmod (path, mode) == -1) { + error (0, errno, _("can't chmod %s"), path); + check_remove (path); + } +} + +/* CPhipps 2000/02/24 - Copy a file. */ +static int xcopy (const char *from, const char *to) +{ + FILE *ifp, *ofp; + struct stat st; + struct timespec times[2]; + static const size_t buf_size = 32 * 1024; + char *buf; + int ret = 0; + + ifp = fopen (from, "r"); + if (!ifp) { + ret = -errno; + if (errno == ENOENT) + return 0; + error (0, errno, "fopen %s", from); + return ret; + } + + if (fstat (fileno (ifp), &st) >= 0) { + times[0] = get_stat_atime (&st); + times[1] = get_stat_mtime (&st); + } else { + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = 0; + times[1].tv_nsec = UTIME_OMIT; + } + + ofp = fopen (to, "w"); + if (!ofp) { + ret = -errno; + error (0, errno, "fopen %s", to); + fclose (ifp); + return ret; + } + + buf = xmalloc (buf_size); + while (!feof (ifp) && !ferror (ifp)) { + size_t in = fread (buf, 1, buf_size, ifp); + if (in > 0) { + if (fwrite (buf, 1, in, ofp) == 0 && ferror (ofp)) { + ret = -errno; + error (0, errno, _("can't write to %s"), to); + break; + } + } else if (ferror (ifp)) { + ret = -errno; + error (0, errno, _("can't read from %s"), from); + break; + } + } + free (buf); + + fclose (ifp); + fclose (ofp); + + if (ret < 0) + check_remove (to); + else { + check_chmod (to, DBMODE); + utimens (to, times); + } + + return ret; +} + +/* rename and chmod the database */ +static void finish_up (struct dbpaths *dbpaths) +{ +#ifdef NDBM +# ifdef BERKELEY_DB + check_rename (dbpaths->tmpdbfile, dbpaths->dbfile); + check_chmod (dbpaths->dbfile, DBMODE); + free (dbpaths->tmpdbfile); + dbpaths->tmpdbfile = NULL; +# else /* not BERKELEY_DB */ + check_rename (dbpaths->tmpdirfile, dbpaths->dirfile); + check_chmod (dbpaths->dirfile, DBMODE); + check_rename (dbpaths->tmppagfile, dbpaths->pagfile); + check_chmod (dbpaths->pagfile, DBMODE); + free (dbpaths->tmpdirfile); + free (dbpaths->tmppagfile); + dbpaths->tmpdirfile = dbpaths->tmppagfile = NULL; +# endif /* BERKELEY_DB */ +#else /* not NDBM */ + check_rename (dbpaths->xtmpfile, dbpaths->xfile); + check_chmod (dbpaths->xfile, DBMODE); + free (dbpaths->xtmpfile); + dbpaths->xtmpfile = NULL; +#endif /* NDBM */ +} + +#ifdef MAN_OWNER +/* change the owner of global man databases */ +static void do_chown (struct dbpaths *dbpaths) +{ +# ifdef NDBM +# ifdef BERKELEY_DB + chown_if_possible (dbpaths->dbfile); +# else /* not BERKELEY_DB */ + chown_if_possible (dbpaths->dirfile); + chown_if_possible (dbpaths->pagfile); +# endif /* BERKELEY_DB */ +# else /* not NDBM */ + chown_if_possible (dbpaths->xfile); +# endif /* NDBM */ +} +#endif /* MAN_OWNER */ + +/* Update a single file in an existing database. */ +static int update_one_file (const char *database, + const char *manpath, const char *filename) +{ + MYDBM_FILE dbf; + + dbf = MYDBM_RWOPEN (database); + if (dbf) { + struct mandata info; + char *manpage; + + memset (&info, 0, sizeof (struct mandata)); + manpage = filename_info (filename, &info, ""); + if (info.name) { + dbdelete (dbf, info.name, &info); + purge_pointers (dbf, info.name); + free (info.name); + } + free (manpage); + + test_manfile (dbf, filename, manpath); + } + MYDBM_CLOSE (dbf); + + return 1; +} + +/* dont actually create any dbs, just do an update */ +static int update_db_wrapper (const char *database, + const char *manpath, const char *catpath) +{ + int amount; + + if (single_filename) + return update_one_file (database, manpath, single_filename); + + amount = update_db (database, manpath, catpath); + if (amount != EOF) + return amount; + + return create_db (database, manpath, catpath); +} + +/* remove incomplete databases */ +static void cleanup_sigsafe (void *arg) +{ + struct dbpaths *dbpaths = arg; + +#ifdef NDBM +# ifdef BERKELEY_DB + if (dbpaths->tmpdbfile) + unlink (dbpaths->tmpdbfile); +# else /* !BERKELEY_DB NDBM */ + if (dbpaths->tmpdirfile) + unlink (dbpaths->tmpdirfile); + if (dbpaths->tmppagfile) + unlink (dbpaths->tmppagfile); +# endif /* BERKELEY_DB NDBM */ +#else /* !NDBM */ + if (dbpaths->xtmpfile) + unlink (dbpaths->xtmpfile); +#endif /* NDBM */ +} + +/* free database names */ +static void cleanup (void *arg) +{ + struct dbpaths *dbpaths = arg; + +#ifdef NDBM +# ifdef BERKELEY_DB + free (dbpaths->dbfile); + free (dbpaths->tmpdbfile); + dbpaths->dbfile = dbpaths->tmpdbfile = NULL; +# else /* !BERKELEY_DB NDBM */ + free (dbpaths->dirfile); + free (dbpaths->pagfile); + free (dbpaths->tmpdirfile); + free (dbpaths->tmppagfile); + dbpaths->dirfile = dbpaths->pagfile = NULL; + dbpaths->tmpdirfile = dbpaths->tmppagfile = NULL; +# endif /* BERKELEY_DB NDBM */ +#else /* !NDBM */ + free (dbpaths->xfile); + free (dbpaths->xtmpfile); + dbpaths->xfile = dbpaths->xtmpfile = NULL; +#endif /* NDBM */ + free (dbpaths); +} + +#define CACHEDIR_TAG \ + "Signature: 8a477f597d28d172789f06886806bc55\n" \ + "# This file is a cache directory tag created by man-db.\n" \ + "# For information about cache directory tags, see:\n" \ + "#\thttp://www.brynosaurus.com/cachedir/\n" + +/* sort out the database names */ +static int mandb (struct dbpaths *dbpaths, + const char *catpath, const char *manpath, + bool global_manpath) +{ + char *database; + int amount; + char *dbname; + int should_create; + + dbname = mkdbname (catpath); + database = xasprintf ("%s/%d", catpath, getpid ()); + + if (!quiet) + printf (_("Processing manual pages under %s...\n"), manpath); + + if (!STREQ (catpath, manpath)) { + char *cachedir_tag; + int fd; + int cachedir_tag_exists = 0; + + cachedir_tag = xasprintf ("%s/CACHEDIR.TAG", catpath); + fd = open (cachedir_tag, O_RDONLY); + if (fd < 0) { + FILE *cachedir_tag_file; + + if (errno != ENOENT) + check_remove (cachedir_tag); + cachedir_tag_file = fopen (cachedir_tag, "w"); + if (cachedir_tag_file) { + cachedir_tag_exists = 1; + fputs (CACHEDIR_TAG, cachedir_tag_file); + fclose (cachedir_tag_file); + } + } else { + cachedir_tag_exists = 1; + close (fd); + } + if (cachedir_tag_exists) { + if (global_manpath) + chown_if_possible (cachedir_tag); + check_chmod (cachedir_tag, DBMODE); + } + free (cachedir_tag); + } + + should_create = (create || force_rescan || opt_test); + +#ifdef NDBM +# ifdef BERKELEY_DB + dbpaths->dbfile = xasprintf ("%s.db", dbname); + free (dbname); + dbpaths->tmpdbfile = xasprintf ("%s.db", database); + if (!should_create) { + if (xcopy (dbpaths->dbfile, dbpaths->tmpdbfile) < 0) + should_create = 1; + } + if (should_create) { + check_remove (dbpaths->tmpdbfile); + amount = create_db (database, manpath, catpath); + if (amount < 0) + goto out; + } else { + amount = update_db_wrapper (database, manpath, catpath); + if (amount < 0) + goto out; + } +# else /* !BERKELEY_DB NDBM */ + dbpaths->dirfile = xasprintf ("%s.dir", dbname); + dbpaths->pagfile = xasprintf ("%s.pag", dbname); + free (dbname); + dbpaths->tmpdirfile = xasprintf ("%s.dir", database); + dbpaths->tmppagfile = xasprintf ("%s.pag", database); + if (!should_create) { + if (xcopy (dbpaths->dirfile, dbpaths->tmpdirfile) < 0 || + xcopy (dbpaths->pagfile, dbpaths->tmppagfile) < 0) + should_create = 1; + } + if (should_create) { + check_remove (dbpaths->tmpdirfile); + check_remove (dbpaths->tmppagfile); + amount = create_db (database, manpath, catpath); + if (amount < 0) + goto out; + } else { + amount = update_db_wrapper (database, manpath, catpath); + if (amount < 0) + goto out; + } +# endif /* BERKELEY_DB NDBM */ +#else /* !NDBM */ + dbpaths->xfile = dbname; /* steal memory */ + dbpaths->xtmpfile = xstrdup (database); + if (!should_create) { + if (xcopy (dbpaths->xfile, dbpaths->xtmpfile) < 0) + should_create = 1; + } + if (should_create) { + check_remove (dbpaths->xtmpfile); + amount = create_db (database, manpath, catpath); + if (amount < 0) + goto out; + } else { + amount = update_db_wrapper (database, manpath, catpath); + if (amount < 0) + goto out; + } +#endif /* NDBM */ + +out: + free (database); + return amount; +} + +static int process_manpath (const char *manpath, bool global_manpath, + gl_map_t tried_catdirs) +{ + char *catpath; + struct tried_catdirs_entry *tried; + struct stat st; + int run_mandb = 0; + struct dbpaths *dbpaths = NULL; + int amount = 0; + + if (global_manpath) { /* system db */ + catpath = get_catpath (manpath, SYSTEM_CAT); + assert (catpath); + } else { /* user db */ + catpath = get_catpath (manpath, USER_CAT); + if (!catpath) + catpath = xstrdup (manpath); + } + tried = XMALLOC (struct tried_catdirs_entry); + tried->manpath = xstrdup (manpath); + tried->seen = 0; + gl_map_put (tried_catdirs, xstrdup (catpath), tried); + + if (stat (manpath, &st) < 0 || !S_ISDIR (st.st_mode)) + goto out; + tried->seen = 1; + + if (single_filename) { + /* The file might be in a per-locale subdirectory that we + * aren't processing right now. + */ + char *manpath_prefix = xasprintf ("%s/man", manpath); + if (STRNEQ (manpath_prefix, single_filename, + strlen (manpath_prefix))) + run_mandb = 1; + free (manpath_prefix); + } else + run_mandb = 1; + + force_rescan = false; + if (purge) { + char *database = mkdbname (catpath); + purged += purge_missing (database, + manpath, catpath, run_mandb); + free (database); + } + + dbpaths = XZALLOC (struct dbpaths); + push_cleanup (cleanup, dbpaths, 0); + push_cleanup (cleanup_sigsafe, dbpaths, 1); + if (run_mandb) { + int ret = mandb (dbpaths, catpath, manpath, global_manpath); + if (ret < 0) { + amount = ret; + goto out; + } + amount += ret; + } + + if (!opt_test && amount) + finish_up (dbpaths); +#ifdef MAN_OWNER + if (global_manpath) + do_chown (dbpaths); +#endif /* MAN_OWNER */ + +out: + if (dbpaths) { + cleanup_sigsafe (dbpaths); + pop_cleanup (cleanup_sigsafe, dbpaths); + cleanup (dbpaths); + pop_cleanup (cleanup, dbpaths); + } + + if (check_for_strays && amount > 0) { + char *database = mkdbname (catpath); + strays += straycats (database, manpath); + free (database); + } + + free (catpath); + + return amount; +} + +static int is_lang_dir (const char *base) +{ + return strlen (base) >= 2 && + base[0] >= 'a' && base[0] <= 'z' && + base[1] >= 'a' && base[1] <= 'z' && + (!base[2] || base[2] < 'a' || base[2] > 'z'); +} + +static void tried_catdirs_free (const void *value) +{ + struct tried_catdirs_entry *tried = + (struct tried_catdirs_entry *) value; + + free (tried->manpath); + free (tried); +} + +static void purge_catdir (gl_map_t tried_catdirs, const char *path) +{ + struct stat st; + + if (stat (path, &st) == 0 && S_ISDIR (st.st_mode) && + !gl_map_get (tried_catdirs, path)) { + if (!quiet) + printf (_("Removing obsolete cat directory %s...\n"), + path); + remove_directory (path, 1); + } +} + +static void purge_catsubdirs (const char *manpath, const char *catpath) +{ + DIR *dir; + struct dirent *ent; + struct stat st; + + dir = opendir (catpath); + if (!dir) + return; + while ((ent = readdir (dir)) != NULL) { + char *mandir, *catdir; + + if (!STRNEQ (ent->d_name, "cat", 3)) + continue; + + mandir = xasprintf ("%s/man%s", manpath, ent->d_name + 3); + catdir = xasprintf ("%s/%s", catpath, ent->d_name); + + if (stat (mandir, &st) != 0 && errno == ENOENT) { + if (!quiet) + printf (_("Removing obsolete cat directory " + "%s...\n"), catdir); + remove_directory (catdir, 1); + } + + free (catdir); + free (mandir); + } + closedir (dir); +} + +/* Remove catdirs whose corresponding mandirs no longer exist. For safety, + * in case people set catdirs to silly locations, we only do this for the + * cat* and NLS subdirectories of catdirs, but not for the top-level catdir + * itself (which might contain other data, or which might be difficult for + * mandb to recreate with the proper permissions). + * + * We need to be careful here to avoid removing catdirs just because we + * happened not to inspect the corresponding mandir this time round. If a + * mandir was inspected and turned out not to exist, then its catdir is + * clearly fair game for removal of NLS subdirectories. These must match + * the usual NLS pattern (two lower-case letters followed by nothing or a + * non-letter). + */ +static void purge_catdirs (gl_map_t tried_catdirs) +{ + const char *path; + struct tried_catdirs_entry *tried; + + GL_MAP_FOREACH_START (tried_catdirs, path, tried) { + char *base; + DIR *dir; + struct dirent *subdirent; + + base = base_name (path); + if (is_lang_dir (base)) { + /* expect to check this as a subdirectory later */ + free (base); + continue; + } + free (base); + + purge_catsubdirs (tried->manpath, path); + + dir = opendir (path); + if (!dir) + continue; + while ((subdirent = readdir (dir)) != NULL) { + char *subdirpath; + const struct tried_catdirs_entry *subtried; + + if (STREQ (subdirent->d_name, ".") || + STREQ (subdirent->d_name, "..")) + continue; + if (STRNEQ (subdirent->d_name, "cat", 3)) + continue; + if (!is_lang_dir (subdirent->d_name)) + continue; + + subdirpath = xasprintf ("%s/%s", path, + subdirent->d_name); + + subtried = gl_map_get (tried_catdirs, subdirpath); + if (subtried && subtried->seen) { + debug ("Seen mandir for %s; not deleting\n", + subdirpath); + /* However, we may still need to purge cat* + * subdirectories. + */ + purge_catsubdirs (subtried->manpath, + subdirpath); + } else + purge_catdir (tried_catdirs, subdirpath); + + free (subdirpath); + } + closedir (dir); + } GL_MAP_FOREACH_END (tried_catdirs); +} + +int main (int argc, char *argv[]) +{ + char *sys_manp; + int amount = 0; + char *mp; + gl_map_t tried_catdirs; +#ifdef SIGPIPE + struct sigaction sa; +#endif /* SIGPIPE */ + +#ifdef __profile__ + char *cwd; +#endif /* __profile__ */ + + set_program_name (argv[0]); + + init_debug (); + pipeline_install_post_fork (pop_all_cleanups); + sandbox = sandbox_init (); + init_locale (); + +#ifdef SIGPIPE + /* Reset SIGPIPE to its default disposition. Too many broken pieces + * of software (Python << 3.2, gnome-session, etc.) spawn child + * processes with SIGPIPE ignored, and this produces noise in cron + * mail. + */ + memset (&sa, 0, sizeof sa); + sa.sa_handler = SIG_DFL; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGPIPE, &sa, NULL); +#endif /* SIGPIPE */ + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + +#ifdef __profile__ + cwd = xgetcwd (); + if (!cwd) { + cwd = xmalloc (1); + cwd[0] = '\0'; + } +#endif /* __profile__ */ + + /* record who we are and drop effective privs for later use */ + init_security (); + +#ifdef MAN_OWNER + man_owner = get_man_owner (); + if (!user && euid != 0 && euid != man_owner->pw_uid) + user = true; +#endif /* MAN_OWNER */ + + read_config_file (user); + + /* This is required for get_catpath(), regardless */ + manp = get_manpath (NULL); /* also calls read_config_file() */ + + /* pick up the system manpath or use the supplied one */ + if (arg_manp) { + free (manp); + manp = xstrdup (arg_manp); + } else if (!user) { + sys_manp = get_mandb_manpath (); + if (sys_manp) { + free (manp); + manp = sys_manp; + } else + error (0, 0, + _("warning: no MANDB_MAP directives in %s, " + "using your manpath"), + CONFIG_FILE); + } + + /* get the manpath as a list of pointers */ + manpathlist = create_pathlist (manp); + + /* finished manpath processing, regain privs */ + regain_effective_privs (); + + tried_catdirs = new_string_map (GL_HASH_MAP, tried_catdirs_free); + + GL_LIST_FOREACH_START (manpathlist, mp) { + bool global_manpath = is_global_mandir (mp); + int ret; + DIR *dir; + struct dirent *subdirent; + + if (global_manpath) { /* system db */ + if (user) + continue; + } else { /* user db */ + drop_effective_privs (); + } + + ret = process_manpath (mp, global_manpath, tried_catdirs); + if (ret < 0) + exit (FATAL); + amount += ret; + + dir = opendir (mp); + if (!dir) { + error (0, errno, _("can't search directory %s"), mp); + goto next_manpath; + } + + while ((subdirent = readdir (dir)) != NULL) { + char *subdirpath; + + /* Look for per-locale subdirectories. */ + if (STREQ (subdirent->d_name, ".") || + STREQ (subdirent->d_name, "..")) + continue; + if (STRNEQ (subdirent->d_name, "man", 3)) + continue; + + subdirpath = xasprintf ("%s/%s", mp, + subdirent->d_name); + ret = process_manpath (subdirpath, global_manpath, + tried_catdirs); + if (ret < 0) + exit (FATAL); + amount += ret; + free (subdirpath); + } + + closedir (dir); + +next_manpath: + if (!global_manpath) + regain_effective_privs (); + + chkr_garbage_detector (); + } GL_LIST_FOREACH_END (manpathlist); + + purge_catdirs (tried_catdirs); + gl_map_free (tried_catdirs); + + if (!quiet) { + printf (ngettext ("%d man subdirectory contained newer " + "manual pages.\n", + "%d man subdirectories contained newer " + "manual pages.\n", amount), + amount); + printf (ngettext ("%d manual page was added.\n", + "%d manual pages were added.\n", pages), + pages); + if (check_for_strays) + printf (ngettext ("%d stray cat was added.\n", + "%d stray cats were added.\n", + strays), + strays); + if (purge) + printf (ngettext ("%d old database entry was " + "purged.\n", + "%d old database entries were " + "purged.\n", purged), + purged); + } + +#ifdef __profile__ + /* For profiling */ + if (cwd[0]) + chdir (cwd); +#endif /* __profile__ */ + + free_pathlist (manpathlist); + free (manp); + if (create && !amount) { + const char *must_create; + if (!quiet) + fprintf (stderr, _("No databases created.")); + must_create = getenv ("MAN_MUST_CREATE"); + if (must_create && STREQ (must_create, "1")) + exit (FAIL); + } + sandbox_free (sandbox); + exit (OK); +} diff --git a/src/manp.c b/src/manp.c new file mode 100644 index 0000000..5441339 --- /dev/null +++ b/src/manp.c @@ -0,0 +1,1385 @@ +/* + * manp.c: Manpath calculations + * + * Copyright (C) 1990, 1991 John W. Eaton. + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011, + * 2012 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * John W. Eaton + * jwe@che.utexas.edu + * Department of Chemical Engineering + * The University of Texas at Austin + * Austin, Texas 78712 + * + * unpack_locale_bits is derived from _nl_explode_name in libintl: + * Copyright (C) 1995-1998, 2000-2001, 2003, 2005 Free Software Foundation, + * Inc. + * Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. + * This was originally LGPL v2 or later, but I (Colin Watson) hereby + * exercise my option under section 3 of LGPL v2 to distribute it under the + * GPL v2 or later as above. + * + * Wed May 4 15:44:47 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk): changes + * to get_dirlist() and manpath(). + * + * This whole code segment is unfriendly and could do with a complete + * overhaul. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <assert.h> +#include <errno.h> +#include <dirent.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "canonicalize.h" +#include "gl_array_list.h" +#include "gl_linkedhash_list.h" +#include "gl_xlist.h" +#include "xgetcwd.h" +#include "xvasprintf.h" + +#include "gettext.h" +#define _(String) gettext (String) + +#include "manconfig.h" + +#include "error.h" +#include "cleanup.h" +#include "glcontainers.h" +#include "security.h" + +#include "manp.h" +#include "globbing.h" + +enum config_flag { + MANDATORY, + MANPATH_MAP, + MANDB_MAP, + MANDB_MAP_USER, + DEFINE, + DEFINE_USER, + SECTION, + SECTION_USER +}; + +struct config_item { + char *key; + char *cont; + enum config_flag flag; +}; + +static gl_list_t config; + +char *user_config_file = NULL; +bool disable_cache; +int min_cat_width = 80, max_cat_width = 80, cat_width = 0; + +static void add_man_subdirs (gl_list_t list, const char *p); +static char *fsstnd (const char *path); +static char *def_path (enum config_flag flag); +static void add_dir_to_list (gl_list_t list, const char *dir); +static void add_dir_to_path_list (gl_list_t list, const char *p); + + +static void config_item_free (const void *elt) +{ + /* gl_list declares the argument as const, but there doesn't seem to + * be a good reason for this. + */ + struct config_item *item = (struct config_item *) elt; + free (item->key); + free (item->cont); + free (item); +} + +static void add_config (const char *key, const char *cont, + enum config_flag flag) +{ + struct config_item *item = XMALLOC (struct config_item); + item->key = xstrdup (key); + item->cont = xstrdup (cont); + item->flag = flag; + gl_list_add_last (config, item); +} + +static const char *get_config (const char *key, enum config_flag flag) +{ + const struct config_item *item; + char *cont = NULL; + + GL_LIST_FOREACH_START (config, item) + if (flag == item->flag && STREQ (key, item->key)) { + cont = item->cont; + break; + } + GL_LIST_FOREACH_END (config); + + return cont; +} + +/* Must not return DEFINEs set in ~/.manpath. This is used to fetch + * definitions used in raised-privilege code; if in doubt, be conservative! + * + * If not setuid, this is identical to get_def_user. + */ +const char *get_def (const char *thing, const char *def) +{ + const char *config_def; + + if (!running_setuid ()) + return get_def_user (thing, def); + + config_def = get_config (thing, DEFINE); + return config_def ? config_def : def; +} + +const char *get_def_user (const char *thing, const char *def) +{ + const char *config_def = get_config (thing, DEFINE_USER); + if (!config_def) + config_def = get_config (thing, DEFINE); + return config_def ? config_def : def; +} + +static void add_sections (char *sections, int user) +{ + char *section_list = xstrdup (sections); + char *sect; + bool first = true; + + debug (" Added sections: "); + for (sect = strtok (section_list, " "); sect; + sect = strtok (NULL, " ")) { + add_config (sect, "", user ? SECTION_USER : SECTION); + if (!first) + debug (", "); + debug ("`%s'", sect); + first = false; + } + debug (".\n"); + free (section_list); +} + +gl_list_t get_sections (void) +{ + const struct config_item *item; + int length_user = 0, length = 0; + gl_list_t sections; + enum config_flag flag; + + GL_LIST_FOREACH_START (config, item) { + if (item->flag == SECTION_USER) + length_user++; + else if (item->flag == SECTION) + length++; + } GL_LIST_FOREACH_END (config); + sections = new_string_list (GL_ARRAY_LIST, true); + if (length_user) + flag = SECTION_USER; + else + flag = SECTION; + GL_LIST_FOREACH_START (config, item) + if (item->flag == flag) + gl_list_add_last (sections, xstrdup (item->key)); + GL_LIST_FOREACH_END (config); + return sections; +} + +static void add_def (const char *thing, const char *config_def, int user) +{ + add_config (thing, config_def, user ? DEFINE_USER : DEFINE); + + debug (" Defined `%s' as `%s'.\n", thing, config_def); +} + +static void add_manpath_map (const char *path, const char *mandir) +{ + if (!path || !mandir) + return; + + add_config (path, mandir, MANPATH_MAP); + + debug (" Path `%s' mapped to mandir `%s'.\n", path, mandir); +} + +static void add_mandb_map (const char *mandir, const char *catdir, int user) +{ + char *tmpcatdir; + + if (!mandir) + return; + + if (STREQ (catdir, "FSSTND")) + tmpcatdir = fsstnd (mandir); + else + tmpcatdir = xstrdup (catdir); + + if (!tmpcatdir) + return; + + add_config (mandir, tmpcatdir, user ? MANDB_MAP_USER : MANDB_MAP); + + debug (" %s mandir `%s', catdir `%s'.\n", + user ? "User" : "Global", mandir, tmpcatdir); + + free (tmpcatdir); +} + +static void add_mandatory (const char *mandir) +{ + if (!mandir) + return; + + add_config (mandir, "", MANDATORY); + + debug (" Mandatory mandir `%s'.\n", mandir); +} + +/* accept (NULL or oldpath) and new path component. return new path */ +static char *pathappend (char *oldpath, const char *appendage) +{ + assert ((!oldpath || *oldpath) && appendage); + /* Remove duplicates */ + if (oldpath) { + char *oldpathtok = xstrdup (oldpath), *tok; + char *app_dedup = xstrdup (appendage); + char *oldpathtok_ptr = oldpathtok; + for (tok = strsep (&oldpathtok_ptr, ":"); tok; + tok = strsep (&oldpathtok_ptr, ":")) { + char *search; + if (!*tok) /* ignore empty fields */ + continue; + search = strstr (app_dedup, tok); + while (search) { + char *terminator = search + strlen (tok); + if (!*terminator) { + /* End of the string, so chop here. */ + *search = 0; + while (search > app_dedup && + *--search == ':') + *search = 0; + break; + } else if (*terminator == ':') { + char *newapp; + *search = 0; + newapp = xasprintf ("%s%s", app_dedup, + terminator + 1); + free (app_dedup); + app_dedup = newapp; + } + search = strstr (terminator, tok); + } + } + free (oldpathtok); + if (!STREQ (appendage, app_dedup)) + debug ("%s:%s reduced to %s%s%s\n", + oldpath, appendage, + oldpath, *app_dedup ? ":" : "", app_dedup); + if (*app_dedup) + oldpath = appendstr (oldpath, ":", app_dedup, + (void *) 0); + free (app_dedup); + return oldpath; + } else + return xstrdup (appendage); +} + +static void gripe_reading_mp_config (const char *file) +{ + error (FAIL, 0, + _("can't make sense of the manpath configuration file %s"), + file); +} + +static void gripe_stat_file (const char *file) +{ + debug_error (_("warning: %s"), file); +} + +static void gripe_not_directory (const char *dir) +{ + if (!quiet) + error (0, 0, _("warning: %s isn't a directory"), dir); +} + +/* accept a manpath list, separated with ':', return the associated + catpath list */ +char *cat_manpath (char *manp) +{ + char *catp = NULL; + const char *path, *catdir; + + for (path = strsep (&manp, ":"); path; path = strsep (&manp, ":")) { + catdir = get_config (path, MANDB_MAP_USER); + if (!catdir) + catdir = get_config (path, MANDB_MAP); + catp = catdir ? pathappend (catp, catdir) + : pathappend (catp, path); + } + + return catp; +} + +/* Unpack a glibc-style locale into its component parts. + * + * This function was inspired by _nl_explode_name in libintl; I've rewritten + * it here with extensive modifications in order not to require libintl or + * glibc internals, because this API is more convenient for man-db, and to + * be consistent with surrounding style. I also dropped the normalised + * codeset handling, which we don't need here. + */ +void unpack_locale_bits (const char *locale, struct locale_bits *bits) +{ + const char *p, *start; + + bits->language = NULL; + bits->territory = NULL; + bits->codeset = NULL; + bits->modifier = NULL; + + /* Now we determine the single parts of the locale name. First look + * for the language. Termination symbols are '_', '.', and '@'. + */ + p = locale; + while (*p && *p != '_' && *p != '.' && *p != '@') + ++p; + if (p == locale) { + /* This does not make sense: language has to be specified. + * Use this entry as it is without exploding. Perhaps it is + * an alias. + */ + bits->language = xstrdup (locale); + goto out; + } + bits->language = xstrndup (locale, p - locale); + + if (*p == '_') { + /* Next is the territory. */ + start = ++p; + while (*p && *p != '.' && *p != '@') + ++p; + bits->territory = xstrndup (start, p - start); + } + + if (*p == '.') { + /* Next is the codeset. */ + start = ++p; + while (*p && *p != '@') + ++p; + bits->codeset = xstrndup (start, p - start); + } + + if (*p == '@') + /* Next is the modifier. */ + bits->modifier = xstrdup (++p); + +out: + if (!bits->territory) + bits->territory = xstrdup (""); + if (!bits->codeset) + bits->codeset = xstrdup (""); + if (!bits->modifier) + bits->modifier = xstrdup (""); +} + +/* Free the contents of a locale_bits structure populated by + * unpack_locale_bits. Does not free the pointer argument. + */ +void free_locale_bits (struct locale_bits *bits) +{ + free (bits->language); + free (bits->territory); + free (bits->codeset); + free (bits->modifier); +} + + +static char *get_nls_manpath (const char *manpathlist, const char *locale) +{ + struct locale_bits lbits; + char *manpath = NULL; + char *manpathlist_copy, *path, *manpathlist_ptr; + + unpack_locale_bits (locale, &lbits); + if (STREQ (lbits.language, "C") || STREQ (lbits.language, "POSIX")) { + free_locale_bits (&lbits); + return xstrdup (manpathlist); + } + + manpathlist_copy = xstrdup (manpathlist); + manpathlist_ptr = manpathlist_copy; + for (path = strsep (&manpathlist_ptr, ":"); path; + path = strsep (&manpathlist_ptr, ":")) { + DIR *mandir = opendir (path); + struct dirent *mandirent; + + if (!mandir) + continue; + + while ((mandirent = readdir (mandir)) != NULL) { + const char *name; + struct locale_bits mbits; + char *fullpath; + + name = mandirent->d_name; + if (STREQ (name, ".") || STREQ (name, "..")) + continue; + if (STRNEQ (name, "man", 3)) + continue; + fullpath = xasprintf ("%s/%s", path, name); + if (is_directory (fullpath) != 1) { + free (fullpath); + continue; + } + + unpack_locale_bits (name, &mbits); + if (STREQ (lbits.language, mbits.language) && + (!*mbits.territory || + STREQ (lbits.territory, mbits.territory)) && + (!*mbits.modifier || + STREQ (lbits.modifier, mbits.modifier))) + manpath = pathappend (manpath, fullpath); + free_locale_bits (&mbits); + free (fullpath); + } + + if (STREQ (lbits.language, "en")) + /* For English, we look in the subdirectories as + * above just in case there's something like + * en_GB.UTF-8, but it's more probable that English + * manual pages reside at the top level. + */ + manpath = pathappend (manpath, path); + + closedir (mandir); + } + free (manpathlist_copy); + + free_locale_bits (&lbits); + return manpath; +} + +char *add_nls_manpaths (const char *manpathlist, const char *locales) +{ + char *manpath = NULL; + char *locales_copy, *tok, *locales_ptr; + char *locale_manpath; + + debug ("add_nls_manpaths(): processing %s\n", manpathlist); + + if (locales == NULL || *locales == '\0') + return xstrdup (manpathlist); + + /* For each locale, we iterate over the manpath and find appropriate + * locale directories for each item. We then concatenate the results + * for all locales. In other words, LANGUAGE=fr:de and + * manpath=/usr/share/man:/usr/local/share/man could result in + * something like this list: + * + * /usr/share/man/fr + * /usr/local/share/man/fr + * /usr/share/man/de + * /usr/local/share/man/de + * /usr/share/man + * /usr/local/share/man + * + * This assumes that it's more important to have documentation in + * the preferred language than to have documentation for the correct + * object (in the case where there are different versions of a + * program in different hierarchies, for example). It is not + * entirely obvious that this is the right assumption, but on the + * other hand the other choice is not entirely obvious either. We + * tie-break on "we've always done it this way", and people can use + * 'man -a' or whatever in the occasional case where we get it + * wrong. + * + * We go to no special effort to de-duplicate directories here. + * create_pathlist will sort it out later; note that it preserves + * order in that it keeps the first of any duplicate set in its + * original position. + */ + + locales_copy = xstrdup (locales); + locales_ptr = locales_copy; + for (tok = strsep (&locales_ptr, ":"); tok; + tok = strsep (&locales_ptr, ":")) { + if (!*tok) /* ignore empty fields */ + continue; + debug ("checking for locale %s\n", tok); + + locale_manpath = get_nls_manpath (manpathlist, tok); + if (locale_manpath) { + if (manpath) + manpath = appendstr (manpath, ":", + locale_manpath, + (void *) 0); + else + manpath = xstrdup (locale_manpath); + free (locale_manpath); + } + } + free (locales_copy); + + /* Always try untranslated pages as a last resort. */ + locale_manpath = get_nls_manpath (manpathlist, "C"); + if (locale_manpath) { + if (manpath) + manpath = appendstr (manpath, ":", + locale_manpath, (void *) 0); + else + manpath = xstrdup (locale_manpath); + free (locale_manpath); + } + + return manpath; +} + +static char *add_system_manpath (const char *systems, const char *manpathlist) +{ + char *one_system; + char *manpath = NULL; + char *tmpsystems; + + if (!systems) + systems = getenv ("SYSTEM"); + + if (!systems || !*systems) + return xstrdup (manpathlist); + + /* Avoid breaking the environment. */ + tmpsystems = xstrdup (systems); + + /* For each systems component */ + + for (one_system = strtok (tmpsystems, ",:"); one_system; + one_system = strtok (NULL, ",:")) { + + /* For each manpathlist component */ + + if (!STREQ (one_system, "man")) { + const char *next, *path; + char *newdir = NULL; + for (path = manpathlist; path; path = next) { + int status; + char *element; + + next = strchr (path, ':'); + if (next) { + element = xstrndup (path, next - path); + ++next; + } else + element = xstrdup (path); + newdir = appendstr (newdir, element, "/", + one_system, (void *) 0); + free (element); + + status = is_directory (newdir); + + if (status == 0) + gripe_not_directory (newdir); + else if (status == 1) { + debug ("adding %s to manpathlist\n", + newdir); + manpath = pathappend (manpath, newdir); + } else + debug_error ("can't stat %s", newdir); + /* reset newdir */ + *newdir = '\0'; + } + free (newdir); + } else + manpath = pathappend (manpath, manpathlist); + } + free (tmpsystems); + + /* + * Thu, 21 Nov 1996 22:24:19 +0200 fpolacco@debian.org + * bug#5534 (man fails if env var SYSTEM is defined) + * with error [man: internal manpath equates to NULL] + * the reason: is_directory (newdir); returns -1 + */ + if (!manpath) { + debug ("add_system_manpath(): " + "internal manpath equates to NULL\n"); + return xstrdup (manpathlist); + } + return manpath; +} + +/* + * Always add system and locale directories to pathlist. + * If the environment variable MANPATH is set, return it. + * If the environment variable PATH is set and has a nonzero length, + * try to determine the corresponding manpath, otherwise, return the + * default manpath. + * + * The man_db.config file is used to map system wide /bin directories + * to top level man page directories. + * + * For directories which are in the user's path but not in the + * man_db.config file, see if there is a subdirectory `man' or `MAN'. + * If so, add that directory to the path. Example: user has + * $HOME/bin in his path and the directory $HOME/bin/man exists -- the + * directory $HOME/bin/man will be added to the manpath. + */ +static char *guess_manpath (const char *systems) +{ + const char *path = getenv ("PATH"); + char *manpathlist, *manpath; + + if (path == NULL || getenv ("MAN_TEST_DISABLE_PATH")) { + /* Things aren't going to work well, but hey... */ + if (path == NULL && !quiet) + error (0, 0, _("warning: $PATH not set")); + + manpathlist = def_path (MANDATORY); + } else { + if (strlen (path) == 0) { + /* Things aren't going to work well here either... */ + if (!quiet) + error (0, 0, _("warning: empty $PATH")); + + return add_system_manpath (systems, + def_path (MANDATORY)); + } + + manpathlist = get_manpath_from_path (path, 1); + } + manpath = add_system_manpath (systems, manpathlist); + free (manpathlist); + return manpath; +} + +char *get_manpath (const char *systems) +{ + char *manpathlist; + + /* need to read config file even if MANPATH set, for mandb(8) */ + read_config_file (false); + + manpathlist = getenv ("MANPATH"); + if (manpathlist && *manpathlist) { + char *system1, *system2, *guessed; + char *pos; + /* This must be it. */ + if (manpathlist[0] == ':') { + if (!quiet) + error (0, 0, + _("warning: $MANPATH set, " + "prepending %s"), + CONFIG_FILE); + system1 = add_system_manpath (systems, manpathlist); + guessed = guess_manpath (systems); + manpathlist = xasprintf ("%s%s", guessed, system1); + free (guessed); + free (system1); + } else if (manpathlist[strlen (manpathlist) - 1] == ':') { + if (!quiet) + error (0, 0, + _("warning: $MANPATH set, " + "appending %s"), + CONFIG_FILE); + system1 = add_system_manpath (systems, manpathlist); + guessed = guess_manpath (systems); + manpathlist = xasprintf ("%s%s", system1, guessed); + free (guessed); + free (system1); + } else if ((pos = strstr (manpathlist,"::"))) { + *(pos++) = '\0'; + if (!quiet) + error (0, 0, + _("warning: $MANPATH set, " + "inserting %s"), + CONFIG_FILE); + system1 = add_system_manpath (systems, manpathlist); + guessed = guess_manpath (systems); + system2 = add_system_manpath (systems, pos); + manpathlist = xasprintf ("%s:%s%s", system1, guessed, + system2); + free (system2); + free (guessed); + free (system1); + } else { + if (!quiet) + error (0, 0, + _("warning: $MANPATH set, ignoring %s"), + CONFIG_FILE); + manpathlist = add_system_manpath (systems, + manpathlist); + } + } else + manpathlist = guess_manpath (systems); + + return manpathlist; +} + +/* Parse the manpath.config file, extracting appropriate information. */ +static void add_to_dirlist (FILE *config_file, int user) +{ + char *bp; + char *buf = NULL; + size_t n = 0; + char key[512], cont[512]; + int val; + int c; + + while (getline (&buf, &n, config_file) >= 0) { + bp = buf; + + while (CTYPE (isspace, *bp)) + bp++; + + /* TODO: would like a (limited) replacement for sscanf() + * here that allocates its own memory. At that point check + * everything that sprintf()s manpath et al! + */ + if (*bp == '#' || *bp == '\0') + goto next; + else if (strncmp (bp, "NOCACHE", 7) == 0) + disable_cache = true; + else if (strncmp (bp, "NO", 2) == 0) + goto next; /* match any word starting with NO */ + else if (sscanf (bp, "MANBIN %*s") == 1) + goto next; + else if (sscanf (bp, "MANDATORY_MANPATH %511s", key) == 1) + add_mandatory (key); + else if (sscanf (bp, "MANPATH_MAP %511s %511s", + key, cont) == 2) + add_manpath_map (key, cont); + else if ((c = sscanf (bp, "MANDB_MAP %511s %511s", + key, cont)) > 0) + add_mandb_map (key, c == 2 ? cont : key, user); + else if ((c = sscanf (bp, "DEFINE %511s %511[^\n]", + key, cont)) > 0) + add_def (key, c == 2 ? cont : "", user); + else if (sscanf (bp, "SECTION %511[^\n]", cont) == 1) + add_sections (cont, user); + else if (sscanf (bp, "SECTIONS %511[^\n]", cont) == 1) + /* Since I keep getting it wrong ... */ + add_sections (cont, user); + else if (sscanf (bp, "MINCATWIDTH %d", &val) == 1) + min_cat_width = val; + else if (sscanf (bp, "MAXCATWIDTH %d", &val) == 1) + max_cat_width = val; + else if (sscanf (bp, "CATWIDTH %d", &val) == 1) + cat_width = val; + else { + error (0, 0, _("can't parse directory list `%s'"), bp); + gripe_reading_mp_config (CONFIG_FILE); + } + +next: + free (buf); + buf = NULL; + } + + free (buf); +} + +static void free_config_file (void *unused _GL_UNUSED) +{ + gl_list_free (config); +} + +void read_config_file (bool optional) +{ + static int done = 0; + char *dotmanpath = NULL; + FILE *config_file; + + if (done) + return; + + config = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL, + config_item_free, true); + push_cleanup (free_config_file, NULL, 0); + + if (user_config_file) + dotmanpath = xstrdup (user_config_file); + else { + char *home = getenv ("HOME"); + if (home) + dotmanpath = xasprintf ("%s/.manpath", home); + } + if (dotmanpath) { + config_file = fopen (dotmanpath, "r"); + if (config_file != NULL) { + debug ("From the config file %s:\n", dotmanpath); + add_to_dirlist (config_file, 1); + fclose (config_file); + } + free (dotmanpath); + } + + if (getenv ("MAN_TEST_DISABLE_SYSTEM_CONFIG") == NULL) { + config_file = fopen (CONFIG_FILE, "r"); + if (config_file == NULL) { + if (optional) + debug ("can't open %s; continuing anyway\n", + CONFIG_FILE); + else + error (FAIL, 0, + _("can't open the manpath " + "configuration file %s"), + CONFIG_FILE); + } else { + debug ("From the config file %s:\n", CONFIG_FILE); + + add_to_dirlist (config_file, 0); + fclose (config_file); + } + } + + done = 1; +} + + +/* + * Construct the default manpath. This picks up mandatory manpaths + * only. + */ +static char *def_path (enum config_flag flag) +{ + char *manpath = NULL; + const struct config_item *item; + + GL_LIST_FOREACH_START (config, item) + if (item->flag == flag) { + gl_list_t expanded_dirs; + const char *expanded_dir; + + expanded_dirs = expand_path (item->key); + GL_LIST_FOREACH_START (expanded_dirs, expanded_dir) { + int status = is_directory (expanded_dir); + + if (status < 0) + gripe_stat_file (expanded_dir); + else if (status == 0 && !quiet) + error (0, 0, + _("warning: mandatory " + "directory %s doesn't exist"), + expanded_dir); + else if (status == 1) + manpath = pathappend (manpath, + expanded_dir); + } GL_LIST_FOREACH_END (expanded_dirs); + gl_list_free (expanded_dirs); + } + GL_LIST_FOREACH_END (config); + + /* If we have complete config file failure... */ + if (!manpath) + return xstrdup ("/usr/man"); + + return manpath; +} + +/* + * If specified with configure, append OVERRIDE_DIR to dir param and add it + * to list. + */ +static void insert_override_dir (gl_list_t list, const char *dir) +{ + char *override_dir = NULL; + + if (!strlen (OVERRIDE_DIR)) + return; + + if ((override_dir = xasprintf ("%s/%s", dir, OVERRIDE_DIR))) { + add_dir_to_list (list, override_dir); + free (override_dir); + } +} + +/* + * For each directory in the user's path, see if it is one of the + * directories listed in the man_db.config file. If so, and it is + * not already in the manpath, add it. If the directory is not listed + * in the man_db.config file, see if there is a subdirectory `../man' or + * `man', or, for FHS-compliance, `../share/man' or `share/man'. If so, + * and it is not already in the manpath, add it. + * Example: user has $HOME/bin in his path and the directory + * $HOME/man exists -- the directory $HOME/man will be added + * to the manpath. + */ +char *get_manpath_from_path (const char *path, int mandatory) +{ + gl_list_t tmplist; + const struct config_item *config_item; + int len; + char *tmppath; + char *p; + char *end; + char *manpathlist; + char *item; + + tmplist = new_string_list (GL_LINKEDHASH_LIST, false); + tmppath = xstrdup (path); + + for (end = p = tmppath; end; p = end + 1) { + bool manpath_map_found = false; + + end = strchr (p, ':'); + if (end) + *end = '\0'; + + /* don't do this for current dir ("." or empty entry in PATH) */ + if (*p == '\0' || strcmp (p, ".") == 0) + continue; + + debug ("path directory %s ", p); + + /* If the directory we're working on has MANPATH_MAP entries + * in the config file, add them to the list. + */ + GL_LIST_FOREACH_START (config, config_item) { + if (MANPATH_MAP != config_item->flag || + !STREQ (p, config_item->key)) + continue; + if (!manpath_map_found) + debug ("is in the config file\n"); + manpath_map_found = true; + insert_override_dir (tmplist, config_item->cont); + add_dir_to_list (tmplist, config_item->cont); + } GL_LIST_FOREACH_END (config); + + /* The directory we're working on isn't in the config file. + See if it has ../man, man, ../share/man, or share/man + subdirectories. If so, and they haven't been added to + the list, do. */ + + if (!manpath_map_found) { + debug ("is not in the config file\n"); + add_man_subdirs (tmplist, p); + } + } + + free (tmppath); + + if (mandatory) { + debug ("adding mandatory man directories\n"); + + GL_LIST_FOREACH_START (config, config_item) { + if (config_item->flag == MANDATORY) { + insert_override_dir (tmplist, + config_item->key); + add_dir_to_list (tmplist, config_item->key); + } + } GL_LIST_FOREACH_END (config); + } + + len = 0; + GL_LIST_FOREACH_START (tmplist, item) + len += strlen (item) + 1; + GL_LIST_FOREACH_END (tmplist); + + if (!len) + /* No path elements in configuration file or with + * appropriate subdirectories. + */ + return xstrdup (""); + + manpathlist = xmalloc (len); + *manpathlist = '\0'; + + p = manpathlist; + GL_LIST_FOREACH_START (tmplist, item) { + len = strlen (item); + memcpy (p, item, len); + p += len; + *p++ = ':'; + } GL_LIST_FOREACH_END (tmplist); + + p[-1] = '\0'; + + gl_list_free (tmplist); + + return manpathlist; +} + +/* Add a directory to the manpath list if it isn't already there. */ +static void add_expanded_dir_to_list (gl_list_t list, const char *dir) +{ + int status; + + if (gl_list_search (list, dir)) + return; + + /* Not found -- add it. */ + + status = is_directory (dir); + + if (status < 0) + gripe_stat_file (dir); + else if (status == 0) + gripe_not_directory (dir); + else if (status == 1) { + debug (" adding %s to manpath\n", dir); + gl_list_add_last (list, xstrdup (dir)); + } +} + +/* + * Add a directory to the manpath list if it isn't already there, expanding + * wildcards. + */ +static void add_dir_to_list (gl_list_t list, const char *dir) +{ + gl_list_t expanded_dirs; + const char *expanded_dir; + + expanded_dirs = expand_path (dir); + GL_LIST_FOREACH_START (expanded_dirs, expanded_dir) + add_expanded_dir_to_list (list, expanded_dir); + GL_LIST_FOREACH_END (expanded_dirs); + gl_list_free (expanded_dirs); +} + +/* path does not exist in config file: check to see if path/../man, + path/man, path/../share/man, or path/share/man exist, and add them to the + list if they do. */ +static void add_man_subdirs (gl_list_t list, const char *path) +{ + char *newpath; + + /* don't assume anything about path, especially that it ends in + "bin" or even has a '/' in it! */ + + char *subdir = strrchr (path, '/'); + if (subdir) { + newpath = xasprintf ("%.*s/man", (int) (subdir - path), path); + if (is_directory (newpath) == 1) { + insert_override_dir (list, newpath); + add_dir_to_list (list, newpath); + } + free (newpath); + } + + newpath = xasprintf ("%s/man", path); + if (is_directory (newpath) == 1) { + insert_override_dir (list, newpath); + add_dir_to_list (list, newpath); + } + free (newpath); + + if (subdir) { + newpath = xasprintf ("%.*s/share/man", + (int) (subdir - path), path); + if (is_directory (newpath) == 1) { + insert_override_dir (list, newpath); + add_dir_to_list (list, newpath); + } + free (newpath); + } + + newpath = xasprintf ("%s/share/man", path); + if (is_directory (newpath) == 1) { + insert_override_dir (list, newpath); + add_dir_to_list (list, newpath); + } + free (newpath); +} + +struct canonicalized_path { + char *path; + char *canon_path; +}; + +static struct canonicalized_path *canonicalized_path_new (const char *path) +{ + char *canon_path; + struct canonicalized_path *cp = NULL; + + canon_path = canonicalize_file_name (path); + if (canon_path) { + cp = XMALLOC (struct canonicalized_path); + cp->path = xstrdup (path); + cp->canon_path = canon_path; /* steal memory */ + } + return cp; +} + +static bool _GL_ATTRIBUTE_PURE canonicalized_path_equals (const void *elt1, + const void *elt2) +{ + const struct canonicalized_path *cp1 = elt1, *cp2 = elt2; + return string_equals (cp1->canon_path, cp2->canon_path); +} + +static size_t _GL_ATTRIBUTE_PURE canonicalized_path_hash (const void *elt) +{ + const struct canonicalized_path *cp = elt; + return string_hash (cp->canon_path); +} + +static void canonicalized_path_free (const void *elt) +{ + /* gl_list declares the argument as const, but there doesn't seem to + * be a good reason for this. + */ + struct canonicalized_path *cp = (struct canonicalized_path *) elt; + free (cp->path); + free (cp->canon_path); + free (cp); +} + +static void add_dir_to_path_list (gl_list_t list, const char *p) +{ + gl_list_t expanded_dirs; + char *expanded_dir; + + expanded_dirs = expand_path (p); + GL_LIST_FOREACH_START (expanded_dirs, expanded_dir) { + int status = is_directory (expanded_dir); + + if (status < 0) + gripe_stat_file (expanded_dir); + else if (status == 0) + gripe_not_directory (expanded_dir); + else { + char *path; + struct canonicalized_path *cp; + + /* deal with relative paths */ + if (*expanded_dir != '/') { + char *cwd = xgetcwd (); + if (!cwd) + error (FATAL, errno, + _("can't determine current directory")); + path = appendstr (cwd, "/", expanded_dir, + (void *) 0); + } else + path = xstrdup (expanded_dir); + + cp = canonicalized_path_new (path); + if (cp && !gl_list_search (list, cp)) { + debug ("adding %s to manpathlist\n", path); + gl_list_add_last (list, cp); + } else if (cp) + canonicalized_path_free (cp); + free (path); + } + } GL_LIST_FOREACH_END (expanded_dirs); + gl_list_free (expanded_dirs); +} + +gl_list_t create_pathlist (const char *manp) +{ + gl_list_t canonicalized_list, list; + const char *p, *end; + const struct canonicalized_path *cp; + + /* Expand the manpath into a list of (path, canonicalized path) + * pairs for easier handling. add_dir_to_path_list only adds items + * if they do not have the same canonicalized path as an existing + * item, thereby eliminating duplicates due to symlinks. + */ + + canonicalized_list = gl_list_create_empty + (GL_LINKEDHASH_LIST, canonicalized_path_equals, + canonicalized_path_hash, canonicalized_path_free, false); + for (p = manp;; p = end + 1) { + end = strchr (p, ':'); + if (end) { + char *element = xstrndup (p, end - p); + add_dir_to_path_list (canonicalized_list, element); + free (element); + } else { + add_dir_to_path_list (canonicalized_list, p); + break; + } + } + + list = new_string_list (GL_ARRAY_LIST, false); + GL_LIST_FOREACH_START (canonicalized_list, cp) + gl_list_add_last (list, xstrdup (cp->path)); + GL_LIST_FOREACH_END (canonicalized_list); + + if (debug_level) { + debug ("final search path = "); + GL_LIST_FOREACH_START (list, p) { + if (!gl_list_previous_node (list, list_node)) + debug ("%s", p); + else + debug (":%s", p); + } GL_LIST_FOREACH_END (list); + debug ("\n"); + } + + gl_list_free (canonicalized_list); + return list; +} + +void free_pathlist (gl_list_t list) +{ + gl_list_free (list); +} + +/* Routine to get list of named system and user manpaths (in reverse order). */ +char *get_mandb_manpath (void) +{ + char *manpath = NULL; + const struct config_item *item; + + GL_LIST_FOREACH_START (config, item) + if (item->flag == MANDB_MAP || item->flag == MANDB_MAP_USER) + manpath = pathappend (manpath, item->key); + GL_LIST_FOREACH_END (config); + + return manpath; +} + +/* Take manpath or manfile path as the first argument, and the type of + * catpaths we want as the other (system catpaths, user catpaths, or both). + * Return catdir mapping or NULL if it isn't a global/user mandir (as + * appropriate). + * + * This routine would seem to work correctly for nls subdirs and would + * specify the (correct) consistent catpath even if not defined in the + * config file. + * + * Do not return user catpaths when cattype == 0! This is used to decide + * whether to drop privileges. When cattype != 0 it's OK to return global + * catpaths. + */ +char *get_catpath (const char *name, int cattype) +{ + const struct config_item *item; + char *ret = NULL; + + GL_LIST_FOREACH_START (config, item) + if (((cattype & SYSTEM_CAT) && item->flag == MANDB_MAP) || + ((cattype & USER_CAT) && item->flag == MANDB_MAP_USER)) { + size_t manlen = strlen (item->key); + if (STRNEQ (name, item->key, manlen)) { + const char *suffix; + char *infix; + char *catpath = xstrdup (item->cont); + + /* For NLS subdirectories (e.g. + * /usr/share/man/de -> /var/cache/man/de), + * we need to find the second-last slash, as + * long as this strictly follows the key. + */ + suffix = strrchr (name, '/'); + if (!suffix) { + ret = appendstr (catpath, + name + manlen, + (void *) 0); + break; + } + + while (suffix > name + manlen) + if (*--suffix == '/') + break; + if (suffix < name + manlen) + suffix = name + manlen; + if (*suffix == '/') + ++suffix; + infix = xstrndup (name + manlen, + suffix - (name + manlen)); + catpath = appendstr (catpath, infix, + (void *) 0); + free (infix); + if (STRNEQ (suffix, "man", 3)) { + suffix += 3; + catpath = appendstr (catpath, "cat", + (void *) 0); + } + catpath = appendstr (catpath, suffix, + (void *) 0); + ret = catpath; + break; + } + } + GL_LIST_FOREACH_END (config); + + return ret; +} + +/* Check to see if the supplied man directory is a system-wide mandir. + * Obviously, user directories must not be included here. + */ +bool is_global_mandir (const char *dir) +{ + const struct config_item *item; + bool ret = false; + + GL_LIST_FOREACH_START (config, item) + if (item->flag == MANDB_MAP && + STRNEQ (dir, item->key, strlen (item->key))) { + ret = true; + break; + } + GL_LIST_FOREACH_END (config); + + return ret; +} + +/* Accept a manpath (not a full pathname to a file) and return an FSSTND + equivalent catpath */ +static char *fsstnd (const char *path) +{ + char *manpath; + char *catpath; + char *element; + + if (strncmp (path, MAN_ROOT, sizeof MAN_ROOT - 1) != 0) { + if (!quiet) + error (0, 0, _("warning: %s does not begin with %s"), + path, MAN_ROOT); + return xstrdup (path); + } + /* get rid of initial "/usr" */ + path += sizeof MAN_ROOT - 1; + manpath = xstrdup (path); + catpath = xmalloc (strlen (path) + sizeof CAT_ROOT - 3); + + /* start with CAT_ROOT */ + (void) strcpy (catpath, CAT_ROOT); + + /* split up path into elements and deal with accordingly */ + for (element = strtok (manpath, "/"); element; + element = strtok (NULL, "/")) { + if (strncmp (element, "man", 3) == 0) { + if (*(element + 3)) { + *element = 'c'; + *(element + 2) = 't'; + } else + continue; + } + (void) strcat (catpath, "/"); + (void) strcat (catpath, element); + } + free (manpath); + return catpath; +} diff --git a/src/manp.h b/src/manp.h new file mode 100644 index 0000000..676891b --- /dev/null +++ b/src/manp.h @@ -0,0 +1,51 @@ +/* + * manp.h: Interface to manpath calculations + * + * Copyright (C) 1990, 1991 John W. Eaton. + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdbool.h> + +#include "gl_list.h" + +struct locale_bits { + char *language; + char *territory; + char *codeset; + char *modifier; +}; + +/* manp.c */ +extern char *cat_manpath (char *manp); +extern void unpack_locale_bits (const char *locale, struct locale_bits *bits); +extern void free_locale_bits (struct locale_bits *bits); +extern char *add_nls_manpaths (const char *manpathlist, const char *locales); +extern char *get_manpath (const char *systems); +extern char *get_manpath_from_path (const char *path, int mandatory); +extern gl_list_t create_pathlist (const char *manp); +extern void free_pathlist (gl_list_t list); +extern char *get_mandb_manpath (void); +extern char *get_catpath (const char *name, int cattype); +extern bool is_global_mandir (const char *dir); +extern void read_config_file (bool optional); +extern const char *get_def (const char *thing, const char *def); +extern const char *get_def_user (const char *thing, const char *def); +extern gl_list_t get_sections (void); diff --git a/src/manpath.c b/src/manpath.c new file mode 100644 index 0000000..ca71101 --- /dev/null +++ b/src/manpath.c @@ -0,0 +1,135 @@ +/* + * manpath.c: display either the manpath or catpath + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Thu Nov 17 08:29:39 GMT 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <limits.h> + +#include "argp.h" +#include "progname.h" + +#include "gettext.h" +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "error.h" + +#include "manp.h" + +int quiet = 0; + +static bool cat = false; +static bool global = false; +static const char *alt_system = ""; +extern char *user_config_file; + +const char *argp_program_version = "manpath " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static struct argp_option options[] = { + { "catpath", 'c', 0, 0, N_("show relative catpaths") }, + { "global", 'g', 0, 0, N_("show the entire global manpath") }, + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "quiet", 'q', 0, 0, N_("produce fewer warnings") }, + { "config-file", 'C', N_("FILE"), 0, N_("use this user configuration file") }, + { "systems", 'm', N_("SYSTEM"), 0, N_("use manual pages from other systems") }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'c': + cat = true; + return 0; + case 'g': + global = true; + quiet = 1; + return 0; + case 'd': + debug_level = true; + return 0; + case 'q': + quiet = 1; + return 0; + case 'C': + user_config_file = arg; + return 0; + case 'm': + alt_system = arg; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP); + break; + } + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp = { options, parse_opt }; + +/* + * Examine user's PATH and print a reasonable MANPATH. + */ +int main (int argc, char *argv[]) +{ + char *path_string; + + set_program_name (argv[0]); + + init_debug (); + init_locale (); + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + + path_string = get_manpath (alt_system); + + if (global) { + path_string = get_mandb_manpath (); + if (!path_string) + error (FAIL, 0, + _("warning: no global manpaths set in " + "config file %s"), + CONFIG_FILE); + } + if (cat) + path_string = cat_manpath (path_string); + + printf ("%s\n", path_string); + exit (OK); +} diff --git a/src/straycats.c b/src/straycats.c new file mode 100644 index 0000000..b07083c --- /dev/null +++ b/src/straycats.c @@ -0,0 +1,366 @@ +/* + * straycats.c: find and process stray cat files + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2010, 2011 + * Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Tue May 3 21:24:51 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> + +#include "canonicalize.h" +#include "dirname.h" +#include "gl_array_list.h" +#include "gl_xlist.h" + +#include "gettext.h" +#define _(String) gettext (String) + +#include "manconfig.h" + +#include "error.h" +#include "glcontainers.h" +#include "pipeline.h" +#include "decompress.h" +#include "encodings.h" +#include "orderfiles.h" +#include "sandbox.h" +#include "security.h" + +#include "mydbm.h" +#include "db_storage.h" + +#include "descriptions.h" +#include "manp.h" +#include "manconv_client.h" +#include "ult_src.h" + +extern man_sandbox *sandbox; + +static char *catdir, *mandir; + +static int check_for_stray (MYDBM_FILE dbf) +{ + DIR *cdir; + struct dirent *catlist; + gl_list_t names; + const char *name; + size_t lenman, lencat; + int strays = 0; + + cdir = opendir (catdir); + if (!cdir) { + error (0, errno, _("can't search directory %s"), catdir); + return 0; + } + + names = new_string_list (GL_ARRAY_LIST, false); + + while ((catlist = readdir (cdir)) != NULL) { + if (*catlist->d_name == '.' && + strlen (catlist->d_name) < (size_t) 3) + continue; + gl_list_add_last (names, xstrdup (catlist->d_name)); + } + closedir (cdir); + + order_files (catdir, &names); + + mandir = appendstr (mandir, "/", (void *) 0); + catdir = appendstr (catdir, "/", (void *) 0); + lenman = strlen (mandir); + lencat = strlen (catdir); + + GL_LIST_FOREACH_START (names, name) { + struct mandata info; + char *ext, *section; + short found; + struct stat buf; + struct compression *comp; + + memset (&info, 0, sizeof (struct mandata)); + + *(mandir + lenman) = *(catdir + lencat) = '\0'; + mandir = appendstr (mandir, name, (void *) 0); + catdir = appendstr (catdir, name, (void *) 0); + + ext = strrchr (mandir, '.'); + if (!ext) { + if (quiet < 2) + error (0, 0, + _("warning: %s: " + "ignoring bogus filename"), + catdir); + continue; + } else if (comp_info (ext, 0)) { + *ext = '\0'; + info.comp = ext + 1; + } else + info.comp = NULL; + + ext = strrchr (mandir, '.'); + *(mandir + lenman - 1) = '\0'; + section = xstrdup (strrchr (mandir, '/') + 4); + *(mandir + lenman - 1) = '/'; + + /* check for bogosity */ + + if (!ext || strncmp (ext + 1, section, strlen (section)) != 0) { + if (quiet < 2) + error (0, 0, + _("warning: %s: " + "ignoring bogus filename"), + catdir); + goto next_section; + } + + /* + * now that we've stripped off the cat compression + * extension (if it has one), we can try some of ours. + */ + + debug ("Testing for existence: %s\n", mandir); + + if (stat (mandir, &buf) == 0) + found = 1; + else if ((comp = comp_file (mandir))) { + found = 1; + free (comp->stem); + } else + found = 0; + + if (!found) { + pipeline *decomp; + struct mandata *exists; + lexgrog lg; + char *lang, *page_encoding; + char *mandir_base; + pipecmd *col_cmd; + char *col_locale; + char *fullpath; + + /* we have a straycat. Need to filter it and get + its whatis (if necessary) */ + + lg.whatis = 0; + *(ext++) = '\0'; + info.ext = ext; + + /* see if we already have it, before going any + further */ + mandir_base = base_name (mandir); + exists = dblookup_exact (dbf, mandir_base, info.ext, + true); + if (exists && + compare_ids (STRAY_CAT, exists->id, 0) >= 0) + goto next_exists; + debug ("%s(%s) is not in the db.\n", + mandir_base, info.ext); + + /* fill in the missing parts of the structure */ + info.name = NULL; + info.sec = section; + info.id = STRAY_CAT; + info.pointer = NULL; + info.filter = "-"; + info.mtime.tv_sec = 0; + info.mtime.tv_nsec = 0; + + drop_effective_privs (); + decomp = decompress_open (catdir); + regain_effective_privs (); + if (!decomp) { + error (0, errno, _("can't open %s"), catdir); + goto next_exists; + } + + lang = lang_dir (mandir); + page_encoding = get_page_encoding (lang); + if (page_encoding) + add_manconv (decomp, page_encoding, "UTF-8"); + free (page_encoding); + free (lang); + + col_cmd = pipecmd_new_argstr + (get_def_user ("col", COL)); + pipecmd_arg (col_cmd, "-bx"); + col_locale = find_charset_locale ("UTF-8"); + if (col_locale) { + pipecmd_setenv (col_cmd, "LC_CTYPE", + col_locale); + free (col_locale); + } + pipecmd_pre_exec (col_cmd, sandbox_load, sandbox_free, + sandbox); + pipeline_command (decomp, col_cmd); + + fullpath = canonicalize_file_name (catdir); + if (!fullpath) { + if (quiet < 2) { + if (errno == ENOENT) + error (0, 0, _("warning: %s is a dangling symlink"), fullpath); + else + error (0, errno, + _("can't resolve %s"), + catdir); + } + } else { + char *catdir_base; + + free (fullpath); + drop_effective_privs (); + pipeline_start (decomp); + regain_effective_privs (); + + strays++; + + lg.type = CATPAGE; + catdir_base = base_name (catdir); + if (find_name_decompressed (decomp, + catdir_base, + &lg)) { + gl_list_t descs; + strays++; + descs = parse_descriptions + (mandir_base, lg.whatis); + store_descriptions (dbf, descs, &info, + NULL, mandir_base, + NULL); + gl_list_free (descs); + } else if (quiet < 2) + error (0, 0, _("warning: %s: whatis parse for %s(%s) failed"), + catdir, mandir_base, info.sec); + free (catdir_base); + } + + free (lg.whatis); + pipeline_free (decomp); +next_exists: + free_mandata_struct (exists); + free (mandir_base); + } +next_section: + free (section); + } GL_LIST_FOREACH_END (names); + gl_list_free (names); + return strays; +} + +static int open_catdir (MYDBM_FILE dbf) +{ + DIR *cdir; + struct dirent *catlist; + size_t catlen, manlen; + int strays = 0; + + cdir = opendir (catdir); + if (!cdir) { + error (0, errno, _("can't search directory %s"), catdir); + return 0; + } + + if (!quiet) + printf (_("Checking for stray cats under %s...\n"), catdir); + + catdir = appendstr (catdir, "/", (void *) 0); + mandir = appendstr (mandir, "/", (void *) 0); + catlen = strlen (catdir); + manlen = strlen (mandir); + + /* should make this case insensitive */ + while ((catlist = readdir (cdir))) { + char *t1; + + if (strncmp (catlist->d_name, "cat", 3) != 0) + continue; + + catdir = appendstr (catdir, catlist->d_name, (void *) 0); + mandir = appendstr (mandir, catlist->d_name, (void *) 0); + + *(t1 = mandir + manlen) = 'm'; + *(t1 + 2) = 'n'; + + strays += check_for_stray (dbf); + + *(catdir + catlen) = *(mandir + manlen) = '\0'; + } + closedir (cdir); + return strays; +} + +int straycats (const char *database, const char *manpath) +{ + MYDBM_FILE dbf; + char *catpath; + int strays; + + dbf = MYDBM_RWOPEN (database); + if (dbf && dbver_rd (dbf)) { + MYDBM_CLOSE (dbf); + dbf = NULL; + } + if (!dbf) { + error (0, errno, _("warning: can't update index cache %s"), + database); + return 0; + } + + catpath = get_catpath (manpath, SYSTEM_CAT | USER_CAT); + + /* look in the usual catpath location */ + mandir = xstrdup (manpath); + catdir = xstrdup (manpath); + strays = open_catdir (dbf); + + /* look in the alternate catpath location if we have one + and it's different from the usual catpath */ + + if (catpath) + debug ("catpath: %s, manpath: %s\n", catpath, manpath); + + if (catpath && strcmp (catpath, manpath) != 0) { + *mandir = *catdir = '\0'; + mandir = appendstr (mandir, manpath, (void *) 0); + catdir = appendstr (catdir, catpath, (void *) 0); + strays += open_catdir (dbf); + } + + free (mandir); + free (catdir); + + free (catpath); + + MYDBM_CLOSE (dbf); + return strays; +} diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am new file mode 100644 index 0000000..9164c83 --- /dev/null +++ b/src/tests/Makefile.am @@ -0,0 +1,73 @@ +## Process this file with automake to produce Makefile.in +## +## Copyright (C) 2009-2019 Colin Watson. +## +## This file is part of man-db. +## +## man-db 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 of the License, or +## (at your option) any later version. +## +## man-db 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 man-db; if not, write to the Free Software Foundation, +## Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +TESTS_ENVIRONMENT = PATH=$(abs_builddir)/..:$$PATH; export PATH; \ + DBTYPE=$(DBTYPE); export DBTYPE; \ + MANDIR_LAYOUT=$(MANDIR_LAYOUT); export MANDIR_LAYOUT; \ + abs_top_builddir=$(abs_top_builddir); export abs_top_builddir; \ + OVERRIDE_DIR="$(override_dir)"; export OVERRIDE_DIR; +# Each test must use the configure-detected shell, not necessarily /bin/sh. +AM_LOG_FLAGS = $(SHELL) +ALL_TESTS = \ + lexgrog-backslash-dash-rhs \ + lexgrog-basic \ + lexgrog-multiple-whatis \ + man-deleted-directory \ + man-exact-section-matches \ + man-executable-page-on-path \ + man-invalid-db-entry \ + man-language-specific-requests \ + man-mandatory-manpath \ + man-missing-locales \ + man-override-dir \ + man-recode-in-place \ + man-recode-suffix \ + man-so-links-same-section \ + man-suffixed-extension \ + man-symlinks-with-matching-names \ + manconv-coding-tags \ + manconv-guess-from-encoding \ + manconv-incomplete-char-at-eof \ + manconv-odd-combinations \ + mandb-basic \ + mandb-bogus-symlink \ + mandb-cachedir-tag \ + mandb-empty-page \ + mandb-regular-file-symlink-changes \ + mandb-symlink-beats-whatis-ref \ + mandb-whatis-broken-link-changes \ + whatis-path-to-executable \ + zsoelim-so-includes +if !CROSS_COMPILING +TESTS = $(ALL_TESTS) +endif + +AM_CPPFLAGS = \ + -I$(top_builddir)/include \ + -I$(top_builddir)/gl/lib \ + -I$(top_srcdir)/gl/lib +AM_CFLAGS = $(WARN_CFLAGS) +check_PROGRAMS = fspause +fspause_SOURCES = fspause.c +fspause_LDADD = \ + $(top_builddir)/gl/lib/libgnu.la \ + $(LIB_NANOSLEEP) + +dist_check_SCRIPTS = testlib.sh $(ALL_TESTS) diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in new file mode 100644 index 0000000..c14132f --- /dev/null +++ b/src/tests/Makefile.in @@ -0,0 +1,2400 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 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@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +check_PROGRAMS = fspause$(EXEEXT) +subdir = src/tests +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/man-arg-automatic-create.m4 \ + $(top_srcdir)/m4/man-arg-automatic-update.m4 \ + $(top_srcdir)/m4/man-arg-cache-owner.m4 \ + $(top_srcdir)/m4/man-arg-cats.m4 \ + $(top_srcdir)/m4/man-arg-config-file.m4 \ + $(top_srcdir)/m4/man-arg-db.m4 \ + $(top_srcdir)/m4/man-arg-device.m4 \ + $(top_srcdir)/m4/man-arg-mandirs.m4 \ + $(top_srcdir)/m4/man-arg-manual.m4 \ + $(top_srcdir)/m4/man-arg-override-dir.m4 \ + $(top_srcdir)/m4/man-arg-sections.m4 \ + $(top_srcdir)/m4/man-arg-setuid.m4 \ + $(top_srcdir)/m4/man-arg-systemdsystemunitdir.m4 \ + $(top_srcdir)/m4/man-arg-systemdtmpfilesdir.m4 \ + $(top_srcdir)/m4/man-arg-undoc.m4 $(top_srcdir)/m4/man-bdb.m4 \ + $(top_srcdir)/m4/man-check-progs.m4 \ + $(top_srcdir)/m4/man-compress-lib.m4 \ + $(top_srcdir)/m4/man-gnu-nroff.m4 \ + $(top_srcdir)/m4/man-heirloom-nroff.m4 \ + $(top_srcdir)/m4/man-libseccomp.m4 \ + $(top_srcdir)/m4/man-linguas.m4 $(top_srcdir)/m4/man-po4a.m4 \ + $(top_srcdir)/m4/man-tar-sort-name.m4 \ + $(top_srcdir)/m4/man-trans-subst.m4 \ + $(top_srcdir)/gl/m4/00gnulib.m4 \ + $(top_srcdir)/gl/m4/__inline.m4 \ + $(top_srcdir)/gl/m4/absolute-header.m4 \ + $(top_srcdir)/gl/m4/alloca.m4 $(top_srcdir)/gl/m4/argp.m4 \ + $(top_srcdir)/gl/m4/asm-underscore.m4 \ + $(top_srcdir)/gl/m4/btowc.m4 \ + $(top_srcdir)/gl/m4/builtin-expect.m4 \ + $(top_srcdir)/gl/m4/canonicalize.m4 \ + $(top_srcdir)/gl/m4/chdir-long.m4 $(top_srcdir)/gl/m4/chown.m4 \ + $(top_srcdir)/gl/m4/clock_time.m4 $(top_srcdir)/gl/m4/close.m4 \ + $(top_srcdir)/gl/m4/closedir.m4 $(top_srcdir)/gl/m4/codeset.m4 \ + $(top_srcdir)/gl/m4/ctype.m4 $(top_srcdir)/gl/m4/d-ino.m4 \ + $(top_srcdir)/gl/m4/d-type.m4 $(top_srcdir)/gl/m4/dirent_h.m4 \ + $(top_srcdir)/gl/m4/dirfd.m4 $(top_srcdir)/gl/m4/dirname.m4 \ + $(top_srcdir)/gl/m4/double-slash-root.m4 \ + $(top_srcdir)/gl/m4/dup.m4 $(top_srcdir)/gl/m4/dup2.m4 \ + $(top_srcdir)/gl/m4/eealloc.m4 $(top_srcdir)/gl/m4/environ.m4 \ + $(top_srcdir)/gl/m4/errno_h.m4 $(top_srcdir)/gl/m4/error.m4 \ + $(top_srcdir)/gl/m4/exponentd.m4 \ + $(top_srcdir)/gl/m4/extensions.m4 \ + $(top_srcdir)/gl/m4/extern-inline.m4 \ + $(top_srcdir)/gl/m4/fchdir.m4 $(top_srcdir)/gl/m4/fcntl-o.m4 \ + $(top_srcdir)/gl/m4/fcntl.m4 $(top_srcdir)/gl/m4/fcntl_h.m4 \ + $(top_srcdir)/gl/m4/fdopendir.m4 \ + $(top_srcdir)/gl/m4/filenamecat.m4 \ + $(top_srcdir)/gl/m4/flexmember.m4 \ + $(top_srcdir)/gl/m4/float_h.m4 $(top_srcdir)/gl/m4/flock.m4 \ + $(top_srcdir)/gl/m4/fnmatch.m4 \ + $(top_srcdir)/gl/m4/fnmatch_h.m4 $(top_srcdir)/gl/m4/fstat.m4 \ + $(top_srcdir)/gl/m4/fstatat.m4 $(top_srcdir)/gl/m4/futimens.m4 \ + $(top_srcdir)/gl/m4/getcwd-abort-bug.m4 \ + $(top_srcdir)/gl/m4/getcwd-path-max.m4 \ + $(top_srcdir)/gl/m4/getcwd.m4 $(top_srcdir)/gl/m4/getdelim.m4 \ + $(top_srcdir)/gl/m4/getdtablesize.m4 \ + $(top_srcdir)/gl/m4/getline.m4 $(top_srcdir)/gl/m4/getlogin.m4 \ + $(top_srcdir)/gl/m4/getlogin_r.m4 \ + $(top_srcdir)/gl/m4/getopt.m4 \ + $(top_srcdir)/gl/m4/getpagesize.m4 \ + $(top_srcdir)/gl/m4/getprogname.m4 \ + $(top_srcdir)/gl/m4/gettext.m4 $(top_srcdir)/gl/m4/gettime.m4 \ + $(top_srcdir)/gl/m4/gettimeofday.m4 \ + $(top_srcdir)/gl/m4/glibc21.m4 $(top_srcdir)/gl/m4/glob.m4 \ + $(top_srcdir)/gl/m4/glob_h.m4 \ + $(top_srcdir)/gl/m4/gnulib-common.m4 \ + $(top_srcdir)/gl/m4/gnulib-comp.m4 \ + $(top_srcdir)/gl/m4/host-cpu-c-abi.m4 \ + $(top_srcdir)/gl/m4/iconv.m4 $(top_srcdir)/gl/m4/idpriv.m4 \ + $(top_srcdir)/gl/m4/include_next.m4 \ + $(top_srcdir)/gl/m4/intlmacosx.m4 \ + $(top_srcdir)/gl/m4/intmax_t.m4 \ + $(top_srcdir)/gl/m4/inttypes.m4 \ + $(top_srcdir)/gl/m4/inttypes_h.m4 $(top_srcdir)/gl/m4/ioctl.m4 \ + $(top_srcdir)/gl/m4/isblank.m4 \ + $(top_srcdir)/gl/m4/langinfo_h.m4 \ + $(top_srcdir)/gl/m4/largefile.m4 $(top_srcdir)/gl/m4/lchown.m4 \ + $(top_srcdir)/gl/m4/lib-ignore.m4 \ + $(top_srcdir)/gl/m4/lib-ld.m4 $(top_srcdir)/gl/m4/lib-link.m4 \ + $(top_srcdir)/gl/m4/lib-prefix.m4 \ + $(top_srcdir)/gl/m4/libtool.m4 $(top_srcdir)/gl/m4/limits-h.m4 \ + $(top_srcdir)/gl/m4/localcharset.m4 \ + $(top_srcdir)/gl/m4/locale-fr.m4 \ + $(top_srcdir)/gl/m4/locale-ja.m4 \ + $(top_srcdir)/gl/m4/locale-zh.m4 \ + $(top_srcdir)/gl/m4/locale_h.m4 \ + $(top_srcdir)/gl/m4/localeconv.m4 \ + $(top_srcdir)/gl/m4/localtime-buffer.m4 \ + $(top_srcdir)/gl/m4/lock.m4 $(top_srcdir)/gl/m4/lstat.m4 \ + $(top_srcdir)/gl/m4/ltoptions.m4 \ + $(top_srcdir)/gl/m4/ltsugar.m4 \ + $(top_srcdir)/gl/m4/ltversion.m4 \ + $(top_srcdir)/gl/m4/lt~obsolete.m4 \ + $(top_srcdir)/gl/m4/malloc.m4 $(top_srcdir)/gl/m4/malloca.m4 \ + $(top_srcdir)/gl/m4/manywarnings.m4 \ + $(top_srcdir)/gl/m4/mbrtowc.m4 $(top_srcdir)/gl/m4/mbsinit.m4 \ + $(top_srcdir)/gl/m4/mbsrtowcs.m4 \ + $(top_srcdir)/gl/m4/mbstate_t.m4 $(top_srcdir)/gl/m4/mbtowc.m4 \ + $(top_srcdir)/gl/m4/memchr.m4 $(top_srcdir)/gl/m4/memmem.m4 \ + $(top_srcdir)/gl/m4/mempcpy.m4 $(top_srcdir)/gl/m4/memrchr.m4 \ + $(top_srcdir)/gl/m4/minmax.m4 $(top_srcdir)/gl/m4/mkdir.m4 \ + $(top_srcdir)/gl/m4/mkdtemp.m4 $(top_srcdir)/gl/m4/mkstemp.m4 \ + $(top_srcdir)/gl/m4/mmap-anon.m4 $(top_srcdir)/gl/m4/mode_t.m4 \ + $(top_srcdir)/gl/m4/msvc-inval.m4 \ + $(top_srcdir)/gl/m4/msvc-nothrow.m4 \ + $(top_srcdir)/gl/m4/multiarch.m4 \ + $(top_srcdir)/gl/m4/nanosleep.m4 \ + $(top_srcdir)/gl/m4/nl_langinfo.m4 $(top_srcdir)/gl/m4/nls.m4 \ + $(top_srcdir)/gl/m4/nocrash.m4 \ + $(top_srcdir)/gl/m4/nonblocking.m4 \ + $(top_srcdir)/gl/m4/off_t.m4 \ + $(top_srcdir)/gl/m4/open-cloexec.m4 \ + $(top_srcdir)/gl/m4/open-slash.m4 $(top_srcdir)/gl/m4/open.m4 \ + $(top_srcdir)/gl/m4/openat.m4 $(top_srcdir)/gl/m4/opendir.m4 \ + $(top_srcdir)/gl/m4/pathmax.m4 $(top_srcdir)/gl/m4/po.m4 \ + $(top_srcdir)/gl/m4/printf.m4 $(top_srcdir)/gl/m4/progtest.m4 \ + $(top_srcdir)/gl/m4/pthread_rwlock_rdlock.m4 \ + $(top_srcdir)/gl/m4/raise.m4 $(top_srcdir)/gl/m4/rawmemchr.m4 \ + $(top_srcdir)/gl/m4/readdir.m4 $(top_srcdir)/gl/m4/readlink.m4 \ + $(top_srcdir)/gl/m4/realloc.m4 $(top_srcdir)/gl/m4/regex.m4 \ + $(top_srcdir)/gl/m4/rename.m4 $(top_srcdir)/gl/m4/renameat.m4 \ + $(top_srcdir)/gl/m4/rewinddir.m4 $(top_srcdir)/gl/m4/rmdir.m4 \ + $(top_srcdir)/gl/m4/same.m4 $(top_srcdir)/gl/m4/save-cwd.m4 \ + $(top_srcdir)/gl/m4/select.m4 $(top_srcdir)/gl/m4/setenv.m4 \ + $(top_srcdir)/gl/m4/setlocale_null.m4 \ + $(top_srcdir)/gl/m4/sigaction.m4 \ + $(top_srcdir)/gl/m4/signal_h.m4 \ + $(top_srcdir)/gl/m4/signalblocking.m4 \ + $(top_srcdir)/gl/m4/size_max.m4 $(top_srcdir)/gl/m4/sleep.m4 \ + $(top_srcdir)/gl/m4/socketlib.m4 \ + $(top_srcdir)/gl/m4/sockets.m4 $(top_srcdir)/gl/m4/socklen.m4 \ + $(top_srcdir)/gl/m4/ssize_t.m4 \ + $(top_srcdir)/gl/m4/stat-time.m4 $(top_srcdir)/gl/m4/stat.m4 \ + $(top_srcdir)/gl/m4/std-gnu11.m4 \ + $(top_srcdir)/gl/m4/stdalign.m4 $(top_srcdir)/gl/m4/stdarg.m4 \ + $(top_srcdir)/gl/m4/stdbool.m4 $(top_srcdir)/gl/m4/stddef_h.m4 \ + $(top_srcdir)/gl/m4/stdint.m4 $(top_srcdir)/gl/m4/stdint_h.m4 \ + $(top_srcdir)/gl/m4/stdio_h.m4 $(top_srcdir)/gl/m4/stdlib_h.m4 \ + $(top_srcdir)/gl/m4/strcase.m4 \ + $(top_srcdir)/gl/m4/strcasestr.m4 \ + $(top_srcdir)/gl/m4/strchrnul.m4 $(top_srcdir)/gl/m4/strdup.m4 \ + $(top_srcdir)/gl/m4/strerror.m4 \ + $(top_srcdir)/gl/m4/string_h.m4 \ + $(top_srcdir)/gl/m4/strings_h.m4 \ + $(top_srcdir)/gl/m4/strndup.m4 $(top_srcdir)/gl/m4/strnlen.m4 \ + $(top_srcdir)/gl/m4/strsep.m4 \ + $(top_srcdir)/gl/m4/sys_file_h.m4 \ + $(top_srcdir)/gl/m4/sys_ioctl_h.m4 \ + $(top_srcdir)/gl/m4/sys_select_h.m4 \ + $(top_srcdir)/gl/m4/sys_socket_h.m4 \ + $(top_srcdir)/gl/m4/sys_stat_h.m4 \ + $(top_srcdir)/gl/m4/sys_time_h.m4 \ + $(top_srcdir)/gl/m4/sys_types_h.m4 \ + $(top_srcdir)/gl/m4/sys_uio_h.m4 \ + $(top_srcdir)/gl/m4/sysexits.m4 \ + $(top_srcdir)/gl/m4/tempname.m4 \ + $(top_srcdir)/gl/m4/threadlib.m4 $(top_srcdir)/gl/m4/time_h.m4 \ + $(top_srcdir)/gl/m4/timespec.m4 \ + $(top_srcdir)/gl/m4/unistd-safer.m4 \ + $(top_srcdir)/gl/m4/unistd_h.m4 $(top_srcdir)/gl/m4/unlink.m4 \ + $(top_srcdir)/gl/m4/unlinkat.m4 $(top_srcdir)/gl/m4/utime.m4 \ + $(top_srcdir)/gl/m4/utime_h.m4 $(top_srcdir)/gl/m4/utimens.m4 \ + $(top_srcdir)/gl/m4/utimes.m4 \ + $(top_srcdir)/gl/m4/vasnprintf.m4 \ + $(top_srcdir)/gl/m4/vasprintf.m4 \ + $(top_srcdir)/gl/m4/visibility.m4 \ + $(top_srcdir)/gl/m4/vsnprintf.m4 \ + $(top_srcdir)/gl/m4/warn-on-use.m4 \ + $(top_srcdir)/gl/m4/warnings.m4 $(top_srcdir)/gl/m4/wchar_h.m4 \ + $(top_srcdir)/gl/m4/wchar_t.m4 $(top_srcdir)/gl/m4/wcrtomb.m4 \ + $(top_srcdir)/gl/m4/wctype_h.m4 $(top_srcdir)/gl/m4/wint_t.m4 \ + $(top_srcdir)/gl/m4/wmemchr.m4 $(top_srcdir)/gl/m4/wmempcpy.m4 \ + $(top_srcdir)/gl/m4/xalloc.m4 $(top_srcdir)/gl/m4/xgetcwd.m4 \ + $(top_srcdir)/gl/m4/xsize.m4 $(top_srcdir)/gl/m4/xstrndup.m4 \ + $(top_srcdir)/gl/m4/xvasprintf.m4 \ + $(top_srcdir)/gl/m4/zzgnulib.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_check_SCRIPTS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am_fspause_OBJECTS = fspause.$(OBJEXT) +fspause_OBJECTS = $(am_fspause_OBJECTS) +am__DEPENDENCIES_1 = +fspause_DEPENDENCIES = $(top_builddir)/gl/lib/libgnu.la \ + $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/fspause.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(fspause_SOURCES) +DIST_SOURCES = $(fspause_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALLOCA_H = @ALLOCA_H@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +ASM_SYMBOL_PREFIX = @ASM_SYMBOL_PREFIX@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ +BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ +BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ +BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ +BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DBLIBS = @DBLIBS@ +DBTYPE = @DBTYPE@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@ +EMULTIHOP_VALUE = @EMULTIHOP_VALUE@ +ENOLINK_HIDDEN = @ENOLINK_HIDDEN@ +ENOLINK_VALUE = @ENOLINK_VALUE@ +EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@ +EOVERFLOW_VALUE = @EOVERFLOW_VALUE@ +ERRNO_H = @ERRNO_H@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FLOAT_H = @FLOAT_H@ +FNMATCH_H = @FNMATCH_H@ +GETOPT_CDEFS_H = @GETOPT_CDEFS_H@ +GETOPT_H = @GETOPT_H@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GLIBC21 = @GLIBC21@ +GLOB_H = @GLOB_H@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNULIB_ACCEPT = @GNULIB_ACCEPT@ +GNULIB_ACCEPT4 = @GNULIB_ACCEPT4@ +GNULIB_ACCESS = @GNULIB_ACCESS@ +GNULIB_ALPHASORT = @GNULIB_ALPHASORT@ +GNULIB_ATOLL = @GNULIB_ATOLL@ +GNULIB_BIND = @GNULIB_BIND@ +GNULIB_BTOWC = @GNULIB_BTOWC@ +GNULIB_CALLOC_POSIX = @GNULIB_CALLOC_POSIX@ +GNULIB_CANONICALIZE_FILE_NAME = @GNULIB_CANONICALIZE_FILE_NAME@ +GNULIB_CHDIR = @GNULIB_CHDIR@ +GNULIB_CHOWN = @GNULIB_CHOWN@ +GNULIB_CLOSE = @GNULIB_CLOSE@ +GNULIB_CLOSEDIR = @GNULIB_CLOSEDIR@ +GNULIB_CONNECT = @GNULIB_CONNECT@ +GNULIB_COPY_FILE_RANGE = @GNULIB_COPY_FILE_RANGE@ +GNULIB_CREAT = @GNULIB_CREAT@ +GNULIB_CTIME = @GNULIB_CTIME@ +GNULIB_DIRFD = @GNULIB_DIRFD@ +GNULIB_DPRINTF = @GNULIB_DPRINTF@ +GNULIB_DUP = @GNULIB_DUP@ +GNULIB_DUP2 = @GNULIB_DUP2@ +GNULIB_DUP3 = @GNULIB_DUP3@ +GNULIB_DUPLOCALE = @GNULIB_DUPLOCALE@ +GNULIB_ENVIRON = @GNULIB_ENVIRON@ +GNULIB_EUIDACCESS = @GNULIB_EUIDACCESS@ +GNULIB_EXPLICIT_BZERO = @GNULIB_EXPLICIT_BZERO@ +GNULIB_FACCESSAT = @GNULIB_FACCESSAT@ +GNULIB_FCHDIR = @GNULIB_FCHDIR@ +GNULIB_FCHMODAT = @GNULIB_FCHMODAT@ +GNULIB_FCHOWNAT = @GNULIB_FCHOWNAT@ +GNULIB_FCLOSE = @GNULIB_FCLOSE@ +GNULIB_FCNTL = @GNULIB_FCNTL@ +GNULIB_FDATASYNC = @GNULIB_FDATASYNC@ +GNULIB_FDOPEN = @GNULIB_FDOPEN@ +GNULIB_FDOPENDIR = @GNULIB_FDOPENDIR@ +GNULIB_FFLUSH = @GNULIB_FFLUSH@ +GNULIB_FFS = @GNULIB_FFS@ +GNULIB_FFSL = @GNULIB_FFSL@ +GNULIB_FFSLL = @GNULIB_FFSLL@ +GNULIB_FGETC = @GNULIB_FGETC@ +GNULIB_FGETS = @GNULIB_FGETS@ +GNULIB_FLOCK = @GNULIB_FLOCK@ +GNULIB_FNMATCH = @GNULIB_FNMATCH@ +GNULIB_FOPEN = @GNULIB_FOPEN@ +GNULIB_FPRINTF = @GNULIB_FPRINTF@ +GNULIB_FPRINTF_POSIX = @GNULIB_FPRINTF_POSIX@ +GNULIB_FPURGE = @GNULIB_FPURGE@ +GNULIB_FPUTC = @GNULIB_FPUTC@ +GNULIB_FPUTS = @GNULIB_FPUTS@ +GNULIB_FREAD = @GNULIB_FREAD@ +GNULIB_FREOPEN = @GNULIB_FREOPEN@ +GNULIB_FSCANF = @GNULIB_FSCANF@ +GNULIB_FSEEK = @GNULIB_FSEEK@ +GNULIB_FSEEKO = @GNULIB_FSEEKO@ +GNULIB_FSTAT = @GNULIB_FSTAT@ +GNULIB_FSTATAT = @GNULIB_FSTATAT@ +GNULIB_FSYNC = @GNULIB_FSYNC@ +GNULIB_FTELL = @GNULIB_FTELL@ +GNULIB_FTELLO = @GNULIB_FTELLO@ +GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@ +GNULIB_FUTIMENS = @GNULIB_FUTIMENS@ +GNULIB_FWRITE = @GNULIB_FWRITE@ +GNULIB_GETC = @GNULIB_GETC@ +GNULIB_GETCHAR = @GNULIB_GETCHAR@ +GNULIB_GETCWD = @GNULIB_GETCWD@ +GNULIB_GETDELIM = @GNULIB_GETDELIM@ +GNULIB_GETDOMAINNAME = @GNULIB_GETDOMAINNAME@ +GNULIB_GETDTABLESIZE = @GNULIB_GETDTABLESIZE@ +GNULIB_GETENTROPY = @GNULIB_GETENTROPY@ +GNULIB_GETGROUPS = @GNULIB_GETGROUPS@ +GNULIB_GETHOSTNAME = @GNULIB_GETHOSTNAME@ +GNULIB_GETLINE = @GNULIB_GETLINE@ +GNULIB_GETLOADAVG = @GNULIB_GETLOADAVG@ +GNULIB_GETLOGIN = @GNULIB_GETLOGIN@ +GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@ +GNULIB_GETOPT_POSIX = @GNULIB_GETOPT_POSIX@ +GNULIB_GETPAGESIZE = @GNULIB_GETPAGESIZE@ +GNULIB_GETPASS = @GNULIB_GETPASS@ +GNULIB_GETPEERNAME = @GNULIB_GETPEERNAME@ +GNULIB_GETSOCKNAME = @GNULIB_GETSOCKNAME@ +GNULIB_GETSOCKOPT = @GNULIB_GETSOCKOPT@ +GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@ +GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@ +GNULIB_GETUSERSHELL = @GNULIB_GETUSERSHELL@ +GNULIB_GLOB = @GNULIB_GLOB@ +GNULIB_GL_UNISTD_H_GETOPT = @GNULIB_GL_UNISTD_H_GETOPT@ +GNULIB_GRANTPT = @GNULIB_GRANTPT@ +GNULIB_GROUP_MEMBER = @GNULIB_GROUP_MEMBER@ +GNULIB_IMAXABS = @GNULIB_IMAXABS@ +GNULIB_IMAXDIV = @GNULIB_IMAXDIV@ +GNULIB_IOCTL = @GNULIB_IOCTL@ +GNULIB_ISATTY = @GNULIB_ISATTY@ +GNULIB_ISBLANK = @GNULIB_ISBLANK@ +GNULIB_ISWBLANK = @GNULIB_ISWBLANK@ +GNULIB_ISWCTYPE = @GNULIB_ISWCTYPE@ +GNULIB_ISWDIGIT = @GNULIB_ISWDIGIT@ +GNULIB_ISWXDIGIT = @GNULIB_ISWXDIGIT@ +GNULIB_LCHMOD = @GNULIB_LCHMOD@ +GNULIB_LCHOWN = @GNULIB_LCHOWN@ +GNULIB_LINK = @GNULIB_LINK@ +GNULIB_LINKAT = @GNULIB_LINKAT@ +GNULIB_LISTEN = @GNULIB_LISTEN@ +GNULIB_LOCALECONV = @GNULIB_LOCALECONV@ +GNULIB_LOCALENAME = @GNULIB_LOCALENAME@ +GNULIB_LOCALTIME = @GNULIB_LOCALTIME@ +GNULIB_LSEEK = @GNULIB_LSEEK@ +GNULIB_LSTAT = @GNULIB_LSTAT@ +GNULIB_MALLOC_POSIX = @GNULIB_MALLOC_POSIX@ +GNULIB_MBRLEN = @GNULIB_MBRLEN@ +GNULIB_MBRTOWC = @GNULIB_MBRTOWC@ +GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@ +GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@ +GNULIB_MBSCHR = @GNULIB_MBSCHR@ +GNULIB_MBSCSPN = @GNULIB_MBSCSPN@ +GNULIB_MBSINIT = @GNULIB_MBSINIT@ +GNULIB_MBSLEN = @GNULIB_MBSLEN@ +GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@ +GNULIB_MBSNLEN = @GNULIB_MBSNLEN@ +GNULIB_MBSNRTOWCS = @GNULIB_MBSNRTOWCS@ +GNULIB_MBSPBRK = @GNULIB_MBSPBRK@ +GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@ +GNULIB_MBSRCHR = @GNULIB_MBSRCHR@ +GNULIB_MBSRTOWCS = @GNULIB_MBSRTOWCS@ +GNULIB_MBSSEP = @GNULIB_MBSSEP@ +GNULIB_MBSSPN = @GNULIB_MBSSPN@ +GNULIB_MBSSTR = @GNULIB_MBSSTR@ +GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@ +GNULIB_MBTOWC = @GNULIB_MBTOWC@ +GNULIB_MEMCHR = @GNULIB_MEMCHR@ +GNULIB_MEMMEM = @GNULIB_MEMMEM@ +GNULIB_MEMPCPY = @GNULIB_MEMPCPY@ +GNULIB_MEMRCHR = @GNULIB_MEMRCHR@ +GNULIB_MKDIRAT = @GNULIB_MKDIRAT@ +GNULIB_MKDTEMP = @GNULIB_MKDTEMP@ +GNULIB_MKFIFO = @GNULIB_MKFIFO@ +GNULIB_MKFIFOAT = @GNULIB_MKFIFOAT@ +GNULIB_MKNOD = @GNULIB_MKNOD@ +GNULIB_MKNODAT = @GNULIB_MKNODAT@ +GNULIB_MKOSTEMP = @GNULIB_MKOSTEMP@ +GNULIB_MKOSTEMPS = @GNULIB_MKOSTEMPS@ +GNULIB_MKSTEMP = @GNULIB_MKSTEMP@ +GNULIB_MKSTEMPS = @GNULIB_MKSTEMPS@ +GNULIB_MKTIME = @GNULIB_MKTIME@ +GNULIB_NANOSLEEP = @GNULIB_NANOSLEEP@ +GNULIB_NL_LANGINFO = @GNULIB_NL_LANGINFO@ +GNULIB_NONBLOCKING = @GNULIB_NONBLOCKING@ +GNULIB_OBSTACK_PRINTF = @GNULIB_OBSTACK_PRINTF@ +GNULIB_OBSTACK_PRINTF_POSIX = @GNULIB_OBSTACK_PRINTF_POSIX@ +GNULIB_OPEN = @GNULIB_OPEN@ +GNULIB_OPENAT = @GNULIB_OPENAT@ +GNULIB_OPENDIR = @GNULIB_OPENDIR@ +GNULIB_OVERRIDES_STRUCT_STAT = @GNULIB_OVERRIDES_STRUCT_STAT@ +GNULIB_OVERRIDES_WINT_T = @GNULIB_OVERRIDES_WINT_T@ +GNULIB_PCLOSE = @GNULIB_PCLOSE@ +GNULIB_PERROR = @GNULIB_PERROR@ +GNULIB_PIPE = @GNULIB_PIPE@ +GNULIB_PIPE2 = @GNULIB_PIPE2@ +GNULIB_POPEN = @GNULIB_POPEN@ +GNULIB_POSIX_OPENPT = @GNULIB_POSIX_OPENPT@ +GNULIB_PREAD = @GNULIB_PREAD@ +GNULIB_PRINTF = @GNULIB_PRINTF@ +GNULIB_PRINTF_POSIX = @GNULIB_PRINTF_POSIX@ +GNULIB_PSELECT = @GNULIB_PSELECT@ +GNULIB_PTHREAD_SIGMASK = @GNULIB_PTHREAD_SIGMASK@ +GNULIB_PTSNAME = @GNULIB_PTSNAME@ +GNULIB_PTSNAME_R = @GNULIB_PTSNAME_R@ +GNULIB_PUTC = @GNULIB_PUTC@ +GNULIB_PUTCHAR = @GNULIB_PUTCHAR@ +GNULIB_PUTENV = @GNULIB_PUTENV@ +GNULIB_PUTS = @GNULIB_PUTS@ +GNULIB_PWRITE = @GNULIB_PWRITE@ +GNULIB_QSORT_R = @GNULIB_QSORT_R@ +GNULIB_RAISE = @GNULIB_RAISE@ +GNULIB_RANDOM = @GNULIB_RANDOM@ +GNULIB_RANDOM_R = @GNULIB_RANDOM_R@ +GNULIB_RAWMEMCHR = @GNULIB_RAWMEMCHR@ +GNULIB_READ = @GNULIB_READ@ +GNULIB_READDIR = @GNULIB_READDIR@ +GNULIB_READLINK = @GNULIB_READLINK@ +GNULIB_READLINKAT = @GNULIB_READLINKAT@ +GNULIB_REALLOCARRAY = @GNULIB_REALLOCARRAY@ +GNULIB_REALLOC_POSIX = @GNULIB_REALLOC_POSIX@ +GNULIB_REALPATH = @GNULIB_REALPATH@ +GNULIB_RECV = @GNULIB_RECV@ +GNULIB_RECVFROM = @GNULIB_RECVFROM@ +GNULIB_REMOVE = @GNULIB_REMOVE@ +GNULIB_RENAME = @GNULIB_RENAME@ +GNULIB_RENAMEAT = @GNULIB_RENAMEAT@ +GNULIB_REWINDDIR = @GNULIB_REWINDDIR@ +GNULIB_RMDIR = @GNULIB_RMDIR@ +GNULIB_RPMATCH = @GNULIB_RPMATCH@ +GNULIB_SCANDIR = @GNULIB_SCANDIR@ +GNULIB_SCANF = @GNULIB_SCANF@ +GNULIB_SECURE_GETENV = @GNULIB_SECURE_GETENV@ +GNULIB_SELECT = @GNULIB_SELECT@ +GNULIB_SEND = @GNULIB_SEND@ +GNULIB_SENDTO = @GNULIB_SENDTO@ +GNULIB_SETENV = @GNULIB_SETENV@ +GNULIB_SETHOSTNAME = @GNULIB_SETHOSTNAME@ +GNULIB_SETLOCALE = @GNULIB_SETLOCALE@ +GNULIB_SETLOCALE_NULL = @GNULIB_SETLOCALE_NULL@ +GNULIB_SETSOCKOPT = @GNULIB_SETSOCKOPT@ +GNULIB_SHUTDOWN = @GNULIB_SHUTDOWN@ +GNULIB_SIGACTION = @GNULIB_SIGACTION@ +GNULIB_SIGNAL_H_SIGPIPE = @GNULIB_SIGNAL_H_SIGPIPE@ +GNULIB_SIGPROCMASK = @GNULIB_SIGPROCMASK@ +GNULIB_SLEEP = @GNULIB_SLEEP@ +GNULIB_SNPRINTF = @GNULIB_SNPRINTF@ +GNULIB_SOCKET = @GNULIB_SOCKET@ +GNULIB_SPRINTF_POSIX = @GNULIB_SPRINTF_POSIX@ +GNULIB_STAT = @GNULIB_STAT@ +GNULIB_STDIO_H_NONBLOCKING = @GNULIB_STDIO_H_NONBLOCKING@ +GNULIB_STDIO_H_SIGPIPE = @GNULIB_STDIO_H_SIGPIPE@ +GNULIB_STPCPY = @GNULIB_STPCPY@ +GNULIB_STPNCPY = @GNULIB_STPNCPY@ +GNULIB_STRCASESTR = @GNULIB_STRCASESTR@ +GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@ +GNULIB_STRDUP = @GNULIB_STRDUP@ +GNULIB_STRERROR = @GNULIB_STRERROR@ +GNULIB_STRERROR_R = @GNULIB_STRERROR_R@ +GNULIB_STRFTIME = @GNULIB_STRFTIME@ +GNULIB_STRNCAT = @GNULIB_STRNCAT@ +GNULIB_STRNDUP = @GNULIB_STRNDUP@ +GNULIB_STRNLEN = @GNULIB_STRNLEN@ +GNULIB_STRPBRK = @GNULIB_STRPBRK@ +GNULIB_STRPTIME = @GNULIB_STRPTIME@ +GNULIB_STRSEP = @GNULIB_STRSEP@ +GNULIB_STRSIGNAL = @GNULIB_STRSIGNAL@ +GNULIB_STRSTR = @GNULIB_STRSTR@ +GNULIB_STRTOD = @GNULIB_STRTOD@ +GNULIB_STRTOIMAX = @GNULIB_STRTOIMAX@ +GNULIB_STRTOK_R = @GNULIB_STRTOK_R@ +GNULIB_STRTOLD = @GNULIB_STRTOLD@ +GNULIB_STRTOLL = @GNULIB_STRTOLL@ +GNULIB_STRTOULL = @GNULIB_STRTOULL@ +GNULIB_STRTOUMAX = @GNULIB_STRTOUMAX@ +GNULIB_STRVERSCMP = @GNULIB_STRVERSCMP@ +GNULIB_SYMLINK = @GNULIB_SYMLINK@ +GNULIB_SYMLINKAT = @GNULIB_SYMLINKAT@ +GNULIB_SYSTEM_POSIX = @GNULIB_SYSTEM_POSIX@ +GNULIB_TIMEGM = @GNULIB_TIMEGM@ +GNULIB_TIME_R = @GNULIB_TIME_R@ +GNULIB_TIME_RZ = @GNULIB_TIME_RZ@ +GNULIB_TMPFILE = @GNULIB_TMPFILE@ +GNULIB_TOWCTRANS = @GNULIB_TOWCTRANS@ +GNULIB_TRUNCATE = @GNULIB_TRUNCATE@ +GNULIB_TTYNAME_R = @GNULIB_TTYNAME_R@ +GNULIB_TZSET = @GNULIB_TZSET@ +GNULIB_UNISTD_H_NONBLOCKING = @GNULIB_UNISTD_H_NONBLOCKING@ +GNULIB_UNISTD_H_SIGPIPE = @GNULIB_UNISTD_H_SIGPIPE@ +GNULIB_UNLINK = @GNULIB_UNLINK@ +GNULIB_UNLINKAT = @GNULIB_UNLINKAT@ +GNULIB_UNLOCKPT = @GNULIB_UNLOCKPT@ +GNULIB_UNSETENV = @GNULIB_UNSETENV@ +GNULIB_USLEEP = @GNULIB_USLEEP@ +GNULIB_UTIME = @GNULIB_UTIME@ +GNULIB_UTIMENSAT = @GNULIB_UTIMENSAT@ +GNULIB_VASPRINTF = @GNULIB_VASPRINTF@ +GNULIB_VDPRINTF = @GNULIB_VDPRINTF@ +GNULIB_VFPRINTF = @GNULIB_VFPRINTF@ +GNULIB_VFPRINTF_POSIX = @GNULIB_VFPRINTF_POSIX@ +GNULIB_VFSCANF = @GNULIB_VFSCANF@ +GNULIB_VPRINTF = @GNULIB_VPRINTF@ +GNULIB_VPRINTF_POSIX = @GNULIB_VPRINTF_POSIX@ +GNULIB_VSCANF = @GNULIB_VSCANF@ +GNULIB_VSNPRINTF = @GNULIB_VSNPRINTF@ +GNULIB_VSPRINTF_POSIX = @GNULIB_VSPRINTF_POSIX@ +GNULIB_WCPCPY = @GNULIB_WCPCPY@ +GNULIB_WCPNCPY = @GNULIB_WCPNCPY@ +GNULIB_WCRTOMB = @GNULIB_WCRTOMB@ +GNULIB_WCSCASECMP = @GNULIB_WCSCASECMP@ +GNULIB_WCSCAT = @GNULIB_WCSCAT@ +GNULIB_WCSCHR = @GNULIB_WCSCHR@ +GNULIB_WCSCMP = @GNULIB_WCSCMP@ +GNULIB_WCSCOLL = @GNULIB_WCSCOLL@ +GNULIB_WCSCPY = @GNULIB_WCSCPY@ +GNULIB_WCSCSPN = @GNULIB_WCSCSPN@ +GNULIB_WCSDUP = @GNULIB_WCSDUP@ +GNULIB_WCSFTIME = @GNULIB_WCSFTIME@ +GNULIB_WCSLEN = @GNULIB_WCSLEN@ +GNULIB_WCSNCASECMP = @GNULIB_WCSNCASECMP@ +GNULIB_WCSNCAT = @GNULIB_WCSNCAT@ +GNULIB_WCSNCMP = @GNULIB_WCSNCMP@ +GNULIB_WCSNCPY = @GNULIB_WCSNCPY@ +GNULIB_WCSNLEN = @GNULIB_WCSNLEN@ +GNULIB_WCSNRTOMBS = @GNULIB_WCSNRTOMBS@ +GNULIB_WCSPBRK = @GNULIB_WCSPBRK@ +GNULIB_WCSRCHR = @GNULIB_WCSRCHR@ +GNULIB_WCSRTOMBS = @GNULIB_WCSRTOMBS@ +GNULIB_WCSSPN = @GNULIB_WCSSPN@ +GNULIB_WCSSTR = @GNULIB_WCSSTR@ +GNULIB_WCSTOK = @GNULIB_WCSTOK@ +GNULIB_WCSWIDTH = @GNULIB_WCSWIDTH@ +GNULIB_WCSXFRM = @GNULIB_WCSXFRM@ +GNULIB_WCTOB = @GNULIB_WCTOB@ +GNULIB_WCTOMB = @GNULIB_WCTOMB@ +GNULIB_WCTRANS = @GNULIB_WCTRANS@ +GNULIB_WCTYPE = @GNULIB_WCTYPE@ +GNULIB_WCWIDTH = @GNULIB_WCWIDTH@ +GNULIB_WMEMCHR = @GNULIB_WMEMCHR@ +GNULIB_WMEMCMP = @GNULIB_WMEMCMP@ +GNULIB_WMEMCPY = @GNULIB_WMEMCPY@ +GNULIB_WMEMMOVE = @GNULIB_WMEMMOVE@ +GNULIB_WMEMPCPY = @GNULIB_WMEMPCPY@ +GNULIB_WMEMSET = @GNULIB_WMEMSET@ +GNULIB_WRITE = @GNULIB_WRITE@ +GNULIB__EXIT = @GNULIB__EXIT@ +GREP = @GREP@ +HAVE_ACCEPT4 = @HAVE_ACCEPT4@ +HAVE_ALLOCA_H = @HAVE_ALLOCA_H@ +HAVE_ALPHASORT = @HAVE_ALPHASORT@ +HAVE_ATOLL = @HAVE_ATOLL@ +HAVE_BTOWC = @HAVE_BTOWC@ +HAVE_C99_STDINT_H = @HAVE_C99_STDINT_H@ +HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@ +HAVE_CHOWN = @HAVE_CHOWN@ +HAVE_CLOSEDIR = @HAVE_CLOSEDIR@ +HAVE_COPY_FILE_RANGE = @HAVE_COPY_FILE_RANGE@ +HAVE_CRTDEFS_H = @HAVE_CRTDEFS_H@ +HAVE_DECL_DIRFD = @HAVE_DECL_DIRFD@ +HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@ +HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@ +HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@ +HAVE_DECL_FDOPENDIR = @HAVE_DECL_FDOPENDIR@ +HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@ +HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@ +HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@ +HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@ +HAVE_DECL_GETDOMAINNAME = @HAVE_DECL_GETDOMAINNAME@ +HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@ +HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@ +HAVE_DECL_GETLOGIN = @HAVE_DECL_GETLOGIN@ +HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@ +HAVE_DECL_GETPAGESIZE = @HAVE_DECL_GETPAGESIZE@ +HAVE_DECL_GETUSERSHELL = @HAVE_DECL_GETUSERSHELL@ +HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@ +HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@ +HAVE_DECL_INITSTATE = @HAVE_DECL_INITSTATE@ +HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@ +HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@ +HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@ +HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@ +HAVE_DECL_SETENV = @HAVE_DECL_SETENV@ +HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@ +HAVE_DECL_SETSTATE = @HAVE_DECL_SETSTATE@ +HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@ +HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@ +HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@ +HAVE_DECL_STRNCASECMP = @HAVE_DECL_STRNCASECMP@ +HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@ +HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@ +HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@ +HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@ +HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@ +HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@ +HAVE_DECL_TRUNCATE = @HAVE_DECL_TRUNCATE@ +HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@ +HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@ +HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@ +HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@ +HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@ +HAVE_DIRENT_H = @HAVE_DIRENT_H@ +HAVE_DPRINTF = @HAVE_DPRINTF@ +HAVE_DUP2 = @HAVE_DUP2@ +HAVE_DUP3 = @HAVE_DUP3@ +HAVE_DUPLOCALE = @HAVE_DUPLOCALE@ +HAVE_EUIDACCESS = @HAVE_EUIDACCESS@ +HAVE_EXPLICIT_BZERO = @HAVE_EXPLICIT_BZERO@ +HAVE_FACCESSAT = @HAVE_FACCESSAT@ +HAVE_FCHDIR = @HAVE_FCHDIR@ +HAVE_FCHMODAT = @HAVE_FCHMODAT@ +HAVE_FCHOWNAT = @HAVE_FCHOWNAT@ +HAVE_FCNTL = @HAVE_FCNTL@ +HAVE_FDATASYNC = @HAVE_FDATASYNC@ +HAVE_FDOPENDIR = @HAVE_FDOPENDIR@ +HAVE_FEATURES_H = @HAVE_FEATURES_H@ +HAVE_FFS = @HAVE_FFS@ +HAVE_FFSL = @HAVE_FFSL@ +HAVE_FFSLL = @HAVE_FFSLL@ +HAVE_FLOCK = @HAVE_FLOCK@ +HAVE_FNMATCH = @HAVE_FNMATCH@ +HAVE_FNMATCH_H = @HAVE_FNMATCH_H@ +HAVE_FREELOCALE = @HAVE_FREELOCALE@ +HAVE_FSEEKO = @HAVE_FSEEKO@ +HAVE_FSTATAT = @HAVE_FSTATAT@ +HAVE_FSYNC = @HAVE_FSYNC@ +HAVE_FTELLO = @HAVE_FTELLO@ +HAVE_FTRUNCATE = @HAVE_FTRUNCATE@ +HAVE_FUTIMENS = @HAVE_FUTIMENS@ +HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@ +HAVE_GETENTROPY = @HAVE_GETENTROPY@ +HAVE_GETGROUPS = @HAVE_GETGROUPS@ +HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@ +HAVE_GETLOGIN = @HAVE_GETLOGIN@ +HAVE_GETOPT_H = @HAVE_GETOPT_H@ +HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@ +HAVE_GETPASS = @HAVE_GETPASS@ +HAVE_GETSUBOPT = @HAVE_GETSUBOPT@ +HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@ +HAVE_GLOB = @HAVE_GLOB@ +HAVE_GLOB_H = @HAVE_GLOB_H@ +HAVE_GLOB_PATTERN_P = @HAVE_GLOB_PATTERN_P@ +HAVE_GRANTPT = @HAVE_GRANTPT@ +HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@ +HAVE_IMAXDIV_T = @HAVE_IMAXDIV_T@ +HAVE_INITSTATE = @HAVE_INITSTATE@ +HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ +HAVE_ISBLANK = @HAVE_ISBLANK@ +HAVE_ISWBLANK = @HAVE_ISWBLANK@ +HAVE_ISWCNTRL = @HAVE_ISWCNTRL@ +HAVE_LANGINFO_ALTMON = @HAVE_LANGINFO_ALTMON@ +HAVE_LANGINFO_CODESET = @HAVE_LANGINFO_CODESET@ +HAVE_LANGINFO_ERA = @HAVE_LANGINFO_ERA@ +HAVE_LANGINFO_H = @HAVE_LANGINFO_H@ +HAVE_LANGINFO_T_FMT_AMPM = @HAVE_LANGINFO_T_FMT_AMPM@ +HAVE_LANGINFO_YESEXPR = @HAVE_LANGINFO_YESEXPR@ +HAVE_LCHMOD = @HAVE_LCHMOD@ +HAVE_LCHOWN = @HAVE_LCHOWN@ +HAVE_LINK = @HAVE_LINK@ +HAVE_LINKAT = @HAVE_LINKAT@ +HAVE_LSTAT = @HAVE_LSTAT@ +HAVE_MAX_ALIGN_T = @HAVE_MAX_ALIGN_T@ +HAVE_MBRLEN = @HAVE_MBRLEN@ +HAVE_MBRTOWC = @HAVE_MBRTOWC@ +HAVE_MBSINIT = @HAVE_MBSINIT@ +HAVE_MBSLEN = @HAVE_MBSLEN@ +HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@ +HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@ +HAVE_MBTOWC = @HAVE_MBTOWC@ +HAVE_MEMCHR = @HAVE_MEMCHR@ +HAVE_MEMPCPY = @HAVE_MEMPCPY@ +HAVE_MKDIRAT = @HAVE_MKDIRAT@ +HAVE_MKDTEMP = @HAVE_MKDTEMP@ +HAVE_MKFIFO = @HAVE_MKFIFO@ +HAVE_MKFIFOAT = @HAVE_MKFIFOAT@ +HAVE_MKNOD = @HAVE_MKNOD@ +HAVE_MKNODAT = @HAVE_MKNODAT@ +HAVE_MKOSTEMP = @HAVE_MKOSTEMP@ +HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@ +HAVE_MKSTEMP = @HAVE_MKSTEMP@ +HAVE_MKSTEMPS = @HAVE_MKSTEMPS@ +HAVE_MSVC_INVALID_PARAMETER_HANDLER = @HAVE_MSVC_INVALID_PARAMETER_HANDLER@ +HAVE_NANOSLEEP = @HAVE_NANOSLEEP@ +HAVE_NEWLOCALE = @HAVE_NEWLOCALE@ +HAVE_NL_LANGINFO = @HAVE_NL_LANGINFO@ +HAVE_OPENAT = @HAVE_OPENAT@ +HAVE_OPENDIR = @HAVE_OPENDIR@ +HAVE_OS_H = @HAVE_OS_H@ +HAVE_PCLOSE = @HAVE_PCLOSE@ +HAVE_PIPE = @HAVE_PIPE@ +HAVE_PIPE2 = @HAVE_PIPE2@ +HAVE_POPEN = @HAVE_POPEN@ +HAVE_POSIX_OPENPT = @HAVE_POSIX_OPENPT@ +HAVE_POSIX_SIGNALBLOCKING = @HAVE_POSIX_SIGNALBLOCKING@ +HAVE_PREAD = @HAVE_PREAD@ +HAVE_PSELECT = @HAVE_PSELECT@ +HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@ +HAVE_PTSNAME = @HAVE_PTSNAME@ +HAVE_PTSNAME_R = @HAVE_PTSNAME_R@ +HAVE_PWRITE = @HAVE_PWRITE@ +HAVE_QSORT_R = @HAVE_QSORT_R@ +HAVE_RAISE = @HAVE_RAISE@ +HAVE_RANDOM = @HAVE_RANDOM@ +HAVE_RANDOM_H = @HAVE_RANDOM_H@ +HAVE_RANDOM_R = @HAVE_RANDOM_R@ +HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@ +HAVE_READDIR = @HAVE_READDIR@ +HAVE_READLINK = @HAVE_READLINK@ +HAVE_READLINKAT = @HAVE_READLINKAT@ +HAVE_REALLOCARRAY = @HAVE_REALLOCARRAY@ +HAVE_REALPATH = @HAVE_REALPATH@ +HAVE_RENAMEAT = @HAVE_RENAMEAT@ +HAVE_REWINDDIR = @HAVE_REWINDDIR@ +HAVE_RPMATCH = @HAVE_RPMATCH@ +HAVE_SA_FAMILY_T = @HAVE_SA_FAMILY_T@ +HAVE_SCANDIR = @HAVE_SCANDIR@ +HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@ +HAVE_SETENV = @HAVE_SETENV@ +HAVE_SETHOSTNAME = @HAVE_SETHOSTNAME@ +HAVE_SETSTATE = @HAVE_SETSTATE@ +HAVE_SIGACTION = @HAVE_SIGACTION@ +HAVE_SIGHANDLER_T = @HAVE_SIGHANDLER_T@ +HAVE_SIGINFO_T = @HAVE_SIGINFO_T@ +HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ +HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ +HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ +HAVE_SIGSET_T = @HAVE_SIGSET_T@ +HAVE_SLEEP = @HAVE_SLEEP@ +HAVE_STDINT_H = @HAVE_STDINT_H@ +HAVE_STPCPY = @HAVE_STPCPY@ +HAVE_STPNCPY = @HAVE_STPNCPY@ +HAVE_STRCASECMP = @HAVE_STRCASECMP@ +HAVE_STRCASESTR = @HAVE_STRCASESTR@ +HAVE_STRCHRNUL = @HAVE_STRCHRNUL@ +HAVE_STRINGS_H = @HAVE_STRINGS_H@ +HAVE_STRPBRK = @HAVE_STRPBRK@ +HAVE_STRPTIME = @HAVE_STRPTIME@ +HAVE_STRSEP = @HAVE_STRSEP@ +HAVE_STRTOD = @HAVE_STRTOD@ +HAVE_STRTOLD = @HAVE_STRTOLD@ +HAVE_STRTOLL = @HAVE_STRTOLL@ +HAVE_STRTOULL = @HAVE_STRTOULL@ +HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@ +HAVE_STRUCT_SIGACTION_SA_SIGACTION = @HAVE_STRUCT_SIGACTION_SA_SIGACTION@ +HAVE_STRUCT_SOCKADDR_STORAGE = @HAVE_STRUCT_SOCKADDR_STORAGE@ +HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY = @HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY@ +HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@ +HAVE_STRVERSCMP = @HAVE_STRVERSCMP@ +HAVE_SYMLINK = @HAVE_SYMLINK@ +HAVE_SYMLINKAT = @HAVE_SYMLINKAT@ +HAVE_SYSEXITS_H = @HAVE_SYSEXITS_H@ +HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ +HAVE_SYS_CDEFS_H = @HAVE_SYS_CDEFS_H@ +HAVE_SYS_FILE_H = @HAVE_SYS_FILE_H@ +HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ +HAVE_SYS_IOCTL_H = @HAVE_SYS_IOCTL_H@ +HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@ +HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@ +HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@ +HAVE_SYS_SOCKET_H = @HAVE_SYS_SOCKET_H@ +HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@ +HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ +HAVE_SYS_UIO_H = @HAVE_SYS_UIO_H@ +HAVE_TIMEGM = @HAVE_TIMEGM@ +HAVE_TIMEZONE_T = @HAVE_TIMEZONE_T@ +HAVE_TYPE_VOLATILE_SIG_ATOMIC_T = @HAVE_TYPE_VOLATILE_SIG_ATOMIC_T@ +HAVE_TZSET = @HAVE_TZSET@ +HAVE_UNISTD_H = @HAVE_UNISTD_H@ +HAVE_UNLINKAT = @HAVE_UNLINKAT@ +HAVE_UNLOCKPT = @HAVE_UNLOCKPT@ +HAVE_USLEEP = @HAVE_USLEEP@ +HAVE_UTIME = @HAVE_UTIME@ +HAVE_UTIMENSAT = @HAVE_UTIMENSAT@ +HAVE_UTIME_H = @HAVE_UTIME_H@ +HAVE_VASPRINTF = @HAVE_VASPRINTF@ +HAVE_VDPRINTF = @HAVE_VDPRINTF@ +HAVE_VISIBILITY = @HAVE_VISIBILITY@ +HAVE_WCHAR_H = @HAVE_WCHAR_H@ +HAVE_WCHAR_T = @HAVE_WCHAR_T@ +HAVE_WCPCPY = @HAVE_WCPCPY@ +HAVE_WCPNCPY = @HAVE_WCPNCPY@ +HAVE_WCRTOMB = @HAVE_WCRTOMB@ +HAVE_WCSCASECMP = @HAVE_WCSCASECMP@ +HAVE_WCSCAT = @HAVE_WCSCAT@ +HAVE_WCSCHR = @HAVE_WCSCHR@ +HAVE_WCSCMP = @HAVE_WCSCMP@ +HAVE_WCSCOLL = @HAVE_WCSCOLL@ +HAVE_WCSCPY = @HAVE_WCSCPY@ +HAVE_WCSCSPN = @HAVE_WCSCSPN@ +HAVE_WCSDUP = @HAVE_WCSDUP@ +HAVE_WCSFTIME = @HAVE_WCSFTIME@ +HAVE_WCSLEN = @HAVE_WCSLEN@ +HAVE_WCSNCASECMP = @HAVE_WCSNCASECMP@ +HAVE_WCSNCAT = @HAVE_WCSNCAT@ +HAVE_WCSNCMP = @HAVE_WCSNCMP@ +HAVE_WCSNCPY = @HAVE_WCSNCPY@ +HAVE_WCSNLEN = @HAVE_WCSNLEN@ +HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@ +HAVE_WCSPBRK = @HAVE_WCSPBRK@ +HAVE_WCSRCHR = @HAVE_WCSRCHR@ +HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@ +HAVE_WCSSPN = @HAVE_WCSSPN@ +HAVE_WCSSTR = @HAVE_WCSSTR@ +HAVE_WCSTOK = @HAVE_WCSTOK@ +HAVE_WCSWIDTH = @HAVE_WCSWIDTH@ +HAVE_WCSXFRM = @HAVE_WCSXFRM@ +HAVE_WCTRANS_T = @HAVE_WCTRANS_T@ +HAVE_WCTYPE_H = @HAVE_WCTYPE_H@ +HAVE_WCTYPE_T = @HAVE_WCTYPE_T@ +HAVE_WINSOCK2_H = @HAVE_WINSOCK2_H@ +HAVE_WINT_T = @HAVE_WINT_T@ +HAVE_WMEMCHR = @HAVE_WMEMCHR@ +HAVE_WMEMCMP = @HAVE_WMEMCMP@ +HAVE_WMEMCPY = @HAVE_WMEMCPY@ +HAVE_WMEMMOVE = @HAVE_WMEMMOVE@ +HAVE_WMEMPCPY = @HAVE_WMEMPCPY@ +HAVE_WMEMSET = @HAVE_WMEMSET@ +HAVE_WS2TCPIP_H = @HAVE_WS2TCPIP_H@ +HAVE_XLOCALE_H = @HAVE_XLOCALE_H@ +HAVE__BOOL = @HAVE__BOOL@ +HAVE__EXIT = @HAVE__EXIT@ +IGNORE_UNUSED_LIBRARIES_CFLAGS = @IGNORE_UNUSED_LIBRARIES_CFLAGS@ +INCLUDE_NEXT = @INCLUDE_NEXT@ +INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@ +INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBCOMPRESS = @LIBCOMPRESS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMULTITHREAD = @LIBMULTITHREAD@ +LIBOBJS = @LIBOBJS@ +LIBPMULTITHREAD = @LIBPMULTITHREAD@ +LIBPTHREAD = @LIBPTHREAD@ +LIBS = @LIBS@ +LIBSOCKET = @LIBSOCKET@ +LIBSTDTHREAD = @LIBSTDTHREAD@ +LIBTHREAD = @LIBTHREAD@ +LIBTOOL = @LIBTOOL@ +LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@ +LIB_GETLOGIN = @LIB_GETLOGIN@ +LIB_HARD_LOCALE = @LIB_HARD_LOCALE@ +LIB_MBRTOWC = @LIB_MBRTOWC@ +LIB_NANOSLEEP = @LIB_NANOSLEEP@ +LIB_NL_LANGINFO = @LIB_NL_LANGINFO@ +LIB_SCHED_YIELD = @LIB_SCHED_YIELD@ +LIB_SELECT = @LIB_SELECT@ +LIB_SETLOCALE_NULL = @LIB_SETLOCALE_NULL@ +LIMITS_H = @LIMITS_H@ +LINGUAS = @LINGUAS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@ +LOCALE_FR = @LOCALE_FR@ +LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@ +LOCALE_JA = @LOCALE_JA@ +LOCALE_ZH_CN = @LOCALE_ZH_CN@ +LTALLOCA = @LTALLOCA@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBMULTITHREAD = @LTLIBMULTITHREAD@ +LTLIBOBJS = @LTLIBOBJS@ +LTLIBTHREAD = @LTLIBTHREAD@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANDIR_LAYOUT = @MANDIR_LAYOUT@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAN_SUBDIRS = @MAN_SUBDIRS@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NEXT_AS_FIRST_DIRECTIVE_CTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_CTYPE_H@ +NEXT_AS_FIRST_DIRECTIVE_DIRENT_H = @NEXT_AS_FIRST_DIRECTIVE_DIRENT_H@ +NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@ +NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@ +NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@ +NEXT_AS_FIRST_DIRECTIVE_FNMATCH_H = @NEXT_AS_FIRST_DIRECTIVE_FNMATCH_H@ +NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@ +NEXT_AS_FIRST_DIRECTIVE_GLOB_H = @NEXT_AS_FIRST_DIRECTIVE_GLOB_H@ +NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@ +NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H = @NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H@ +NEXT_AS_FIRST_DIRECTIVE_LIMITS_H = @NEXT_AS_FIRST_DIRECTIVE_LIMITS_H@ +NEXT_AS_FIRST_DIRECTIVE_LOCALE_H = @NEXT_AS_FIRST_DIRECTIVE_LOCALE_H@ +NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@ +NEXT_AS_FIRST_DIRECTIVE_STDARG_H = @NEXT_AS_FIRST_DIRECTIVE_STDARG_H@ +NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@ +NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@ +NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@ +NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@ +NEXT_AS_FIRST_DIRECTIVE_STRINGS_H = @NEXT_AS_FIRST_DIRECTIVE_STRINGS_H@ +NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@ +NEXT_AS_FIRST_DIRECTIVE_SYSEXITS_H = @NEXT_AS_FIRST_DIRECTIVE_SYSEXITS_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_FILE_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_FILE_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H@ +NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@ +NEXT_AS_FIRST_DIRECTIVE_UTIME_H = @NEXT_AS_FIRST_DIRECTIVE_UTIME_H@ +NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@ +NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H@ +NEXT_CTYPE_H = @NEXT_CTYPE_H@ +NEXT_DIRENT_H = @NEXT_DIRENT_H@ +NEXT_ERRNO_H = @NEXT_ERRNO_H@ +NEXT_FCNTL_H = @NEXT_FCNTL_H@ +NEXT_FLOAT_H = @NEXT_FLOAT_H@ +NEXT_FNMATCH_H = @NEXT_FNMATCH_H@ +NEXT_GETOPT_H = @NEXT_GETOPT_H@ +NEXT_GLOB_H = @NEXT_GLOB_H@ +NEXT_INTTYPES_H = @NEXT_INTTYPES_H@ +NEXT_LANGINFO_H = @NEXT_LANGINFO_H@ +NEXT_LIMITS_H = @NEXT_LIMITS_H@ +NEXT_LOCALE_H = @NEXT_LOCALE_H@ +NEXT_SIGNAL_H = @NEXT_SIGNAL_H@ +NEXT_STDARG_H = @NEXT_STDARG_H@ +NEXT_STDDEF_H = @NEXT_STDDEF_H@ +NEXT_STDINT_H = @NEXT_STDINT_H@ +NEXT_STDIO_H = @NEXT_STDIO_H@ +NEXT_STDLIB_H = @NEXT_STDLIB_H@ +NEXT_STRINGS_H = @NEXT_STRINGS_H@ +NEXT_STRING_H = @NEXT_STRING_H@ +NEXT_SYSEXITS_H = @NEXT_SYSEXITS_H@ +NEXT_SYS_FILE_H = @NEXT_SYS_FILE_H@ +NEXT_SYS_IOCTL_H = @NEXT_SYS_IOCTL_H@ +NEXT_SYS_SELECT_H = @NEXT_SYS_SELECT_H@ +NEXT_SYS_SOCKET_H = @NEXT_SYS_SOCKET_H@ +NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@ +NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@ +NEXT_SYS_TYPES_H = @NEXT_SYS_TYPES_H@ +NEXT_SYS_UIO_H = @NEXT_SYS_UIO_H@ +NEXT_TIME_H = @NEXT_TIME_H@ +NEXT_UNISTD_H = @NEXT_UNISTD_H@ +NEXT_UTIME_H = @NEXT_UTIME_H@ +NEXT_WCHAR_H = @NEXT_WCHAR_H@ +NEXT_WCTYPE_H = @NEXT_WCTYPE_H@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PO4A = @PO4A@ +POSUB = @POSUB@ +PRAGMA_COLUMNS = @PRAGMA_COLUMNS@ +PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@ +PRIPTR_PREFIX = @PRIPTR_PREFIX@ +PRI_MACROS_BROKEN = @PRI_MACROS_BROKEN@ +PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@ +PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ +RANLIB = @RANLIB@ +REPLACE_ACCESS = @REPLACE_ACCESS@ +REPLACE_BTOWC = @REPLACE_BTOWC@ +REPLACE_CALLOC = @REPLACE_CALLOC@ +REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@ +REPLACE_CHOWN = @REPLACE_CHOWN@ +REPLACE_CLOSE = @REPLACE_CLOSE@ +REPLACE_CLOSEDIR = @REPLACE_CLOSEDIR@ +REPLACE_CREAT = @REPLACE_CREAT@ +REPLACE_CTIME = @REPLACE_CTIME@ +REPLACE_DIRFD = @REPLACE_DIRFD@ +REPLACE_DPRINTF = @REPLACE_DPRINTF@ +REPLACE_DUP = @REPLACE_DUP@ +REPLACE_DUP2 = @REPLACE_DUP2@ +REPLACE_DUPLOCALE = @REPLACE_DUPLOCALE@ +REPLACE_FACCESSAT = @REPLACE_FACCESSAT@ +REPLACE_FCHMODAT = @REPLACE_FCHMODAT@ +REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@ +REPLACE_FCLOSE = @REPLACE_FCLOSE@ +REPLACE_FCNTL = @REPLACE_FCNTL@ +REPLACE_FDOPEN = @REPLACE_FDOPEN@ +REPLACE_FDOPENDIR = @REPLACE_FDOPENDIR@ +REPLACE_FFLUSH = @REPLACE_FFLUSH@ +REPLACE_FNMATCH = @REPLACE_FNMATCH@ +REPLACE_FOPEN = @REPLACE_FOPEN@ +REPLACE_FPRINTF = @REPLACE_FPRINTF@ +REPLACE_FPURGE = @REPLACE_FPURGE@ +REPLACE_FREELOCALE = @REPLACE_FREELOCALE@ +REPLACE_FREOPEN = @REPLACE_FREOPEN@ +REPLACE_FSEEK = @REPLACE_FSEEK@ +REPLACE_FSEEKO = @REPLACE_FSEEKO@ +REPLACE_FSTAT = @REPLACE_FSTAT@ +REPLACE_FSTATAT = @REPLACE_FSTATAT@ +REPLACE_FTELL = @REPLACE_FTELL@ +REPLACE_FTELLO = @REPLACE_FTELLO@ +REPLACE_FTRUNCATE = @REPLACE_FTRUNCATE@ +REPLACE_FUTIMENS = @REPLACE_FUTIMENS@ +REPLACE_GETCWD = @REPLACE_GETCWD@ +REPLACE_GETDELIM = @REPLACE_GETDELIM@ +REPLACE_GETDOMAINNAME = @REPLACE_GETDOMAINNAME@ +REPLACE_GETDTABLESIZE = @REPLACE_GETDTABLESIZE@ +REPLACE_GETGROUPS = @REPLACE_GETGROUPS@ +REPLACE_GETLINE = @REPLACE_GETLINE@ +REPLACE_GETLOGIN_R = @REPLACE_GETLOGIN_R@ +REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@ +REPLACE_GETPASS = @REPLACE_GETPASS@ +REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@ +REPLACE_GLOB = @REPLACE_GLOB@ +REPLACE_GLOB_PATTERN_P = @REPLACE_GLOB_PATTERN_P@ +REPLACE_GMTIME = @REPLACE_GMTIME@ +REPLACE_INITSTATE = @REPLACE_INITSTATE@ +REPLACE_IOCTL = @REPLACE_IOCTL@ +REPLACE_ISATTY = @REPLACE_ISATTY@ +REPLACE_ISWBLANK = @REPLACE_ISWBLANK@ +REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@ +REPLACE_ISWDIGIT = @REPLACE_ISWDIGIT@ +REPLACE_ISWXDIGIT = @REPLACE_ISWXDIGIT@ +REPLACE_ITOLD = @REPLACE_ITOLD@ +REPLACE_LCHOWN = @REPLACE_LCHOWN@ +REPLACE_LINK = @REPLACE_LINK@ +REPLACE_LINKAT = @REPLACE_LINKAT@ +REPLACE_LOCALECONV = @REPLACE_LOCALECONV@ +REPLACE_LOCALTIME = @REPLACE_LOCALTIME@ +REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@ +REPLACE_LSEEK = @REPLACE_LSEEK@ +REPLACE_LSTAT = @REPLACE_LSTAT@ +REPLACE_MALLOC = @REPLACE_MALLOC@ +REPLACE_MBRLEN = @REPLACE_MBRLEN@ +REPLACE_MBRTOWC = @REPLACE_MBRTOWC@ +REPLACE_MBSINIT = @REPLACE_MBSINIT@ +REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@ +REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@ +REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@ +REPLACE_MBTOWC = @REPLACE_MBTOWC@ +REPLACE_MEMCHR = @REPLACE_MEMCHR@ +REPLACE_MEMMEM = @REPLACE_MEMMEM@ +REPLACE_MKDIR = @REPLACE_MKDIR@ +REPLACE_MKFIFO = @REPLACE_MKFIFO@ +REPLACE_MKNOD = @REPLACE_MKNOD@ +REPLACE_MKSTEMP = @REPLACE_MKSTEMP@ +REPLACE_MKTIME = @REPLACE_MKTIME@ +REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@ +REPLACE_NEWLOCALE = @REPLACE_NEWLOCALE@ +REPLACE_NL_LANGINFO = @REPLACE_NL_LANGINFO@ +REPLACE_NULL = @REPLACE_NULL@ +REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@ +REPLACE_OPEN = @REPLACE_OPEN@ +REPLACE_OPENAT = @REPLACE_OPENAT@ +REPLACE_OPENDIR = @REPLACE_OPENDIR@ +REPLACE_PERROR = @REPLACE_PERROR@ +REPLACE_POPEN = @REPLACE_POPEN@ +REPLACE_PREAD = @REPLACE_PREAD@ +REPLACE_PRINTF = @REPLACE_PRINTF@ +REPLACE_PSELECT = @REPLACE_PSELECT@ +REPLACE_PTHREAD_SIGMASK = @REPLACE_PTHREAD_SIGMASK@ +REPLACE_PTSNAME = @REPLACE_PTSNAME@ +REPLACE_PTSNAME_R = @REPLACE_PTSNAME_R@ +REPLACE_PUTENV = @REPLACE_PUTENV@ +REPLACE_PWRITE = @REPLACE_PWRITE@ +REPLACE_QSORT_R = @REPLACE_QSORT_R@ +REPLACE_RAISE = @REPLACE_RAISE@ +REPLACE_RANDOM = @REPLACE_RANDOM@ +REPLACE_RANDOM_R = @REPLACE_RANDOM_R@ +REPLACE_READ = @REPLACE_READ@ +REPLACE_READLINK = @REPLACE_READLINK@ +REPLACE_READLINKAT = @REPLACE_READLINKAT@ +REPLACE_REALLOC = @REPLACE_REALLOC@ +REPLACE_REALPATH = @REPLACE_REALPATH@ +REPLACE_REMOVE = @REPLACE_REMOVE@ +REPLACE_RENAME = @REPLACE_RENAME@ +REPLACE_RENAMEAT = @REPLACE_RENAMEAT@ +REPLACE_RMDIR = @REPLACE_RMDIR@ +REPLACE_SELECT = @REPLACE_SELECT@ +REPLACE_SETENV = @REPLACE_SETENV@ +REPLACE_SETLOCALE = @REPLACE_SETLOCALE@ +REPLACE_SETSTATE = @REPLACE_SETSTATE@ +REPLACE_SLEEP = @REPLACE_SLEEP@ +REPLACE_SNPRINTF = @REPLACE_SNPRINTF@ +REPLACE_SPRINTF = @REPLACE_SPRINTF@ +REPLACE_STAT = @REPLACE_STAT@ +REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@ +REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@ +REPLACE_STPNCPY = @REPLACE_STPNCPY@ +REPLACE_STRCASESTR = @REPLACE_STRCASESTR@ +REPLACE_STRCHRNUL = @REPLACE_STRCHRNUL@ +REPLACE_STRDUP = @REPLACE_STRDUP@ +REPLACE_STRERROR = @REPLACE_STRERROR@ +REPLACE_STRERROR_R = @REPLACE_STRERROR_R@ +REPLACE_STRFTIME = @REPLACE_STRFTIME@ +REPLACE_STRNCAT = @REPLACE_STRNCAT@ +REPLACE_STRNDUP = @REPLACE_STRNDUP@ +REPLACE_STRNLEN = @REPLACE_STRNLEN@ +REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@ +REPLACE_STRSTR = @REPLACE_STRSTR@ +REPLACE_STRTOD = @REPLACE_STRTOD@ +REPLACE_STRTOIMAX = @REPLACE_STRTOIMAX@ +REPLACE_STRTOK_R = @REPLACE_STRTOK_R@ +REPLACE_STRTOLD = @REPLACE_STRTOLD@ +REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@ +REPLACE_STRUCT_LCONV = @REPLACE_STRUCT_LCONV@ +REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@ +REPLACE_SYMLINK = @REPLACE_SYMLINK@ +REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@ +REPLACE_TIMEGM = @REPLACE_TIMEGM@ +REPLACE_TMPFILE = @REPLACE_TMPFILE@ +REPLACE_TOWLOWER = @REPLACE_TOWLOWER@ +REPLACE_TRUNCATE = @REPLACE_TRUNCATE@ +REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@ +REPLACE_TZSET = @REPLACE_TZSET@ +REPLACE_UNLINK = @REPLACE_UNLINK@ +REPLACE_UNLINKAT = @REPLACE_UNLINKAT@ +REPLACE_UNSETENV = @REPLACE_UNSETENV@ +REPLACE_USLEEP = @REPLACE_USLEEP@ +REPLACE_UTIME = @REPLACE_UTIME@ +REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@ +REPLACE_VASPRINTF = @REPLACE_VASPRINTF@ +REPLACE_VDPRINTF = @REPLACE_VDPRINTF@ +REPLACE_VFPRINTF = @REPLACE_VFPRINTF@ +REPLACE_VPRINTF = @REPLACE_VPRINTF@ +REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@ +REPLACE_VSPRINTF = @REPLACE_VSPRINTF@ +REPLACE_WCRTOMB = @REPLACE_WCRTOMB@ +REPLACE_WCSFTIME = @REPLACE_WCSFTIME@ +REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@ +REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@ +REPLACE_WCSTOK = @REPLACE_WCSTOK@ +REPLACE_WCSWIDTH = @REPLACE_WCSWIDTH@ +REPLACE_WCTOB = @REPLACE_WCTOB@ +REPLACE_WCTOMB = @REPLACE_WCTOMB@ +REPLACE_WCWIDTH = @REPLACE_WCWIDTH@ +REPLACE_WRITE = @REPLACE_WRITE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ +SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ +STDALIGN_H = @STDALIGN_H@ +STDARG_H = @STDARG_H@ +STDBOOL_H = @STDBOOL_H@ +STDDEF_H = @STDDEF_H@ +STDINT_H = @STDINT_H@ +STRIP = @STRIP@ +SYSEXITS_H = @SYSEXITS_H@ +SYS_IOCTL_H_HAVE_WINSOCK2_H = @SYS_IOCTL_H_HAVE_WINSOCK2_H@ +SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@ +TBL_X_FORMAT = @TBL_X_FORMAT@ +TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@ +TRANS_APROPOS = @TRANS_APROPOS@ +TRANS_APROPOS_UPPER = @TRANS_APROPOS_UPPER@ +TRANS_CATMAN = @TRANS_CATMAN@ +TRANS_CATMAN_UPPER = @TRANS_CATMAN_UPPER@ +TRANS_LEXGROG = @TRANS_LEXGROG@ +TRANS_LEXGROG_UPPER = @TRANS_LEXGROG_UPPER@ +TRANS_MAN = @TRANS_MAN@ +TRANS_MANCONV = @TRANS_MANCONV@ +TRANS_MANCONV_UPPER = @TRANS_MANCONV_UPPER@ +TRANS_MANDB = @TRANS_MANDB@ +TRANS_MANDB_UPPER = @TRANS_MANDB_UPPER@ +TRANS_MANPATH = @TRANS_MANPATH@ +TRANS_MANPATH_UPPER = @TRANS_MANPATH_UPPER@ +TRANS_MAN_RECODE = @TRANS_MAN_RECODE@ +TRANS_MAN_RECODE_UPPER = @TRANS_MAN_RECODE_UPPER@ +TRANS_MAN_UPPER = @TRANS_MAN_UPPER@ +TRANS_WHATIS = @TRANS_WHATIS@ +TRANS_WHATIS_UPPER = @TRANS_WHATIS_UPPER@ +TRANS_ZSOELIM = @TRANS_ZSOELIM@ +TRANS_ZSOELIM_UPPER = @TRANS_ZSOELIM_UPPER@ +TROFF = @TROFF@ +UINT32_MAX_LT_UINTMAX_MAX = @UINT32_MAX_LT_UINTMAX_MAX@ +UINT64_MAX_EQ_ULONG_MAX = @UINT64_MAX_EQ_ULONG_MAX@ +UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@ +UNISTD_H_DEFINES_STRUCT_TIMESPEC = @UNISTD_H_DEFINES_STRUCT_TIMESPEC@ +UNISTD_H_HAVE_SYS_RANDOM_H = @UNISTD_H_HAVE_SYS_RANDOM_H@ +UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@ +UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +USE_NLS = @USE_NLS@ +UTIME_H = @UTIME_H@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ +WINDOWS_64_BIT_OFF_T = @WINDOWS_64_BIT_OFF_T@ +WINDOWS_64_BIT_ST_SIZE = @WINDOWS_64_BIT_ST_SIZE@ +WINDOWS_STAT_INODES = @WINDOWS_STAT_INODES@ +WINDOWS_STAT_TIMESPEC = @WINDOWS_STAT_TIMESPEC@ +WINT_T_SUFFIX = @WINT_T_SUFFIX@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +browser = @browser@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +bunzip2 = @bunzip2@ +bzip2 = @bzip2@ +cache_top_owner = @cache_top_owner@ +cat = @cat@ +col = @col@ +compress = @compress@ +compress_ext = @compress_ext@ +compressor = @compressor@ +config_file = @config_file@ +config_file_basename = @config_file_basename@ +config_file_dirname = @config_file_dirname@ +datadir = @datadir@ +datarootdir = @datarootdir@ +date = @date@ +docdir = @docdir@ +dvidir = @dvidir@ +eqn = @eqn@ +exec_prefix = @exec_prefix@ +gl_LIBOBJS = @gl_LIBOBJS@ +gl_LTLIBOBJS = @gl_LTLIBOBJS@ +gltests_LIBOBJS = @gltests_LIBOBJS@ +gltests_LTLIBOBJS = @gltests_LTLIBOBJS@ +gltests_WITNESS = @gltests_WITNESS@ +grap = @grap@ +grep = @grep@ +gunzip = @gunzip@ +gzip = @gzip@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libpipeline_CFLAGS = @libpipeline_CFLAGS@ +libpipeline_LIBS = @libpipeline_LIBS@ +libseccomp_CFLAGS = @libseccomp_CFLAGS@ +libseccomp_LIBS = @libseccomp_LIBS@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lzip = @lzip@ +lzma = @lzma@ +man_mode = @man_mode@ +man_owner = @man_owner@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +neqn = @neqn@ +nroff = @nroff@ +oldincludedir = @oldincludedir@ +override_dir = @override_dir@ +pager = @pager@ +pdfdir = @pdfdir@ +pic = @pic@ +preconv = @preconv@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +refer = @refer@ +roff_version = @roff_version@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sections = @sections@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ +target_alias = @target_alias@ +tbl = @tbl@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +tr = @tr@ +troff = @troff@ +troff_as_troff_input = @troff_as_troff_input@ +uncompress = @uncompress@ +unlzip = @unlzip@ +unlzma = @unlzma@ +unxz = @unxz@ +unzstd = @unzstd@ +vgrind = @vgrind@ +xz = @xz@ +zstd = @zstd@ +TESTS_ENVIRONMENT = PATH=$(abs_builddir)/..:$$PATH; export PATH; \ + DBTYPE=$(DBTYPE); export DBTYPE; \ + MANDIR_LAYOUT=$(MANDIR_LAYOUT); export MANDIR_LAYOUT; \ + abs_top_builddir=$(abs_top_builddir); export abs_top_builddir; \ + OVERRIDE_DIR="$(override_dir)"; export OVERRIDE_DIR; + +# Each test must use the configure-detected shell, not necessarily /bin/sh. +AM_LOG_FLAGS = $(SHELL) +ALL_TESTS = \ + lexgrog-backslash-dash-rhs \ + lexgrog-basic \ + lexgrog-multiple-whatis \ + man-deleted-directory \ + man-exact-section-matches \ + man-executable-page-on-path \ + man-invalid-db-entry \ + man-language-specific-requests \ + man-mandatory-manpath \ + man-missing-locales \ + man-override-dir \ + man-recode-in-place \ + man-recode-suffix \ + man-so-links-same-section \ + man-suffixed-extension \ + man-symlinks-with-matching-names \ + manconv-coding-tags \ + manconv-guess-from-encoding \ + manconv-incomplete-char-at-eof \ + manconv-odd-combinations \ + mandb-basic \ + mandb-bogus-symlink \ + mandb-cachedir-tag \ + mandb-empty-page \ + mandb-regular-file-symlink-changes \ + mandb-symlink-beats-whatis-ref \ + mandb-whatis-broken-link-changes \ + whatis-path-to-executable \ + zsoelim-so-includes + +@CROSS_COMPILING_FALSE@TESTS = $(ALL_TESTS) +AM_CPPFLAGS = \ + -I$(top_builddir)/include \ + -I$(top_builddir)/gl/lib \ + -I$(top_srcdir)/gl/lib + +AM_CFLAGS = $(WARN_CFLAGS) +fspause_SOURCES = fspause.c +fspause_LDADD = \ + $(top_builddir)/gl/lib/libgnu.la \ + $(LIB_NANOSLEEP) + +dist_check_SCRIPTS = testlib.sh $(ALL_TESTS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs +$(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 ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/tests/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + 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 +$(am__aclocal_m4_deps): + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +fspause$(EXEEXT): $(fspause_OBJECTS) $(fspause_DEPENDENCIES) $(EXTRA_fspause_DEPENDENCIES) + @rm -f fspause$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(fspause_OBJECTS) $(fspause_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fspause.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: $(check_PROGRAMS) $(dist_check_SCRIPTS) + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) $(dist_check_SCRIPTS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +lexgrog-backslash-dash-rhs.log: lexgrog-backslash-dash-rhs + @p='lexgrog-backslash-dash-rhs'; \ + b='lexgrog-backslash-dash-rhs'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +lexgrog-basic.log: lexgrog-basic + @p='lexgrog-basic'; \ + b='lexgrog-basic'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +lexgrog-multiple-whatis.log: lexgrog-multiple-whatis + @p='lexgrog-multiple-whatis'; \ + b='lexgrog-multiple-whatis'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-deleted-directory.log: man-deleted-directory + @p='man-deleted-directory'; \ + b='man-deleted-directory'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-exact-section-matches.log: man-exact-section-matches + @p='man-exact-section-matches'; \ + b='man-exact-section-matches'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-executable-page-on-path.log: man-executable-page-on-path + @p='man-executable-page-on-path'; \ + b='man-executable-page-on-path'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-invalid-db-entry.log: man-invalid-db-entry + @p='man-invalid-db-entry'; \ + b='man-invalid-db-entry'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-language-specific-requests.log: man-language-specific-requests + @p='man-language-specific-requests'; \ + b='man-language-specific-requests'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-mandatory-manpath.log: man-mandatory-manpath + @p='man-mandatory-manpath'; \ + b='man-mandatory-manpath'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-missing-locales.log: man-missing-locales + @p='man-missing-locales'; \ + b='man-missing-locales'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-override-dir.log: man-override-dir + @p='man-override-dir'; \ + b='man-override-dir'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-recode-in-place.log: man-recode-in-place + @p='man-recode-in-place'; \ + b='man-recode-in-place'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-recode-suffix.log: man-recode-suffix + @p='man-recode-suffix'; \ + b='man-recode-suffix'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-so-links-same-section.log: man-so-links-same-section + @p='man-so-links-same-section'; \ + b='man-so-links-same-section'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-suffixed-extension.log: man-suffixed-extension + @p='man-suffixed-extension'; \ + b='man-suffixed-extension'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +man-symlinks-with-matching-names.log: man-symlinks-with-matching-names + @p='man-symlinks-with-matching-names'; \ + b='man-symlinks-with-matching-names'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +manconv-coding-tags.log: manconv-coding-tags + @p='manconv-coding-tags'; \ + b='manconv-coding-tags'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +manconv-guess-from-encoding.log: manconv-guess-from-encoding + @p='manconv-guess-from-encoding'; \ + b='manconv-guess-from-encoding'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +manconv-incomplete-char-at-eof.log: manconv-incomplete-char-at-eof + @p='manconv-incomplete-char-at-eof'; \ + b='manconv-incomplete-char-at-eof'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +manconv-odd-combinations.log: manconv-odd-combinations + @p='manconv-odd-combinations'; \ + b='manconv-odd-combinations'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +mandb-basic.log: mandb-basic + @p='mandb-basic'; \ + b='mandb-basic'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +mandb-bogus-symlink.log: mandb-bogus-symlink + @p='mandb-bogus-symlink'; \ + b='mandb-bogus-symlink'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +mandb-cachedir-tag.log: mandb-cachedir-tag + @p='mandb-cachedir-tag'; \ + b='mandb-cachedir-tag'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +mandb-empty-page.log: mandb-empty-page + @p='mandb-empty-page'; \ + b='mandb-empty-page'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +mandb-regular-file-symlink-changes.log: mandb-regular-file-symlink-changes + @p='mandb-regular-file-symlink-changes'; \ + b='mandb-regular-file-symlink-changes'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +mandb-symlink-beats-whatis-ref.log: mandb-symlink-beats-whatis-ref + @p='mandb-symlink-beats-whatis-ref'; \ + b='mandb-symlink-beats-whatis-ref'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +mandb-whatis-broken-link-changes.log: mandb-whatis-broken-link-changes + @p='mandb-whatis-broken-link-changes'; \ + b='mandb-whatis-broken-link-changes'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +whatis-path-to-executable.log: whatis-path-to-executable + @p='whatis-path-to-executable'; \ + b='whatis-path-to-executable'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +zsoelim-so-includes.log: zsoelim-so-includes + @p='zsoelim-so-includes'; \ + b='zsoelim-so-includes'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$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 + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) \ + $(dist_check_SCRIPTS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile +installdirs: +install: 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: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/fspause.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/fspause.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + check-am clean clean-checkPROGRAMS clean-generic clean-libtool \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + recheck tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# 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/src/tests/fspause.c b/src/tests/fspause.c new file mode 100644 index 0000000..092fbb4 --- /dev/null +++ b/src/tests/fspause.c @@ -0,0 +1,112 @@ +/* + * fspause.c: pause until a file timestamp updates + * + * Copyright (C) 2014 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> + +#include "progname.h" +#include "stat-time.h" +#include "timespec.h" +#include "xalloc.h" + +#include "manconfig.h" + +static char *filename; +static int fd = -1; + +#define MUST(name, cond) \ + do { \ + if (!(cond)) { \ + fprintf (stderr, "fspause: " name " failed\n"); \ + abort (); \ + } \ + } while (0) + +static void unlink_tempfile (void) +{ + if (fd >= 0) { + MUST ("close", close (fd) >= 0); + MUST ("unlink", unlink (filename) >= 0); + } +} + +static void delay (int delay_ns) +{ + struct timespec delay_ts; + + delay_ts.tv_sec = delay_ns / 1000000000; + delay_ts.tv_nsec = delay_ns % 1000000000; + for (;;) { + errno = 0; + if (nanosleep (&delay_ts, NULL) == 0) + break; + MUST ("nanosleep", errno == 0 || errno == EINTR); + } +} + +static int try_delay (struct stat *st, int delay_ns) +{ + struct timespec start_ts, end_ts; + + start_ts = get_stat_mtime (st); + delay (delay_ns); + MUST ("write", write (fd, "\n", 1) == 1); + MUST ("fstat", fstat (fd, st) >= 0); + end_ts = get_stat_mtime (st); + return timespec_cmp (start_ts, end_ts) != 0; +} + +int main (int argc _GL_UNUSED, char **argv) +{ + struct stat st; + int delay_ns; + + set_program_name (argv[0]); + + filename = xstrdup ("fspause.tmp.XXXXXX"); + MUST ("mkstemp", (fd = mkstemp (filename)) >= 0); + atexit (unlink_tempfile); + MUST ("fstat", fstat (fd, &st) >= 0); + + /* 0x40000000 nanoseconds is just over a second. The effective + * maximum delay we will allow is thus about two seconds. This + * saves us having to keep track of anything more complicated than a + * single signed 32-bit int. + */ + for (delay_ns = 1; delay_ns < 0x40000000; delay_ns *= 2) { + if (try_delay (&st, delay_ns)) + return 0; + } + + fprintf (stderr, + "fspause: temporary file timestamp refuses to change!\n"); + return 1; +} diff --git a/src/tests/lexgrog-backslash-dash-rhs b/src/tests/lexgrog-backslash-dash-rhs new file mode 100644 index 0000000..a6a66a9 --- /dev/null +++ b/src/tests/lexgrog-backslash-dash-rhs @@ -0,0 +1,20 @@ +#! /bin/sh + +# Test handling of \- in the right-hand side of a NAME section. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${LEXGROG=lexgrog} + +init + +write_page lextest 1 "$tmpdir/usr/share/man/man1/lextest.1.gz" UTF-8 gz '' \ + 'lextest \- see lextest \-\-help' +cat >"$tmpdir/3.exp" <<EOF +$tmpdir/usr/share/man/man1/lextest.1.gz: "lextest - see lextest --help" +EOF +run $LEXGROG "$tmpdir/usr/share/man/man1/lextest.1.gz" >"$tmpdir/3.out" +expect_pass 'multiple whatis definitions' 'diff -u "$tmpdir/3.exp" "$tmpdir/3.out"' + +finish diff --git a/src/tests/lexgrog-basic b/src/tests/lexgrog-basic new file mode 100755 index 0000000..8ab8a0f --- /dev/null +++ b/src/tests/lexgrog-basic @@ -0,0 +1,18 @@ +#! /bin/sh + +# Basic lexgrog tests. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${LEXGROG=lexgrog} + +init + +write_page lextest 1 "$tmpdir/usr/share/man/man1/lextest.1.gz" UTF-8 gz '' \ + 'lextest \- simple lexgrog test' +echo "$tmpdir/usr/share/man/man1/lextest.1.gz: \"lextest - simple lexgrog test\"" >"$tmpdir/1.exp" +run $LEXGROG "$tmpdir/usr/share/man/man1/lextest.1.gz" >"$tmpdir/1.out" +expect_pass 'simple lexgrog test' 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +finish diff --git a/src/tests/lexgrog-multiple-whatis b/src/tests/lexgrog-multiple-whatis new file mode 100755 index 0000000..eac8dbd --- /dev/null +++ b/src/tests/lexgrog-multiple-whatis @@ -0,0 +1,26 @@ +#! /bin/sh + +# Test multiple whatis definitions. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${LEXGROG=lexgrog} + +init + +name_section="\ +lextest \\- one whatis definition +.br +lextest2 \\- another whatis definition" + +write_page lextest 1 "$tmpdir/usr/share/man/man1/lextest.1.gz" UTF-8 gz '' \ + "$name_section" +cat >"$tmpdir/2.exp" <<EOF +$tmpdir/usr/share/man/man1/lextest.1.gz: "lextest - one whatis definition" +$tmpdir/usr/share/man/man1/lextest.1.gz: "lextest2 - another whatis definition" +EOF +run $LEXGROG "$tmpdir/usr/share/man/man1/lextest.1.gz" >"$tmpdir/2.out" +expect_pass 'multiple whatis definitions' 'diff -u "$tmpdir/2.exp" "$tmpdir/2.out"' + +finish diff --git a/src/tests/man-deleted-directory b/src/tests/man-deleted-directory new file mode 100755 index 0000000..75ec2a5 --- /dev/null +++ b/src/tests/man-deleted-directory @@ -0,0 +1,26 @@ +#! /bin/sh + +# Test that man can run from a deleted directory. +# https://bugs.debian.org/764384 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +init +echo "MANDATORY_MANPATH $abstmpdir/usr/share/man" >"$tmpdir/manpath.config" +MANPATH="$abstmpdir/usr/share/man" +export MANPATH + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1" \ + UTF-8 '' '' 'test \- test' +mkdir "$tmpdir/zombie" +cd "$tmpdir/zombie" +rmdir "$abstmpdir/zombie" || \ + skip "can't remove current working directory on this system" +run $MAN -C "$abstmpdir/manpath.config" test >/dev/null +code=$? +expect_pass 'run from deleted directory' 'test "$code" = 0' + +finish diff --git a/src/tests/man-exact-section-matches b/src/tests/man-exact-section-matches new file mode 100755 index 0000000..d2bac98 --- /dev/null +++ b/src/tests/man-exact-section-matches @@ -0,0 +1,41 @@ +#! /bin/sh + +# Test for: +# https://bugzilla.redhat.com/show_bug.cgi?id=684977 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +case $MANDIR_LAYOUT in + ""|GNU) + ;; + *) + skip "only applicable to GNU layout" + ;; +esac + +: ${MAN=man} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH + +# Force default section order. +cat >>"$tmpdir/manpath.config" <<EOF +SECTION 1 n l 8 3 0 2 5 4 9 6 7 +EOF + +write_page md5sum 3pm "$tmpdir/usr/share/man/man3/open.3pm.gz" \ + UTF-8 gz '' 'open \- section 3pm' +write_page md5sum 3p "$tmpdir/usr/share/man/man3p/open.3p.gz" \ + UTF-8 gz '' 'open \- section 3p' +cat >"$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/man/man3p/open.3p.gz +$abstmpdir/usr/share/man/man3/open.3pm.gz +EOF +run $MAN -C "$tmpdir/manpath.config" -aw 3p open >"$tmpdir/1.out" +expect_pass 'exact section matches win' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +finish diff --git a/src/tests/man-executable-page-on-path b/src/tests/man-executable-page-on-path new file mode 100755 index 0000000..f05291b --- /dev/null +++ b/src/tests/man-executable-page-on-path @@ -0,0 +1,23 @@ +#! /bin/sh + +# Test for: +# https://bugs.debian.org/608490 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH + +write_page file 1 "$tmpdir/file.1" UTF-8 '' '' 'file \- test' +chmod +x "$tmpdir/file.1" +PATH="$PATH:$tmpdir" run $MAN \ + -C "$tmpdir/manpath.config" "$tmpdir/file.1" >/dev/null +code=$? +expect_pass 'executable page on path' 'test "$code" = 0' + +finish diff --git a/src/tests/man-invalid-db-entry b/src/tests/man-invalid-db-entry new file mode 100755 index 0000000..8c32e3d --- /dev/null +++ b/src/tests/man-invalid-db-entry @@ -0,0 +1,31 @@ +#! /bin/sh + +# Test for invalid DB entry. +# https://bugzilla.redhat.com/show_bug.cgi?id=841431 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} +: ${MANDB=mandb} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH + +MAN_TEST_DISABLE_UNDOCUMENTED=1 +export MAN_TEST_DISABLE_UNDOCUMENTED + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1" \ + UTF-8 '' '' 'test \- top-level test page' +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" + +rm -f "$tmpdir/usr/share/man/man1/test.1" + +echo "No manual entry for test" > "$tmpdir/1.exp" +LC_ALL=C run $MAN -C "$tmpdir/manpath.config" test 2> "$tmpdir/1.out" +expect_pass 'invalid DB entry' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +finish diff --git a/src/tests/man-language-specific-requests b/src/tests/man-language-specific-requests new file mode 100755 index 0000000..cb19790 --- /dev/null +++ b/src/tests/man-language-specific-requests @@ -0,0 +1,58 @@ +#! /bin/sh + +# Test additional language-specific requests for localized man pages. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH + +cat >"$tmpdir/fake-program" <<EOF +#! /bin/sh +exec cat +EOF +chmod +x "$tmpdir/fake-program" +PATH="$abstmpdir:$PATH" +export PATH + +cat >>"$tmpdir/manpath.config" <<EOF +DEFINE tbl fake-program +DEFINE nroff fake-program +EOF + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1" \ + UTF-8 '' '' 'test \- top-level test page' + +write_page test 1 "$tmpdir/usr/share/man/xyzzy/man1/test.1" \ + UTF-8 '' '' 'test \- xyzzy language page for test' + +write_page xyz 1 "$tmpdir/usr/share/man/man1/xyz.1" \ + UTF-8 '' '' 'test \- top-level xyz page' + +cat >"$tmpdir/1.exp" <<'EOF' +. mso xyzzy.tmac +.hla xyzzy +test \- xyzzy language page for test +EOF + +cat >"$tmpdir/2.exp" <<'EOF' +.TH xyz 1 +test \- top-level xyz page +EOF + +run $MAN -L xyzzy_foo.bar -C "$tmpdir/manpath.config" test |\ + grep 'xyzzy' >"$tmpdir/1.out" +expect_pass 'language-specific requests for localized man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +run $MAN -L xyzzy_foo.bar -C "$tmpdir/manpath.config" xyz |\ + grep 'xyz' >"$tmpdir/2.out" +expect_pass 'no language-specific requests for top-level man page' \ + 'diff -u "$tmpdir/2.exp" "$tmpdir/2.out"' + +finish diff --git a/src/tests/man-mandatory-manpath b/src/tests/man-mandatory-manpath new file mode 100755 index 0000000..75633ac --- /dev/null +++ b/src/tests/man-mandatory-manpath @@ -0,0 +1,173 @@ +#! /bin/sh + +# Test for wildcards in MANDATORY_MANPATH in config file and in MANPATH. +# https://bugzilla.redhat.com/show_bug.cgi?id=677669 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +init +> "$tmpdir/manpath.config" + +MAN_TEST_DISABLE_PATH=1 +export MAN_TEST_DISABLE_PATH + +write_page manx 1 "$tmpdir/usr/share/man/man1/manx.1.gz" \ + UTF-8 gz '' 'manx \- an interface to the system reference manuals' +write_page manpathx 1 "$tmpdir/usr/share/prog/a/man/man1/manpathx.1.gz" \ + UTF-8 gz '' 'manpathx \- determine search path for manual pages' +write_page whatisx 1 "$tmpdir/usr/share/prog/b/man/man1/whatisx.1.gz" \ + UTF-8 gz '' 'whatisx \- display manual page descriptions' + +# +# Testing -M option +# + +# Without wildcards +Mpath="$tmpdir/usr/share/man" + +run $MAN -C "$tmpdir/manpath.config" \ + -aw -M "${Mpath}" manpathx > "$tmpdir/1.out" 2> /dev/null +> "$tmpdir/1.exp" +expect_pass 'wildcards: -M option: without wildcards: check missing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +run $MAN -C "$tmpdir/manpath.config" \ + -aw -M "${Mpath}" manx > "$tmpdir/1.out" +cat > "$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/man/man1/manx.1.gz +EOF +expect_pass 'wildcards: -M option: without wildcards: check existing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +# With wildcards +Mpath="$tmpdir/usr/share/prog/*/man" + +run $MAN -C "$tmpdir/manpath.config" \ + -aw -M "${Mpath}" manx > "$tmpdir/1.out" 2> /dev/null +> "$tmpdir/1.exp" +expect_pass 'wildcards: -M option: with wildcards: check missing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +run $MAN -C "$tmpdir/manpath.config" \ + -aw -M "${Mpath}" manpathx > "$tmpdir/1.out" +cat > "$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/prog/a/man/man1/manpathx.1.gz +EOF +expect_pass 'wildcards: -M option: with wildcards: check existing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +Mpath="$tmpdir/usr/share/prog/[ab]/man" + +run $MAN -C "$tmpdir/manpath.config" \ + -aw -M "${Mpath}" whatisx > "$tmpdir/1.out" +cat > "$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/prog/b/man/man1/whatisx.1.gz +EOF +expect_pass 'wildcards: -M option: with wildcards: check existing man page II' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +# +# Testing MANPATH +# + +# Without wildcards +MANPATH="$tmpdir/usr/share/man" +export MANPATH + +run $MAN -C "$tmpdir/manpath.config" \ + -aw manpathx > "$tmpdir/1.out" 2> /dev/null +> "$tmpdir/1.exp" +expect_pass 'wildcards: MANPATH: without wildcards: check missing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +run $MAN -C "$tmpdir/manpath.config" \ + -aw manx > "$tmpdir/1.out" +cat > "$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/man/man1/manx.1.gz +EOF +expect_pass 'wildcards: MANPATH: without wildcards: check existing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +# With wildcards +MANPATH="$tmpdir/usr/share/prog/*/man" +export MANPATH + +run $MAN -C "$tmpdir/manpath.config" \ + -aw manx > "$tmpdir/1.out" 2> /dev/null +> "$tmpdir/1.exp" +expect_pass 'wildcards: MANPATH: with wildcards: check missing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +run $MAN -C "$tmpdir/manpath.config" \ + -aw manpathx > "$tmpdir/1.out" +cat > "$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/prog/a/man/man1/manpathx.1.gz +EOF +expect_pass 'wildcards: MANPATH: with wildcards: check existing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +MANPATH="$tmpdir/usr/share/prog/[ab]/man" +export MANPATH + +run $MAN -C "$tmpdir/manpath.config" \ + -aw whatisx > "$tmpdir/1.out" +cat > "$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/prog/b/man/man1/whatisx.1.gz +EOF +expect_pass 'wildcards: MANPATH: with wildcards: check existing man page II' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +unset MANPATH + +# +# Testing MANDATORY_MANPATH +# + +# Without wildcards +fake_config /usr/share/man + +run $MAN -C "$tmpdir/manpath.config" \ + -aw manpathx > "$tmpdir/1.out" 2> /dev/null +> "$tmpdir/1.exp" +expect_pass 'wildcards: MANDATORY_MANPATH: without wildcards: check missing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +run $MAN -C "$tmpdir/manpath.config" \ + -aw manx > "$tmpdir/1.out" +cat > "$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/man/man1/manx.1.gz +EOF +expect_pass 'wildcards: MANDATORY_MANPATH: without wildcards: check existing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +# With wildcards +fake_config "/usr/share/prog/*/man" + +run $MAN -C "$tmpdir/manpath.config" \ + -aw manx > "$tmpdir/1.out" 2> /dev/null +> "$tmpdir/1.exp" +expect_pass 'wildcards: MANDATORY_MANPATH: with wildcards: check missing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +run $MAN -C "$tmpdir/manpath.config" \ + -aw manpathx > "$tmpdir/1.out" +cat > "$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/prog/a/man/man1/manpathx.1.gz +EOF +expect_pass 'wildcards: MANDATORY_MANPATH: with wildcards: check existing man page' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +fake_config "/usr/share/prog/[ab]/man" + +run $MAN -C "$tmpdir/manpath.config" \ + -aw whatisx > "$tmpdir/1.out" +cat > "$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/prog/b/man/man1/whatisx.1.gz +EOF +expect_pass 'wildcards: MANDATORY_MANPATH: with wildcards: check existing man page II' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +finish diff --git a/src/tests/man-missing-locales b/src/tests/man-missing-locales new file mode 100755 index 0000000..c4301c5 --- /dev/null +++ b/src/tests/man-missing-locales @@ -0,0 +1,28 @@ +#! /bin/sh + +# Testing empty locales on systems without /usr/share/i18n/SUPPORTED file. +# https://bugzilla.redhat.com/show_bug.cgi?id=657409 +# +# File /usr/share/i18n/SUPPORTED must be missing for this test to be effective. +# + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +init +fake_config /usr/share/man + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1" \ + UTF-8 '' '' 'test \- top-level test page' + +LANG= +LC_CTYPE= +LC_ALL= +run $MAN -C "$tmpdir/manpath.config" -E UTF-8 test > /dev/null +# $? is deliberately expanded here. +expect_pass 'missing locales' "test $? -eq 0" + +finish + diff --git a/src/tests/man-override-dir b/src/tests/man-override-dir new file mode 100755 index 0000000..4dc0331 --- /dev/null +++ b/src/tests/man-override-dir @@ -0,0 +1,46 @@ +#! /bin/sh + +# Testing override dir. This test covers both use cases - when override dir is +# enabled and when it's not. +# + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +if [ -n "$OVERRIDE_DIR" ]; then + OVERRIDE=$OVERRIDE_DIR +else + OVERRIDE="override" +fi + +init +fake_config /usr/share/man +mkdir -p "${tmpdir}/usr/share/man/${OVERRIDE}/man1" +MANPATH="$tmpdir/usr/share/man" +export MANPATH + + +write_page abc 1 "${tmpdir}/usr/share/man/man1/abc.1" \ + UTF-8 '' '' 'abc \- top-level test page' +write_page abc 1 "${tmpdir}/usr/share/man/${OVERRIDE}/man1/abc.1" \ + UTF-8 '' '' 'abc \- modified test page' + +if [ -n "$OVERRIDE_DIR" ]; then +cat >"$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/man/${OVERRIDE}/man1/abc.1 +$abstmpdir/usr/share/man/man1/abc.1 +EOF +else +cat >"$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/man/man1/abc.1 +EOF +fi + + +run $MAN -C "$tmpdir/manpath.config" -aw abc >"$tmpdir/1.out" +expect_pass 'testing override dir' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +finish diff --git a/src/tests/man-recode-in-place b/src/tests/man-recode-in-place new file mode 100755 index 0000000..bb75aa7 --- /dev/null +++ b/src/tests/man-recode-in-place @@ -0,0 +1,46 @@ +#! /bin/sh + +# Test man-recode's --in-place behaviour. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN_RECODE=man-recode} + +init + +cat >"$tmpdir/a.1.exp" <<'EOF' +.SH NAME +a \- á +EOF +cp "$tmpdir/a.1.exp" "$tmpdir/a.1" +cat >"$tmpdir/b.1.exp" <<'EOF' +'\" -*- coding: UTF-8 -*- +.SH NAME +b \- é +EOF +gzip -c <"$tmpdir/b.1.exp" >"$tmpdir/b.1.gz" +cat >"$tmpdir/c.1.exp" <<'EOF' +'\" -*- coding: UTF-8 +.SH NAME +b \- é +EOF +cat >"$tmpdir/c.1" <<'EOF' +'\" -*- coding: ISO-8859-1 +EOF +<"$tmpdir/c.1.exp" tail -n +2 | iconv -f UTF-8 -t ISO-8859-1 >>"$tmpdir/c.1" +gzip "$tmpdir/c.1" + +run $MAN_RECODE -t UTF-8 --in-place \ + "$tmpdir/a.1" "$tmpdir/b.1.gz" "$tmpdir/c.1.gz" +expect_pass '--in-place with no coding tag' \ + 'diff -u "$tmpdir/a.1.exp" "$tmpdir/a.1"' +expect_pass '--in-place with gzip and coding tag matching target encoding' \ + 'diff -u "$tmpdir/b.1.exp" "$tmpdir/b.1"' +expect_pass \ + '--in-place with gzip and coding tag not matching target encoding' \ + 'diff -u "$tmpdir/c.1.exp" "$tmpdir/c.1"' +expect_pass '--in-place removes compressed input files' \ + 'test ! -f "$tmpdir/b.1.gz" && test ! -f "$tmpdir/c.1.gz"' + +finish diff --git a/src/tests/man-recode-suffix b/src/tests/man-recode-suffix new file mode 100755 index 0000000..c00be13 --- /dev/null +++ b/src/tests/man-recode-suffix @@ -0,0 +1,43 @@ +#! /bin/sh + +# Test man-recode's --suffix behaviour. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN_RECODE=man-recode} + +init + +cat >"$tmpdir/a.1.exp" <<'EOF' +.SH NAME +a \- á +EOF +cp "$tmpdir/a.1.exp" "$tmpdir/a.1" +cat >"$tmpdir/b.1.exp" <<'EOF' +'\" -*- coding: UTF-8 -*- +.SH NAME +b \- é +EOF +gzip -c <"$tmpdir/b.1.exp" >"$tmpdir/b.1.gz" +cat >"$tmpdir/c.1.exp" <<'EOF' +'\" -*- coding: UTF-8 +.SH NAME +b \- é +EOF +cat >"$tmpdir/c.1" <<'EOF' +'\" -*- coding: ISO-8859-1 +EOF +<"$tmpdir/c.1.exp" tail -n +2 | iconv -f UTF-8 -t ISO-8859-1 >>"$tmpdir/c.1" +gzip "$tmpdir/c.1" + +run $MAN_RECODE -t UTF-8 --suffix .out \ + "$tmpdir/a.1" "$tmpdir/b.1.gz" "$tmpdir/c.1.gz" +expect_pass '--suffix with no coding tag' \ + 'diff -u "$tmpdir/a.1.exp" "$tmpdir/a.1.out"' +expect_pass '--suffix with gzip and coding tag matching target encoding' \ + 'diff -u "$tmpdir/b.1.exp" "$tmpdir/b.1.out"' +expect_pass '--suffix with gzip and coding tag not matching target encoding' \ + 'diff -u "$tmpdir/c.1.exp" "$tmpdir/c.1.out"' + +finish diff --git a/src/tests/man-so-links-same-section b/src/tests/man-so-links-same-section new file mode 100755 index 0000000..32eddaa --- /dev/null +++ b/src/tests/man-so-links-same-section @@ -0,0 +1,90 @@ +#! /bin/sh + +# Test for relative .so links between man pages in the same section (e.g. ".so bar.1"). +# https://bugzilla.redhat.com/show_bug.cgi?id=693458 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH + +cat >"$tmpdir/fake-program" <<EOF +#! /bin/sh +exec cat +EOF +chmod +x "$tmpdir/fake-program" +PATH="$abstmpdir:$PATH" +export PATH + +cat >>"$tmpdir/manpath.config" <<EOF +DEFINE tbl fake-program +DEFINE nroff fake-program +EOF + +# There are 2 kind of tests. First, when destination is not gzipped, what means +# that .so link contains full filename and second, when the destination is +# gzipped, and .so link doesn't contain the file suffix. + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1" \ + UTF-8 '' '' 'test \- top-level test page' +echo '.so man1/test.1' >"$tmpdir/usr/share/man/man1/test-fullso.1" +echo '.so test.1' >"$tmpdir/usr/share/man/man1/test-relso.1" + +write_page testb 1 "$tmpdir/usr/share/man/man1/testb.1.gz" \ + UTF-8 'gz' '' 'testb \- top-level test page' +echo '.so man1/testb.1' >"$tmpdir/usr/share/man/man1/test-fullsob.1" +echo '.so testb.1' >"$tmpdir/usr/share/man/man1/test-relsob.1" + +cat >"$tmpdir/1.exp" <<'EOF' +.TH test 1 +.SH NAME +test \- top-level test page +.SH DESCRIPTION +test +EOF + +cat >"$tmpdir/2.exp" <<'EOF' +.TH testb 1 +.SH NAME +testb \- top-level test page +.SH DESCRIPTION +test +EOF + +run $MAN -C "$tmpdir/manpath.config" test | \ + grep -v '^\.l[flt] ' >"$tmpdir/1.out" +expect_pass 'test(1) without .so link' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +run $MAN -C "$tmpdir/manpath.config" test-fullso | \ + grep -v '^\.l[flt] ' >"$tmpdir/2.out" +expect_pass 'test-fullso(1) .so link with section' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/2.out"' + +run $MAN -C "$tmpdir/manpath.config" test-relso | \ + grep -v '^\.l[flt] ' >"$tmpdir/3.out" +expect_pass 'test-relso(1) .so link without section' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/3.out"' + + +run $MAN -C "$tmpdir/manpath.config" testb | \ + grep -v '^\.l[flt] ' >"$tmpdir/4.out" +expect_pass 'testb(1) without .so link; gzipped' \ + 'diff -u "$tmpdir/2.exp" "$tmpdir/4.out"' + +run $MAN -C "$tmpdir/manpath.config" test-fullsob | \ + grep -v '^\.l[flt] ' >"$tmpdir/5.out" +expect_pass 'test-fullsob(1) .so link with section; gzipped' \ + 'diff -u "$tmpdir/2.exp" "$tmpdir/5.out"' + +run $MAN -C "$tmpdir/manpath.config" test-relsob | \ + grep -v '^\.l[flt] ' >"$tmpdir/6.out" +expect_pass 'test-relsob(1) .so link without section; gzipped' \ + 'diff -u "$tmpdir/2.exp" "$tmpdir/6.out"' + +finish diff --git a/src/tests/man-suffixed-extension b/src/tests/man-suffixed-extension new file mode 100755 index 0000000..100efff --- /dev/null +++ b/src/tests/man-suffixed-extension @@ -0,0 +1,53 @@ +#!/bin/sh + +# Test for: +# man chmod.2 => man 2 chmod +# man 'chmod(2)' => man 2 chmod +# man chmod.2p => man 2p chmod +# man 'chmod(2p)' => man 2p chmod + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH + +page_name="chmod" + +write_page "$page_name" 1 "$tmpdir/usr/share/man/man1/${page_name}.1.gz" \ + UTF-8 gz '' "$page_name \- coreutils $page_name manual page" +write_page "$page_name" 2 "$tmpdir/usr/share/man/man2/${page_name}.2.gz" \ + UTF-8 gz '' "$page_name \- $page_name() syscall manual page" + +cat >"$tmpdir/2.exp" <<EOF +$abstmpdir/usr/share/man/man2/${page_name}.2.gz +EOF + +run $MAN -C "$tmpdir/manpath.config" -aw "$page_name".2 >"$tmpdir/2.out" +expect_pass '"man name.2" is the same as "man 2 name"' \ + 'diff -u "$tmpdir/2.exp" "$tmpdir/2.out"' +run $MAN -C "$tmpdir/manpath.config" -aw "$page_name(2)" >"$tmpdir/2.out" +expect_pass '"man '\''name(2)'\''" is the same as "man 2 name"' \ + 'diff -u "$tmpdir/2.exp" "$tmpdir/2.out"' + +( + cd "$tmpdir/usr/share/man/man2/" + mv "${page_name}.2.gz" "${page_name}.2p.gz" +) + +cat >"$tmpdir/2p.exp" <<EOF +$abstmpdir/usr/share/man/man2/${page_name}.2p.gz +EOF + +run $MAN -C "$tmpdir/manpath.config" -aw "$page_name".2p >"$tmpdir/2p.out" +expect_pass '"man name.2p" is the same as "man 2p name"' \ + 'diff -u "$tmpdir/2p.exp" "$tmpdir/2p.out"' +run $MAN -C "$tmpdir/manpath.config" -aw "$page_name(2p)" >"$tmpdir/2p.out" +expect_pass '"man '\''name(2p)'\''" is the same as "man 2p name"' \ + 'diff -u "$tmpdir/2p.exp" "$tmpdir/2p.out"' + +finish diff --git a/src/tests/man-symlinks-with-matching-names b/src/tests/man-symlinks-with-matching-names new file mode 100755 index 0000000..ff264d6 --- /dev/null +++ b/src/tests/man-symlinks-with-matching-names @@ -0,0 +1,31 @@ +#! /bin/sh + +# Test for: +# https://bugs.debian.org/163347 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +init +fake_config /usr/local/man /usr/share/man +MANPATH="$tmpdir/usr/local/man:$tmpdir/usr/share/man" +export MANPATH + +write_page md5sum 1 "$tmpdir/usr/share/man/man1/md5sum.1.gz" \ + UTF-8 gz '' 'md5sum \- Debian md5sum manual page' +write_page md5sum 1 "$tmpdir/usr/share/man/man1/md5sum.textutils.1.gz" \ + UTF-8 gz '' 'md5sum \- coreutils md5sum manual page' +mkdir -p "$tmpdir/usr/local/man/man1" +ln -s ../../../share/man/man1/md5sum.textutils.1.gz \ + "$tmpdir/usr/local/man/man1/md5sum.1.gz" +cat >"$tmpdir/1.exp" <<EOF +$abstmpdir/usr/share/man/man1/md5sum.textutils.1.gz +$abstmpdir/usr/share/man/man1/md5sum.1.gz +EOF +run $MAN -C "$tmpdir/manpath.config" -aw md5sum >"$tmpdir/1.out" +expect_pass 'symlinks with matching names win' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +finish diff --git a/src/tests/manconv-coding-tags b/src/tests/manconv-coding-tags new file mode 100755 index 0000000..455caf0 --- /dev/null +++ b/src/tests/manconv-coding-tags @@ -0,0 +1,61 @@ +#! /bin/sh + +# Test manconv's support for Emacs-style coding: tags. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANCONV=manconv} + +init + +cat >"$tmpdir/1.exp" <<'EOF' +'\" -*- coding: UTF-8 +á +EOF +cat >"$tmpdir/1.inp" <<'EOF' +'\" -*- coding: ISO-8859-1 +EOF +<"$tmpdir/1.exp" tail -n +2 | iconv -f UTF-8 -t ISO-8859-1 >>"$tmpdir/1.inp" +run $MANCONV -f UTF-8 -t UTF-8 <"$tmpdir/1.inp" >"$tmpdir/1.out" +expect_pass 'simple coding tag' 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +cat >"$tmpdir/2.exp" <<'EOF' +'\" -*- mode: troff; coding: UTF-8 -*- +á +EOF +cat >"$tmpdir/2.inp" <<'EOF' +'\" -*- mode: troff; coding: ISO-8859-1 -*- +EOF +<"$tmpdir/2.exp" tail -n +2 | iconv -f UTF-8 -t ISO-8859-1 >>"$tmpdir/2.inp" +run $MANCONV -f UTF-8 -t UTF-8 <"$tmpdir/2.inp" >"$tmpdir/2.out" +expect_pass 'mode and coding tags' 'diff -u "$tmpdir/2.exp" "$tmpdir/2.out"' + +cat >"$tmpdir/3.exp" <<'EOF' +'\" -*- mode: troff; coding: UTF-8 -*- +á +EOF +cat >"$tmpdir/3.inp" <<'EOF' +'\" -*- mode: troff; coding: ISO-LATIN-1 -*- +EOF +<"$tmpdir/3.exp" tail -n +2 | iconv -f UTF-8 -t ISO-8859-1 >>"$tmpdir/3.inp" +run $MANCONV -f UTF-8 -t UTF-8 <"$tmpdir/3.inp" >"$tmpdir/3.out" +expect_pass 'iso-latin-1 coding alias' 'diff -u "$tmpdir/3.exp" "$tmpdir/3.out"' + +cat >"$tmpdir/4.inp" <<'EOF' +'\" -*- nroff -*- +EOF +run $MANCONV -f UTF-8 -t UTF-8 <"$tmpdir/4.inp" >"$tmpdir/4.out" +expect_pass 'preprocessor comment but no coding tag' \ + 'diff -u "$tmpdir/4.inp" "$tmpdir/4.out"' + +cat >"$tmpdir/5.exp" <<'EOF' +'\" -*- coding: utf-8 +á +EOF +cp "$tmpdir/5.exp" "$tmpdir/5.inp" +run $MANCONV -f UTF-8 -t UTF-8 <"$tmpdir/5.inp" >"$tmpdir/5.out" +expect_pass 'coding tag matches target encoding' \ + 'diff -u "$tmpdir/5.inp" "$tmpdir/5.out"' + +finish diff --git a/src/tests/manconv-guess-from-encoding b/src/tests/manconv-guess-from-encoding new file mode 100755 index 0000000..15b206b --- /dev/null +++ b/src/tests/manconv-guess-from-encoding @@ -0,0 +1,38 @@ +#! /bin/sh + +# Test manconv's support for guessing the input encoding if it is not +# explicitly specified. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANCONV=manconv} + +init + +write_page coding-tag 7 \ + "$tmpdir/usr/share/man/man7/coding-tag.7" \ + ISO-8859-1 '' '' 'coding-tag \- é' +iconv -f ISO-8859-1 -t UTF-8 \ + <"$tmpdir/usr/share/man/man7/coding-tag.7" \ + >"$tmpdir/coding-tag.7.exp" +run $MANCONV -t UTF-8 "$tmpdir/usr/share/man/man7/coding-tag.7" \ + >"$tmpdir/coding-tag.7.out" +expect_pass 'recode from encoding guessed from directory name' \ + 'diff -u "$tmpdir/coding-tag.7.exp" "$tmpdir/coding-tag.7.out"' + +write_page lang-dir 7 \ + "$tmpdir/usr/share/man/fr_FR.ISO-8859-1/man7/lang-dir.7.gz" \ + ISO-8859-1 gz '-*- coding: ISO-8859-1 -*-' 'lang-dir \- é' +cat >"$tmpdir/lang-dir.7.exp" <<'EOF' +'\" -*- coding: UTF-8 -*- +EOF +zcat "$tmpdir/usr/share/man/fr_FR.ISO-8859-1/man7/lang-dir.7.gz" | \ + tail -n +2 | iconv -f ISO-8859-1 -t UTF-8 >>"$tmpdir/lang-dir.7.exp" +run $MANCONV -t UTF-8 \ + "$tmpdir/usr/share/man/fr_FR.ISO-8859-1/man7/lang-dir.7.gz" \ + >"$tmpdir/lang-dir.7.out" +expect_pass 'recode from encoding guessed from directory name' \ + 'diff -u "$tmpdir/lang-dir.7.exp" "$tmpdir/lang-dir.7.out"' + +finish diff --git a/src/tests/manconv-incomplete-char-at-eof b/src/tests/manconv-incomplete-char-at-eof new file mode 100755 index 0000000..3557657 --- /dev/null +++ b/src/tests/manconv-incomplete-char-at-eof @@ -0,0 +1,15 @@ +#! /bin/sh + +# Test manconv's handling of incomplete characters at end of file. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANCONV=manconv} + +init + +printf '\314' >"$tmpdir/1.inp" # 0xCC +expect_pass 'incomplete character at EOF' '! run $MANCONV -f EUC-JP -t UTF-8//IGNORE <"$tmpdir/1.inp" >/dev/null' + +finish diff --git a/src/tests/manconv-odd-combinations b/src/tests/manconv-odd-combinations new file mode 100755 index 0000000..63817a4 --- /dev/null +++ b/src/tests/manconv-odd-combinations @@ -0,0 +1,78 @@ +#! /bin/sh + +# Test manconv's handling of various odd encoding combinations. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANCONV=manconv} + +init + +(for x in $(seq 160 255); do + printf "\\$(printf %03o "$x")" +done +echo) >"$tmpdir/1.inp" + +iconv -f ISO-8859-1 -t UTF-8 <"$tmpdir/1.inp" >"$tmpdir/1.exp" +run $MANCONV -f UTF-8:ISO-8859-1 -t UTF-8 <"$tmpdir/1.inp" >"$tmpdir/1.out" +expect_pass '-f UTF-8:ISO-8859-1 -t UTF-8 on ISO-8859-1 input' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +iconv -f ISO-8859-2 -t UTF-8 <"$tmpdir/1.inp" >"$tmpdir/1-latin2.exp" +run $MANCONV -f UTF-8:ISO-8859-2 -t UTF-8 \ + <"$tmpdir/1.inp" >"$tmpdir/1-latin2.out" +expect_pass '-f UTF-8:ISO-8859-2 -t UTF-8 on ISO-8859-2 input' \ + 'diff -u "$tmpdir/1-latin2.exp" "$tmpdir/1-latin2.out"' + +(for x in $(seq 1 1000); do + printf '‐' +done +echo 'Б' | iconv -f UTF-8 -t KOI8-R +echo '‐') >"$tmpdir/2.inp" +iconv -f KOI8-R -t UTF-8 <"$tmpdir/2.inp" >"$tmpdir/2.exp" +run $MANCONV -f UTF-8:KOI8-R -t UTF-8 <"$tmpdir/2.inp" >"$tmpdir/2.out" +expect_pass '-f UTF-8:KOI8-R -t UTF-8 on KOI8-R input with UTF-8 prefix' \ + 'diff -u "$tmpdir/2.exp" "$tmpdir/2.out"' + +(for x in $(seq 160 255); do + printf "\\$(printf %03o "$x")" +done +echo) | iconv -f ISO-8859-1 -t UTF-8 >"$tmpdir/3.inp" +run $MANCONV -f UTF-8:ISO-8859-1 -t UTF-8 <"$tmpdir/3.inp" >"$tmpdir/3.out" +expect_pass '-f UTF-8:ISO-8859-1 -t UTF-8 preserves UTF-8 input' \ + 'diff -u "$tmpdir/3.inp" "$tmpdir/3.out"' + +# U+00B7 MIDDLE DOT is not representable in ISO-8859-2, and so should be +# omitted. However, manconv should still recognise that the input was UTF-8 +# rather than falling back to ISO-8859-2. +cat >"$tmpdir/4.inp" <<'EOF' +š·ł +EOF +iconv -f UTF-8 -t ISO-8859-2 >"$tmpdir/4.exp" <<EOF +šł +EOF +run $MANCONV -f UTF-8:ISO-8859-2 -t ISO-8859-2//IGNORE \ + <"$tmpdir/4.inp" >"$tmpdir/4.out" +expect_pass 'recognises input encoding and omits invalid output character' \ + 'diff -u "$tmpdir/4.exp" "$tmpdir/4.out"' + +# 0xAE does not exist in ISO-8859-7, so manconv won't be able to recode this +# to UTF-8 without conversion errors. (In the original case where this was +# seen in the wild, the coding: tag should actually have read ISO-8859-13.) +iconv -f UTF-8 -t ISO-8859-13 >"$tmpdir/5.inp" <<'EOF' +'\" -*- coding: ISO-8859-7 +REGISTERED SIGN: ® +trailing data +EOF +cat >"$tmpdir/5.exp" <<'EOF' +'\" -*- coding: UTF-8 +EOF +<"$tmpdir/5.inp" tail -n +2 | iconv -f ISO-8859-7 -t UTF-8//IGNORE \ + >>"$tmpdir/5.exp" 2>/dev/null +run $MANCONV -f UTF-8:ISO-8859-1 -t UTF-8//IGNORE \ + <"$tmpdir/5.inp" >"$tmpdir/5.out" +expect_pass 'copes with invalid input characters' \ + 'diff -u "$tmpdir/5.exp" "$tmpdir/5.out"' + +finish diff --git a/src/tests/mandb-basic b/src/tests/mandb-basic new file mode 100755 index 0000000..0037bb4 --- /dev/null +++ b/src/tests/mandb-basic @@ -0,0 +1,24 @@ +#! /bin/sh + +# Basic mandb tests. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANDB=mandb} +: ${ACCESSDB=accessdb} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH +db_ext="$(db_ext)" + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1.gz" UTF-8 gz t \ + 'test \- simple mandb test' +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +echo 'test -> "- 1 1 MTIME A - - gz simple mandb test"' >"$tmpdir/1.exp" +accessdb_filter "$tmpdir/usr/share/man/index$db_ext" >"$tmpdir/1.out" +expect_pass 'simple mandb test' 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +finish diff --git a/src/tests/mandb-bogus-symlink b/src/tests/mandb-bogus-symlink new file mode 100755 index 0000000..b0ec9aa --- /dev/null +++ b/src/tests/mandb-bogus-symlink @@ -0,0 +1,25 @@ +#! /bin/sh + +# Test for double free or corruption crash with bogus filename and symlink of man page. +# https://bugzilla.redhat.com/show_bug.cgi?id=702904 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANDB=mandb} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH + +mkdir -p "$tmpdir/usr/share/man/man8" +mkdir -p "$tmpdir/usr/lib/aa-bbb" +write_page test1 8 "$tmpdir/usr/lib/aa-bbb/aa-test1.8.gz" UTF-8 gz t \ + 'test1 \- testing man page' +ln -s "../../../lib/aa-bbb/aa-test1.8.gz" "$tmpdir/usr/share/man/man8/aa-test1.8.gz" +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +# $? is deliberately expanded here. +expect_pass 'double free' "test $? -eq 0" + +finish diff --git a/src/tests/mandb-cachedir-tag b/src/tests/mandb-cachedir-tag new file mode 100755 index 0000000..657c8e0 --- /dev/null +++ b/src/tests/mandb-cachedir-tag @@ -0,0 +1,28 @@ +#! /bin/sh + +# Don't create CACHEDIR.TAG in manpath + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANDB=mandb} + +init +fake_config /usr/share/man +mkdir -p "$tmpdir/usr/share/man" +mkdir -p "$tmpdir/usr/dir/man" +mkdir -p "$tmpdir/var/cache/man" +echo "MANDATORY_MANPATH $abstmpdir/usr/share/man" > "$tmpdir/manpath.config" +echo "MANDATORY_MANPATH $abstmpdir/usr/dir/man" >> "$tmpdir/manpath.config" +echo "MANDB_MAP $abstmpdir/usr/share/man $abstmpdir/var/cache/man" >> "$tmpdir/manpath.config" + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1" UTF-8 '' '' \ + 'test \- simple mandb test' +write_page test2 1 "$tmpdir/usr/dir/man/man1/test2.1" UTF-8 '' '' \ + 'test2 \- simple mandb test' +run $MANDB -C "$tmpdir/manpath.config" -q "$tmpdir/usr/share/man:$tmpdir/usr/dir/man" +expect_pass "CACHEDIR.TAG exists" "[ -e $tmpdir/var/cache/man/CACHEDIR.TAG ]" +expect_pass "CACHEDIR.TAG doesn't exist 01" "[ ! -e $tmpdir/usr/share/man/CACHEDIR.TAG ]" +expect_pass "CACHEDIR.TAG doesn't exist 02" "[ ! -e $tmpdir/usr/dir/man/CACHEDIR.TAG ]" + +finish diff --git a/src/tests/mandb-empty-page b/src/tests/mandb-empty-page new file mode 100755 index 0000000..88e6ad0 --- /dev/null +++ b/src/tests/mandb-empty-page @@ -0,0 +1,30 @@ +#! /bin/sh + +# Test handling of empty files. +# https://bugs.debian.org/622104 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANDB=mandb} + +init +fake_config /usr/share/man /usr/X11R6/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH +db_ext="$(db_ext)" + +mkdir -p "$tmpdir/usr/share/man/man1" +touch "$tmpdir/usr/share/man/man1/empty.1" +gzip -9 "$tmpdir/usr/share/man/man1/empty.1" +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +# $? is deliberately expanded here. +expect_pass 'empty page' "test $? -eq 0" + +./fspause +ln -s empty.1.gz "$tmpdir/usr/share/man/man1/empty2.1.gz" +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +# $? is deliberately expanded here. +expect_pass 'symlink to empty page' "test $? -eq 0" + +finish diff --git a/src/tests/mandb-regular-file-symlink-changes b/src/tests/mandb-regular-file-symlink-changes new file mode 100755 index 0000000..79b6676 --- /dev/null +++ b/src/tests/mandb-regular-file-symlink-changes @@ -0,0 +1,67 @@ +#! /bin/sh + +# What happens when a manual page changes from a regular file to a symbolic +# link and back? +# https://bugs.debian.org/490582 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANDB=mandb} +: ${ACCESSDB=accessdb} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH +db_ext="$(db_ext)" + +write_page fs 5 "$tmpdir/usr/share/man/man5/fs.5.gz" \ + UTF-8 gz t 'fs \- fs(5)' +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +cat >"$tmpdir/1.exp" <<EOF +fs -> "- 5 5 MTIME A - - gz fs(5)" +EOF +accessdb_filter "$tmpdir/usr/share/man/index$db_ext" >"$tmpdir/1.out" +expect_pass 'fs(5) setup' 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +./fspause +write_page filesystems 5 "$tmpdir/usr/share/man/man5/filesystems.5.gz" \ + UTF-8 gz t 'filesystems \- filesystems(5)' +ln -sf filesystems.5.gz "$tmpdir/usr/share/man/man5/fs.5.gz" +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +cat >"$tmpdir/2.exp" <<EOF +filesystems -> "- 5 5 MTIME A - - gz filesystems(5)" +fs -> "- 5 5 MTIME B - - gz filesystems(5)" +EOF +accessdb_filter "$tmpdir/usr/share/man/index$db_ext" >"$tmpdir/2.out" +expect_pass 'mandb notices regular file -> symlink' \ + 'diff -u "$tmpdir/2.exp" "$tmpdir/2.out"' + +./fspause +ln -sf fs.5.gz "$tmpdir/usr/share/man/man5/fs2.5.gz" +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +cat >"$tmpdir/3.exp" <<EOF +filesystems -> "- 5 5 MTIME A - - gz filesystems(5)" +fs -> "- 5 5 MTIME B - - gz filesystems(5)" +fs2 -> "- 5 5 MTIME B - - gz filesystems(5)" +EOF +accessdb_filter "$tmpdir/usr/share/man/index$db_ext" >"$tmpdir/3.out" +expect_pass 'mandb notices two-level symlink' \ + 'diff -u "$tmpdir/3.exp" "$tmpdir/3.out"' + +./fspause +rm -f "$tmpdir/usr/share/man/man5/fs.5.gz" +write_page fs 5 "$tmpdir/usr/share/man/man5/fs.5.gz" \ + UTF-8 gz t 'fs \- new fs(5)' +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +cat >"$tmpdir/4.exp" <<EOF +filesystems -> "- 5 5 MTIME A - - gz filesystems(5)" +fs -> "- 5 5 MTIME A - - gz new fs(5)" +fs2 -> "- 5 5 MTIME B - - gz filesystems(5)" +EOF +accessdb_filter "$tmpdir/usr/share/man/index$db_ext" >"$tmpdir/4.out" +expect_pass 'mandb notices symlink -> regular file' \ + 'diff -u "$tmpdir/4.exp" "$tmpdir/4.out"' + +finish diff --git a/src/tests/mandb-symlink-beats-whatis-ref b/src/tests/mandb-symlink-beats-whatis-ref new file mode 100755 index 0000000..379bbe0 --- /dev/null +++ b/src/tests/mandb-symlink-beats-whatis-ref @@ -0,0 +1,60 @@ +#! /bin/sh + +# Test for: +# https://bugs.debian.org/204249 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANDB=mandb} +: ${ACCESSDB=accessdb} + +init +fake_config /usr/share/man /usr/X11R6/man +MANPATH="$tmpdir/usr/share/man:$tmpdir/usr/X11R6/man" +export MANPATH +db_ext="$(db_ext)" + +write_page xterm 1x "$tmpdir/usr/X11R6/man/man1/xterm.1x.gz" \ + UTF-8 gz '' 'xterm \- terminal emulator for X' +mkdir -p "$tmpdir/usr/share/man/man1" +ln -s ../../../X11R6/man/man1/xterm.1x.gz \ + "$tmpdir/usr/share/man/man1/x-terminal-emulator.1.gz" +run $MANDB -C "$tmpdir/manpath.config" -u -q \ + "$tmpdir/usr/share/man:$tmpdir/usr/X11R6/man" +cat >"$tmpdir/1-share.exp" <<EOF +x-terminal-emulator -> "- 1 1 MTIME B - - gz terminal emulator for X" +EOF +cat >"$tmpdir/1-X11R6.exp" <<EOF +xterm -> "- 1x 1 MTIME A - - gz terminal emulator for X" +EOF +accessdb_filter "$tmpdir/usr/share/man/index$db_ext" >"$tmpdir/1-share.out" +accessdb_filter "$tmpdir/usr/X11R6/man/index$db_ext" >"$tmpdir/1-X11R6.out" +expect_pass '/usr/share/man x-terminal-emulator -> xterm' \ + 'diff -u "$tmpdir/1-share.exp" "$tmpdir/1-share.out"' +expect_pass '/usr/X11R6/man x-terminal-emulator -> xterm' \ + 'diff -u "$tmpdir/1-X11R6.exp" "$tmpdir/1-X11R6.out"' + +./fspause +write_page uxterm 1x "$tmpdir/usr/X11R6/man/man1/uxterm.1x.gz" \ + UTF-8 gz '' \ + 'uxterm \- X terminal emulator for Unicode (UTF-8) environments' +ln -sf ../../../X11R6/man/man1/uxterm.1x.gz \ + "$tmpdir/usr/share/man/man1/x-terminal-emulator.1.gz" +run $MANDB -C "$tmpdir/manpath.config" -u -q \ + "$tmpdir/usr/share/man:$tmpdir/usr/X11R6/man" +cat >"$tmpdir/2-share.exp" <<EOF +x-terminal-emulator -> "- 1 1 MTIME B - - gz X terminal emulator for Unicode (UTF-8) environments" +EOF +cat >"$tmpdir/2-X11R6.exp" <<EOF +uxterm -> "- 1x 1 MTIME A - - gz X terminal emulator for Unicode (UTF-8) environments" +xterm -> "- 1x 1 MTIME A - - gz terminal emulator for X" +EOF +accessdb_filter "$tmpdir/usr/share/man/index$db_ext" >"$tmpdir/2-share.out" +accessdb_filter "$tmpdir/usr/X11R6/man/index$db_ext" >"$tmpdir/2-X11R6.out" +expect_pass '/usr/share/man x-terminal-emulator -> uxterm' \ + 'diff -u "$tmpdir/2-share.exp" "$tmpdir/2-share.out"' +expect_pass '/usr/X11R6/man x-terminal-emulator -> uxterm' \ + 'diff -u "$tmpdir/2-X11R6.exp" "$tmpdir/2-X11R6.out"' + +finish diff --git a/src/tests/mandb-whatis-broken-link-changes b/src/tests/mandb-whatis-broken-link-changes new file mode 100755 index 0000000..23d2309 --- /dev/null +++ b/src/tests/mandb-whatis-broken-link-changes @@ -0,0 +1,55 @@ +#! /bin/sh + +# Ensure that we don't repeatedly rescan when a whatis entry turns into a +# broken link. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANDB=mandb} +: ${ACCESSDB=accessdb} + +init +fake_config /usr/share/man +MANPATH="$tmpdir/usr/share/man" +export MANPATH +db_ext="$(db_ext)" + +NL=' +' + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1.gz" UTF-8 gz t \ + "test \- test page${NL}.br${NL}testlink \- link to test page" +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +cat >"$tmpdir/1.exp" <<EOF +test -> "- 1 1 MTIME A - - gz test page" +testlink -> "- 1 1 MTIME C test - gz " +EOF +accessdb_filter "$tmpdir/usr/share/man/index$db_ext" >"$tmpdir/1.out" +expect_pass 'setup' 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +./fspause +echo '.so nonexistent.1' | gzip -9c >"$tmpdir/usr/share/man/man1/testlink.1.gz" +run $MANDB -C "$tmpdir/manpath.config" -u -q "$tmpdir/usr/share/man" +cat >"$tmpdir/2.exp" <<EOF +test -> "- 1 1 MTIME A - - gz test page" +testlink -> "- 1 1 MTIME C test - gz " +EOF +accessdb_filter "$tmpdir/usr/share/man/index$db_ext" >"$tmpdir/2.out" +expect_pass 'broken whatis' 'diff -u "$tmpdir/2.exp" "$tmpdir/2.out"' + +./fspause +LC_ALL=C run $MANDB -C "$tmpdir/manpath.config" -u \ + "$tmpdir/usr/share/man" >"$tmpdir/3.out" 2>/dev/null +cat >"$tmpdir/3.exp" <<EOF +Purging old database entries in $abstmpdir/usr/share/man... +Processing manual pages under $abstmpdir/usr/share/man... +Processing manual pages under $abstmpdir/usr/share/man/cat1... +0 man subdirectories contained newer manual pages. +0 manual pages were added. +0 stray cats were added. +0 old database entries were purged. +EOF +expect_pass 'mandb does not rescan' 'diff -u "$tmpdir/3.exp" "$tmpdir/3.out"' + +finish diff --git a/src/tests/testlib.sh b/src/tests/testlib.sh new file mode 100644 index 0000000..f23df4b --- /dev/null +++ b/src/tests/testlib.sh @@ -0,0 +1,108 @@ +failures=0 + +# Save tests the trouble of exporting variables they set when executing 'run'. +export LC_ALL + +# Isolate tests from whatever the system configuration may happen to be. +MAN_TEST_DISABLE_SYSTEM_CONFIG=1 +export MAN_TEST_DISABLE_SYSTEM_CONFIG + +init () { + # Create a temporary directory in /tmp or ./ , + # put path to it into $tmpdir and $abstmpdir, + # remove it on exit. + { + tmpdir=$(mktemp -d) && + abstmpdir="$tmpdir" && + test -d "$tmpdir" + } || { + tmpdir="tmp-${0##*/}" + abstmpdir="$(pwd -P)/$tmpdir" + mkdir "$tmpdir" + } || + exit $? + trap 'rm -rf "$tmpdir"' HUP INT QUIT TERM +} + +run () { + "$abs_top_builddir/libtool" --mode=execute \ + -dlopen "$abs_top_builddir/lib/.libs/libman.la" \ + -dlopen "$abs_top_builddir/libdb/.libs/libmandb.la" \ + "$@" +} + +fake_config () { + for dir; do + echo "MANDATORY_MANPATH $tmpdir$dir" + done >"$tmpdir/manpath.config" +} + +db_ext () { + case $DBTYPE in + gdbm) echo .db ;; + btree) echo .bt ;; + esac +} + +# Arguments: name section path encoding compression_extension preprocessor_line name_line +write_page () { + mkdir -p "${3%/*}" + >"$3.tmp1" + if [ "$6" ]; then + echo "'\\\" $6" >>"$3.tmp1" + fi + cat >>"$3.tmp1" <<EOF +.TH $1 $2 +.SH NAME +$7 +.SH DESCRIPTION +test +EOF + iconv -f UTF-8 -t "$4" <"$3.tmp1" >"$3.tmp2" + case $5 in + '') cat ;; + gz|z) gzip -9c ;; + Z) compress -c ;; + bz2) bzip2 -9c ;; + lzma) lzma -9c ;; + esac <"$3.tmp2" >"$3" + rm -f "$3.tmp1" "$3.tmp2" +} + +accessdb_filter () { + # e.g. 'test -> "- 1 1 1250702063 A - - gz simple mandb test"' + run $ACCESSDB "$1" | grep -v '^\$' | \ + sed 's/\(-> "[^ ][^ ]* [^ ][^ ]* [^ ][^ ]* \)[^ ][^ ]* [^ ][^ ]* /\1MTIME /' +} + +expect_pass () { + ret=0 + eval "$2" || ret=$? + if [ "$ret" = 0 ]; then + echo " PASS: $1" + else + failures="$(($failures + 1))" + echo " FAIL: $1" + fi +} + +skip () { + echo " SKIP: $1" + rm -rf "$abstmpdir" + exit 77 +} + +finish () { + case $failures in + 0) + rm -rf "$abstmpdir" + exit 0 + ;; + *) + if [ -z "$TEST_FAILURE_KEEP" ]; then + rm -rf "$abstmpdir" + fi + exit 1 + ;; + esac +} diff --git a/src/tests/whatis-path-to-executable b/src/tests/whatis-path-to-executable new file mode 100755 index 0000000..07b18a5 --- /dev/null +++ b/src/tests/whatis-path-to-executable @@ -0,0 +1,56 @@ +#! /bin/sh + +# Test that whatis behaves appropriately when given a path to an executable. + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MANDB=mandb} +: ${WHATIS=whatis} + +init +fake_config /usr/share/man /usr/local/man +cat >>"$tmpdir/manpath.config" <<EOF +MANPATH_MAP $tmpdir/usr/bin $tmpdir/usr/share/man +MANPATH_MAP $tmpdir/usr/local/bin $tmpdir/usr/local/man +EOF +MANPATH="$tmpdir/usr/share/man:$tmpdir/usr/local/man" +export MANPATH + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1.gz" \ + UTF-8 gz '' 'test \- /usr/bin/test' +write_page test 8 "$tmpdir/usr/local/man/man8/test.8.gz" \ + UTF-8 gz '' 'test \- /usr/local/bin/test' +mkdir -p "$tmpdir/usr/bin" "$tmpdir/usr/local/bin" +touch "$tmpdir/usr/bin/test" "$tmpdir/usr/local/bin/test" +chmod +x "$tmpdir/usr/bin/test" "$tmpdir/usr/local/bin/test" +run $MANDB -C "$tmpdir/manpath.config" -u -q \ + "$tmpdir/usr/share/man:$tmpdir/usr/local/man" + +cat >"$tmpdir/1.exp" <<EOF +test (1) - /usr/bin/test +test (8) - /usr/local/bin/test +EOF +PATH="$PATH:$tmpdir/usr/bin:$tmpdir/usr/local/bin" run $WHATIS \ + -C "$tmpdir/manpath.config" test >"$tmpdir/1.out" +expect_pass 'simple name returns all matches' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +cat >"$tmpdir/2.exp" <<EOF +test (1) - /usr/bin/test +EOF +PATH="$PATH:$tmpdir/usr/bin:$tmpdir/usr/local/bin" run $WHATIS \ + -C "$tmpdir/manpath.config" "$tmpdir/usr/bin/test" >"$tmpdir/2.out" +expect_pass '/usr/bin/test only returns appropriate match' \ + 'diff -u "$tmpdir/2.exp" "$tmpdir/2.out"' + +cat >"$tmpdir/3.exp" <<EOF +test (8) - /usr/local/bin/test +EOF +PATH="$PATH:$tmpdir/usr/bin:$tmpdir/usr/local/bin" run $WHATIS \ + -C "$tmpdir/manpath.config" "$tmpdir/usr/local/bin/test" \ + >"$tmpdir/3.out" +expect_pass '/usr/local/bin/test only returns appropriate match' \ + 'diff -u "$tmpdir/3.exp" "$tmpdir/3.out"' + +finish diff --git a/src/tests/zsoelim-so-includes b/src/tests/zsoelim-so-includes new file mode 100755 index 0000000..311bf16 --- /dev/null +++ b/src/tests/zsoelim-so-includes @@ -0,0 +1,61 @@ +#! /bin/sh + +# Test for: +# https://bugs.debian.org/503472 + +: ${srcdir=.} +. "$srcdir/testlib.sh" + +: ${MAN=man} + +init +fake_config /usr/local/man /usr/share/man +MANPATH="$tmpdir/usr/local/man:$tmpdir/usr/share/man" +export MANPATH + +cat >"$tmpdir/fake-program" <<EOF +#! /bin/sh +exec cat +EOF +chmod +x "$tmpdir/fake-program" +PATH="$abstmpdir:$PATH" +export PATH + +cat >>"$tmpdir/manpath.config" <<EOF +DEFINE tbl fake-program +DEFINE nroff fake-program +EOF + +write_page test 1 "$tmpdir/usr/share/man/man1/test.1" \ + UTF-8 '' '' 'test \- top-level test page' +echo '.so man7/test2.7' >>"$tmpdir/usr/share/man/man1/test.1" +write_page test2 7 "$tmpdir/usr/local/man/man7/test2.7" \ + UTF-8 '' '' 'test2 \- second-level local test page' +echo '.so test3.1' >>"$tmpdir/usr/local/man/man7/test2.7" +write_page test3 1 "$tmpdir/usr/local/man/man1/test3.1" \ + UTF-8 '' '' 'test3 \- third-level local test page' +write_page test3 1 "$tmpdir/usr/share/man/man1/test3.1" \ + UTF-8 '' '' 'test3 \- third-level test page' +cat >"$tmpdir/1.exp" <<'EOF' +.TH test 1 +.SH NAME +test \- top-level test page +.SH DESCRIPTION +test +.TH test2 7 +.SH NAME +test2 \- second-level local test page +.SH DESCRIPTION +test +.TH test3 1 +.SH NAME +test3 \- third-level test page +.SH DESCRIPTION +test +EOF +run $MAN -C "$tmpdir/manpath.config" test | \ + grep -v '^\.l[flt] ' >"$tmpdir/1.out" +expect_pass 'test(1) expanded correctly' \ + 'diff -u "$tmpdir/1.exp" "$tmpdir/1.out"' + +finish diff --git a/src/ult_src.c b/src/ult_src.c new file mode 100644 index 0000000..43e7a9b --- /dev/null +++ b/src/ult_src.c @@ -0,0 +1,379 @@ +/* + * ult_src.c: Find the ultimate source of a page + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011, + * 2012 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * code to seek out the original (ultimate) source man file for + * any specified man file. Soft and hard links and .so inclusions + * are traced. Use: reduce amount of cat files to a minimum. + * + * Mon May 2 11:14:28 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <assert.h> +#include <dirent.h> +#include <unistd.h> + +#include "canonicalize.h" +#include "dirname.h" +#include "error.h" +#include "gl_xlist.h" +#include "xvasprintf.h" + +#include "gettext.h" +#define _(String) gettext (String) + +#include "manconfig.h" + +#include "pipeline.h" +#include "decompress.h" + +#include "globbing.h" +#include "ult_src.h" + +/* Find minimum value hard link filename for given file and inode. + * Returns a newly allocated string. + */ +static char *ult_hardlink (const char *fullpath, ino_t inode) +{ + DIR *mdir; + struct dirent *manlist; + char *base, *dir, *ret; + const char *slash; + + slash = strrchr (fullpath, '/'); + assert (slash); + dir = xstrndup (fullpath, slash - fullpath); + base = xstrdup (++slash); + + mdir = opendir (dir); + if (mdir == NULL) { + if (quiet < 2) + error (0, errno, _("can't search directory %s"), dir); + free (dir); + free (base); + return NULL; + } + + while ((manlist = readdir (mdir))) { + if (manlist->d_ino == inode && + strcmp (base, manlist->d_name) > 0) { + free (base); + base = xstrdup (manlist->d_name); + debug ("ult_hardlink: (%s)\n", base); + } + } + closedir (mdir); + + /* If we already are the link with the smallest name value */ + /* return NULL */ + + if (strcmp (base, slash) == 0) { + free (dir); + free (base); + return NULL; + } + + ret = xasprintf ("%s/%s", dir, base); + free (dir); + free (base); + return ret; +} + +/* Resolve all symbolic links within 'fullpath'. + * Returns a newly allocated string. + */ +static char *ult_softlink (const char *fullpath) +{ + char *resolved_path; + + resolved_path = canonicalize_file_name (fullpath); + if (!resolved_path) { + /* discard the unresolved path */ + if (quiet < 2) { + if (errno == ENOENT) + error (0, 0, + _("warning: %s is a dangling symlink"), + fullpath); + else + error (0, errno, _("can't resolve %s"), + fullpath); + } + return NULL; + } + + debug ("ult_softlink: (%s)\n", resolved_path); + + return resolved_path; +} + +/* Test 'buffer' to see if it contains a .so include. If so and it's not an + * absolute filename, return newly allocated string whose contents are the + * include. + */ +static char *test_for_include (const char *buffer) +{ + if (!buffer) + return NULL; + + /* strip out any leading whitespace (if any) */ + while (CTYPE (isspace, *buffer)) + buffer++; + + /* see if the `command' is a .so */ + if (strncmp (buffer, ".so", 3) == 0) { + buffer += 3; + + /* strip out any whitespace between the command and + it's argumant */ + while (CTYPE (isspace, *buffer)) + buffer++; + + /* If .so's argument is an absolute filename, it could be + * either (i) a macro inclusion, (ii) a non local manual page + * or (iii) a (somewhat bogus) reference to a local manual + * page. + * + * If (i) or (ii), we must not follow the reference. (iii) is + * a problem with the manual page, thus we don't want to + * follow any absolute inclusions in our quest for the + * ultimate source file */ + if (*buffer != '/') { + const char *end = buffer; + while (*end && !CTYPE (isspace, *end)) + ++end; + return xstrndup (buffer, end - buffer); + } + } + return NULL; +} + +static char *find_include (const char *name, const char *path, + const char *include) +{ + char *ret; + char *dirname; + char *temp_file; + + /* Restore the original path from before ult_softlink() etc., in + * case it went outside the mantree. + */ + ret = xasprintf ("%s/%s", path, include); + + /* If the original path from above doesn't exist, try to create new + * path as if the "include" was relative to the current man page. + */ + if (CAN_ACCESS (ret, F_OK)) + return ret; + + dirname = dir_name (name); + temp_file = xasprintf ("%s/%s", dirname, include); + free (dirname); + + if (CAN_ACCESS (temp_file, F_OK)) { + /* Just plain include. */ + free (ret); + ret = canonicalize_file_name (temp_file); + } else { + /* Try globbing - the file suffix might be missing. */ + char *temp_file_asterisk = xasprintf ("%s*", temp_file); + gl_list_t candidate_files = expand_path (temp_file_asterisk); + + free (temp_file_asterisk); + if (gl_list_size (candidate_files)) { + const char *candidate_file = gl_list_get_at + (candidate_files, 0); + if (CAN_ACCESS (candidate_file, F_OK)) { + free (ret); + ret = canonicalize_file_name (candidate_file); + } + } + gl_list_free (candidate_files); + } + free (temp_file); + + return ret; +} + +/* + * recursive function which finds the ultimate source file by following + * any ".so filename" directives in the first line of the man pages. + * Also (optionally) traces symlinks and hard links(!). + * + * name is full pathname, path is the MANPATH directory (/usr/man) + * flags is a combination of SO_LINK | SOFT_LINK | HARD_LINK + */ +const char *ult_src (const char *name, const char *path, + struct stat *buf, int flags, gl_list_t trace) +{ + static char *base; /* must be static */ + static short recurse; /* must be static */ + + /* initialise the function */ + + if (trace) + gl_list_add_last (trace, xstrdup (name)); + + /* as ult_softlink() & ult_hardlink() do all of their respective + * resolving in one call, only need to sort them out once + */ + + if (recurse == 0) { + struct stat new_buf; + free (base); + base = xstrdup (name); + + debug ("\nult_src: File %s in mantree %s\n", name, path); + + /* If we don't have a buf, allocate and assign one */ + if (!buf && ((flags & SOFT_LINK) || (flags & HARD_LINK))) { + buf = &new_buf; + if (lstat (base, buf) == -1) { + if (quiet < 2) + error (0, errno, _("can't resolve %s"), + base); + return NULL; + } + } + + /* Permit semi local (inter-tree) soft links */ + if (flags & SOFT_LINK) { + assert (buf); /* initialised above */ + if (S_ISLNK (buf->st_mode)) { + /* Is a symlink, resolve it. */ + char *softlink = ult_softlink (base); + if (softlink) { + free (base); + base = softlink; + } else + return NULL; + } + } + + /* Only deal with local (inter-dir) HARD links */ + if (flags & HARD_LINK) { + assert (buf); /* initialised above */ + if (buf->st_nlink > 1) { + /* Has HARD links, find least value */ + char *hardlink = ult_hardlink (base, + buf->st_ino); + if (hardlink) { + free (base); + base = hardlink; + } + } + } + } + + /* keep a check on recursion level */ + else if (recurse == 10) { + if (quiet < 2) + error (0, 0, _("%s is self referencing"), name); + return NULL; + } + + if (flags & SO_LINK) { + const char *buffer; + char *decomp_base; + pipeline *decomp; + char *include; + struct stat st; + + if (stat (base, &st) < 0) { + struct compression *comp = comp_file (base); + + if (comp) { + free (base); + base = comp->stem; + comp->stem = NULL; /* steal memory */ + } else { + if (quiet < 2) + error (0, errno, _("can't open %s"), + base); + return NULL; + } + } + + /* base may change for recursive calls to ult_src, but + * decompress_open doesn't keep its own copy. + */ + decomp_base = xstrdup (base); + decomp = decompress_open (decomp_base); + if (!decomp) { + if (quiet < 2) + error (0, errno, _("can't open %s"), base); + free (decomp_base); + return NULL; + } + pipeline_start (decomp); + + /* make sure that we skip over any comments */ + do { + buffer = pipeline_readline (decomp); + } while (buffer && STRNEQ (buffer, ".\\\"", 3)); + + include = test_for_include (buffer); + if (include) { + char *new_name; + const char *ult; + + free (base); + base = find_include (name, path, include); + free (include); + + debug ("ult_src: points to %s\n", base); + + recurse++; + /* Take a copy; it's unwise to pass base directly to + * a recursive call, as it may be freed. + */ + new_name = xstrdup (base); + ult = ult_src (new_name, path, NULL, flags, trace); + free (new_name); + recurse--; + + pipeline_wait (decomp); + pipeline_free (decomp); + free (decomp_base); + return ult; + } + + pipeline_wait (decomp); + pipeline_free (decomp); + free (decomp_base); + } + + /* We have the ultimate source */ + if (trace) + gl_list_add_last (trace, xstrdup (base)); + return base; +} diff --git a/src/ult_src.h b/src/ult_src.h new file mode 100644 index 0000000..857700d --- /dev/null +++ b/src/ult_src.h @@ -0,0 +1,39 @@ +/* + * ult_src.h: Interface to finding the ultimate source of a page + * + * Copyright (C) 1990, 1991 John W. Eaton. + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2002, 2003, 2011 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gl_list.h" + +#define SO_LINK 0001 +#define SOFT_LINK 0002 +#define HARD_LINK 0004 + +struct stat; + +/* If ult_trace is non-NULL, it should be a gl_list_t of const char * which + * ult_src populates with the trace of the link chain from a given file. + * Any names listed here should not have WHATIS_MAN entries created for + * them. + */ +extern const char *ult_src (const char *name, const char *path, + struct stat *buf, int flags, gl_list_t trace); diff --git a/src/whatis.c b/src/whatis.c new file mode 100644 index 0000000..d255916 --- /dev/null +++ b/src/whatis.c @@ -0,0 +1,981 @@ +/* + * whatis.c: search the index or whatis database(s) for words. + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011, + * 2012 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * routines for whatis and apropos programs. Whatis looks up the + * keyword for the description, apropos searches the entire database + * for word matches. + * + * Mon Aug 8 20:35:30 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + * + * CJW: Add more safety in the face of corrupted databases. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> + +#include "gettext.h" +#include <locale.h> +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#ifdef HAVE_ICONV +# include <iconv.h> +#endif /* HAVE_ICONV */ + +#include <sys/types.h> +#include <sys/stat.h> +#include "regex.h" + +#include "argp.h" +#include "dirname.h" +#include "gl_hash_set.h" +#include "gl_list.h" +#include "gl_xset.h" +#include "fnmatch.h" +#include "progname.h" +#include "xvasprintf.h" + +#include "manconfig.h" + +#include "cleanup.h" +#include "error.h" +#include "glcontainers.h" +#include "pipeline.h" +#include "pathsearch.h" +#include "linelength.h" +#include "wordfnmatch.h" +#include "xregcomp.h" +#include "encodings.h" +#include "sandbox.h" + +#include "mydbm.h" +#include "db_storage.h" + +#include "manp.h" + +static gl_list_t manpathlist; + +extern char *user_config_file; +static char **keywords; +static int num_keywords; + +bool am_apropos; +int quiet = 1; +man_sandbox *sandbox; + +#ifdef HAVE_ICONV +iconv_t conv_to_locale; +#endif /* HAVE_ICONV */ + +static regex_t *preg; +static bool regex_opt; +static bool exact; + +static bool wildcard; + +static bool require_all; + +static bool long_output; + +static char **sections; + +static char *manp = NULL; +static const char *alt_systems = ""; +static const char *locale = NULL; +static char *multiple_locale = NULL, *internal_locale; + +static gl_set_t display_seen = NULL; + +const char *argp_program_version; /* initialised in main */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("KEYWORD..."); +static const char apropos_doc[] = "\v" N_("The --regex option is enabled by default."); + +static struct argp_option options[] = { + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "verbose", 'v', 0, 0, N_("print verbose warning messages") }, + { "regex", 'r', 0, 0, N_("interpret each keyword as a regex"), 10 }, + { "exact", 'e', 0, 0, N_("search each keyword for exact match") }, /* apropos only */ + { "wildcard", 'w', 0, 0, N_("the keyword(s) contain wildcards") }, + { "and", 'a', 0, 0, N_("require all keywords to match"), 20 }, /* apropos only */ + { "long", 'l', 0, 0, N_("do not trim output to terminal width"), 30 }, + { "sections", 's', N_("LIST"), 0, N_("search only these sections (colon-separated)"), 40 }, + { "section", 0, 0, OPTION_ALIAS }, + { "systems", 'm', N_("SYSTEM"), 0, N_("use manual pages from other systems") }, + { "manpath", 'M', N_("PATH"), 0, N_("set search path for manual pages to PATH") }, + { "locale", 'L', N_("LOCALE"), 0, N_("define the locale for this search") }, + { "config-file", 'C', N_("FILE"), 0, N_("use this user configuration file") }, + { "whatis", 'f', 0, OPTION_HIDDEN, 0 }, + { "apropos", 'k', 0, OPTION_HIDDEN, 0 }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static char **split_sections (const char *sections_str) +{ + int i = 0; + char *str = xstrdup (sections_str); + const char *section; + char **out = NULL; + + /* Although this is documented as colon-separated, at least Solaris + * man's -s option takes a comma-separated list, so we accept that + * too for compatibility. + */ + for (section = strtok (str, ":,"); section; + section = strtok (NULL, ":,")) { + out = xnrealloc (out, i + 2, sizeof *out); + out[i++] = xstrdup (section); + } + if (i) + out[i] = NULL; + + free (str); + return out; +} + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'd': + debug_level = true; + return 0; + case 'v': + quiet = 0; + return 0; + case 'r': + regex_opt = true; + return 0; + case 'e': + /* Only makes sense for apropos, but has + * historically been accepted by whatis anyway. + */ + regex_opt = false; + exact = true; + return 0; + case 'w': + regex_opt = false; + wildcard = true; + return 0; + case 'a': + if (am_apropos) + require_all = true; + else + argp_usage (state); + return 0; + case 'l': + long_output = true; + return 0; + case 's': + sections = split_sections (arg); + return 0; + case 'm': + alt_systems = arg; + return 0; + case 'M': + manp = xstrdup (arg); + return 0; + case 'L': + locale = arg; + return 0; + case 'C': + user_config_file = arg; + return 0; + case 'f': + /* helpful override if program name detection fails */ + am_apropos = false; + return 0; + case 'k': + /* helpful override if program name detection fails */ + am_apropos = true; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP & + ~ARGP_HELP_PRE_DOC); + break; + case ARGP_KEY_ARGS: + keywords = state->argv + state->next; + num_keywords = state->argc - state->next; + return 0; + case ARGP_KEY_NO_ARGS: + /* Make sure that we have a keyword! */ + printf (_("%s what?\n"), program_name); + exit (FAIL); + case ARGP_KEY_SUCCESS: + if (am_apropos && !exact && !wildcard) + regex_opt = true; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static char *help_filter (int key, const char *text, void *input _GL_UNUSED) +{ + switch (key) { + case ARGP_KEY_HELP_PRE_DOC: + /* We have no pre-options help text, but the input + * text may contain header junk due to gettext (""). + */ + return NULL; + default: + return (char *) text; + } +} + +static struct argp apropos_argp = { options, parse_opt, args_doc, apropos_doc, + 0, help_filter }; +static struct argp whatis_argp = { options, parse_opt, args_doc }; + +static char *locale_manpath (const char *manpath) +{ + char *all_locales; + char *new_manpath; + + if (multiple_locale && *multiple_locale) { + if (internal_locale && *internal_locale) + all_locales = xasprintf ("%s:%s", multiple_locale, + internal_locale); + else + all_locales = xstrdup (multiple_locale); + } else { + if (internal_locale && *internal_locale) + all_locales = xstrdup (internal_locale); + else + all_locales = NULL; + } + + new_manpath = add_nls_manpaths (manpath, all_locales); + free (all_locales); + + return new_manpath; +} + +#ifdef HAVE_ICONV +static char *simple_convert (iconv_t conv, char *string) +{ + if (conv != (iconv_t) -1) { + size_t string_conv_alloc = strlen (string) + 1; + char *string_conv = xmalloc (string_conv_alloc); + for (;;) { + char *inptr = string, *outptr = string_conv; + size_t inleft = strlen (string); + size_t outleft = string_conv_alloc - 1; + if (iconv (conv, (ICONV_CONST char **) &inptr, &inleft, + &outptr, &outleft) == (size_t) -1 && + errno == E2BIG) { + string_conv_alloc <<= 1; + string_conv = xrealloc (string_conv, + string_conv_alloc); + } else { + /* Either we succeeded, or we've done our + * best; go ahead and print what we've got. + */ + string_conv[string_conv_alloc - 1 - outleft] = + '\0'; + break; + } + } + return string_conv; + } else + return xstrdup (string); +} +#else /* !HAVE_ICONV */ +# define simple_convert(conv, string) xstrdup (string) +#endif /* HAVE_ICONV */ + +/* Do the old thing, if we cannot find the relevant database. + * This invokes grep once per argument; we can't do much about this because + * we need to know which arguments failed. The only way to speed this up + * would be to implement grep internally, but it hardly seems worth it for a + * legacy mechanism. + */ +static void use_grep (const char * const *pages, int num_pages, char *manpath, + bool *found) +{ + char *whatis_file = xasprintf ("%s/whatis", manpath); + + if (CAN_ACCESS (whatis_file, R_OK)) { + const char *flags; + int i; + + if (am_apropos) { + if (regex_opt) + flags = get_def_user ( + "apropos_regex_grep_flags", + APROPOS_REGEX_GREP_FLAGS); + else + flags = get_def_user ("apropos_grep_flags", + APROPOS_GREP_FLAGS); + } else + flags = get_def_user ("whatis_grep_flags", + WHATIS_GREP_FLAGS); + + for (i = 0; i < num_pages; ++i) { + pipeline *grep_pl; + pipecmd *grep_cmd; + char *anchored_page; + + if (am_apropos) + anchored_page = xstrdup (pages[i]); + else + anchored_page = xasprintf ("^%s", pages[i]); + + grep_cmd = pipecmd_new_argstr (get_def_user ("grep", + GREP)); + pipecmd_argstr (grep_cmd, flags); + pipecmd_args (grep_cmd, anchored_page, whatis_file, + (void *) 0); + pipecmd_pre_exec (grep_cmd, sandbox_load, sandbox_free, + sandbox); + grep_pl = pipeline_new_commands (grep_cmd, (void *) 0); + + if (pipeline_run (grep_pl) == 0) + found[i] = true; + + free (anchored_page); + } + } else + debug ("warning: can't read the fallback whatis text database " + "%s/whatis\n", manpath); + + free (whatis_file); +} + +static struct mandata *resolve_pointers (MYDBM_FILE dbf, struct mandata *info, + const char *page) +{ + int rounds; + const char *newpage; + + if (*(info->pointer) == '-' || + ((!info->name || STREQ (info->name, page)) && + STREQ (info->pointer, page))) + return info; + + /* Now we have to work through pointers. The limit of 10 is fairly + * arbitrary: it's just there to avoid an infinite loop. + */ + newpage = info->pointer; + info = dblookup_exact (dbf, newpage, info->ext, true); + for (rounds = 0; rounds < 10; rounds++) { + struct mandata *newinfo; + + /* If the pointer lookup fails, do nothing. */ + if (!info) + return NULL; + + if (*(info->pointer) == '-' || + ((!info->name || STREQ (info->name, newpage)) && + STREQ (info->pointer, newpage))) + return info; + + newinfo = dblookup_exact (dbf, info->pointer, info->ext, true); + free_mandata_struct (info); + info = newinfo; + } + + if (!quiet) + error (0, 0, _("warning: %s contains a pointer loop"), page); + return NULL; +} + +/* fill_in_whatis() is really a ../libdb/db_lookup.c routine but whatis.c + is the only file that actually requires access to the whatis text... */ + +/* Take mandata struct (earlier returned from a dblookup()) and return + the relative whatis */ +static char *get_whatis (struct mandata *info, const char *page) +{ + if (!info) + return xstrdup (_("(unknown subject)")); + + /* See if we need to fill in the whatis here. */ + if (info->whatis != NULL && *(info->whatis)) + return xstrdup (info->whatis); + if (!quiet && *(info->pointer) != '-') + error (0, 0, _("warning: %s contains a pointer loop"), + page); + return xstrdup (_("(unknown subject)")); +} + +/* print out any matches found */ +static void display (MYDBM_FILE dbf, struct mandata *info, const char *page) +{ + struct mandata *newinfo; + char *string, *whatis, *string_conv; + const char *page_name; + char *key; + int line_len, rest; + + newinfo = resolve_pointers (dbf, info, page); + whatis = get_whatis (newinfo, page); + if (newinfo == NULL) + newinfo = info; + + dbprintf (newinfo); + + if (newinfo->name) + page_name = newinfo->name; + else + page_name = page; + + key = xasprintf ("%s (%s)", page_name, newinfo->ext); + if (gl_set_search (display_seen, key)) + goto out; + gl_set_add (display_seen, xstrdup (key)); + + line_len = get_line_length (); + + if (!long_output && strlen (page_name) > (size_t) (line_len / 2)) + string = xasprintf ("%.*s...", line_len / 2 - 3, page_name); + else + string = xstrdup (page_name); + string = appendstr (string, " (", newinfo->ext, ")", (void *) 0); + if (!STREQ (newinfo->pointer, "-") && !STREQ (newinfo->pointer, page)) + string = appendstr (string, " [", newinfo->pointer, "]", + (void *) 0); + + if (strlen (string) < (size_t) 20) { + int i; + string = xrealloc (string, 21); + for (i = strlen (string); i < 20; ++i) + string[i] = ' '; + string[i] = '\0'; + } + string = appendstr (string, " - ", (void *) 0); + + rest = line_len - strlen (string); + if (!long_output && strlen (whatis) > (size_t) rest) { + whatis[rest - 3] = '\0'; + string = appendstr (string, whatis, "...\n", (void *) 0); + } else + string = appendstr (string, whatis, "\n", (void *) 0); + + string_conv = simple_convert (conv_to_locale, string); + fputs (string_conv, stdout); + + free (string_conv); + free (string); + +out: + free (key); + free (whatis); + if (newinfo != info) + free_mandata_struct (newinfo); +} + +/* lookup the page and display the results */ +static int do_whatis_section (MYDBM_FILE dbf, + const char *page, const char *section) +{ + gl_list_t infos; + struct mandata *info; + int count = 0; + + infos = dblookup_all (dbf, page, section, false); + GL_LIST_FOREACH_START (infos, info) { + display (dbf, info, page); + count++; + } GL_LIST_FOREACH_END (infos); + gl_list_free (infos); + return count; +} + +static bool suitable_manpath (const char *manpath, const char *page_dir) +{ + char *page_manp, *pm; + gl_list_t page_manpathlist; + bool ret; + + page_manp = get_manpath_from_path (page_dir, 0); + if (!page_manp || !*page_manp) { + free (page_manp); + return false; + } + pm = locale_manpath (page_manp); + free (page_manp); + page_manp = pm; + page_manpathlist = create_pathlist (page_manp); + + ret = gl_list_search (page_manpathlist, manpath) ? true : false; + + free_pathlist (page_manpathlist); + free (page_manp); + return ret; +} + +static void do_whatis (MYDBM_FILE dbf, + const char * const *pages, int num_pages, + const char *manpath, bool *found) +{ + int i; + + for (i = 0; i < num_pages; ++i) { + char *page = xstrdup (pages[i]); + struct stat st; + + if (strchr (page, '/') && stat (page, &st) == 0 && + !S_ISDIR (st.st_mode) && + st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + /* Perhaps an executable. If its directory is on + * $PATH, then we only want to process this page for + * matching manual hierarchies. + */ + char *page_dir = dir_name (page); + + if (directory_on_path (page_dir)) { + if (suitable_manpath (manpath, page_dir)) { + char *old_page = page; + page = base_name (old_page); + free (old_page); + } else { + debug ("%s not on manpath for %s\n", + manpath, page); + free (page_dir); + free (page); + continue; + } + } + free (page_dir); + } + + if (sections) { + char * const *section; + + for (section = sections; *section; ++section) { + if (do_whatis_section (dbf, page, *section)) + found[i] = true; + } + } else { + if (do_whatis_section (dbf, page, NULL)) + found[i] = true; + } + + free (page); + } +} + +static bool any_set (int num_pages, bool *found_here) +{ + int i; + + for (i = 0; i < num_pages; ++i) + if (found_here[i]) + return true; + return false; +} + +static bool all_set (int num_pages, bool *found_here) +{ + int i; + + for (i = 0; i < num_pages; ++i) + if (!found_here[i]) + return false; + return true; +} + +static void parse_name (const char * const *pages, int num_pages, + const char *dbname, bool *found, bool *found_here) +{ + int i; + + if (regex_opt) { + for (i = 0; i < num_pages; ++i) { + if (regexec (&preg[i], dbname, 0, + (regmatch_t *) 0, 0) == 0) + found[i] = found_here[i] = true; + } + return; + } + + if (am_apropos && !wildcard) { + for (i = 0; i < num_pages; ++i) { + if (strcasecmp (dbname, pages[i]) == 0) + found[i] = found_here[i] = true; + } + return; + } + + for (i = 0; i < num_pages; ++i) { + if (fnmatch (pages[i], dbname, FNM_CASEFOLD) == 0) + found[i] = found_here[i] = true; + } +} + +/* return true on word match */ +static bool _GL_ATTRIBUTE_PURE match (const char *page, const char *whatis) +{ + size_t len = strlen (page); + const char *begin; + char *p; + + begin = whatis; + + /* check for string match, then see if it is a _word_ */ + while (whatis && (p = strcasestr (whatis, page))) { + char *left = p - 1; + char *right = p + len; + + if ((p == begin || (!CTYPE (isalpha, *left) && *left != '_')) && + (!*right || (!CTYPE (isalpha, *right) && *right != '_'))) + return true; + whatis = p + 1; + } + + return false; +} + +static void parse_whatis (const char * const *pages, int num_pages, + const char *whatis, bool *found, bool *found_here) +{ + int i; + + if (regex_opt) { + for (i = 0; i < num_pages; ++i) { + if (regexec (&preg[i], whatis, 0, + (regmatch_t *) 0, 0) == 0) + found[i] = found_here[i] = true; + } + return; + } + + if (wildcard) { + for (i = 0; i < num_pages; ++i) { + if (exact) { + if (fnmatch (pages[i], whatis, 0) == 0) + found[i] = found_here[i] = true; + } else { + if (word_fnmatch (pages[i], whatis)) + found[i] = found_here[i] = true; + } + } + return; + } + + for (i = 0; i < num_pages; ++i) { + if (match (pages[i], whatis)) + found[i] = found_here[i] = true; + } +} + +/* cjwatson: Optimized functions don't seem to be correct in some + * circumstances; disabled for now. + */ +#undef BTREE + +/* scan for the page, print any matches */ +static void do_apropos (MYDBM_FILE dbf, + const char * const *pages, int num_pages, bool *found) +{ + datum key, cont; + bool *found_here; + bool (*combine) (int, bool *); +#ifndef BTREE + datum nextkey; +#else /* BTREE */ + int end; +#endif /* !BTREE */ + + found_here = XNMALLOC (num_pages, bool); + combine = require_all ? all_set : any_set; + +#ifndef BTREE + key = MYDBM_FIRSTKEY (dbf); + while (MYDBM_DPTR (key)) { + cont = MYDBM_FETCH (dbf, key); +#else /* BTREE */ + end = man_btree_nextkeydata (dbf, &key, &cont); + while (!end) { +#endif /* !BTREE */ + char *tab; + struct mandata info; + + memset (&info, 0, sizeof (info)); + + /* bug#4372, NULL pointer dereference in MYDBM_DPTR (cont), + * fix by dassen@wi.leidenuniv.nl (J.H.M.Dassen), thanx Ray. + * cjwatson: In that case, complain and exit, otherwise we + * might loop (bug #95052). + */ + if (!MYDBM_DPTR (cont)) + { + debug ("key was %s\n", MYDBM_DPTR (key)); + error (FATAL, 0, + _("Database %s corrupted; rebuild with " + "mandb --create"), + dbf->name); + } + + if (*MYDBM_DPTR (key) == '$') + goto nextpage; + + if (*MYDBM_DPTR (cont) == '\t') + goto nextpage; + + /* a real page */ + + split_content (dbf, MYDBM_DPTR (cont), &info); + + /* If there are sections given, does any of them match + * either the section or extension of this page? + */ + if (sections) { + char * const *section; + int matched = 0; + + for (section = sections; *section; ++section) { + if (STREQ (*section, info.sec) || + STREQ (*section, info.ext)) { + matched = 1; + break; + } + } + + if (!matched) + goto nextpage; + } + + tab = strrchr (MYDBM_DPTR (key), '\t'); + if (tab) + *tab = '\0'; + + memset (found_here, 0, num_pages * sizeof (*found_here)); + parse_name (pages, num_pages, + MYDBM_DPTR (key), found, found_here); + if (am_apropos) { + char *whatis; + + whatis = info.whatis ? xstrdup (info.whatis) : NULL; + if (!combine (num_pages, found_here) && whatis) + parse_whatis (pages, num_pages, + whatis, found, found_here); + free (whatis); + } + if (combine (num_pages, found_here)) + display (dbf, &info, MYDBM_DPTR (key)); + + if (tab) + *tab = '\t'; +nextpage: +#ifndef BTREE + nextkey = MYDBM_NEXTKEY (dbf, key); + MYDBM_FREE_DPTR (cont); + MYDBM_FREE_DPTR (key); + key = nextkey; +#else /* BTREE */ + MYDBM_FREE_DPTR (cont); + MYDBM_FREE_DPTR (key); + end = man_btree_nextkeydata (dbf, &key, &cont); +#endif /* !BTREE */ + info.addr = NULL; /* == MYDBM_DPTR (cont), freed above */ + free_mandata_elements (&info); + } + + free (found_here); +} + +/* loop through the man paths, searching for a match */ +static bool search (const char * const *pages, int num_pages) +{ + bool *found = XCALLOC (num_pages, bool); + char *catpath, *mp; + bool any_found; + int i; + + GL_LIST_FOREACH_START (manpathlist, mp) { + char *database; + MYDBM_FILE dbf; + + catpath = get_catpath (mp, SYSTEM_CAT | USER_CAT); + + if (catpath) { + database = mkdbname (catpath); + free (catpath); + } else + database = mkdbname (mp); + + debug ("path=%s\n", mp); + + dbf = MYDBM_RDOPEN (database); + if (dbf && dbver_rd (dbf)) { + MYDBM_CLOSE (dbf); + dbf = NULL; + } + if (!dbf) { + use_grep (pages, num_pages, mp, found); + continue; + } + + if (am_apropos) + do_apropos (dbf, pages, num_pages, found); + else { + if (regex_opt || wildcard) + do_apropos (dbf, pages, num_pages, found); + else + do_whatis (dbf, pages, num_pages, mp, found); + } + MYDBM_CLOSE (dbf); + free (database); + } GL_LIST_FOREACH_END (manpathlist); + + chkr_garbage_detector (); + + any_found = false; + for (i = 0; i < num_pages; ++i) { + if (found[i]) + any_found = true; + else + fprintf (stderr, _("%s: nothing appropriate.\n"), + pages[i]); + } + + free (found); + return any_found; +} + +int main (int argc, char *argv[]) +{ + char *program_base_name; +#ifdef HAVE_ICONV + char *locale_charset; +#endif + int status = OK; + + set_program_name (argv[0]); + program_base_name = base_name (program_name); + if (STREQ (program_base_name, APROPOS_NAME)) { + am_apropos = true; + argp_program_version = "apropos " PACKAGE_VERSION; + } else { + struct argp_option *optionp; + am_apropos = false; + argp_program_version = "whatis " PACKAGE_VERSION; + for (optionp = (struct argp_option *) whatis_argp.options; + optionp->name || optionp->key || optionp->arg || + optionp->flags || optionp->doc || optionp->group; + ++optionp) { + if (!optionp->name) + continue; + if (STREQ (optionp->name, "exact") || + STREQ (optionp->name, "and")) + optionp->flags |= OPTION_HIDDEN; + } + } + free (program_base_name); + + init_debug (); + pipeline_install_post_fork (pop_all_cleanups); + sandbox = sandbox_init (); + init_locale (); + + internal_locale = setlocale (LC_MESSAGES, NULL); + /* Use LANGUAGE only when LC_MESSAGES locale category is + * neither "C" nor "POSIX". */ + if (internal_locale && strcmp (internal_locale, "C") && + strcmp (internal_locale, "POSIX")) + multiple_locale = getenv ("LANGUAGE"); + internal_locale = xstrdup (internal_locale ? internal_locale : "C"); + + if (argp_parse (am_apropos ? &apropos_argp : &whatis_argp, argc, argv, + 0, 0, 0)) + exit (FAIL); + + read_config_file (user_config_file != NULL); + + /* close this locale and reinitialise if a new locale was + issued as an argument or in $MANOPT */ + if (locale) { + free (internal_locale); + internal_locale = setlocale (LC_ALL, locale); + if (internal_locale) + internal_locale = xstrdup (internal_locale); + else + internal_locale = xstrdup (locale); + + debug ("main(): locale = %s, internal_locale = %s\n", + locale, internal_locale); + if (internal_locale) { + setenv ("LANGUAGE", internal_locale, 1); + locale_changed (); + multiple_locale = NULL; + } + } + + /* sort out the internal manpath */ + if (manp == NULL) + manp = locale_manpath (get_manpath (alt_systems)); + else + free (get_manpath (NULL)); + + manpathlist = create_pathlist (manp); + + display_seen = new_string_set (GL_HASH_SET); + +#ifdef HAVE_ICONV + locale_charset = xasprintf ("%s//IGNORE", get_locale_charset ()); + conv_to_locale = iconv_open (locale_charset, "UTF-8"); + free (locale_charset); +#endif /* HAVE_ICONV */ + + if (regex_opt) { + int i; + preg = XNMALLOC (num_keywords, regex_t); + for (i = 0; i < num_keywords; ++i) + xregcomp (&preg[i], keywords[i], + REG_EXTENDED | REG_NOSUB | REG_ICASE); + } + + if (!search ((const char **) keywords, num_keywords)) + status = NOT_FOUND; + + if (regex_opt) { + int i; + for (i = 0; i < num_keywords; ++i) + regfree (&preg[i]); + free (preg); + } + +#ifdef HAVE_ICONV + if (conv_to_locale != (iconv_t) -1) + iconv_close (conv_to_locale); +#endif /* HAVE_ICONV */ + gl_set_free (display_seen); + free_pathlist (manpathlist); + free (manp); + free (internal_locale); + sandbox_free (sandbox); + exit (status); +} diff --git a/src/zsoelim.c b/src/zsoelim.c new file mode 100644 index 0000000..bf5c8ff --- /dev/null +++ b/src/zsoelim.c @@ -0,0 +1,2617 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "manconfig.h" + +/* Flex emits several functions which might reasonably have various + * attributes applied and many unused macros; none of these are our problem. + */ +#if GNUC_PREREQ(8,0) +# pragma GCC diagnostic ignored "-Wsuggest-attribute=malloc" +#endif +#pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" +#pragma GCC diagnostic ignored "-Wunused-macros" + +#line 17 "zsoelim.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = NULL; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_flush_buffer ( YY_BUFFER_STATE b ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state ( void ); + +static void yyensure_buffer_stack ( void ); +static void yy_load_buffer_state ( void ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); + +void *yyalloc ( yy_size_t ); +void *yyrealloc ( void *, yy_size_t ); +void yyfree ( void * ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap() (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +FILE *yyin = NULL, *yyout = NULL; + +typedef int yy_state_type; + +extern int yylineno; +int yylineno = 1; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +static const flex_int16_t yy_nxt[][13] = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + }, + + { + 13, 14, 14, 15, 14, 14, 14, 14, 14, 14, + 14, 14, 14 + }, + + { + 13, 16, 16, 15, 16, 17, 16, 16, 16, 16, + 16, 16, 16 + }, + + { + 13, 18, 19, 20, 21, 18, 18, 18, 18, 18, + 18, 18, 18 + }, + + { + 13, 18, 19, 20, 21, 18, 18, 18, 18, 18, + 18, 18, 18 + + }, + + { + 13, 22, 22, 23, 22, 22, 22, 22, 22, 22, + 22, 22, 22 + }, + + { + 13, 22, 22, 23, 22, 24, 22, 22, 22, 22, + 22, 22, 22 + }, + + { + 13, 19, 25, 26, 19, 19, 19, 19, 19, 19, + 19, 19, 19 + }, + + { + 13, 19, 25, 26, 19, 19, 19, 19, 19, 19, + 19, 19, 19 + }, + + { + 13, 27, 27, 28, 29, 27, 30, 27, 27, 27, + 27, 27, 27 + + }, + + { + 13, 27, 27, 28, 29, 27, 30, 27, 27, 27, + 27, 27, 27 + }, + + { + 13, 31, 32, 33, 34, 31, 31, 31, 31, 31, + 31, 31, 31 + }, + + { + 13, 31, 32, 33, 34, 31, 31, 31, 31, 31, + 31, 31, 31 + }, + + { + -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, + -13, -13, -13 + }, + + { + 13, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14 + + }, + + { + 13, -15, -15, -15, -15, -15, -15, -15, -15, -15, + -15, -15, -15 + }, + + { + 13, 35, 35, -16, 35, 35, 35, 35, 35, 35, + 35, 35, 35 + }, + + { + 13, 36, 36, 36, 36, 36, 36, 37, 36, 36, + 38, 36, 39 + }, + + { + 13, 40, -18, -18, 41, 40, 40, 40, 40, 40, + 40, 40, 40 + }, + + { + 13, -19, -19, -19, -19, -19, -19, -19, -19, -19, + -19, -19, -19 + + }, + + { + 13, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -20, -20, -20 + }, + + { + 13, 40, -21, -21, -21, 40, 40, 40, 40, 40, + 40, 40, 40 + }, + + { + 13, 42, 42, -22, 42, 42, 42, 42, 42, 42, + 42, 42, 42 + }, + + { + 13, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23 + }, + + { + 13, 42, 42, -24, 42, 43, 42, 42, 42, 42, + 42, 42, 42 + + }, + + { + 13, -25, 44, 45, -25, -25, -25, -25, -25, -25, + -25, -25, -25 + }, + + { + 13, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26 + }, + + { + 13, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27 + }, + + { + 13, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28 + }, + + { + 13, -29, -29, -29, -29, -29, 46, -29, -29, -29, + -29, -29, -29 + + }, + + { + 13, -30, -30, -30, 47, -30, 46, -30, -30, -30, + -30, -30, -30 + }, + + { + 13, 48, -31, -31, 49, 48, 48, 48, 48, 48, + 48, 48, 48 + }, + + { + 13, -32, 50, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32 + }, + + { + 13, -33, -33, -33, -33, -33, -33, -33, -33, -33, + -33, -33, -33 + }, + + { + 13, 48, -34, -34, -34, 48, 48, 48, 48, 48, + 48, 48, 48 + + }, + + { + 13, 35, 35, -35, 35, 35, 35, 35, 35, 35, + 35, 35, 35 + }, + + { + 13, 51, 51, -36, 51, 51, 51, 51, 51, 51, + 51, 51, 51 + }, + + { + 13, 51, 51, -37, 51, 51, 51, 51, 52, 51, + 51, 51, 51 + }, + + { + 13, 53, 53, 53, 53, 53, 53, 53, 53, 54, + 53, 53, 53 + }, + + { + 13, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 56, 55 + + }, + + { + 13, 40, -40, -40, 41, 40, 40, 40, 40, 40, + 40, 40, 40 + }, + + { + 13, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41 + }, + + { + 13, 42, 42, -42, 42, 42, 42, 42, 42, 42, + 42, 42, 42 + }, + + { + 13, 57, 57, -43, 57, 57, 57, 57, 57, 57, + 57, 57, 57 + }, + + { + 13, -44, 44, 45, -44, -44, -44, -44, -44, -44, + -44, -44, -44 + + }, + + { + 13, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45 + }, + + { + 13, -46, -46, -46, 47, -46, 46, -46, -46, -46, + -46, -46, -46 + }, + + { + 13, -47, -47, -47, -47, -47, -47, -47, -47, -47, + -47, -47, -47 + }, + + { + 13, 48, -48, -48, 49, 48, 48, 48, 48, 48, + 48, 48, 48 + }, + + { + 13, -49, -49, -49, -49, -49, -49, -49, -49, -49, + -49, -49, -49 + + }, + + { + 13, -50, 50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50 + }, + + { + 13, 51, 51, -51, 51, 51, 51, 51, 51, 51, + 51, 51, 51 + }, + + { + 13, 58, 59, -52, 58, 58, 58, 58, 58, 58, + 58, 58, 58 + }, + + { + 13, 60, 60, -53, 60, 60, 60, 60, 60, 60, + 60, 60, 60 + }, + + { + 13, -54, 61, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54 + + }, + + { + 13, 62, 62, -55, 62, 62, 62, 62, 62, 62, + 62, 62, 62 + }, + + { + 13, -56, 63, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56 + }, + + { + 13, 57, 57, -57, 57, 57, 57, 57, 57, 57, + 57, 57, 57 + }, + + { + 13, 58, 58, -58, 58, 58, 58, 58, 58, 58, + 58, 58, 58 + }, + + { + 13, 58, 59, -59, 58, 58, 58, 58, 58, 58, + 58, 58, 58 + + }, + + { + 13, 60, 60, -60, 60, 60, 60, 60, 60, 60, + 60, 60, 60 + }, + + { + 13, -61, 61, -61, -61, -61, -61, -61, -61, -61, + -61, -61, -61 + }, + + { + 13, 62, 62, -62, 62, 62, 62, 62, 62, 62, + 62, 62, 62 + }, + + { + 13, -63, 63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63 + }, + + } ; + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yynoreturn yy_fatal_error ( const char* msg ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; +#define YY_NUM_RULES 24 +#define YY_END_OF_BUFFER 25 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[64] = + { 0, + 0, 0, 0, 0, 16, 16, 0, 0, 0, 0, + 0, 0, 25, 10, 11, 4, 10, 12, 24, 14, + 24, 16, 17, 16, 24, 13, 22, 23, 22, 18, + 19, 20, 21, 22, 4, 5, 5, 9, 8, 12, + 12, 16, 15, 0, 13, 18, 18, 19, 19, 20, + 5, 5, 6, 3, 7, 2, 15, 1, 1, 6, + 3, 7, 2 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 5, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, + + 8, 9, 1, 1, 1, 1, 1, 10, 1, 1, + 11, 1, 1, 1, 12, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "zsoelim.l" + +#line 19 "zsoelim.l" + +/* + * zsoelim.l: eliminate .so includes within *roff source + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 1997 Fabrizio Polacco. + * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 + * Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Added functionality over gsoelim to allow for compressed .so includes. + * This is required as the first *roff preprocessor in order to deal with + * 100% of compressed source files correctly. A replacement tmac.andoc was + * considered, but would not have been portable to all systems. + * + * Wed Oct 12 18:46:11 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + * + * Tue, 14 Oct 1997 Fabrizio Polacco <fpolacco@debian.org> + * - added changes that were done to .c instead of -l source + * - added new start condition to avoid execution of .so requests + * inside a macro definition. + */ + +#define MAX_SO_DEPTH 10 /* max .so recursion depth */ +#undef ACCEPT_QUOTES /* accept quoted roff requests */ + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#define NAME so_name[so_stack_ptr] +#define LINE so_line[so_stack_ptr] +#define PIPE so_pipe[so_stack_ptr] + +#include "dirname.h" +#include "gl_linkedhash_list.h" +#include "gl_xlist.h" +#include "xgetcwd.h" +#include "xvasprintf.h" + +#include "gettext.h" +#include <locale.h> +#define _(String) gettext (String) + +#include "error.h" +#include "glcontainers.h" +#include "pipeline.h" +#include "decompress.h" + +#include "globbing.h" +#include "zsoelim.h" + +#ifdef ACCEPT_QUOTES +# define ZAP_QUOTES zap_quotes () +static void zap_quotes (void); +#else +# define ZAP_QUOTES +#endif + +static YY_BUFFER_STATE so_stack[MAX_SO_DEPTH]; +static char *so_name[MAX_SO_DEPTH]; +static int so_line[MAX_SO_DEPTH]; +static pipeline *so_pipe[MAX_SO_DEPTH]; +static int so_stack_ptr; +static int no_newline; +static gl_list_t so_manpathlist; +static const char *so_parent_path; + +struct zsoelim_stdin_data { + char *path; + gl_list_t manpathlist; +}; + +/* The flex documentation says that yyin is only used by YY_INPUT, so we + * should safely be able to abuse it as a handy way to keep track of the + * current 'pipeline *' rather than the usual 'FILE *'. + */ +#define YY_INPUT(buf,result,max_size) { \ + size_t size = max_size; \ + const char *block = pipeline_read ((pipeline *) yyin, &size); \ + if (block && size != 0) { \ + memcpy (buf, block, size); \ + buf[size] = '\0'; \ + result = size; \ + } else \ + result = YY_NULL; \ +} +#define YY_NO_INPUT +#line 884 "zsoelim.c" + +#line 886 "zsoelim.c" + +#define INITIAL 0 +#define so 1 +#define de 2 +#define end_request 3 +#define lfnumber 4 +#define lfname 5 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals ( void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( void ); + +int yyget_debug ( void ); + +void yyset_debug ( int debug_flag ); + +YY_EXTRA_TYPE yyget_extra ( void ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in ( void ); + +void yyset_in ( FILE * _in_str ); + +FILE *yyget_out ( void ); + +void yyset_out ( FILE * _out_str ); + + int yyget_leng ( void ); + +char *yyget_text ( void ); + +int yyget_lineno ( void ); + +void yyset_lineno ( int _line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( void ); +#else +extern int yywrap ( void ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * ); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + { +#line 140 "zsoelim.l" + + +#line 1112 "zsoelim.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); + yy_current_state += YY_AT_BOL(); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) + { + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + + ++yy_cp; + } + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos) + 1; + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 142 "zsoelim.l" +{ + no_newline = 1; + ECHO; + BEGIN (de); /* Now we're inside of a macro definition: ends with a comment */ + } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 148 "zsoelim.l" +{ + no_newline = 1; + BEGIN (so); /* Now we're in the .so environment */ + } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 153 "zsoelim.l" +{ + no_newline = 1; + ECHO; /* Now we're in the .lf environment */ + BEGIN (lfnumber); + } + YY_BREAK +case 4: +#line 160 "zsoelim.l" +case 5: +/* rule 5 can match eol */ +#line 161 "zsoelim.l" +case 6: +/* rule 6 can match eol */ +#line 162 "zsoelim.l" +case 7: +/* rule 7 can match eol */ +#line 163 "zsoelim.l" +case 8: +/* rule 8 can match eol */ +#line 164 "zsoelim.l" +case 9: +/* rule 9 can match eol */ +#line 165 "zsoelim.l" +case 10: +/* rule 10 can match eol */ +YY_RULE_SETUP +#line 165 "zsoelim.l" +{ + no_newline = 1; + ECHO; + } + YY_BREAK +case 11: +/* rule 11 can match eol */ +YY_RULE_SETUP +#line 170 "zsoelim.l" +{ + no_newline = 0; + putchar ('\n'); + LINE++; + } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 177 "zsoelim.l" +{ /* file names including whitespace ? */ + if (so_stack_ptr == MAX_SO_DEPTH - 1) + error (FATAL, 0, + _("%s:%d: .so requests nested too " + "deeply or are recursive"), + NAME, LINE); + + ZAP_QUOTES; + so_stack[so_stack_ptr++] = YY_CURRENT_BUFFER; + LINE = 1; + + no_newline = 0; + + if (zsoelim_open_file (yytext, so_manpathlist, + so_parent_path)) { + --so_stack_ptr; +#ifndef __alpha + error (OK, 0, + _("%s:%d: warning: failed .so request"), + NAME, LINE); + printf (".so %s\n", yytext); +#endif + BEGIN (end_request); + } else { + printf (".lf 1 %s\n", yytext); + yy_switch_to_buffer + (yy_create_buffer (yyin, YY_BUF_SIZE)); + BEGIN (INITIAL); + } + + } + YY_BREAK +case 13: +/* rule 13 can match eol */ +YY_RULE_SETUP +#line 209 "zsoelim.l" +{ + no_newline = 0; + BEGIN (INITIAL); + } + YY_BREAK +case 14: +/* rule 14 can match eol */ +YY_RULE_SETUP +#line 214 "zsoelim.l" +{ + no_newline = 0; + error (OK, 0, + _("%s:%d: warning: newline in .so request, " + "ignoring"), + NAME, LINE); + putchar ('\n'); + LINE++; + BEGIN (INITIAL); + } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 225 "zsoelim.l" +{ + no_newline = 1; + ECHO; + BEGIN (INITIAL); + } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 231 "zsoelim.l" +{ + no_newline = 1; + ECHO; + } + YY_BREAK +case 17: +/* rule 17 can match eol */ +YY_RULE_SETUP +#line 236 "zsoelim.l" +{ + no_newline = 0; + putchar ('\n'); + LINE++; + } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 243 "zsoelim.l" +{ + no_newline = 1; + ECHO; + ZAP_QUOTES; + LINE = atoi (yytext); + BEGIN (lfname); + } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 251 "zsoelim.l" +{ /* file names including whitespace ?? */ + no_newline = 1; + ECHO; + putchar ('\n'); + ZAP_QUOTES; + if (NAME) + free (NAME); + NAME = xstrdup (yytext); + BEGIN (end_request); + } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 262 "zsoelim.l" +{ + no_newline = 1; + ECHO; + } + YY_BREAK +case 21: +/* rule 21 can match eol */ +YY_RULE_SETUP +#line 267 "zsoelim.l" +{ + no_newline = 0; + putchar ('\n'); + LINE++; + BEGIN (INITIAL); + } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 274 "zsoelim.l" +{ + no_newline = 1; + error (OK, 0, + _("%s:%d: warning: malformed .lf request, " + "ignoring"), + NAME, LINE); + putchar (*yytext); + BEGIN (INITIAL); + } + YY_BREAK +case 23: +/* rule 23 can match eol */ +YY_RULE_SETUP +#line 284 "zsoelim.l" +{ + no_newline = 0; + error (OK, 0, + _("%s:%d: warning: newline in .lf request, " + "ignoring"), + NAME, LINE); + putchar ('\n'); + LINE++; + BEGIN (INITIAL); + } + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(so): +case YY_STATE_EOF(de): +case YY_STATE_EOF(end_request): +case YY_STATE_EOF(lfnumber): +case YY_STATE_EOF(lfname): +#line 295 "zsoelim.l" +{ + pipeline_wait (PIPE); + pipeline_free (PIPE); + PIPE = NULL; + free (NAME); + NAME = NULL; + + if (no_newline) + putchar ('\n'); + + if (--so_stack_ptr < 0) { + yyterminate (); + } else { + yy_delete_buffer (YY_CURRENT_BUFFER); + yy_switch_to_buffer (so_stack[so_stack_ptr]); + printf (".lf %d %s\n", LINE += 1, NAME); + } + no_newline = 0; + BEGIN (end_request); + } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 315 "zsoelim.l" +ECHO; + YY_BREAK +#line 1411 "zsoelim.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + yy_state_type yy_current_state; + char *yy_cp; + + yy_current_state = (yy_start); + yy_current_state += YY_AT_BOL(); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + int yy_is_jam; + char *yy_cp = (yy_c_buf_p); + + yy_current_state = yy_nxt[yy_current_state][1]; + yy_is_jam = (yy_current_state <= 0); + + if ( ! yy_is_jam ) + { + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + } + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf ); + + yyfree( (void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + yy_size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr ) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg ) +{ + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param _line_number line number + * + */ +void yyset_lineno (int _line_number ) +{ + + yylineno = _line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str ) +{ + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str ) +{ + yyout = _out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int _bdebug ) +{ + yy_flex_debug = _bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = NULL; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = NULL; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n ) +{ + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s ) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 315 "zsoelim.l" + + +#ifdef ACCEPT_QUOTES +/* remove leading and trailing quotes in requests */ +static void zap_quotes (void) +{ + if (*yytext == '"') { + if (yytext[yyleng - 1] == '"') { + yytext[yyleng - 1] = '\0'; + yytext++; + } else + error (OK, 0, + _("%s:%d: unterminated quote in roff request"), + NAME, LINE); + } +} +#endif + +/* initialise the stack and call the parser */ +void zsoelim_parse_file (gl_list_t manpathlist, const char *parent_path) +{ + const char *line; + int linenum = 1; + + so_stack_ptr = 0; + so_manpathlist = manpathlist; + so_parent_path = parent_path; + + /* Skip over the first line if it's something that manconv might + * need to know about. + */ + line = pipeline_peekline ((pipeline *) yyin); + if (line && + (STRNEQ (line, PP_COOKIE, 4) || STRNEQ (line, ".\\\" ", 4))) { + fputs (line, stdout); + pipeline_peek_skip ((pipeline *) yyin, strlen (line)); + ++linenum; + } + + printf (".lf %d %s\n", linenum, NAME); + LINE = 1; + yylex (); +} + +static pipeline *try_compressed (char **filename) +{ + struct compression *comp; + size_t len = strlen (*filename); + pipeline *decomp; + + /* Try the uncompressed name first. */ + (*filename)[len - 1] = '\0'; + debug ("trying %s\n", *filename); + decomp = decompress_open (*filename); + if (decomp) + return decomp; + (*filename)[len - 1] = '.'; + + for (comp = comp_list; comp->ext; ++comp) { + *filename = appendstr (*filename, comp->ext, NULL); + debug ("trying %s\n", *filename); + decomp = decompress_open (*filename); + if (decomp) + return decomp; + (*filename)[len] = '\0'; + } + + return NULL; +} + +/* This routine is used to open the specified file or uncompress a compressed + version and open that instead */ +int zsoelim_open_file (const char *filename, gl_list_t manpathlist, + const char *parent_path) +{ + pipeline *decomp; + + if (parent_path) + debug ("opening %s (parent path: %s)\n", + filename, parent_path); + else + debug ("opening %s\n", filename); + + if (strcmp (filename, "-") == 0) { + decomp = decompress_fdopen (dup (STDIN_FILENO)); + NAME = xstrdup (filename); + } else { + char *compfile; + const char *mp; + + /* If there is no parent path, try opening directly first. */ + if (!parent_path) { + compfile = xasprintf ("%s.", filename); + + decomp = try_compressed (&compfile); + if (decomp) { + NAME = compfile; + goto out; + } else + free (compfile); + } + + if (strchr (filename, '/')) { + /* File name with a directory part. Try looking it + * up within each manpath entry. + */ + if (parent_path) { + compfile = xasprintf ("%s/%s.", parent_path, + filename); + + decomp = try_compressed (&compfile); + if (decomp) { + NAME = compfile; + goto out; + } + + free (compfile); + } + + GL_LIST_FOREACH_START (manpathlist, mp) { + if (parent_path && STREQ (mp, parent_path)) + continue; + + compfile = xasprintf ("%s/%s.", mp, filename); + + decomp = try_compressed (&compfile); + if (decomp) { + NAME = compfile; + goto out; + } + + free (compfile); + } GL_LIST_FOREACH_END (manpathlist); + } else { + /* File name with no directory part. Try searching + * the manpath. + */ + char *name, *sec, *dot; + gl_list_t names; + const char *found_name; + + name = xstrdup (filename); + dot = strchr (name, '.'); + if (!dot) { + free (name); + goto out; + } + *dot++ = '\0'; + sec = dot; + dot = strchr (dot, '.'); + if (dot) + *dot = '\0'; + + if (parent_path) { + names = look_for_file (parent_path, sec, name, + 0, LFF_MATCHCASE); + GL_LIST_FOREACH_START (names, found_name) { + decomp = decompress_open (found_name); + if (decomp) { + NAME = xstrdup (found_name); + gl_list_free (names); + goto out; + } + } GL_LIST_FOREACH_END (names); + gl_list_free (names); + } + + GL_LIST_FOREACH_START (manpathlist, mp) { + if (parent_path && STREQ (mp, parent_path)) + continue; + + names = look_for_file (mp, sec, name, + 0, LFF_MATCHCASE); + GL_LIST_FOREACH_START (names, found_name) { + decomp = decompress_open (found_name); + if (decomp) { + NAME = xstrdup (found_name); + gl_list_free (names); + free (name); + goto out; + } + } GL_LIST_FOREACH_END (names); + gl_list_free (names); + } GL_LIST_FOREACH_END (manpathlist); + + free (name); + } + + /* If there is a parent path, try opening directly last. */ + if (parent_path) { + compfile = xasprintf ("%s.", filename); + + decomp = try_compressed (&compfile); + if (decomp) { + NAME = compfile; + goto out; + } else + free (compfile); + } + +out: + if (!decomp) { + error (0, errno, _("can't open %s"), filename); + return 1; + } + } + + debug ("opened %s\n", NAME); + + pipeline_start (decomp); + PIPE = decomp; + /* only used by YY_INPUT, which casts it back to 'pipeline *' */ + yyin = (FILE *) decomp; + + return 0; +} + +void zsoelim_stdin (void *data) +{ + struct zsoelim_stdin_data *zsoelim_data = data; + gl_list_t empty; + + empty = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, NULL, + true); + zsoelim_open_file ("-", empty, zsoelim_data->path); + gl_list_free (empty); + zsoelim_parse_file (zsoelim_data->manpathlist, zsoelim_data->path); +} + +struct zsoelim_stdin_data *zsoelim_stdin_data_new (const char *path, + gl_list_t manpathlist) +{ + struct zsoelim_stdin_data *data = XMALLOC (struct zsoelim_stdin_data); + + data->path = path ? xstrdup (path) : NULL; + data->manpathlist = manpathlist; + + return data; +} + +void zsoelim_stdin_data_free (void *data) +{ + struct zsoelim_stdin_data *zsoelim_data = data; + + free (zsoelim_data->path); + free (zsoelim_data); +} + diff --git a/src/zsoelim.h b/src/zsoelim.h new file mode 100644 index 0000000..4aaf53d --- /dev/null +++ b/src/zsoelim.h @@ -0,0 +1,34 @@ +/* + * zsoelim.h: interface to eliminating .so includes within *roff source + * + * Copyright (C) 2008 Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gl_list.h" + +int zsoelim_open_file (const char *filename, gl_list_t manpathlist, + const char *parent_path); +void zsoelim_parse_file (gl_list_t manpathlist, const char *parent_path); + +struct zsoelim_stdin_data; + +void zsoelim_stdin (void *data); +struct zsoelim_stdin_data *zsoelim_stdin_data_new (const char *path, + gl_list_t manpathlist); +void zsoelim_stdin_data_free (void *data); diff --git a/src/zsoelim.l b/src/zsoelim.l new file mode 100644 index 0000000..a8a7e3b --- /dev/null +++ b/src/zsoelim.l @@ -0,0 +1,561 @@ +%top{ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "manconfig.h" + +/* Flex emits several functions which might reasonably have various + * attributes applied and many unused macros; none of these are our problem. + */ +#if GNUC_PREREQ(8,0) +# pragma GCC diagnostic ignored "-Wsuggest-attribute=malloc" +#endif +#pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" +#pragma GCC diagnostic ignored "-Wunused-macros" +} + +%{ + +/* + * zsoelim.l: eliminate .so includes within *roff source + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 1997 Fabrizio Polacco. + * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 + * Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Added functionality over gsoelim to allow for compressed .so includes. + * This is required as the first *roff preprocessor in order to deal with + * 100% of compressed source files correctly. A replacement tmac.andoc was + * considered, but would not have been portable to all systems. + * + * Wed Oct 12 18:46:11 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + * + * Tue, 14 Oct 1997 Fabrizio Polacco <fpolacco@debian.org> + * - added changes that were done to .c instead of -l source + * - added new start condition to avoid execution of .so requests + * inside a macro definition. + */ + +#define MAX_SO_DEPTH 10 /* max .so recursion depth */ +#undef ACCEPT_QUOTES /* accept quoted roff requests */ + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#define NAME so_name[so_stack_ptr] +#define LINE so_line[so_stack_ptr] +#define PIPE so_pipe[so_stack_ptr] + +#include "dirname.h" +#include "gl_linkedhash_list.h" +#include "gl_xlist.h" +#include "xgetcwd.h" +#include "xvasprintf.h" + +#include "gettext.h" +#include <locale.h> +#define _(String) gettext (String) + +#include "error.h" +#include "glcontainers.h" +#include "pipeline.h" +#include "decompress.h" + +#include "globbing.h" +#include "zsoelim.h" + +#ifdef ACCEPT_QUOTES +# define ZAP_QUOTES zap_quotes () +static void zap_quotes (void); +#else +# define ZAP_QUOTES +#endif + +static YY_BUFFER_STATE so_stack[MAX_SO_DEPTH]; +static char *so_name[MAX_SO_DEPTH]; +static int so_line[MAX_SO_DEPTH]; +static pipeline *so_pipe[MAX_SO_DEPTH]; +static int so_stack_ptr; +static int no_newline; +static gl_list_t so_manpathlist; +static const char *so_parent_path; + +struct zsoelim_stdin_data { + char *path; + gl_list_t manpathlist; +}; + +/* The flex documentation says that yyin is only used by YY_INPUT, so we + * should safely be able to abuse it as a handy way to keep track of the + * current 'pipeline *' rather than the usual 'FILE *'. + */ +#define YY_INPUT(buf,result,max_size) { \ + size_t size = max_size; \ + const char *block = pipeline_read ((pipeline *) yyin, &size); \ + if (block && size != 0) { \ + memcpy (buf, block, size); \ + buf[size] = '\0'; \ + result = size; \ + } else \ + result = YY_NULL; \ +} +#define YY_NO_INPUT +%} + +%x so +%x de +%x end_request +%x lfnumber +%x lfname + +W [ \t] + +%option full noread ecs +%option 8bit batch never-interactive +%option noyywrap nounput + +%% + +^\.de{W}*.+ { + no_newline = 1; + ECHO; + BEGIN (de); /* Now we're inside of a macro definition: ends with a comment */ + } + +^\.so{W}* { + no_newline = 1; + BEGIN (so); /* Now we're in the .so environment */ + } + +^\.lf{W}* { + no_newline = 1; + ECHO; /* Now we're in the .lf environment */ + BEGIN (lfnumber); + } + +^[^\.\n].* | /* fallback */ +^\.[^sl].* | +^\.l[^f].* | +^\.s[^o].* | +^\.s | +^\.l | +. { + no_newline = 1; + ECHO; + } + +\n { + no_newline = 0; + putchar ('\n'); + LINE++; + } + + +<so>\"?[^ \t\n\"]+\"? { /* file names including whitespace ? */ + if (so_stack_ptr == MAX_SO_DEPTH - 1) + error (FATAL, 0, + _("%s:%d: .so requests nested too " + "deeply or are recursive"), + NAME, LINE); + + ZAP_QUOTES; + so_stack[so_stack_ptr++] = YY_CURRENT_BUFFER; + LINE = 1; + + no_newline = 0; + + if (zsoelim_open_file (yytext, so_manpathlist, + so_parent_path)) { + --so_stack_ptr; +#ifndef __alpha + error (OK, 0, + _("%s:%d: warning: failed .so request"), + NAME, LINE); + printf (".so %s\n", yytext); +#endif + BEGIN (end_request); + } else { + printf (".lf 1 %s\n", yytext); + yy_switch_to_buffer + (yy_create_buffer (yyin, YY_BUF_SIZE)); + BEGIN (INITIAL); + } + + } + +<end_request>{W}*\n { + no_newline = 0; + BEGIN (INITIAL); + } + +<so>\n { + no_newline = 0; + error (OK, 0, + _("%s:%d: warning: newline in .so request, " + "ignoring"), + NAME, LINE); + putchar ('\n'); + LINE++; + BEGIN (INITIAL); + } + +<de>^\.\..* { + no_newline = 1; + ECHO; + BEGIN (INITIAL); + } + +<de>.* { + no_newline = 1; + ECHO; + } + +<de>\n { + no_newline = 0; + putchar ('\n'); + LINE++; + } + + +<lfnumber>\"?[0-9]+\"? { + no_newline = 1; + ECHO; + ZAP_QUOTES; + LINE = atoi (yytext); + BEGIN (lfname); + } + +<lfname>\"?[^ \t\n\"]+\"? { /* file names including whitespace ?? */ + no_newline = 1; + ECHO; + putchar ('\n'); + ZAP_QUOTES; + if (NAME) + free (NAME); + NAME = xstrdup (yytext); + BEGIN (end_request); + } + +<lfname>{W}+ { + no_newline = 1; + ECHO; + } + +<lfname>\n { + no_newline = 0; + putchar ('\n'); + LINE++; + BEGIN (INITIAL); + } + +<lfnumber,lfname>. { + no_newline = 1; + error (OK, 0, + _("%s:%d: warning: malformed .lf request, " + "ignoring"), + NAME, LINE); + putchar (*yytext); + BEGIN (INITIAL); + } + +<lfnumber>\n { + no_newline = 0; + error (OK, 0, + _("%s:%d: warning: newline in .lf request, " + "ignoring"), + NAME, LINE); + putchar ('\n'); + LINE++; + BEGIN (INITIAL); + } + +<<EOF>> { + pipeline_wait (PIPE); + pipeline_free (PIPE); + PIPE = NULL; + free (NAME); + NAME = NULL; + + if (no_newline) + putchar ('\n'); + + if (--so_stack_ptr < 0) { + yyterminate (); + } else { + yy_delete_buffer (YY_CURRENT_BUFFER); + yy_switch_to_buffer (so_stack[so_stack_ptr]); + printf (".lf %d %s\n", LINE += 1, NAME); + } + no_newline = 0; + BEGIN (end_request); + } +%% + +#ifdef ACCEPT_QUOTES +/* remove leading and trailing quotes in requests */ +static void zap_quotes (void) +{ + if (*yytext == '"') { + if (yytext[yyleng - 1] == '"') { + yytext[yyleng - 1] = '\0'; + yytext++; + } else + error (OK, 0, + _("%s:%d: unterminated quote in roff request"), + NAME, LINE); + } +} +#endif + +/* initialise the stack and call the parser */ +void zsoelim_parse_file (gl_list_t manpathlist, const char *parent_path) +{ + const char *line; + int linenum = 1; + + so_stack_ptr = 0; + so_manpathlist = manpathlist; + so_parent_path = parent_path; + + /* Skip over the first line if it's something that manconv might + * need to know about. + */ + line = pipeline_peekline ((pipeline *) yyin); + if (line && + (STRNEQ (line, PP_COOKIE, 4) || STRNEQ (line, ".\\\" ", 4))) { + fputs (line, stdout); + pipeline_peek_skip ((pipeline *) yyin, strlen (line)); + ++linenum; + } + + printf (".lf %d %s\n", linenum, NAME); + LINE = 1; + yylex (); +} + +static pipeline *try_compressed (char **filename) +{ + struct compression *comp; + size_t len = strlen (*filename); + pipeline *decomp; + + /* Try the uncompressed name first. */ + (*filename)[len - 1] = '\0'; + debug ("trying %s\n", *filename); + decomp = decompress_open (*filename); + if (decomp) + return decomp; + (*filename)[len - 1] = '.'; + + for (comp = comp_list; comp->ext; ++comp) { + *filename = appendstr (*filename, comp->ext, NULL); + debug ("trying %s\n", *filename); + decomp = decompress_open (*filename); + if (decomp) + return decomp; + (*filename)[len] = '\0'; + } + + return NULL; +} + +/* This routine is used to open the specified file or uncompress a compressed + version and open that instead */ +int zsoelim_open_file (const char *filename, gl_list_t manpathlist, + const char *parent_path) +{ + pipeline *decomp; + + if (parent_path) + debug ("opening %s (parent path: %s)\n", + filename, parent_path); + else + debug ("opening %s\n", filename); + + if (strcmp (filename, "-") == 0) { + decomp = decompress_fdopen (dup (STDIN_FILENO)); + NAME = xstrdup (filename); + } else { + char *compfile; + const char *mp; + + /* If there is no parent path, try opening directly first. */ + if (!parent_path) { + compfile = xasprintf ("%s.", filename); + + decomp = try_compressed (&compfile); + if (decomp) { + NAME = compfile; + goto out; + } else + free (compfile); + } + + if (strchr (filename, '/')) { + /* File name with a directory part. Try looking it + * up within each manpath entry. + */ + if (parent_path) { + compfile = xasprintf ("%s/%s.", parent_path, + filename); + + decomp = try_compressed (&compfile); + if (decomp) { + NAME = compfile; + goto out; + } + + free (compfile); + } + + GL_LIST_FOREACH_START (manpathlist, mp) { + if (parent_path && STREQ (mp, parent_path)) + continue; + + compfile = xasprintf ("%s/%s.", mp, filename); + + decomp = try_compressed (&compfile); + if (decomp) { + NAME = compfile; + goto out; + } + + free (compfile); + } GL_LIST_FOREACH_END (manpathlist); + } else { + /* File name with no directory part. Try searching + * the manpath. + */ + char *name, *sec, *dot; + gl_list_t names; + const char *found_name; + + name = xstrdup (filename); + dot = strchr (name, '.'); + if (!dot) { + free (name); + goto out; + } + *dot++ = '\0'; + sec = dot; + dot = strchr (dot, '.'); + if (dot) + *dot = '\0'; + + if (parent_path) { + names = look_for_file (parent_path, sec, name, + 0, LFF_MATCHCASE); + GL_LIST_FOREACH_START (names, found_name) { + decomp = decompress_open (found_name); + if (decomp) { + NAME = xstrdup (found_name); + gl_list_free (names); + goto out; + } + } GL_LIST_FOREACH_END (names); + gl_list_free (names); + } + + GL_LIST_FOREACH_START (manpathlist, mp) { + if (parent_path && STREQ (mp, parent_path)) + continue; + + names = look_for_file (mp, sec, name, + 0, LFF_MATCHCASE); + GL_LIST_FOREACH_START (names, found_name) { + decomp = decompress_open (found_name); + if (decomp) { + NAME = xstrdup (found_name); + gl_list_free (names); + free (name); + goto out; + } + } GL_LIST_FOREACH_END (names); + gl_list_free (names); + } GL_LIST_FOREACH_END (manpathlist); + + free (name); + } + + /* If there is a parent path, try opening directly last. */ + if (parent_path) { + compfile = xasprintf ("%s.", filename); + + decomp = try_compressed (&compfile); + if (decomp) { + NAME = compfile; + goto out; + } else + free (compfile); + } + +out: + if (!decomp) { + error (0, errno, _("can't open %s"), filename); + return 1; + } + } + + debug ("opened %s\n", NAME); + + pipeline_start (decomp); + PIPE = decomp; + /* only used by YY_INPUT, which casts it back to 'pipeline *' */ + yyin = (FILE *) decomp; + + return 0; +} + +void zsoelim_stdin (void *data) +{ + struct zsoelim_stdin_data *zsoelim_data = data; + gl_list_t empty; + + empty = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, NULL, + true); + zsoelim_open_file ("-", empty, zsoelim_data->path); + gl_list_free (empty); + zsoelim_parse_file (zsoelim_data->manpathlist, zsoelim_data->path); +} + +struct zsoelim_stdin_data *zsoelim_stdin_data_new (const char *path, + gl_list_t manpathlist) +{ + struct zsoelim_stdin_data *data = XMALLOC (struct zsoelim_stdin_data); + + data->path = path ? xstrdup (path) : NULL; + data->manpathlist = manpathlist; + + return data; +} + +void zsoelim_stdin_data_free (void *data) +{ + struct zsoelim_stdin_data *zsoelim_data = data; + + free (zsoelim_data->path); + free (zsoelim_data); +} diff --git a/src/zsoelim_main.c b/src/zsoelim_main.c new file mode 100644 index 0000000..d74a28d --- /dev/null +++ b/src/zsoelim_main.c @@ -0,0 +1,159 @@ +/* + * zsoelim_main.c: eliminate .so includes within *roff source + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 1997 Fabrizio Polacco. + * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010 + * Colin Watson. + * + * This file is part of man-db. + * + * man-db 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 of the License, or + * (at your option) any later version. + * + * man-db 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 man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdbool.h> +#include <stdlib.h> + +#include "argp.h" +#include "gl_list.h" +#include "progname.h" +#include "xvasprintf.h" + +#include "gettext.h" +#include <locale.h> +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "cleanup.h" +#include "error.h" +#include "pipeline.h" +#include "decompress.h" +#include "sandbox.h" + +#include "manp.h" +#include "zsoelim.h" + +int quiet = 1; +man_sandbox *sandbox; + +static gl_list_t manpathlist; + +static char **files; +static int num_files; + +const char *argp_program_version = "zsoelim " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("FILE..."); + +static struct argp_option options[] = { + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "compatible", 'C', 0, 0, N_("compatibility switch (ignored)"), 1 }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg _GL_UNUSED, + struct argp_state *state) +{ + switch (key) { + case 'd': + debug_level = true; + return 0; + case 'C': + return 0; /* compatibility with GNU soelim */ + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP); + break; + case ARGP_KEY_NO_ARGS: + /* open stdin */ + files = xmalloc (sizeof *files); + files[0] = xstrdup ("-"); + num_files = 1; + return 0; + case ARGP_KEY_ARGS: + files = state->argv + state->next; + num_files = state->argc - state->next; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp = { options, parse_opt, args_doc }; + +int main (int argc, char *argv[]) +{ + char *multiple_locale = NULL, *internal_locale, *all_locales; + char *manp; + int i; + + set_program_name (argv[0]); + + init_debug (); + pipeline_install_post_fork (pop_all_cleanups); + sandbox = sandbox_init (); + init_locale (); + + internal_locale = setlocale (LC_MESSAGES, NULL); + /* Use LANGUAGE only when LC_MESSAGES locale category is + * neither "C" nor "POSIX". */ + if (internal_locale && strcmp (internal_locale, "C") && + strcmp (internal_locale, "POSIX")) + multiple_locale = getenv ("LANGUAGE"); + internal_locale = xstrdup (internal_locale ? internal_locale : "C"); + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + + if (multiple_locale && *multiple_locale) { + if (internal_locale && *internal_locale) + all_locales = xasprintf ("%s:%s", + multiple_locale, + internal_locale); + else + all_locales = xstrdup (multiple_locale); + } else { + if (internal_locale && *internal_locale) + all_locales = xstrdup (internal_locale); + else + all_locales = NULL; + } + + manp = add_nls_manpaths (get_manpath (NULL), all_locales); + free (all_locales); + + manpathlist = create_pathlist (manp); + + /* parse files in command line order */ + for (i = 0; i < num_files; ++i) { + if (zsoelim_open_file (files[i], manpathlist, NULL)) + continue; + zsoelim_parse_file (manpathlist, NULL); + } + + free_pathlist (manpathlist); + free (manp); + free (internal_locale); + sandbox_free (sandbox); + + return OK; +} |