From 5280f9a0cd1f9ba200422ebba65d1e0133410995 Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Sat, 13 Sep 2014 09:43:21 -0700 Subject: Initial. --- src/Makefile | 124 +++++ src/Makefile.in | 120 +++++ src/apropos | 88 ++++ src/apropos.sh | 88 ++++ src/defs.h | 26 + src/different.c | 72 +++ src/different.h | 3 + src/glob.c | 682 ++++++++++++++++++++++++++ src/glob.h | 1 + src/gripedefs.h | 87 ++++ src/gripes.c | 139 ++++++ src/gripes.h | 5 + src/join.c | 28 ++ src/makemsg | Bin 0 -> 11755 bytes src/makemsg.c | 175 +++++++ src/makewhatis | 460 ++++++++++++++++++ src/makewhatis.in | 456 ++++++++++++++++++ src/makewhatis.sh | 456 ++++++++++++++++++ src/man-config.c | 297 ++++++++++++ src/man-config.h | 6 + src/man-getopt.c | 322 +++++++++++++ src/man-getopt.h | 6 + src/man-iconv.c | 163 +++++++ src/man-iconv.h | 1 + src/man.c | 1366 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/man.conf | 144 ++++++ src/man.conf.in | 140 ++++++ src/man.h | 22 + src/man2dvi | 36 ++ src/manfile.c | 337 +++++++++++++ src/manfile.h | 36 ++ src/manpath.c | 412 ++++++++++++++++ src/manpath.h | 5 + src/msg.c | 106 +++++ src/mwi | 19 + src/ndir.h | 51 ++ src/paths.h | 43 ++ src/paths.h.in | 39 ++ src/to_cat.c | 171 +++++++ src/to_cat.h | 3 + src/util.c | 305 ++++++++++++ src/util.h | 13 + src/version.h | 1 + src/whatis | 88 ++++ 44 files changed, 7142 insertions(+) create mode 100644 src/Makefile create mode 100644 src/Makefile.in create mode 100755 src/apropos create mode 100644 src/apropos.sh create mode 100644 src/defs.h create mode 100644 src/different.c create mode 100644 src/different.h create mode 100644 src/glob.c create mode 100644 src/glob.h create mode 100644 src/gripedefs.h create mode 100644 src/gripes.c create mode 100644 src/gripes.h create mode 100644 src/join.c create mode 100755 src/makemsg create mode 100644 src/makemsg.c create mode 100755 src/makewhatis create mode 100644 src/makewhatis.in create mode 100644 src/makewhatis.sh create mode 100644 src/man-config.c create mode 100644 src/man-config.h create mode 100644 src/man-getopt.c create mode 100644 src/man-getopt.h create mode 100644 src/man-iconv.c create mode 100644 src/man-iconv.h create mode 100644 src/man.c create mode 100644 src/man.conf create mode 100644 src/man.conf.in create mode 100644 src/man.h create mode 100755 src/man2dvi create mode 100644 src/manfile.c create mode 100644 src/manfile.h create mode 100644 src/manpath.c create mode 100644 src/manpath.h create mode 100644 src/msg.c create mode 100755 src/mwi create mode 100644 src/ndir.h create mode 100644 src/paths.h create mode 100644 src/paths.h.in create mode 100644 src/to_cat.c create mode 100644 src/to_cat.h create mode 100644 src/util.c create mode 100644 src/util.h create mode 100644 src/version.h create mode 100755 src/whatis (limited to 'src') diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..c68bd77 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,124 @@ +# +# Generated automatically from Makefile.in by the +# configure script. +# +# +# Master Makefile for man, apropos, whatis, and makewhatis +# +# Copyright (c) 1990, 1991, John W. Eaton. +# Copyright (c) 1994-2001, Andries E. Brouwer +# +# You may distribute under the terms of the GNU General Public +# License as specified in the README file that comes with the man 1.0 +# distribution. +# +# various changes - aeb, March 1994 +# use of catalogs - aeb, June 1994 + +CC = gcc -O +BUILD_CC = gcc -O +INSTALL = install +EXEEXT = + +pager = /bin/less -is + +DEFS = -DSTDC_HEADERS -DTERMIOS_HEADER -DPOSIX -DDO_COMPRESS +CWARN = -Wall -Wstrict-prototypes -Wmissing-prototypes +CWARNNP = -Wall + +.c.o: + $(CC) -c $(CWARN) $(CFLAGS) -I. $(DEFS) $< + +# LDFLAGS = -g +LDFLAGS ?= -s + +LIBOBJS = + +all: man$(EXEEXT) man.conf apropos whatis makewhatis + +MANOBJS = man.o manfile.o manpath.o man-config.o man-getopt.o \ + man-iconv.o to_cat.o different.o gripes.o glob.o util.o msg.o + +man$(EXEEXT): $(MANOBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) -o man$(EXEEXT) $(MANOBJS) $(LIBOBJS) $(LIBS) + +# CC may be a cross compiler, but BUILD_CC must compile for +# the present machine. +makemsg: + $(BUILD_CC) -o makemsg makemsg.c + +msg.c gripedefs.h: ../msgs/mess.en makemsg + ./makemsg ../msgs/mess.en gripedefs.h msg.c + +# glob.c does not have prototypes +glob.o: glob.c ndir.h + $(CC) -c $(CWARNNP) $(CFLAGS) -I. $(DEFS) glob.c + +man-config.o man-getopt.o man.o manpath.o to_cat.o: defs.h +different.o man.o: different.h +man.o manfile.o: glob.h +different.o gripes.o man-config.o man-getopt.o man.o manfile.o manpath.o util.o: gripes.h gripedefs.h +different.o man-config.o man-getopt.o man.o manpath.o: man-config.h +gripes.o man-config.o man-getopt.o man.o manpath.o util.o: man.h +man-getopt.o man.o manpath.o: man-getopt.h +man.o manfile.o to_cat.o: manfile.h +man.o man-iconv.o: man-iconv.h +man.o manpath.o: manpath.h +man-config.o: paths.h +different.o man-config.o man-getopt.o man.o manpath.o util.o: util.h +man-getopt.o: version.h +msg.o: msg.c +gripes.o: ../catopen/catopen.c + +man.conf: man.conf.in ../conf_script + ../conf_script man.conf + +paths.h: paths.h.in ../conf_script + ../conf_script paths.h + +version.h: ../version Makefile + vers=`sed -e s/man-// ../version`; \ + echo "static char version[] = \"$$vers\";" > version.h + +apropos: apropos.sh Makefile + rm -f apropos + sed -e 's,%apropos_or_whatis%,apropos,' \ + -e 's,%version%,man-1.6g,' \ + -e 's,%manpathoption%,--path,' \ + apropos.sh > apropos + chmod +x apropos + +whatis: apropos.sh Makefile + rm -f whatis + sed -e 's,%apropos_or_whatis%,whatis,' \ + -e 's,%version%,man-1.6g,' \ + -e 's,%manpathoption%,--path,' \ + apropos.sh > whatis + chmod +x whatis + +makewhatis: makewhatis.sh Makefile + rm -f makewhatis + cp makewhatis.sh makewhatis.in + ../conf_script makewhatis + chmod +x makewhatis + +MANCONFIG=$(DESTDIR)$(PREFIX)/usr/share/misc/man.conf + +install: all apropos whatis makewhatis + mkdir -p $(DESTDIR)$(PREFIX)/usr/bin + $(INSTALL) -c man$(EXEEXT) $(DESTDIR)$(PREFIX)/usr/bin/man + $(INSTALL) -c -m 755 apropos $(DESTDIR)$(PREFIX)/usr/bin/apropos + $(INSTALL) -c -m 755 whatis $(DESTDIR)$(PREFIX)/usr/bin/whatis + $(INSTALL) -c -m 755 man2dvi $(DESTDIR)$(PREFIX)/usr/bin/man2dvi + mkdir -p $(DESTDIR)$(PREFIX)/usr/sbin + $(INSTALL) -c -m 754 makewhatis $(DESTDIR)$(PREFIX)/usr/sbin/makewhatis + mkdir -p $(DESTDIR)$(PREFIX)/usr/share/misc + if [ -f $(MANCONFIG) ]; then mv $(MANCONFIG) $(MANCONFIG).orig; fi + $(INSTALL) -c -m 644 man.conf $(MANCONFIG) + +clean: + rm -f *.o *~ core man$(EXEEXT) apropos whatis makewhatis makemsg + +spotless: clean + rm -f Makefile config.status paths.h version.h man.conf + rm -f gripedefs.h msg.c mess.*.cat diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..0b08305 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,120 @@ +# +# Master Makefile for man, apropos, whatis, and makewhatis +# +# Copyright (c) 1990, 1991, John W. Eaton. +# Copyright (c) 1994-2001, Andries E. Brouwer +# +# You may distribute under the terms of the GNU General Public +# License as specified in the README file that comes with the man 1.0 +# distribution. +# +# various changes - aeb, March 1994 +# use of catalogs - aeb, June 1994 + +CC = @CC@ +BUILD_CC = @BUILD_CC@ +INSTALL = @INSTALL@ +EXEEXT = @EXEEXT@ + +pager = @pager@ + +DEFS = @DEFS@ +CWARN = -Wall -Wstrict-prototypes -Wmissing-prototypes +CWARNNP = -Wall + +.c.o: + $(CC) -c $(CWARN) $(CFLAGS) -I. $(DEFS) $< + +# LDFLAGS = -g +LDFLAGS ?= -s + +LIBOBJS = @LIBOBJS@ + +all: man$(EXEEXT) man.conf apropos whatis makewhatis + +MANOBJS = man.o manfile.o manpath.o man-config.o man-getopt.o \ + man-iconv.o to_cat.o different.o gripes.o glob.o util.o msg.o + +man$(EXEEXT): $(MANOBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) -o man$(EXEEXT) $(MANOBJS) $(LIBOBJS) $(LIBS) + +# CC may be a cross compiler, but BUILD_CC must compile for +# the present machine. +makemsg: + $(BUILD_CC) -o makemsg makemsg.c + +msg.c gripedefs.h: ../msgs/mess.en makemsg + ./makemsg ../msgs/mess.en gripedefs.h msg.c + +# glob.c does not have prototypes +glob.o: glob.c ndir.h + $(CC) -c $(CWARNNP) $(CFLAGS) -I. $(DEFS) glob.c + +man-config.o man-getopt.o man.o manpath.o to_cat.o: defs.h +different.o man.o: different.h +man.o manfile.o: glob.h +different.o gripes.o man-config.o man-getopt.o man.o manfile.o manpath.o util.o: gripes.h gripedefs.h +different.o man-config.o man-getopt.o man.o manpath.o: man-config.h +gripes.o man-config.o man-getopt.o man.o manpath.o util.o: man.h +man-getopt.o man.o manpath.o: man-getopt.h +man.o manfile.o to_cat.o: manfile.h +man.o man-iconv.o: man-iconv.h +man.o manpath.o: manpath.h +man-config.o: paths.h +different.o man-config.o man-getopt.o man.o manpath.o util.o: util.h +man-getopt.o: version.h +msg.o: msg.c +gripes.o: ../catopen/catopen.c + +man.conf: man.conf.in ../conf_script + ../conf_script man.conf + +paths.h: paths.h.in ../conf_script + ../conf_script paths.h + +version.h: ../version Makefile + vers=`sed -e s/man-// ../version`; \ + echo "static char version[] = \"$$vers\";" > version.h + +apropos: apropos.sh Makefile + rm -f apropos + sed -e 's,%apropos_or_whatis%,apropos,' \ + -e 's,%version%,@version@,' \ + -e 's,%manpathoption%,@manpathoption@,' \ + apropos.sh > apropos + chmod +x apropos + +whatis: apropos.sh Makefile + rm -f whatis + sed -e 's,%apropos_or_whatis%,whatis,' \ + -e 's,%version%,@version@,' \ + -e 's,%manpathoption%,@manpathoption@,' \ + apropos.sh > whatis + chmod +x whatis + +makewhatis: makewhatis.sh Makefile + rm -f makewhatis + cp makewhatis.sh makewhatis.in + ../conf_script makewhatis + chmod +x makewhatis + +MANCONFIG=$(DESTDIR)$(PREFIX)@man_config_file@ + +install: all apropos whatis makewhatis + mkdir -p $(DESTDIR)$(PREFIX)@bindir@ + $(INSTALL) -c @man_install_flags@ man$(EXEEXT) $(DESTDIR)$(PREFIX)@man@ + $(INSTALL) -c -m 755 apropos $(DESTDIR)$(PREFIX)@apropos@ + $(INSTALL) -c -m 755 whatis $(DESTDIR)$(PREFIX)@whatis@ + $(INSTALL) -c -m 755 man2dvi $(DESTDIR)$(PREFIX)@man2dvi@ + mkdir -p $(DESTDIR)$(PREFIX)@sbindir@ + $(INSTALL) -c -m 754 makewhatis $(DESTDIR)$(PREFIX)@makewhatis@ + mkdir -p $(DESTDIR)$(PREFIX)@man_config_dir@ + if [ -f $(MANCONFIG) ]; then mv $(MANCONFIG) $(MANCONFIG).orig; fi + $(INSTALL) -c -m 644 man.conf $(MANCONFIG) + +clean: + rm -f *.o *~ core man$(EXEEXT) apropos whatis makewhatis makemsg + +spotless: clean + rm -f Makefile config.status paths.h version.h man.conf + rm -f gripedefs.h msg.c mess.*.cat diff --git a/src/apropos b/src/apropos new file mode 100755 index 0000000..a85968a --- /dev/null +++ b/src/apropos @@ -0,0 +1,88 @@ +#!/bin/sh +# +# apropos -- search the whatis database for keywords. +# whatis -- idem, but match only commands (as whole words). +# +# Copyright (c) 1990, 1991, John W. Eaton. +# Copyright (c) 1994-1999, Andries E. Brouwer. +# +# You may distribute under the terms of the GNU General Public +# License as specified in the README file that comes with the man +# distribution. +# +# apropos/whatis-1.5m aeb 2003-08-01 (from man-1.6g) +# +# keep old PATH - 000323 - Bryan Henderson +# also look in /var/cache/man - 030801 - aeb + +program=`basename $0` + +# When man pages in your favorite locale look to grep like binary files +# (and you use GNU grep) you may want to add the 'a' option to *grepopt1. +aproposgrepopt1='i' +aproposgrepopt2='' +whatisgrepopt1='iw' +whatisgrepopt2='^' +grepopt1=$aproposgrepopt1 +grepopt2=$aproposgrepopt2 + +if [ $# = 0 ] +then + echo "usage: $program keyword ..." + exit 1 +fi + +manpath=`man --path | tr : '\040'` + +if [ "$manpath" = "" ] +then + echo "$program: manpath is null" + exit 1 +fi + +args= +for arg in $*; do + case $arg in + --version|-V|-v) + echo "$program from man-1.6g" + exit 0 + ;; + --help|-h) + echo "usage: $program keyword ..." + exit 0 + ;; + -*) + echo "$program: $arg: unknown option" + exit 1 + ;; + *) + args="$args $arg" + esac +done + +while [ "$1" != "" ] +do + found=0 + for d in /var/cache/man $manpath /usr/lib + do + if [ -f $d/whatis ] + then + if grep -"$grepopt1" "$grepopt2""$1" $d/whatis + then + found=1 +# Some people are satisfied with a single occurrence +# But it is better to give all +# break + fi + fi + done + + if [ $found = 0 ] + then + echo "$1: nothing appropriate" + fi + + shift +done + +exit diff --git a/src/apropos.sh b/src/apropos.sh new file mode 100644 index 0000000..f4b88ea --- /dev/null +++ b/src/apropos.sh @@ -0,0 +1,88 @@ +#!/bin/sh +# +# apropos -- search the whatis database for keywords. +# whatis -- idem, but match only commands (as whole words). +# +# Copyright (c) 1990, 1991, John W. Eaton. +# Copyright (c) 1994-1999, Andries E. Brouwer. +# +# You may distribute under the terms of the GNU General Public +# License as specified in the README file that comes with the man +# distribution. +# +# apropos/whatis-1.5m aeb 2003-08-01 (from %version%) +# +# keep old PATH - 000323 - Bryan Henderson +# also look in /var/cache/man - 030801 - aeb + +program=`basename $0` + +# When man pages in your favorite locale look to grep like binary files +# (and you use GNU grep) you may want to add the 'a' option to *grepopt1. +aproposgrepopt1='i' +aproposgrepopt2='' +whatisgrepopt1='iw' +whatisgrepopt2='^' +grepopt1=$%apropos_or_whatis%grepopt1 +grepopt2=$%apropos_or_whatis%grepopt2 + +if [ $# = 0 ] +then + echo "usage: $program keyword ..." + exit 1 +fi + +manpath=`man %manpathoption% | tr : '\040'` + +if [ "$manpath" = "" ] +then + echo "$program: manpath is null" + exit 1 +fi + +args= +for arg in $*; do + case $arg in + --version|-V|-v) + echo "$program from %version%" + exit 0 + ;; + --help|-h) + echo "usage: $program keyword ..." + exit 0 + ;; + -*) + echo "$program: $arg: unknown option" + exit 1 + ;; + *) + args="$args $arg" + esac +done + +while [ "$1" != "" ] +do + found=0 + for d in /var/cache/man $manpath /usr/lib + do + if [ -f $d/whatis ] + then + if grep -"$grepopt1" "$grepopt2""$1" $d/whatis + then + found=1 +# Some people are satisfied with a single occurrence +# But it is better to give all +# break + fi + fi + done + + if [ $found = 0 ] + then + echo "$1: nothing appropriate" + fi + + shift +done + +exit diff --git a/src/defs.h b/src/defs.h new file mode 100644 index 0000000..f33c05d --- /dev/null +++ b/src/defs.h @@ -0,0 +1,26 @@ +/* defs.h */ +#undef MAXPATHLEN /* make sure struct dirs has a + well-defined size (thanks to + Pierre.Humblet@eurecom.fr) */ +#include + +#define MAN 0 +#define CAT 1 +#define SCAT 2 + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +struct dir { + struct dir *nxt; + char *dir; +}; + +struct dirs { + struct dirs *nxt; + char mandir[MAXPATHLEN]; + char catdir[MAXPATHLEN]; + char bindir[MAXPATHLEN]; + int mandatory; +}; diff --git a/src/different.c b/src/different.c new file mode 100644 index 0000000..cfce5fb --- /dev/null +++ b/src/different.c @@ -0,0 +1,72 @@ +#include +#include +#include + +#include "different.h" +#include "gripes.h" +#include "man-config.h" +#include "util.h" + +static struct filelist { + char *pathname; + struct filelist *next; +} cat_list, man_list; + +static int +is_different(const char *file, struct filelist *p) { + char *command; + const char *cmp = getval("CMP"); + int ret; + + if (cmp) { + while (p->next) { + command = my_xsprintf("%s %S %S\n", cmp, file, p->pathname); + ret = do_system_command (command, 1); + if (ret == 0) { + gripe(IDENTICAL, file, p->pathname); + return 0; + } + p = p->next; + } + p->next = (struct filelist *) my_malloc(sizeof(struct filelist)); + p->pathname = my_strdup(file); + p->next->next = 0; + } + return 1; +} + +static int +free_filelist (struct filelist *list){ +struct filelist *current, *next; + + current = list; + if (current != list) + do { + next = current->next; + if (current != list) + free(current); + current = next; + } while (current->next != NULL); + + list->next = NULL; + + return 0; +} + +int +different_cat_file (const char *file) { + return is_different (file, &cat_list); +} + +int +different_man_file (const char *file) { + return is_different (file, &man_list); +} + +int +free_catman_filelists (void){ + + free_filelist(&man_list); + free_filelist(&cat_list); +return 0; +} diff --git a/src/different.h b/src/different.h new file mode 100644 index 0000000..e952500 --- /dev/null +++ b/src/different.h @@ -0,0 +1,3 @@ +int different_cat_file (const char *file); +int different_man_file (const char *file); +int free_catman_filelists (void); diff --git a/src/glob.c b/src/glob.c new file mode 100644 index 0000000..71b93d7 --- /dev/null +++ b/src/glob.c @@ -0,0 +1,682 @@ +/* File-name wildcard pattern matching for GNU. + Copyright (C) 1985, 1988, 1989, 1990, 1991 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* To whomever it may concern: I have never seen the code which most + Unix programs use to perform this function. I wrote this from scratch + based on specifications for the pattern matching. --RMS. */ + +#ifdef SHELL +#include "config.h" +#endif /* SHELL */ + +#include + +#if defined (USGr3) && !defined (DIRENT) +#define DIRENT +#endif /* USGr3 */ +#if defined (Xenix) && !defined (SYSNDIR) +#define SYSNDIR +#endif /* Xenix */ + +#if defined (POSIX) || defined (DIRENT) || defined (__GNU_LIBRARY__) +#include +#define direct dirent +#define D_NAMLEN(d) strlen((d)->d_name) +#else /* not POSIX or DIRENT or __GNU_LIBRARY__ */ +#define D_NAMLEN(d) ((d)->d_namlen) +#ifdef USG +#if defined (SYSNDIR) +#include +#else /* SYSNDIR */ +#include "ndir.h" +#endif /* not SYSNDIR */ +#else /* not USG */ +#include +#endif /* USG */ +#endif /* POSIX or DIRENT or __GNU_LIBRARY__ */ + +#ifdef __QNX__ +#define REAL_DIR_ENTRY(dp) (dp->d_stat.st_ino != 0) +#elif defined (_POSIX_SOURCE) +/* Posix does not require that the d_ino field be present, and some + systems do not provide it. */ +#define REAL_DIR_ENTRY(dp) 1 +#else +#define REAL_DIR_ENTRY(dp) (dp->d_ino != 0) +#endif /* _POSIX_SOURCE */ + +#if defined (STDC_HEADERS) || defined (__GNU_LIBRARY__) +#include +#include +#define STDC_STRINGS +#else /* STDC_HEADERS or __GNU_LIBRARY__ */ + +#if defined (USG) +#include +#ifndef POSIX +#include +#endif /* POSIX */ +#define STDC_STRINGS +#else /* not USG */ +#ifdef NeXT +#include +#else /* NeXT */ +#include +#endif /* NeXT */ +/* Declaring bcopy causes errors on systems whose declarations are different. + If the declaration is omitted, everything works fine. */ +#endif /* not USG */ + +extern char *malloc (); +extern char *realloc (); +extern void free (); + +#ifndef NULL +#define NULL 0 +#endif +#endif /* Not STDC_HEADERS or __GNU_LIBRARY__. */ + +#ifdef STDC_STRINGS +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#define index strchr +#define rindex strrchr +#endif /* STDC_STRINGS */ + +#ifndef alloca +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* Not GCC. */ +#ifdef sparc +#include +#else /* Not sparc. */ +extern char *alloca (); +#endif /* sparc. */ +#endif /* GCC. */ +#endif + +/* Nonzero if '*' and '?' do not match an initial '.' for glob_filename. */ +int noglob_dot_filenames = 1; + +static int glob_match_after_star (); + +/* Return nonzero if PATTERN has any special globbing chars in it. */ + +int +glob_pattern_p (pattern) + char *pattern; +{ + register char *p = pattern; + register char c; + int open = 0; + + while ((c = *p++) != '\0') + switch (c) + { + case '?': + case '*': + return 1; + + case '[': /* Only accept an open brace if there is a close */ + open++; /* brace to match it. Bracket expressions must be */ + continue; /* complete, according to Posix.2 */ + case ']': + if (open) + return 1; + continue; + + case '\\': + if (*p++ == '\0') + return 0; + } + + return 0; +} + + +/* Match the pattern PATTERN against the string TEXT; + return 1 if it matches, 0 otherwise. + + A match means the entire string TEXT is used up in matching. + + In the pattern string, `*' matches any sequence of characters, + `?' matches any character, [SET] matches any character in the specified set, + [!SET] matches any character not in the specified set. + + A set is composed of characters or ranges; a range looks like + character hyphen character (as in 0-9 or A-Z). + [0-9a-zA-Z_] is the set of characters allowed in C identifiers. + Any other character in the pattern must be matched exactly. + + To suppress the special syntactic significance of any of `[]*?!-\', + and match the character exactly, precede it with a `\'. + + If DOT_SPECIAL is nonzero, + `*' and `?' do not match `.' at the beginning of TEXT. */ + +int +glob_match (pattern, text, dot_special) + char *pattern, *text; + int dot_special; +{ + register char *p = pattern, *t = text; + register char c; + + while ((c = *p++) != '\0') + switch (c) + { + case '?': + if (*t == '\0' || (dot_special && t == text && *t == '.')) + return 0; + else + ++t; + break; + + case '\\': + if (*p++ != *t++) + return 0; + break; + + case '*': + if (dot_special && t == text && *t == '.') + return 0; + return glob_match_after_star (p, t); + + case '[': + { + register char c1 = *t++; + int invert; + + if (c1 == '\0') + return 0; + + invert = (*p == '!'); + + if (invert) + p++; + + c = *p++; + while (1) + { + register char cstart = c, cend = c; + + if (c == '\\') + { + cstart = *p++; + cend = cstart; + } + + if (cstart == '\0') + return 0; /* Missing ']'. */ + + c = *p++; + + if (c == '-') + { + cend = *p++; + if (cend == '\\') + cend = *p++; + if (cend == '\0') + return 0; + c = *p++; + } + if (c1 >= cstart && c1 <= cend) + goto match; + if (c == ']') + break; + } + if (!invert) + return 0; + break; + + match: + /* Skip the rest of the [...] construct that already matched. */ + while (c != ']') + { + if (c == '\0') + return 0; + c = *p++; + if (c == '\0') + return 0; + if (c == '\\') + p++; + } + if (invert) + return 0; + break; + } + + default: + if (c != *t++) + return 0; + } + + return *t == '\0'; +} + +/* Like glob_match, but match PATTERN against any final segment of TEXT. */ + +static int +glob_match_after_star (pattern, text) + char *pattern, *text; +{ + register char *p = pattern, *t = text; + register char c, c1; + + while ((c = *p++) == '?' || c == '*') + if (c == '?' && *t++ == '\0') + return 0; + + if (c == '\0') + return 1; + + if (c == '\\') + c1 = *p; + else + c1 = c; + + --p; + while (1) + { + if ((c == '[' || *t == c1) && glob_match (p, t, 0)) + return 1; + if (*t++ == '\0') + return 0; + } +} + +/* Return a vector of names of files in directory DIR + whose names match glob pattern PAT. + The names are not in any particular order. + Wildcards at the beginning of PAT do not match an initial period + if noglob_dot_filenames is nonzero. + + The vector is terminated by an element that is a null pointer. + + To free the space allocated, first free the vector's elements, + then free the vector. + + Return NULL if cannot get enough memory to hold the pointer + and the names. + + Return -1 if cannot access directory DIR. + Look in errno for more information. */ + +char ** +glob_vector (pat, dir) + char *pat; + char *dir; +{ + struct globval + { + struct globval *next; + char *name; + }; + + DIR *d; + register struct direct *dp; + struct globval *lastlink; + register struct globval *nextlink; + register char *nextname; + unsigned int count; + int lose; + register char **name_vector = 0; + register unsigned int i; +#ifdef ALLOCA_MISSING + struct globval *templink; +#endif + + d = opendir (dir); + if (d == NULL) + return (char **) -1; + + lastlink = NULL; + count = 0; + lose = 0; + + /* Scan the directory, finding all names that match. + For each name that matches, allocate a struct globval + on the stack and store the name in it. + Chain those structs together; lastlink is the front of the chain. */ + while (1) + { +#if defined (SHELL) + /* Make globbing interruptible in the bash shell. */ + extern int interrupt_state; + + if (interrupt_state) + { + closedir (d); + lose = 1; + goto lost; + } +#endif /* SHELL */ + + dp = readdir (d); + if (dp == NULL) + break; + if (REAL_DIR_ENTRY (dp) + && glob_match (pat, dp->d_name, noglob_dot_filenames)) + { +#ifdef ALLOCA_MISSING + nextlink = (struct globval *) malloc (sizeof (struct globval)); +#else + nextlink = (struct globval *) alloca (sizeof (struct globval)); +#endif + nextlink->next = lastlink; + i = D_NAMLEN (dp) + 1; + nextname = (char *) malloc (i); + if (nextname == NULL) + { + lose = 1; + break; + } + lastlink = nextlink; + nextlink->name = nextname; + bcopy (dp->d_name, nextname, i); + count++; + } + } + closedir (d); + + if (!lose) + { + name_vector = (char **) malloc ((count + 1) * sizeof (char *)); + lose |= name_vector == NULL; + } + + /* Have we run out of memory? */ +#ifdef SHELL + lost: +#endif + if (lose) + { + /* Here free the strings we have got. */ + while (lastlink) + { + free (lastlink->name); +#ifdef ALLOCA_MISSING + templink = lastlink->next; + free ((char *) lastlink); + lastlink = templink; +#else + lastlink = lastlink->next; +#endif + } + return NULL; + } + + /* Copy the name pointers from the linked list into the vector. */ + for (i = 0; i < count; ++i) + { + name_vector[i] = lastlink->name; +#ifdef ALLOCA_MISSING + templink = lastlink->next; + free ((char *) lastlink); + lastlink = templink; +#else + lastlink = lastlink->next; +#endif + } + + name_vector[count] = NULL; + return name_vector; +} + +/* Return a new array, replacing ARRAY, which is the concatenation + of each string in ARRAY to DIR. + Return NULL if out of memory. */ + +static char ** +glob_dir_to_array (dir, array) + char *dir, **array; +{ + register unsigned int i, l; + int add_slash = 0; + char **result; + + l = strlen (dir); + if (l == 0) + return array; + + if (dir[l - 1] != '/') + add_slash++; + + for (i = 0; array[i] != NULL; i++) + ; + + result = (char **) malloc ((i + 1) * sizeof (char *)); + if (result == NULL) + return NULL; + + for (i = 0; array[i] != NULL; i++) + { + result[i] = (char *) malloc (1 + l + add_slash + strlen (array[i])); + if (result[i] == NULL) + return NULL; + strcpy (result[i], dir); + if (add_slash) + result[i][l] = '/'; + strcpy (result[i] + l + add_slash, array[i]); + } + result[i] = NULL; + + /* Free the input array. */ + for (i = 0; array[i] != NULL; i++) + free (array[i]); + free ((char *) array); + return result; +} + +/* Do globbing on PATHNAME. Return an array of pathnames that match, + marking the end of the array with a null-pointer as an element. + If no pathnames match, then the array is empty (first element is null). + If there isn't enough memory, then return NULL. + If a file system error occurs, return -1; `errno' has the error code. + + Wildcards at the beginning of PAT, or following a slash, + do not match an initial period if noglob_dot_filenames is nonzero. */ + +char ** +glob_filename (const char *pathname) +{ + char **result; + unsigned int result_size; + char *directory_name; + const char *filename; + unsigned int directory_len; + + result = (char **) malloc (sizeof (char *)); + result_size = 1; + if (result == NULL) + return NULL; + + result[0] = NULL; + + /* Find the filename. */ + filename = rindex (pathname, '/'); + if (filename == NULL) + { + filename = pathname; + directory_name = ""; + directory_len = 0; + } + else + { + directory_len = (filename - pathname) + 1; +#ifdef ALLOCA_MISSING + directory_name = (char *) malloc (directory_len + 1); +#else + directory_name = (char *) alloca (directory_len + 1); +#endif + bcopy (pathname, directory_name, directory_len); + directory_name[directory_len] = '\0'; + ++filename; + } + + /* If directory_name contains globbing characters, then we + have to expand the previous levels. Just recurse. */ + if (glob_pattern_p (directory_name)) + { + char **directories; + register unsigned int i; + + if (directory_name[directory_len - 1] == '/') + directory_name[directory_len - 1] = '\0'; + + directories = glob_filename (directory_name); +#ifdef ALLOCA_MISSING + free ((char *) directory_name); +#endif + if (directories == NULL) + goto memory_error; + else if (directories == (char **) -1) + return (char **) -1; + else if (*directories == NULL) + { + free ((char *) directories); + return (char **) -1; + } + + /* We have successfully globbed the preceding directory name. + For each name in DIRECTORIES, call glob_vector on it and + FILENAME. Concatenate the results together. */ + for (i = 0; directories[i] != NULL; i++) + { + char **temp_results = glob_vector (filename, directories[i]); + if (temp_results == NULL) + goto memory_error; + else if (temp_results == (char **) -1) + /* This filename is probably not a directory. Ignore it. */ + ; + else + { + char **array = glob_dir_to_array (directories[i], temp_results); + register unsigned int l; + + l = 0; + while (array[l] != NULL) + ++l; + + result = (char **) realloc (result, + (result_size + l) * sizeof (char *)); + if (result == NULL) + goto memory_error; + + for (l = 0; array[l] != NULL; ++l) + result[result_size++ - 1] = array[l]; + result[result_size - 1] = NULL; + free ((char *) array); + } + } + /* Free the directories. */ + for (i = 0; directories[i] != NULL; i++) + free (directories[i]); + free ((char *) directories); + + return result; + } + + /* If there is only a directory name, return it. */ + if (*filename == '\0') + { + result = (char **) realloc ((char *) result, 2 * sizeof (char *)); + if (result != NULL) + { + result[0] = (char *) malloc (directory_len + 1); + if (result[0] == NULL) + { +#ifdef ALLOCA_MISSING + free ((char *) directory_name); +#endif + goto memory_error; + } + bcopy (directory_name, result[0], directory_len + 1); + result[1] = NULL; + } +#ifdef ALLOCA_MISSING + free ((char *) directory_name); +#endif + return result; + } + else + { + /* Otherwise, just return what glob_vector + returns appended to the directory name. */ + char **temp_results = glob_vector (filename, + (directory_len == 0 + ? "." : directory_name)); + + if (temp_results == NULL || temp_results == (char **) -1) + { +#ifdef NO_ALLOCA + free ((char *) directory_name); +#endif + return temp_results; + } + + temp_results = glob_dir_to_array (directory_name, temp_results); +#ifdef NO_ALLOCA + free ((char *) directory_name); +#endif + return temp_results; + } + + /* We get to memory error if the program has run out of memory, or + if this is the shell, and we have been interrupted. */ + memory_error: + if (result != NULL) + { + register unsigned int i; + for (i = 0; result[i] != NULL; ++i) + free (result[i]); + free ((char *) result); + } +#if defined (SHELL) + { + extern int interrupt_state; + + if (interrupt_state) + throw_to_top_level (); + } +#endif /* SHELL */ + return NULL; +} + +#ifdef TEST + +main (argc, argv) + int argc; + char **argv; +{ + char **value; + int i, optind; + + for (optind = 1; optind < argc; optind++) + { + value = glob_filename (argv[optind]); + if (value == NULL) + puts ("virtual memory exhausted"); + else if (value == (char **) -1) + perror (argv[optind]); + else + for (i = 0; value[i] != NULL; i++) + puts (value[i]); + } + exit (0); +} + +#endif /* TEST */ diff --git a/src/glob.h b/src/glob.h new file mode 100644 index 0000000..fd6e07c --- /dev/null +++ b/src/glob.h @@ -0,0 +1 @@ +char **glob_filename (const char *); diff --git a/src/gripedefs.h b/src/gripedefs.h new file mode 100644 index 0000000..57aa898 --- /dev/null +++ b/src/gripedefs.h @@ -0,0 +1,87 @@ +#define BAD_CONFIG_FILE 1 +#define CONFIG_OPEN_ERROR 2 +#define PARSE_ERROR_IN_CONFIG 3 +#define INCOMPAT 4 +#define NO_ALTERNATE 5 +#define NO_COMPRESS 6 +#define NO_NAME_FROM_SECTION 7 +#define NO_NAME_NO_SECTION 8 +#define NO_SUCH_ENTRY_IN_SECTION 9 +#define NO_SUCH_ENTRY 10 +#define PAGER_IS 11 +#define SYSTEM_FAILED 12 +#define VERSION 13 +#define OUT_OF_MEMORY 14 +#define ROFF_CMD_FROM_FILE_ERROR 15 +#define MANROFFSEQ_ERROR 16 +#define ROFF_CMD_FROM_COMMANDLINE_ERROR 17 +#define UNRECOGNIZED_LINE 18 +#define GETVAL_ERROR 19 +#define FOUND_MANDIR 20 +#define FOUND_MAP 21 +#define FOUND_CATDIR 22 +#define LINE_TOO_LONG 23 +#define SECTION 24 +#define UNLINKED 25 +#define GLOBBING 26 +#define EXPANSION_FAILED 27 +#define OPEN_ERROR 28 +#define READ_ERROR 29 +#define FOUND_EQN 30 +#define FOUND_GRAP 31 +#define FOUND_PIC 32 +#define FOUND_TBL 33 +#define FOUND_VGRIND 34 +#define FOUND_REFER 35 +#define ROFF_FROM_COMMAND_LINE 36 +#define ROFF_FROM_FILE 37 +#define ROFF_FROM_ENV 38 +#define USING_DEFAULT 39 +#define PLEASE_WAIT 40 +#define CHANGED_MODE 41 +#define CAT_OPEN_ERROR 42 +#define PROPOSED_CATFILE 43 +#define IS_NEWER_RESULT 44 +#define TRYING_SECTION 45 +#define SEARCHING 46 +#define ALREADY_IN_MANPATH 47 +#define CANNOT_STAT 48 +#define IS_NO_DIR 49 +#define ADDING_TO_MANPATH 50 +#define PATH_DIR 51 +#define IS_IN_CONFIG 52 +#define IS_NOT_IN_CONFIG 53 +#define MAN_NEARBY 54 +#define NO_MAN_NEARBY 55 +#define ADDING_MANDIRS 56 +#define CATNAME_IS 57 +#define NO_EXEC 58 +#define USAGE1 59 +#define USAGE2 60 +#define USAGE3 61 +#define USAGE4 62 +#define USAGE5 63 +#define USAGE6 64 +#define USAGE7 65 +#define USAGE8 66 +#define USER_CANNOT_OPEN_CAT 67 +#define USER_CAN_OPEN_CAT 68 +#define CANNOT_FORK 69 +#define WAIT_FAILED 70 +#define GOT_WRONG_PID 71 +#define CHILD_TERMINATED_ABNORMALLY 72 +#define IDENTICAL 73 +#define MAN_FOUND 74 +#define NO_TROFF 75 +#define NO_CAT_FOR_NONSTD_LL 76 +#define BROWSER_IS 77 +#define HTMLPAGER_IS 78 +#define FOUND_FILE 79 +#define CALLTRACE1 80 +#define CALLTRACE2 81 +#define NO_MATCH 82 +#define GLOB_FOR_FILE 83 +#define CALLTRACE3 84 +#define ABOUT_TO_GLOB 85 + +#define MAXMSG 85 diff --git a/src/gripes.c b/src/gripes.c new file mode 100644 index 0000000..505f8b8 --- /dev/null +++ b/src/gripes.c @@ -0,0 +1,139 @@ +#include +#include +#include + +#include "gripes.h" +#include "man.h" /* for progname */ + +extern char *msg[]; + +static char *mantexts = "man"; /* e.g. /usr/lib/locale/%L/man.cat */ + +#ifdef NONLS + +static char * +getmsg (int n) { + char *s; + + if (0 < n && n <= MAXMSG) + s = msg[n]; + else { + fprintf (stderr, "man: internal error - cannot find message %d\n", n); + exit (1); + } + return s; +} + +#else /* NONLS */ + +#include +#include +#include "../catopen/catopen.c" + +nl_catd catfd = (nl_catd) -1; +int cat_is_open = 0; + +static void +catinit (void) { + if (!cat_is_open) { +#ifdef NL_CAT_LOCALE + catfd = my_catopen(mantexts,NL_CAT_LOCALE); +#else + catfd = my_catopen(mantexts,0); +#endif + if (catfd == (nl_catd) -1) { + /* + * Only complain if LANG exists, and LANG != "en" + * (or when debugging). Also accept en_ZA etc. + * No messages for C locale. + */ + char *s, *lg; + s = getenv("NLSPATH"); + lg = getenv("LANG"); + if (!lg) + lg = getenv("LC_MESSAGES"); + if (!lg) + lg = getenv("LC_ALL"); + if (lg && strncmp(lg, "en", 2) && strcmp(lg, "C") && strcmp(lg, "POSIX")) { + fprintf(stderr, + "Cannot open the message catalog \"%s\" for locale \"%s\"\n" + "(NLSPATH=\"%s\")\n\n", + mantexts, lg, s ? s : ""); + } else if (debug) { + fprintf(stderr, +"Looked whether there exists a message catalog %s, but there is none\n" +"(and for English messages none is needed)\n\n", + mantexts); + } + } + } + cat_is_open = 1; +} + +/* + * This routine is unnecessary, but people ask for such things. + * + * Maybe man is suid or sgid to some user that owns the cat directories. + * Maybe NLSPATH can be manipulated by the user - even though + * modern glibc avoids using environment variables when the + * program is suid or sgid. + * So, maybe the string s that we are returning was user invented + * and we have to avoid %n and the like. + * + * As a random hack, only allow %s,%d,%o, and only two %-signs. + */ +static int +is_suspect (char *s) { + int ct = 0; + + while (*s) { + if (*s++ == '%') { + ct++; + if (*s != 's' && *s != 'd' && *s != 'o') + return 1; + } + } + return (ct > 2); +} + +static char * +getmsg (int n) { + char *s = ""; + + catinit (); + if (catfd != (nl_catd) -1) { + s = catgets(catfd, 1, n, ""); + if (*s && is_suspect(s)) + s = ""; + } + if (*s == 0 && 0 < n && n <= MAXMSG) + s = msg[n]; + if (*s == 0) { + fprintf(stderr, + "man: internal error - cannot find message %d\n", n); + exit (1); + } + return s; +} + +#endif /* NONLS */ + +void +gripe (int n, ...) { + va_list p; + + va_start(p, n); + vfprintf (stderr, getmsg(n), p); + va_end(p); + fflush (stderr); +} + +void +fatal (int n, ...) { + va_list p; + fprintf (stderr, "%s: ", progname); + va_start(p, n); + vfprintf (stderr, getmsg(n), p); + va_end(p); + exit (1); +} diff --git a/src/gripes.h b/src/gripes.h new file mode 100644 index 0000000..8699eca --- /dev/null +++ b/src/gripes.h @@ -0,0 +1,5 @@ +#include "gripedefs.h" + +void gripe (int n, ...); +void fatal (int n, ...); + diff --git a/src/join.c b/src/join.c new file mode 100644 index 0000000..1461ace --- /dev/null +++ b/src/join.c @@ -0,0 +1,28 @@ + +/* note: this routine frees its arguments! */ +char ** +my_join (char **np1, char **np2) { + int lth1, lth2; + char **p, **q, **np; + + if (np1 == NULL) + return np2; + if (np2 == NULL) + return np1; + lth1 = lth2 = 0; + for (p = np1; *p; p++) + lth1++; + for (p = np2; *p; p++) + lth2++; + p = np = (char **) my_malloc((lth1+lth2+1)*sizeof(*np)); + q = np1; + while(*q) + *p++ = *q++; + q = np2; + while(*q) + *p++ = *q++; + *p = 0; + free(np1); + free(np2); + return np; +} diff --git a/src/makemsg b/src/makemsg new file mode 100755 index 0000000..7c1893c Binary files /dev/null and b/src/makemsg differ diff --git a/src/makemsg.c b/src/makemsg.c new file mode 100644 index 0000000..34b3846 --- /dev/null +++ b/src/makemsg.c @@ -0,0 +1,175 @@ +/* makemsg.c - aeb - 940605 */ +/* + * Read a file input with lines + * LABEL "text" + * and either output two files: + * a file msgout.c with content char *msg[] = { "text", ... }; + * and a file msgout.h with content #define LABEL 1 + * or output a single file: + * a message catalog with lines 1 "text" + * + * The former two are used during compilation of the main program + * and give default (English) messages. The latter output file is + * input for gencat, and used in non-English locales. + * + * Call: + * makemsg input msgout.h msgout.c + * or + * makemsg -c input message_catalog + */ +#include +#include +#include +#ifdef __QNX__ +#include +#endif +extern char *index(const char *, int); +extern char *rindex(const char *, int); + +#define BUFSIZE 4096 + +#define whitespace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') + +static void +usage(void){ + fprintf (stderr, "call is: makemsg input msgout.h msgout.c\n"); + fprintf (stderr, "or: makemsg -c input catalog\n"); + exit (1); +} + +int +main(int argc, char **argv) { + FILE *fin, *foh, *foc; + char *s, *t; + char *infile, *outcfile, *outhfile; + char buf[BUFSIZE]; + int defct = 0; + int makecat = 0; + +#define getbuf if (fgets (buf, sizeof(buf), fin) == NULL) {\ + fprintf (stderr, "makemsg: unexpected end of input\n");\ + fprintf (stderr, "[output file(s) removed]\n");\ + unlink (outcfile);\ + if (!makecat) unlink (outhfile);\ + exit (1);\ + } + + if (argc != 4) + usage (); + + outhfile = 0; foh = 0; /* just to keep gcc happy */ + + if (!strcmp(argv[1], "-c")) { + makecat = 1; + infile = argv[2]; + outcfile = argv[3]; + } else { + infile = argv[1]; + outhfile = argv[2]; + outcfile = argv[3]; + } + + fin = fopen (infile, "r"); + if (!fin) { + perror (infile); + fprintf (stderr, "makemsg: cannot open input file %s\n", infile); + usage (); + } + + /* help people not to confuse the order of these args */ + if (!makecat) { + s = rindex(outhfile, '.'); + if (!s || s[1] != 'h') { + fprintf (stderr, "defines output file should have name ending in .h\n"); + usage (); + } + s = rindex(outcfile, '.'); + if (!s || s[1] != 'c') { + fprintf (stderr, "string output file should have name ending in .c\n"); + usage (); + } + } + + if (!makecat) { + foh = fopen (outhfile, "w"); + if (!foh) { + perror (argv[1]); + fprintf (stderr, "makemsg: cannot open output file %s\n", outhfile); + usage (); + } + } + foc = fopen (outcfile, "w"); + if (!foc) { + perror (argv[2]); + fprintf (stderr, "makemsg: cannot open output file %s\n", outcfile); + usage (); + } + + if (makecat) + fputs ("$quote \"\n$set 1\n", foc); + else + fputs ("char *msg[] = {\n \"\",\n", foc); + + while (fgets (buf, sizeof(buf), fin) != NULL) { + char ss; + + /* skip leading blanks and blank lines */ + s = buf; + while (whitespace(*s)) + s++; + if (*s == 0) + continue; + + /* extract label part */ + t = s; + while (*s && !whitespace(*s)) + s++; + ss = *s; + *s = 0; + if (makecat) { + /* the format here used to be "%d ", but that breaks + glibc-2.1.2 gencat */ + fprintf (foc, "%d ", ++defct); /* gencat cannot handle %2d */ + } else { + fprintf (foh, "#define %s %d\n", t, ++defct); + fprintf (foc, "/* %2d */ ", defct); + } + *s = ss; + + /* skip blanks and newlines until string found */ + while (whitespace(*s) || *s == 0) { + if (*s == 0) { + getbuf; + s = buf; + } else + s++; + } + + /* output string - it may extend over several lines */ + while ((t = index(s, '\n')) == NULL || (t > buf && t[-1] == '\\')) { + fputs (s, foc); + getbuf; + s = buf; + } + *t = 0; + fputs (s, foc); + if (makecat) + fputs ("\n", foc); + else + fputs (",\n", foc); + } + + if (!makecat) { + fputs ("};\n", foc); + fprintf (foh, "\n#define MAXMSG %d\n", defct); + } + + if (!makecat) { + fclose (foh); + } + + fclose (foc); + fclose (fin); + + return 0; +} diff --git a/src/makewhatis b/src/makewhatis new file mode 100755 index 0000000..639d488 --- /dev/null +++ b/src/makewhatis @@ -0,0 +1,460 @@ +#!/bin/sh +# Generated automatically from makewhatis.in by the +# configure script. +# +#!/bin/sh +# makewhatis: create the whatis database +# Created: Sun Jun 14 10:49:37 1992 +# Revised: Sat Jan 8 14:12:37 1994 by faith@cs.unc.edu +# Revised: Sat Mar 23 17:56:18 1996 by micheal@actrix.gen.nz +# Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu) +# May be freely distributed and modified as long as copyright is retained. +# +# Wed Dec 23 13:27:50 1992: Rik Faith (faith@cs.unc.edu) applied changes +# based on Mitchum DSouza (mitchum.dsouza@mrc-apu.cam.ac.uk) cat patches. +# Also, cleaned up code and make it work with NET-2 doc pages. +# +# makewhatis-1.4: aeb 940802, 941007, 950417 +# Fixed so that the -c option works correctly for the cat pages +# on my machine. Fix for -u by Nan Zou (nan@ksu.ksu.edu). +# Many minor changes. +# The -s option is undocumented, and may well disappear again. +# +# Sat Mar 23 1996: Michael Hamilton (michael@actrix.gen.nz). +# I changed the script to invoke gawk only once for each directory tree. +# This speeds things up considerably (from 30 minutes down to 1.5 minutes +# on my 486DX66). +# 960401 - aeb: slight adaptation to work correctly with cat pages. +# 960510 - added fixes by brennan@raven.ca.boeing.com, author of mawk. +# 971012 - replaced "test -z" - it doesnt work on SunOS 4.1.3_U1. +# 980710 - be more careful with TMPFILE. +# 000323 - do not change PATH, better treatment of catpages - Bryan Henderson. +# 011117 - avoid suspicious filenames. +# 030310 - find files only; fix LAPACK cruft; no /usr/man default; +# use /dev/stderr instead of /dev/tty; handle files with strange names; +# add support for chinese, hungarian, indonesian, japanese, korean, +# polish, russian (Thierry Vignaud). +# +# makewhatis 1.6: Federico Lucifredi +# 060608 - Corrected traps. +# 060719 - section choosing behavior to match man's (Mike frysinger). +# +# Note for Slackware users: "makewhatis -v -w -c" will work. +# +# makewhatis flc 060719 (from man-1.6g) + +program=`basename $0` + +# In case both /usr/man and /usr/share/man exist, the former is local +# and should be first. +# It is a bug to add /var/cache/man to DEFCATPATH. +dm= +for d in /usr/man /usr/share/man /usr/X11R6/man /usr/local/man +do + if [ -d $d ]; then + if [ x$dm = x ]; then dm=$d; else dm=$dm:$d; fi + fi +done +DEFMANPATH=$dm +dc= +for d in /usr/man/preformat /usr/man /usr/share/man/preformat /usr/share/man +do + if [ -d $d ]; then + if [ x$dc = x ]; then dc=$d; else dc=$dc:$d; fi + fi +done +DEFCATPATH=$dc + +# In case /usr is read-only, make /usr/foo/whatis (etc) a symlink to +# something like /var/cache/man/foo-whatis. +# Some distributions make a single big /var/cache/man/whatis file, +# but that leads to problems and bugs. + +# AWK=/usr/bin/gawk +AWK=/usr/bin/awk + +# Find a place for our temporary files. If security is not a concern, use +# TMPFILE=/tmp/whatis$$; TMPFILEDIR=none +# Of course makewhatis should only have the required permissions +# (for reading and writing directories like /usr/man). +# We try here to be careful (and avoid preconstructed symlinks) +# in case makewhatis is run as root, by creating a subdirectory of /tmp. + +TMPFILEDIR=/tmp/whatis.tmp.dir.$$ +rm -rf $TMPFILEDIR +if ! mkdir -m 0700 $TMPFILEDIR; then + echo Could not create $TMPFILEDIR + exit 1; +fi +TMPFILE=$TMPFILEDIR/w + +# make sure TMPFILEDIR is deleted if program is killed or terminates +# (just delete this line if your shell doesnt know about trap) +trap "rm -rf $TMPFILEDIR" 0 +trap "rm -rf $TMPFILEDIR; exit 255" 1 2 3 15 + +# default find arg: no directories, no empty files +findarg0="-type f -size +0" + +topath=manpath + +defmanpath=$DEFMANPATH +defcatpath= + +if [ -n "$MANSECT" ]; then + sections=$MANSECT +else + sections=`$AWK '($1 == "MANSECT") { print $2 }' /usr/share/misc/man.conf` + if [ x"$sections" = x ]; then + sections="1:1p:8:2:3:3p:4:5:6:7:9:0p:tcl:n:l:p:o" + fi +fi +sections=`echo $sections | sed -e 's/:/ /g'` + +for name in "$@" +do +if [ -n "$setsections" ]; then + setsections= + sections=$name + continue +fi +case $name in + --version|-V) + echo "$program from man-1.6g" + exit 0;; + -c) topath=catpath + defmanpath= + defcatpath=$DEFCATPATH + continue;; + -s) setsections=1 + continue;; + -u) findarg="-ctime 0" + update=1 + continue;; + -v) verbose=1 + continue;; + -w) manpath=`man --path` + catpath=$manpath + continue;; + -*) echo "Usage: makewhatis [-s sections] [-u] [-v] [-w] [manpath] [-c [catpath]]" + echo " This will build the whatis database for the man pages" + echo " found in manpath and the cat pages found in catpath." + echo " -s: sections (default: $sections)" + echo " -u: update database with new pages" + echo " -v: verbose" + echo " -w: use manpath obtained from \`man --path\`" + echo " [manpath]: man directories (default: $DEFMANPATH)" + echo " [catpath]: cat directories (default: the first existing" + echo " directory in $DEFCATPATH)" + exit;; + *) if [ -d $name ] + then + eval $topath="\$$topath":$name + else + echo "No such directory $name" + exit + fi;; +esac +done + +manpath=`echo ${manpath-$defmanpath} | tr : ' '` +if [ x"$catpath" = x ]; then + for d in `echo $defcatpath | tr : ' '` + do + if [ -d $d ]; then catpath=$d; break; fi + done +fi +catpath=`echo ${catpath} | tr : ' '` + +# first truncate all the whatis files that will be created new, +# then only update - we might visit the same directory twice +if [ x$update = x ]; then + for pages in man cat + do + eval path="\$$pages"path + for mandir in $path + do + cp /dev/null $mandir/whatis + done + done +fi + +for pages in man cat +do + export pages + eval path="\$$pages"path + for mandir in $path + do + if [ x$verbose != x ]; then + echo "about to enter $mandir" > /dev/stderr + fi + + # kludge for Slackware's /usr/man/preformat + if [ $mandir = /usr/man/preformat ] + then + mandir1=/usr/man + else + mandir1=$mandir + fi + + # if $mandir is on a readonly partition, and the whatis file + # is not a symlink, then let's skip trying to update it + if [ ! -L ${mandir1}/whatis ] + then + if [ -e ${mandir1}/whatis ] && [ ! -w ${mandir1}/whatis ] + then + if [ x$verbose != x ]; then + echo skipping $mandir - whatis file is readonly > /dev/stderr + fi + continue + elif [ ! -e ${mandir1}/whatis ] && [ ! -w ${mandir1} ] + then + if [ x$verbose != x ]; then + echo skipping $mandir - directory is readonly > /dev/stderr + fi + continue + fi + fi + + if [ -s ${mandir}/whatis -a $pages = man -a x$update = x ]; then + if [ x$verbose != x ]; then + echo skipping $mandir - we did it already > /dev/stderr + fi + else + here=`pwd` + cd $mandir + for i in $sections + do + if [ -d ${pages}$i ] + then + cd ${pages}$i + section=$i + curdir=$mandir/${pages}$i + export section verbose curdir + find $mandir/${pages}$i/. -name '*' $findarg0 $findarg -print | $AWK ' + + function readline() { + if (use_zcat || use_bzcat || use_lzcat) { + result = (pipe_cmd | getline); + if (result < 0) { + print "Pipe error: " pipe_cmd " " ERRNO > "/dev/stderr"; + } + } else { + result = (getline < filename); + if (result < 0) { + print "Read file error: " filename " " ERRNO > "/dev/stderr"; + } + } + return result; + } + + function closeline() { + if (use_zcat || use_bzcat || use_lzcat) { + return close(pipe_cmd); + } else { + return close(filename); + } + } + + function do_one() { + insh = 0; thisjoin = 1; done = 0; + entire_line = ""; + + if (verbose) { + print "adding " filename > "/dev/stderr" + } + + use_zcat = match(filename,"\\.Z$") || + match(filename,"\\.z$") || match(filename,"\\.gz$"); + if (!use_zcat) + use_bzcat = match(filename,"\\.bz2"); + if(!use_bzcat) + use_lzcat = match(filename,"\\.lzma"); + if (use_zcat || use_bzcat || use_lzcat ) { + filename_no_gz = substr(filename, 0, RSTART - 1); + } else { + filename_no_gz = filename; + } + match(filename_no_gz, "/[^/]+$"); + progname = substr(filename, RSTART + 1, RLENGTH - 1); + if (match(progname, "\\." section "[A-Za-z]+")) { + actual_section = substr(progname, RSTART + 1, RLENGTH - 1); + } else { + actual_section = section; + } + sub(/\..*/, "", progname); + if (use_zcat || use_bzcat || use_lzcat) { + if (use_zcat) { + pipe_cmd = "zcat \"" filename "\""; + } else if (use_bzcat) { + pipe_cmd = "bzcat \"" filename "\""; + } else { + pipe_cmd = "lzcat \"" filename "\""; + } + # try to avoid suspicious stuff + if (filename ~ /[;&|`$(]/) { + print "ignored strange file name " filename " in " curdir > "/dev/stderr"; + return; + } + } + + while (!done && readline() > 0) { + gsub(/.\b/, ""); + if (($1 ~ /^\.[Ss][Hh]/ && + ($2 ~ /[Nn][Aa][Mm][Ee]/ || + $2 ~ /^JMÉNO/ || $2 ~ /^NAVN/ || $2 ~ /^NUME/ || + $2 ~ /^BEZEICHNUNG/ || $2 ~ /^NOMBRE/ || + $2 ~ /^NIMI/ || $2 ~ /^NOM/ || $2 ~ /^IME/ || + $2 ~ /^N[ÉE]V/ || $2 ~ /^NAMA/ || $2 ~ /^̾Á°/ || + $2 ~ /^̾¾Î/ || $2 ~ /^À̸§/ || $2 ~ /^NAZWA/ || + $2 ~ /^îáú÷áîéå/ || $2 ~ /^Ãû³Æ/ || $2 ~ /^¦WºÙ/ || + $2 ~ /^NOME/ || $2 ~ /^NAAM/ || $2 ~ /^ÈÌÅ/)) || + (pages == "cat" && $1 ~ /^NAME/)) { + if (!insh) { + insh = 1; + } else { + done = 1; + } + } else if (insh) { + if ($1 ~ /^\.[Ss][HhYS]/ || + (pages == "cat" && + ($1 ~ /^S[yYeE]/ || $1 ~ /^DESCRIPTION/ || + $1 ~ /^COMMAND/ || $1 ~ /^OVERVIEW/ || + $1 ~ /^STRUCTURES/ || $1 ~ /^INTRODUCTION/ || + $0 ~ /^[^ ]/))) { + # end insh for Synopsis, Syntax, but also for + # DESCRIPTION (e.g., XFree86.1x), + # COMMAND (e.g., xspread.1) + # OVERVIEW (e.g., TclCommandWriting.3) + # STRUCTURES (e.g., XEvent.3x) + # INTRODUCTION (e.g., TclX.n) + # and anything at all that begins in Column 1, so + # is probably a section header. + done = 1; + } else { + if ($0 ~ progname"-") { # Fix old cat pages + sub(progname"-", progname" - "); + } + if ($0 ~ /[^ \\]-$/) { + sub(/-$/, ""); # Handle Hyphenations + nextjoin = 1; + } else if ($0 ~ /\\c$/) { + sub(/\\c$/, ""); # Handle Continuations + nextjoin = 1; + } else + nextjoin = 0; + + sub(/^.[IB] /, ""); # Kill bold and italics + sub(/^.BI /, ""); # + sub(/^.SM /, ""); # Kill small + sub(/^.Nm /, ""); # Kill bold + sub(/^.Tn /, ""); # Kill normal + sub(/^.Li /, ""); # Kill .Li + sub(/^.Dq /, ""); # Kill .Dq + sub(/^.Nd */, "- "); # Convert .Nd to dash + sub(/\\\".*/, ""); # Trim pending comments + sub(/ *$/, ""); # Trim pending spaces + sub(/^\.$/, ""); # Kill blank comments + sub(/^'"'"'.*/, ""); # Kill comment/troff lines + sub(/^.in .*/, ""); # Kill various macros + sub(/^.ti .*/, ""); + sub(/^.ta .*/, ""); + sub(/^.Vb .*/, ""); + sub(/^.[PLTH]P$/, ""); # .PP/.LP/.TP/.HP + sub(/^.Pp$/, ""); + sub(/^.[iI]X .*$/, ""); + sub(/^.nolinks$/, ""); + sub(/^.B$/, ""); + sub(/^.nf$/, ""); + + if (($1 ~ /^\.../ || $1 == "") && + (entire_line ~ / - / || entire_line ~ / \\- /)) { + # Assume that this ends the description of one line + # Sometimes there are several descriptions in one page, + # as in outb(2). + handle_entire_line(); + entire_line = ""; + thisjoin = 1; + } else { + if (thisjoin) { + entire_line = entire_line $0; + } else { + entire_line = entire_line " " $0; + } + thisjoin = nextjoin; + } + } + } + } + handle_entire_line(); + closeline(); + } + + function handle_entire_line() { + x = entire_line; # Keep it short + + gsub(/\015/, "", x); # Kill DOS remains + gsub(/ /, " ", x); # Translate tabs to spaces + gsub(/ +/, " ", x); # Collapse spaces + gsub(/ *, */, ", ", x); # Fix comma spacings + sub(/^ /, "", x); # Kill initial spaces + sub(/ $/, "", x); # Kill trailing spaces + sub(/__+/, "_", x); # Collapse underscores + + gsub(/\\f\(../, "", x); # Kill font changes + gsub(/\\f[PRIB0123]/, "", x); # Kill font changes + gsub(/\\s[-+0-9]*/, "", x); # Kill size changes + gsub(/\\&/, "", x); # Kill \& + gsub(/\\\|/, "", x); # Kill \| + gsub(/\\\((ru|ul)/, "_", x); # Translate + gsub(/\\\((mi|hy|em)/, "-", x); # Translate + gsub(/\\\*\(../, "", x); # Kill troff strings + gsub(/\\/, "", x); # Kill all backslashes + gsub(/"/, "", x); # Kill quotes (from .Nd "foo bar") + sub(/

/, "", x);# Yuk! HTML cruft + gsub(/\000.*/, "X", x); # Binary cruft in LAPACK pages + gsub(/ +/, " ", x); # Collapse spaces (again) + sub(/^ /, "", x); # Kill initial spaces (again) + sub(/ $/, "", x); # Kill trailing spaces (again) + sub(/\.$/, "", x); # Kill trailing period + + if (!match(x, / - /)) + return; + + after_dash = substr(x, RSTART); + head = substr(x, 1, RSTART-1) ", "; + while (match(head, /, /)) { + prog = substr(head, 1, RSTART-1); + head = substr(head, RSTART+2); + if (prog != progname) + prog = prog " [" progname "]"; + printf "%-*s (%s) %s\n", 20, prog, actual_section, after_dash; + } + } + + { # Main action - process each filename read in. + filename = $0; + do_one(); + } + ' pages=$pages section=$section verbose=$verbose curdir=$curdir + cd .. + fi + done > $TMPFILE + + cd "$here" + + if [ -f ${mandir1}/whatis ] + then + cat ${mandir1}/whatis >> $TMPFILE + fi + tr -s '\n' < $TMPFILE | sort -u > ${mandir1}/whatis + + chmod 644 ${mandir1}/whatis + rm $TMPFILE + fi + done +done + +# remove tempdir +rm -rf $TMPFILEDIR + diff --git a/src/makewhatis.in b/src/makewhatis.in new file mode 100644 index 0000000..5665feb --- /dev/null +++ b/src/makewhatis.in @@ -0,0 +1,456 @@ +#!/bin/sh +# makewhatis: create the whatis database +# Created: Sun Jun 14 10:49:37 1992 +# Revised: Sat Jan 8 14:12:37 1994 by faith@cs.unc.edu +# Revised: Sat Mar 23 17:56:18 1996 by micheal@actrix.gen.nz +# Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu) +# May be freely distributed and modified as long as copyright is retained. +# +# Wed Dec 23 13:27:50 1992: Rik Faith (faith@cs.unc.edu) applied changes +# based on Mitchum DSouza (mitchum.dsouza@mrc-apu.cam.ac.uk) cat patches. +# Also, cleaned up code and make it work with NET-2 doc pages. +# +# makewhatis-1.4: aeb 940802, 941007, 950417 +# Fixed so that the -c option works correctly for the cat pages +# on my machine. Fix for -u by Nan Zou (nan@ksu.ksu.edu). +# Many minor changes. +# The -s option is undocumented, and may well disappear again. +# +# Sat Mar 23 1996: Michael Hamilton (michael@actrix.gen.nz). +# I changed the script to invoke gawk only once for each directory tree. +# This speeds things up considerably (from 30 minutes down to 1.5 minutes +# on my 486DX66). +# 960401 - aeb: slight adaptation to work correctly with cat pages. +# 960510 - added fixes by brennan@raven.ca.boeing.com, author of mawk. +# 971012 - replaced "test -z" - it doesnt work on SunOS 4.1.3_U1. +# 980710 - be more careful with TMPFILE. +# 000323 - do not change PATH, better treatment of catpages - Bryan Henderson. +# 011117 - avoid suspicious filenames. +# 030310 - find files only; fix LAPACK cruft; no /usr/man default; +# use /dev/stderr instead of /dev/tty; handle files with strange names; +# add support for chinese, hungarian, indonesian, japanese, korean, +# polish, russian (Thierry Vignaud). +# +# makewhatis 1.6: Federico Lucifredi +# 060608 - Corrected traps. +# 060719 - section choosing behavior to match man's (Mike frysinger). +# +# Note for Slackware users: "makewhatis -v -w -c" will work. +# +# makewhatis flc 060719 (from @version@) + +program=`basename $0` + +# In case both /usr/man and /usr/share/man exist, the former is local +# and should be first. +# It is a bug to add /var/cache/man to DEFCATPATH. +dm= +for d in /usr/man /usr/share/man /usr/X11R6/man /usr/local/man +do + if [ -d $d ]; then + if [ x$dm = x ]; then dm=$d; else dm=$dm:$d; fi + fi +done +DEFMANPATH=$dm +dc= +for d in /usr/man/preformat /usr/man /usr/share/man/preformat /usr/share/man +do + if [ -d $d ]; then + if [ x$dc = x ]; then dc=$d; else dc=$dc:$d; fi + fi +done +DEFCATPATH=$dc + +# In case /usr is read-only, make /usr/foo/whatis (etc) a symlink to +# something like /var/cache/man/foo-whatis. +# Some distributions make a single big /var/cache/man/whatis file, +# but that leads to problems and bugs. + +# AWK=/usr/bin/gawk +AWK=@awk@ + +# Find a place for our temporary files. If security is not a concern, use +# TMPFILE=/tmp/whatis$$; TMPFILEDIR=none +# Of course makewhatis should only have the required permissions +# (for reading and writing directories like /usr/man). +# We try here to be careful (and avoid preconstructed symlinks) +# in case makewhatis is run as root, by creating a subdirectory of /tmp. + +TMPFILEDIR=/tmp/whatis.tmp.dir.$$ +rm -rf $TMPFILEDIR +if ! mkdir -m 0700 $TMPFILEDIR; then + echo Could not create $TMPFILEDIR + exit 1; +fi +TMPFILE=$TMPFILEDIR/w + +# make sure TMPFILEDIR is deleted if program is killed or terminates +# (just delete this line if your shell doesnt know about trap) +trap "rm -rf $TMPFILEDIR" 0 +trap "rm -rf $TMPFILEDIR; exit 255" 1 2 3 15 + +# default find arg: no directories, no empty files +findarg0="-type f -size +0" + +topath=manpath + +defmanpath=$DEFMANPATH +defcatpath= + +if [ -n "$MANSECT" ]; then + sections=$MANSECT +else + sections=`$AWK '($1 == "MANSECT") { print $2 }' @man_config_file@` + if [ x"$sections" = x ]; then + sections="@sections@" + fi +fi +sections=`echo $sections | sed -e 's/:/ /g'` + +for name in "$@" +do +if [ -n "$setsections" ]; then + setsections= + sections=$name + continue +fi +case $name in + --version|-V) + echo "$program from @version@" + exit 0;; + -c) topath=catpath + defmanpath= + defcatpath=$DEFCATPATH + continue;; + -s) setsections=1 + continue;; + -u) findarg="-ctime 0" + update=1 + continue;; + -v) verbose=1 + continue;; + -w) manpath=`man --path` + catpath=$manpath + continue;; + -*) echo "Usage: makewhatis [-s sections] [-u] [-v] [-w] [manpath] [-c [catpath]]" + echo " This will build the whatis database for the man pages" + echo " found in manpath and the cat pages found in catpath." + echo " -s: sections (default: $sections)" + echo " -u: update database with new pages" + echo " -v: verbose" + echo " -w: use manpath obtained from \`man --path\`" + echo " [manpath]: man directories (default: $DEFMANPATH)" + echo " [catpath]: cat directories (default: the first existing" + echo " directory in $DEFCATPATH)" + exit;; + *) if [ -d $name ] + then + eval $topath="\$$topath":$name + else + echo "No such directory $name" + exit + fi;; +esac +done + +manpath=`echo ${manpath-$defmanpath} | tr : ' '` +if [ x"$catpath" = x ]; then + for d in `echo $defcatpath | tr : ' '` + do + if [ -d $d ]; then catpath=$d; break; fi + done +fi +catpath=`echo ${catpath} | tr : ' '` + +# first truncate all the whatis files that will be created new, +# then only update - we might visit the same directory twice +if [ x$update = x ]; then + for pages in man cat + do + eval path="\$$pages"path + for mandir in $path + do + cp /dev/null $mandir/whatis + done + done +fi + +for pages in man cat +do + export pages + eval path="\$$pages"path + for mandir in $path + do + if [ x$verbose != x ]; then + echo "about to enter $mandir" > /dev/stderr + fi + + # kludge for Slackware's /usr/man/preformat + if [ $mandir = /usr/man/preformat ] + then + mandir1=/usr/man + else + mandir1=$mandir + fi + + # if $mandir is on a readonly partition, and the whatis file + # is not a symlink, then let's skip trying to update it + if [ ! -L ${mandir1}/whatis ] + then + if [ -e ${mandir1}/whatis ] && [ ! -w ${mandir1}/whatis ] + then + if [ x$verbose != x ]; then + echo skipping $mandir - whatis file is readonly > /dev/stderr + fi + continue + elif [ ! -e ${mandir1}/whatis ] && [ ! -w ${mandir1} ] + then + if [ x$verbose != x ]; then + echo skipping $mandir - directory is readonly > /dev/stderr + fi + continue + fi + fi + + if [ -s ${mandir}/whatis -a $pages = man -a x$update = x ]; then + if [ x$verbose != x ]; then + echo skipping $mandir - we did it already > /dev/stderr + fi + else + here=`pwd` + cd $mandir + for i in $sections + do + if [ -d ${pages}$i ] + then + cd ${pages}$i + section=$i + curdir=$mandir/${pages}$i + export section verbose curdir + find $mandir/${pages}$i/. -name '*' $findarg0 $findarg -print | $AWK ' + + function readline() { + if (use_zcat || use_bzcat || use_lzcat) { + result = (pipe_cmd | getline); + if (result < 0) { + print "Pipe error: " pipe_cmd " " ERRNO > "/dev/stderr"; + } + } else { + result = (getline < filename); + if (result < 0) { + print "Read file error: " filename " " ERRNO > "/dev/stderr"; + } + } + return result; + } + + function closeline() { + if (use_zcat || use_bzcat || use_lzcat) { + return close(pipe_cmd); + } else { + return close(filename); + } + } + + function do_one() { + insh = 0; thisjoin = 1; done = 0; + entire_line = ""; + + if (verbose) { + print "adding " filename > "/dev/stderr" + } + + use_zcat = match(filename,"\\.Z$") || + match(filename,"\\.z$") || match(filename,"\\.gz$"); + if (!use_zcat) + use_bzcat = match(filename,"\\.bz2"); + if(!use_bzcat) + use_lzcat = match(filename,"\\.lzma"); + if (use_zcat || use_bzcat || use_lzcat ) { + filename_no_gz = substr(filename, 0, RSTART - 1); + } else { + filename_no_gz = filename; + } + match(filename_no_gz, "/[^/]+$"); + progname = substr(filename, RSTART + 1, RLENGTH - 1); + if (match(progname, "\\." section "[A-Za-z]+")) { + actual_section = substr(progname, RSTART + 1, RLENGTH - 1); + } else { + actual_section = section; + } + sub(/\..*/, "", progname); + if (use_zcat || use_bzcat || use_lzcat) { + if (use_zcat) { + pipe_cmd = "zcat \"" filename "\""; + } else if (use_bzcat) { + pipe_cmd = "bzcat \"" filename "\""; + } else { + pipe_cmd = "lzcat \"" filename "\""; + } + # try to avoid suspicious stuff + if (filename ~ /[;&|`$(]/) { + print "ignored strange file name " filename " in " curdir > "/dev/stderr"; + return; + } + } + + while (!done && readline() > 0) { + gsub(/.\b/, ""); + if (($1 ~ /^\.[Ss][Hh]/ && + ($2 ~ /[Nn][Aa][Mm][Ee]/ || + $2 ~ /^JMÉNO/ || $2 ~ /^NAVN/ || $2 ~ /^NUME/ || + $2 ~ /^BEZEICHNUNG/ || $2 ~ /^NOMBRE/ || + $2 ~ /^NIMI/ || $2 ~ /^NOM/ || $2 ~ /^IME/ || + $2 ~ /^N[ÉE]V/ || $2 ~ /^NAMA/ || $2 ~ /^̾Á°/ || + $2 ~ /^̾¾Î/ || $2 ~ /^À̸§/ || $2 ~ /^NAZWA/ || + $2 ~ /^îáú÷áîéå/ || $2 ~ /^Ãû³Æ/ || $2 ~ /^¦WºÙ/ || + $2 ~ /^NOME/ || $2 ~ /^NAAM/ || $2 ~ /^ÈÌÅ/)) || + (pages == "cat" && $1 ~ /^NAME/)) { + if (!insh) { + insh = 1; + } else { + done = 1; + } + } else if (insh) { + if ($1 ~ /^\.[Ss][HhYS]/ || + (pages == "cat" && + ($1 ~ /^S[yYeE]/ || $1 ~ /^DESCRIPTION/ || + $1 ~ /^COMMAND/ || $1 ~ /^OVERVIEW/ || + $1 ~ /^STRUCTURES/ || $1 ~ /^INTRODUCTION/ || + $0 ~ /^[^ ]/))) { + # end insh for Synopsis, Syntax, but also for + # DESCRIPTION (e.g., XFree86.1x), + # COMMAND (e.g., xspread.1) + # OVERVIEW (e.g., TclCommandWriting.3) + # STRUCTURES (e.g., XEvent.3x) + # INTRODUCTION (e.g., TclX.n) + # and anything at all that begins in Column 1, so + # is probably a section header. + done = 1; + } else { + if ($0 ~ progname"-") { # Fix old cat pages + sub(progname"-", progname" - "); + } + if ($0 ~ /[^ \\]-$/) { + sub(/-$/, ""); # Handle Hyphenations + nextjoin = 1; + } else if ($0 ~ /\\c$/) { + sub(/\\c$/, ""); # Handle Continuations + nextjoin = 1; + } else + nextjoin = 0; + + sub(/^.[IB] /, ""); # Kill bold and italics + sub(/^.BI /, ""); # + sub(/^.SM /, ""); # Kill small + sub(/^.Nm /, ""); # Kill bold + sub(/^.Tn /, ""); # Kill normal + sub(/^.Li /, ""); # Kill .Li + sub(/^.Dq /, ""); # Kill .Dq + sub(/^.Nd */, "- "); # Convert .Nd to dash + sub(/\\\".*/, ""); # Trim pending comments + sub(/ *$/, ""); # Trim pending spaces + sub(/^\.$/, ""); # Kill blank comments + sub(/^'"'"'.*/, ""); # Kill comment/troff lines + sub(/^.in .*/, ""); # Kill various macros + sub(/^.ti .*/, ""); + sub(/^.ta .*/, ""); + sub(/^.Vb .*/, ""); + sub(/^.[PLTH]P$/, ""); # .PP/.LP/.TP/.HP + sub(/^.Pp$/, ""); + sub(/^.[iI]X .*$/, ""); + sub(/^.nolinks$/, ""); + sub(/^.B$/, ""); + sub(/^.nf$/, ""); + + if (($1 ~ /^\.../ || $1 == "") && + (entire_line ~ / - / || entire_line ~ / \\- /)) { + # Assume that this ends the description of one line + # Sometimes there are several descriptions in one page, + # as in outb(2). + handle_entire_line(); + entire_line = ""; + thisjoin = 1; + } else { + if (thisjoin) { + entire_line = entire_line $0; + } else { + entire_line = entire_line " " $0; + } + thisjoin = nextjoin; + } + } + } + } + handle_entire_line(); + closeline(); + } + + function handle_entire_line() { + x = entire_line; # Keep it short + + gsub(/\015/, "", x); # Kill DOS remains + gsub(/ /, " ", x); # Translate tabs to spaces + gsub(/ +/, " ", x); # Collapse spaces + gsub(/ *, */, ", ", x); # Fix comma spacings + sub(/^ /, "", x); # Kill initial spaces + sub(/ $/, "", x); # Kill trailing spaces + sub(/__+/, "_", x); # Collapse underscores + + gsub(/\\f\(../, "", x); # Kill font changes + gsub(/\\f[PRIB0123]/, "", x); # Kill font changes + gsub(/\\s[-+0-9]*/, "", x); # Kill size changes + gsub(/\\&/, "", x); # Kill \& + gsub(/\\\|/, "", x); # Kill \| + gsub(/\\\((ru|ul)/, "_", x); # Translate + gsub(/\\\((mi|hy|em)/, "-", x); # Translate + gsub(/\\\*\(../, "", x); # Kill troff strings + gsub(/\\/, "", x); # Kill all backslashes + gsub(/"/, "", x); # Kill quotes (from .Nd "foo bar") + sub(/

/, "", x);# Yuk! HTML cruft + gsub(/\000.*/, "X", x); # Binary cruft in LAPACK pages + gsub(/ +/, " ", x); # Collapse spaces (again) + sub(/^ /, "", x); # Kill initial spaces (again) + sub(/ $/, "", x); # Kill trailing spaces (again) + sub(/\.$/, "", x); # Kill trailing period + + if (!match(x, / - /)) + return; + + after_dash = substr(x, RSTART); + head = substr(x, 1, RSTART-1) ", "; + while (match(head, /, /)) { + prog = substr(head, 1, RSTART-1); + head = substr(head, RSTART+2); + if (prog != progname) + prog = prog " [" progname "]"; + printf "%-*s (%s) %s\n", 20, prog, actual_section, after_dash; + } + } + + { # Main action - process each filename read in. + filename = $0; + do_one(); + } + ' pages=$pages section=$section verbose=$verbose curdir=$curdir + cd .. + fi + done > $TMPFILE + + cd "$here" + + if [ -f ${mandir1}/whatis ] + then + cat ${mandir1}/whatis >> $TMPFILE + fi + tr -s '\n' < $TMPFILE | sort -u > ${mandir1}/whatis + + chmod 644 ${mandir1}/whatis + rm $TMPFILE + fi + done +done + +# remove tempdir +rm -rf $TMPFILEDIR + diff --git a/src/makewhatis.sh b/src/makewhatis.sh new file mode 100644 index 0000000..5665feb --- /dev/null +++ b/src/makewhatis.sh @@ -0,0 +1,456 @@ +#!/bin/sh +# makewhatis: create the whatis database +# Created: Sun Jun 14 10:49:37 1992 +# Revised: Sat Jan 8 14:12:37 1994 by faith@cs.unc.edu +# Revised: Sat Mar 23 17:56:18 1996 by micheal@actrix.gen.nz +# Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu) +# May be freely distributed and modified as long as copyright is retained. +# +# Wed Dec 23 13:27:50 1992: Rik Faith (faith@cs.unc.edu) applied changes +# based on Mitchum DSouza (mitchum.dsouza@mrc-apu.cam.ac.uk) cat patches. +# Also, cleaned up code and make it work with NET-2 doc pages. +# +# makewhatis-1.4: aeb 940802, 941007, 950417 +# Fixed so that the -c option works correctly for the cat pages +# on my machine. Fix for -u by Nan Zou (nan@ksu.ksu.edu). +# Many minor changes. +# The -s option is undocumented, and may well disappear again. +# +# Sat Mar 23 1996: Michael Hamilton (michael@actrix.gen.nz). +# I changed the script to invoke gawk only once for each directory tree. +# This speeds things up considerably (from 30 minutes down to 1.5 minutes +# on my 486DX66). +# 960401 - aeb: slight adaptation to work correctly with cat pages. +# 960510 - added fixes by brennan@raven.ca.boeing.com, author of mawk. +# 971012 - replaced "test -z" - it doesnt work on SunOS 4.1.3_U1. +# 980710 - be more careful with TMPFILE. +# 000323 - do not change PATH, better treatment of catpages - Bryan Henderson. +# 011117 - avoid suspicious filenames. +# 030310 - find files only; fix LAPACK cruft; no /usr/man default; +# use /dev/stderr instead of /dev/tty; handle files with strange names; +# add support for chinese, hungarian, indonesian, japanese, korean, +# polish, russian (Thierry Vignaud). +# +# makewhatis 1.6: Federico Lucifredi +# 060608 - Corrected traps. +# 060719 - section choosing behavior to match man's (Mike frysinger). +# +# Note for Slackware users: "makewhatis -v -w -c" will work. +# +# makewhatis flc 060719 (from @version@) + +program=`basename $0` + +# In case both /usr/man and /usr/share/man exist, the former is local +# and should be first. +# It is a bug to add /var/cache/man to DEFCATPATH. +dm= +for d in /usr/man /usr/share/man /usr/X11R6/man /usr/local/man +do + if [ -d $d ]; then + if [ x$dm = x ]; then dm=$d; else dm=$dm:$d; fi + fi +done +DEFMANPATH=$dm +dc= +for d in /usr/man/preformat /usr/man /usr/share/man/preformat /usr/share/man +do + if [ -d $d ]; then + if [ x$dc = x ]; then dc=$d; else dc=$dc:$d; fi + fi +done +DEFCATPATH=$dc + +# In case /usr is read-only, make /usr/foo/whatis (etc) a symlink to +# something like /var/cache/man/foo-whatis. +# Some distributions make a single big /var/cache/man/whatis file, +# but that leads to problems and bugs. + +# AWK=/usr/bin/gawk +AWK=@awk@ + +# Find a place for our temporary files. If security is not a concern, use +# TMPFILE=/tmp/whatis$$; TMPFILEDIR=none +# Of course makewhatis should only have the required permissions +# (for reading and writing directories like /usr/man). +# We try here to be careful (and avoid preconstructed symlinks) +# in case makewhatis is run as root, by creating a subdirectory of /tmp. + +TMPFILEDIR=/tmp/whatis.tmp.dir.$$ +rm -rf $TMPFILEDIR +if ! mkdir -m 0700 $TMPFILEDIR; then + echo Could not create $TMPFILEDIR + exit 1; +fi +TMPFILE=$TMPFILEDIR/w + +# make sure TMPFILEDIR is deleted if program is killed or terminates +# (just delete this line if your shell doesnt know about trap) +trap "rm -rf $TMPFILEDIR" 0 +trap "rm -rf $TMPFILEDIR; exit 255" 1 2 3 15 + +# default find arg: no directories, no empty files +findarg0="-type f -size +0" + +topath=manpath + +defmanpath=$DEFMANPATH +defcatpath= + +if [ -n "$MANSECT" ]; then + sections=$MANSECT +else + sections=`$AWK '($1 == "MANSECT") { print $2 }' @man_config_file@` + if [ x"$sections" = x ]; then + sections="@sections@" + fi +fi +sections=`echo $sections | sed -e 's/:/ /g'` + +for name in "$@" +do +if [ -n "$setsections" ]; then + setsections= + sections=$name + continue +fi +case $name in + --version|-V) + echo "$program from @version@" + exit 0;; + -c) topath=catpath + defmanpath= + defcatpath=$DEFCATPATH + continue;; + -s) setsections=1 + continue;; + -u) findarg="-ctime 0" + update=1 + continue;; + -v) verbose=1 + continue;; + -w) manpath=`man --path` + catpath=$manpath + continue;; + -*) echo "Usage: makewhatis [-s sections] [-u] [-v] [-w] [manpath] [-c [catpath]]" + echo " This will build the whatis database for the man pages" + echo " found in manpath and the cat pages found in catpath." + echo " -s: sections (default: $sections)" + echo " -u: update database with new pages" + echo " -v: verbose" + echo " -w: use manpath obtained from \`man --path\`" + echo " [manpath]: man directories (default: $DEFMANPATH)" + echo " [catpath]: cat directories (default: the first existing" + echo " directory in $DEFCATPATH)" + exit;; + *) if [ -d $name ] + then + eval $topath="\$$topath":$name + else + echo "No such directory $name" + exit + fi;; +esac +done + +manpath=`echo ${manpath-$defmanpath} | tr : ' '` +if [ x"$catpath" = x ]; then + for d in `echo $defcatpath | tr : ' '` + do + if [ -d $d ]; then catpath=$d; break; fi + done +fi +catpath=`echo ${catpath} | tr : ' '` + +# first truncate all the whatis files that will be created new, +# then only update - we might visit the same directory twice +if [ x$update = x ]; then + for pages in man cat + do + eval path="\$$pages"path + for mandir in $path + do + cp /dev/null $mandir/whatis + done + done +fi + +for pages in man cat +do + export pages + eval path="\$$pages"path + for mandir in $path + do + if [ x$verbose != x ]; then + echo "about to enter $mandir" > /dev/stderr + fi + + # kludge for Slackware's /usr/man/preformat + if [ $mandir = /usr/man/preformat ] + then + mandir1=/usr/man + else + mandir1=$mandir + fi + + # if $mandir is on a readonly partition, and the whatis file + # is not a symlink, then let's skip trying to update it + if [ ! -L ${mandir1}/whatis ] + then + if [ -e ${mandir1}/whatis ] && [ ! -w ${mandir1}/whatis ] + then + if [ x$verbose != x ]; then + echo skipping $mandir - whatis file is readonly > /dev/stderr + fi + continue + elif [ ! -e ${mandir1}/whatis ] && [ ! -w ${mandir1} ] + then + if [ x$verbose != x ]; then + echo skipping $mandir - directory is readonly > /dev/stderr + fi + continue + fi + fi + + if [ -s ${mandir}/whatis -a $pages = man -a x$update = x ]; then + if [ x$verbose != x ]; then + echo skipping $mandir - we did it already > /dev/stderr + fi + else + here=`pwd` + cd $mandir + for i in $sections + do + if [ -d ${pages}$i ] + then + cd ${pages}$i + section=$i + curdir=$mandir/${pages}$i + export section verbose curdir + find $mandir/${pages}$i/. -name '*' $findarg0 $findarg -print | $AWK ' + + function readline() { + if (use_zcat || use_bzcat || use_lzcat) { + result = (pipe_cmd | getline); + if (result < 0) { + print "Pipe error: " pipe_cmd " " ERRNO > "/dev/stderr"; + } + } else { + result = (getline < filename); + if (result < 0) { + print "Read file error: " filename " " ERRNO > "/dev/stderr"; + } + } + return result; + } + + function closeline() { + if (use_zcat || use_bzcat || use_lzcat) { + return close(pipe_cmd); + } else { + return close(filename); + } + } + + function do_one() { + insh = 0; thisjoin = 1; done = 0; + entire_line = ""; + + if (verbose) { + print "adding " filename > "/dev/stderr" + } + + use_zcat = match(filename,"\\.Z$") || + match(filename,"\\.z$") || match(filename,"\\.gz$"); + if (!use_zcat) + use_bzcat = match(filename,"\\.bz2"); + if(!use_bzcat) + use_lzcat = match(filename,"\\.lzma"); + if (use_zcat || use_bzcat || use_lzcat ) { + filename_no_gz = substr(filename, 0, RSTART - 1); + } else { + filename_no_gz = filename; + } + match(filename_no_gz, "/[^/]+$"); + progname = substr(filename, RSTART + 1, RLENGTH - 1); + if (match(progname, "\\." section "[A-Za-z]+")) { + actual_section = substr(progname, RSTART + 1, RLENGTH - 1); + } else { + actual_section = section; + } + sub(/\..*/, "", progname); + if (use_zcat || use_bzcat || use_lzcat) { + if (use_zcat) { + pipe_cmd = "zcat \"" filename "\""; + } else if (use_bzcat) { + pipe_cmd = "bzcat \"" filename "\""; + } else { + pipe_cmd = "lzcat \"" filename "\""; + } + # try to avoid suspicious stuff + if (filename ~ /[;&|`$(]/) { + print "ignored strange file name " filename " in " curdir > "/dev/stderr"; + return; + } + } + + while (!done && readline() > 0) { + gsub(/.\b/, ""); + if (($1 ~ /^\.[Ss][Hh]/ && + ($2 ~ /[Nn][Aa][Mm][Ee]/ || + $2 ~ /^JMÉNO/ || $2 ~ /^NAVN/ || $2 ~ /^NUME/ || + $2 ~ /^BEZEICHNUNG/ || $2 ~ /^NOMBRE/ || + $2 ~ /^NIMI/ || $2 ~ /^NOM/ || $2 ~ /^IME/ || + $2 ~ /^N[ÉE]V/ || $2 ~ /^NAMA/ || $2 ~ /^̾Á°/ || + $2 ~ /^̾¾Î/ || $2 ~ /^À̸§/ || $2 ~ /^NAZWA/ || + $2 ~ /^îáú÷áîéå/ || $2 ~ /^Ãû³Æ/ || $2 ~ /^¦WºÙ/ || + $2 ~ /^NOME/ || $2 ~ /^NAAM/ || $2 ~ /^ÈÌÅ/)) || + (pages == "cat" && $1 ~ /^NAME/)) { + if (!insh) { + insh = 1; + } else { + done = 1; + } + } else if (insh) { + if ($1 ~ /^\.[Ss][HhYS]/ || + (pages == "cat" && + ($1 ~ /^S[yYeE]/ || $1 ~ /^DESCRIPTION/ || + $1 ~ /^COMMAND/ || $1 ~ /^OVERVIEW/ || + $1 ~ /^STRUCTURES/ || $1 ~ /^INTRODUCTION/ || + $0 ~ /^[^ ]/))) { + # end insh for Synopsis, Syntax, but also for + # DESCRIPTION (e.g., XFree86.1x), + # COMMAND (e.g., xspread.1) + # OVERVIEW (e.g., TclCommandWriting.3) + # STRUCTURES (e.g., XEvent.3x) + # INTRODUCTION (e.g., TclX.n) + # and anything at all that begins in Column 1, so + # is probably a section header. + done = 1; + } else { + if ($0 ~ progname"-") { # Fix old cat pages + sub(progname"-", progname" - "); + } + if ($0 ~ /[^ \\]-$/) { + sub(/-$/, ""); # Handle Hyphenations + nextjoin = 1; + } else if ($0 ~ /\\c$/) { + sub(/\\c$/, ""); # Handle Continuations + nextjoin = 1; + } else + nextjoin = 0; + + sub(/^.[IB] /, ""); # Kill bold and italics + sub(/^.BI /, ""); # + sub(/^.SM /, ""); # Kill small + sub(/^.Nm /, ""); # Kill bold + sub(/^.Tn /, ""); # Kill normal + sub(/^.Li /, ""); # Kill .Li + sub(/^.Dq /, ""); # Kill .Dq + sub(/^.Nd */, "- "); # Convert .Nd to dash + sub(/\\\".*/, ""); # Trim pending comments + sub(/ *$/, ""); # Trim pending spaces + sub(/^\.$/, ""); # Kill blank comments + sub(/^'"'"'.*/, ""); # Kill comment/troff lines + sub(/^.in .*/, ""); # Kill various macros + sub(/^.ti .*/, ""); + sub(/^.ta .*/, ""); + sub(/^.Vb .*/, ""); + sub(/^.[PLTH]P$/, ""); # .PP/.LP/.TP/.HP + sub(/^.Pp$/, ""); + sub(/^.[iI]X .*$/, ""); + sub(/^.nolinks$/, ""); + sub(/^.B$/, ""); + sub(/^.nf$/, ""); + + if (($1 ~ /^\.../ || $1 == "") && + (entire_line ~ / - / || entire_line ~ / \\- /)) { + # Assume that this ends the description of one line + # Sometimes there are several descriptions in one page, + # as in outb(2). + handle_entire_line(); + entire_line = ""; + thisjoin = 1; + } else { + if (thisjoin) { + entire_line = entire_line $0; + } else { + entire_line = entire_line " " $0; + } + thisjoin = nextjoin; + } + } + } + } + handle_entire_line(); + closeline(); + } + + function handle_entire_line() { + x = entire_line; # Keep it short + + gsub(/\015/, "", x); # Kill DOS remains + gsub(/ /, " ", x); # Translate tabs to spaces + gsub(/ +/, " ", x); # Collapse spaces + gsub(/ *, */, ", ", x); # Fix comma spacings + sub(/^ /, "", x); # Kill initial spaces + sub(/ $/, "", x); # Kill trailing spaces + sub(/__+/, "_", x); # Collapse underscores + + gsub(/\\f\(../, "", x); # Kill font changes + gsub(/\\f[PRIB0123]/, "", x); # Kill font changes + gsub(/\\s[-+0-9]*/, "", x); # Kill size changes + gsub(/\\&/, "", x); # Kill \& + gsub(/\\\|/, "", x); # Kill \| + gsub(/\\\((ru|ul)/, "_", x); # Translate + gsub(/\\\((mi|hy|em)/, "-", x); # Translate + gsub(/\\\*\(../, "", x); # Kill troff strings + gsub(/\\/, "", x); # Kill all backslashes + gsub(/"/, "", x); # Kill quotes (from .Nd "foo bar") + sub(/

/, "", x);# Yuk! HTML cruft + gsub(/\000.*/, "X", x); # Binary cruft in LAPACK pages + gsub(/ +/, " ", x); # Collapse spaces (again) + sub(/^ /, "", x); # Kill initial spaces (again) + sub(/ $/, "", x); # Kill trailing spaces (again) + sub(/\.$/, "", x); # Kill trailing period + + if (!match(x, / - /)) + return; + + after_dash = substr(x, RSTART); + head = substr(x, 1, RSTART-1) ", "; + while (match(head, /, /)) { + prog = substr(head, 1, RSTART-1); + head = substr(head, RSTART+2); + if (prog != progname) + prog = prog " [" progname "]"; + printf "%-*s (%s) %s\n", 20, prog, actual_section, after_dash; + } + } + + { # Main action - process each filename read in. + filename = $0; + do_one(); + } + ' pages=$pages section=$section verbose=$verbose curdir=$curdir + cd .. + fi + done > $TMPFILE + + cd "$here" + + if [ -f ${mandir1}/whatis ] + then + cat ${mandir1}/whatis >> $TMPFILE + fi + tr -s '\n' < $TMPFILE | sort -u > ${mandir1}/whatis + + chmod 644 ${mandir1}/whatis + rm $TMPFILE + fi + done +done + +# remove tempdir +rm -rf $TMPFILEDIR + diff --git a/src/man-config.c b/src/man-config.c new file mode 100644 index 0000000..d66cef6 --- /dev/null +++ b/src/man-config.c @@ -0,0 +1,297 @@ +/* + * man-config.c + * + * Read the man.conf file + * + * Input line types: + * MANBIN /usr/bin/man + * MANPATH /usr/X386/man [/var/catman/X386] + * MANPATH_MAP /usr/bin /usr/man + * FHS + * FSSTND + * NOAUTOPATH + * NROFF /usr/bin/groff -Tascii -mandoc + * BROWSER /usr/bin/lynx + * HTMLPAGER /usr/bin/lynx -dump + * .gz /usr/bin/gunzip -c + * # Comment + * + * Allow globbing in MANPATH elements. + * This is useful e.g. for having MANPATH /opt/ * /man + * (avoid comment within comment). + */ + +#include +#include +#include + +#include "defs.h" +#include "glob.h" +#include "man-config.h" +#include "man.h" +#include "paths.h" +#include "gripes.h" +#include "util.h" + +#define BUFSIZE 4096 + +extern char *rindex (const char *, int); /* not always in */ + +#define whitespace(x) ((x) == ' ' || (x) == '\t') + +/* directories listed in config file */ +struct dirs cfdirlist; /* linked list, 1st entry unused */ + +static void +addval (char *buf) { + int i, len; + char *bp; + + for (i = 0; i < sizeof(paths)/sizeof(paths[0]); i++) { + len = strlen (paths[i].name); + bp = buf + len; + if(!strncmp (buf, paths[i].name, len) && (!*bp || whitespace(*bp))) { + while(whitespace(*bp)) + bp++; + paths[i].path = my_strdup(bp); + return; + } + } + gripe (UNRECOGNIZED_LINE, buf); +} + +const char * +getval (const char *cmd) { + int i; + + for (i = 0; i < sizeof(paths)/sizeof(paths[0]); i++) + if (!strcmp (cmd, paths[i].name)) + return paths[i].path; /* never NULL */ + gripe (GETVAL_ERROR, cmd); + return ""; /* impossible */ +} + +static void +adddir (const char *bp, int mandatory) { + int i; + struct dirs *dlp; + + while (whitespace(*bp)) + bp++; + if (*bp == 0) + gripe (PARSE_ERROR_IN_CONFIG); + + dlp = &cfdirlist; + while (dlp->nxt) + dlp = dlp->nxt; + dlp->nxt = (struct dirs *) my_malloc (sizeof(struct dirs)); + dlp = dlp->nxt; + dlp->mandatory = mandatory; + dlp->nxt = 0; + + if (!mandatory) { + i = 0; + while (*bp && !whitespace(*bp)) { + if (i < MAXPATHLEN - 1) + dlp->bindir[i++] = *bp; + bp++; + } + dlp->bindir[i] = 0; + + while (whitespace(*bp)) + bp++; + } else { + dlp->bindir[0] = 0; + } + + i = 0; + while (*bp && !whitespace(*bp)) { + if (i < MAXPATHLEN - 1) + dlp->mandir[i++] = *bp; + bp++; + } + dlp->mandir[i] = 0; + + while (whitespace(*bp)) + bp++; + + i = 0; + while (*bp && !whitespace(*bp)) { + if (i < MAXPATHLEN - 1) + dlp->catdir[i++] = *bp; + bp++; + } + dlp->catdir[i] = 0; + + if (debug) { + if (dlp->mandatory) + gripe (FOUND_MANDIR, dlp->mandir); + else + gripe (FOUND_MAP, dlp->bindir, dlp->mandir); + if (dlp->catdir[0]) + gripe (FOUND_CATDIR, dlp->catdir); + } +} + +static void +addglobdir (const char *bp, int mandatory) { + const char *dir; + + while (whitespace(*bp)) + bp++; + + dir = bp; + if (index(dir, '*') || index(dir, '?') || index(dir, '[')) { + char **dp = glob_filename (dir); + + if (dp && dp != (char **) -1) { + while (*dp) + adddir(*dp++, mandatory); + return; + } + } + adddir(dir, mandatory); +} + +static struct xp { + char *extension; /* non-null, including initial . */ + char *expander; + struct xp *nxt; +} uncompressors; /* linked list, 1st entry unused */ + +static void +addext (char *bp) { + char *p, csv; + struct xp *xpp; + + xpp = &uncompressors; + while (xpp->nxt) + xpp = xpp->nxt; + xpp->nxt = (struct xp *) my_malloc (sizeof(struct xp)); + xpp = xpp->nxt; + xpp->nxt = 0; + + p = bp; + while(*p && !whitespace(*p)) + p++; + csv = *p; + *p = 0; + xpp->extension = my_strdup(bp); + + *p = csv; + while(whitespace(*p)) + p++; + xpp->expander = my_strdup(p); +} + +const char * +get_expander (const char *file) { + struct xp *xp; + char *extp = NULL; + + if (dohp) { + /* Some HP systems have both man1 and man1.Z */ + /* For man1.Z/file.1 let extp=".Z" */ + /* For .1 return NULL */ + int len = strlen (dohp); + char *dirname_end = rindex (file, '/'); + if (dirname_end && !strncmp (dirname_end-len, dohp, len)) + extp = dohp; + } else + extp = rindex (file, '.'); + if (extp != NULL) { + if (uncompressors.nxt) { + for (xp = uncompressors.nxt; xp; xp = xp->nxt) + if (!strcmp (extp, xp->extension)) + return (xp->expander); + } else if (!strcmp (extp, getval("COMPRESS_EXT"))) { + return getval("DECOMPRESS"); + } + } + return NULL; +} + +const char *configuration_file = "[no configuration file]"; + +char *default_config_files[] = { + CONFIG_FILE, /* compiled-in default */ + "/etc/man.conf", "/etc/man.config", + "/usr/lib/man.conf", "/usr/lib/man.config", + "/usr/share/misc/man.conf", "/usr/share/misc/man.config" +}; + +#define SIZE(x) (sizeof(x)/sizeof((x)[0])) + +void +read_config_file (const char *cf) { + char *bp; + char *p; + char buf[BUFSIZE]; + FILE *config = NULL; + + if (cf) { + /* User explicitly specified a config file */ + if ((config = fopen (cf, "r")) == NULL) { + perror (cf); + gripe (CONFIG_OPEN_ERROR, cf); + return; + } + } else { + /* Try some things - unfortunately we cannot lookup + the config file to use in the config file :-). */ + int i; + + for(i=0; i < SIZE(default_config_files); i++) { + cf = default_config_files[i]; + if ((config = fopen (cf, "r")) != NULL) + break; + } + + if (config == NULL) { + gripe (CONFIG_OPEN_ERROR, CONFIG_FILE); + return; + } + } + + if (debug) + fprintf(stderr, "Reading config file %s\n", cf); + configuration_file = cf; + + while ((bp = fgets (buf, BUFSIZE, config)) != NULL) { + while (whitespace(*bp)) + bp++; + + for (p = bp; *p && *p != '#' && *p != '\n'; p++) ; + if (!*p) { + gripe (LINE_TOO_LONG); + gripe (BAD_CONFIG_FILE, cf); + return; + } + while (p > bp && whitespace(p[-1])) + p--; + *p = 0; + + if (*bp == 0) + continue; + + if (!strncmp ("MANPATH_MAP", bp, 11)) + adddir (bp+11, 0); + else if (!strncmp ("MANPATH", bp, 7)) + addglobdir (bp+7, 1); + else if(!strncmp ("MANDATORY_MANPATH", bp, 17))/* backwards compatible */ + adddir (bp+17, 1); + else if (!strncmp ("FHS", bp, 3)) + fhs = 1; + else if (!strncmp ("FSSTND", bp, 6)) + fsstnd = 1; + else if (!strncmp ("NOAUTOPATH", bp, 10)) + noautopath = 1; + else if (!strncmp ("NOCACHE", bp, 7)) + nocache = 1; + else if (*bp == '.') + addext (bp); + else + addval (bp); + } +} + diff --git a/src/man-config.h b/src/man-config.h new file mode 100644 index 0000000..b364ffd --- /dev/null +++ b/src/man-config.h @@ -0,0 +1,6 @@ +const char *getval (const char *); +const char *get_expander (const char *); +void read_config_file (const char *cf); + +extern struct dirs cfdirlist; +extern const char *configuration_file; diff --git a/src/man-getopt.c b/src/man-getopt.c new file mode 100644 index 0000000..07aecdb --- /dev/null +++ b/src/man-getopt.c @@ -0,0 +1,322 @@ +#include +#include +#include +#include + +#include "defs.h" +#include "gripes.h" +#include "man.h" +#include "man-config.h" +#include "man-getopt.h" +#include "util.h" +#include "version.h" + +int alt_system; +char *alt_system_name; +char *opt_manpath; +int global_apropos = 0; + +static void +print_version (void) { + gripe (VERSION, progname, version); +} + +static void +usage (void) { + print_version(); + gripe (USAGE1, progname); + + gripe (USAGE2); /* only for alt_systems */ + + gripe (USAGE3); + gripe (USAGE4); + gripe (USAGE5); /* maybe only if troff found? */ + gripe (USAGE6); + + gripe (USAGE7); /* only for alt_systems */ + + gripe (USAGE8); + exit(1); +} + +static char short_opts[] = "B:C:H:xM:P:S:acdDfFhkKm:p:s:tvVwW?"; + +#ifndef NOGETOPT +#undef _GNU_SOURCE +#define _GNU_SOURCE +#include + +static const struct option long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "path", no_argument, NULL, 'w' }, + { "preformat", no_argument, NULL, 'F' }, + { NULL, 0, NULL, 0 } +}; +#endif + +/* + * Read options, return count. + */ +static int +get_options_from_argvec(int argc, char **argv, char **config_file, + char **manpath) { + char *s; + int c; + int optct = 0; + +#ifndef NOGETOPT + while ((c = getopt_long (argc, argv, short_opts, long_opts, NULL)) != -1){ +#else + while ((c = getopt (argc, argv, short_opts)) != -1) { +#endif + switch (c) { + case 'C': + no_privileges (); + if (config_file) + *config_file = my_strdup (optarg); + break; + case'F': + preformat = 1; + break; + case 'M': + if (manpath) + *manpath = my_strdup (optarg); + break; + case 'P': + pager = my_strdup (optarg); + break; + case 'B': + browser = my_strdup (optarg); + break; + case 'H': + htmlpager = my_strdup (optarg); + break; + case 'S': + colon_sep_section_list = my_strdup (optarg); + break; + case 's': + /* undocumented; compatibility with Sun */ + s = colon_sep_section_list = my_strdup (optarg); + while (*s) { + if (*s == ',') + *s = ':'; + s++; + } + break; + case 'a': + findall++; + break; + case 'c': + nocats++; + break; + case 'D': + debug++; + case 'd': + debug++; + break; + case 'f': + if (do_troff) + fatal (INCOMPAT, "-f", "-t"); + if (apropos) + fatal (INCOMPAT, "-f", "-k"); + if (print_where) + fatal (INCOMPAT, "-f", "-w"); + whatis++; + break; + case 'k': + if (do_troff) + fatal (INCOMPAT, "-k", "-t"); + if (whatis) + fatal (INCOMPAT, "-k", "-f"); + if (print_where) + fatal (INCOMPAT, "-k", "-w"); + apropos++; + break; + case 'K': + global_apropos++; + break; + case 'm': + alt_system++; + alt_system_name = my_strdup (optarg); + break; + /* or: gripe (NO_ALTERNATE); exit(1); */ + case 'p': + roff_directive = my_strdup (optarg); + break; + case 't': + if (apropos) + fatal (INCOMPAT, "-t", "-k"); + if (whatis) + fatal (INCOMPAT, "-t", "-f"); + if (print_where) + fatal (INCOMPAT, "-t", "-w"); + do_troff++; + break; + case 'v': + case 'V': + print_version(); + exit(0); + case 'W': + one_per_line++; + /* fall through */ + case 'w': + if (apropos) + fatal (INCOMPAT, "-w", "-k"); + if (whatis) + fatal (INCOMPAT, "-w", "-f"); + if (do_troff) + fatal (INCOMPAT, "-w", "-t"); + print_where++; + break; + case 'h': + case '?': + default: + usage(); + break; + } + optct++; + } + + return optct; +} + +static void +get_options_from_string(const char *s) { + char *s0, *ss; + int argct; + char **argvec; + int optindsv; + + if (!s || *s == 0) + return; + + /* In order to avoid having a list of options in two places, + massage the string so that it can be fed to getopt() */ + + s0 = my_strdup(s); + + /* count arguments */ + argct = 0; + ss = s0; + while (*ss) { + while (*ss == ' ') + ss++; + if (*ss) { + argct++; + while (*ss && *ss != ' ') + ss++; + } + } + + /* allocate argvec */ + argvec = (char **) my_malloc((argct+2)*sizeof(char *)); + argct = 0; + argvec[argct++] = "dummy"; + ss = s0; + while (*ss) { + while (*ss == ' ') + *ss++ = 0; + if (*ss) { + argvec[argct++] = ss; + while (*ss && *ss != ' ') + ss++; + } + } + argvec[argct] = 0; + + optindsv = optind; + optind = 1; + get_options_from_argvec(argct, argvec, NULL, NULL); + optind = optindsv; +} + +static void +mysetenv(const char *name, const char *value) { +#if defined(__sgi__) || defined(__sun__) || defined(sun) + int len = strlen(value)+1+strlen(value)+1; + char *str = my_malloc(len); + sprintf(str, "%s=%s", name, value); + putenv(str); +#else + setenv(name, value, 1); +#endif +} + +/* + * Get options from the command line and user environment. + * Also reads the configuration file. + */ + +void +man_getopt (int argc, char **argv) { + char *config_file = NULL; + char *manp = NULL; + int optct = 0; + + optct = get_options_from_argvec(argc, argv, &config_file, &manp); + + read_config_file (config_file); + + /* If no options were given and MANDEFOPTIONS is set, use that */ + if (optct == 0) { + const char *defopts = getval ("MANDEFOPTIONS"); + get_options_from_string(defopts); + } + + /* In case an explicit -P option was given, put it in the + environment for possible use with -k or -K. + Ignore errors (out of memory?) */ + + if (pager && (global_apropos || apropos || whatis)) + mysetenv("PAGER", pager); + + if (pager == NULL || *pager == '\0') + if (((pager = getenv ("MANPAGER")) == NULL) || (*pager == '\0')) + if (((pager = getenv ("PAGER")) == NULL) || (*pager == '\0')) + pager = getval ("PAGER"); + + if (debug) + gripe (PAGER_IS, pager); + + /* Ditto for BROWSER and -B */ + if (browser && (global_apropos || apropos || whatis)) + mysetenv("BROWSER", browser); + + if (browser == NULL || *browser == '\0') + if ((browser = getenv ("BROWSER")) == NULL) + browser = getval ("BROWSER"); + + if (debug) + gripe (BROWSER_IS, browser); + + /* Ditto for HTMLHTMLPAGER and -H */ + if (htmlpager && (global_apropos || apropos || whatis)) + mysetenv("HTMLPAGER", htmlpager); + + if (htmlpager == NULL || *htmlpager == '\0') + if ((htmlpager = getenv ("HTMLPAGER")) == NULL) + htmlpager = getval ("HTMLPAGER"); + + if (debug) + gripe (HTMLPAGER_IS, htmlpager); + + if (do_compress && !*getval ("COMPRESS")) { + if (debug) + gripe (NO_COMPRESS); + do_compress = 0; + } + + if (do_troff && !*getval ("TROFF")) { + gripe (NO_TROFF, configuration_file); + exit (1); + } + + opt_manpath = manp; /* do not yet expand manpath - + maybe it is not needed */ + + if (alt_system_name == NULL || *alt_system_name == '\0') + if ((alt_system_name = getenv ("SYSTEM")) != NULL) + alt_system_name = my_strdup (alt_system_name); + +} diff --git a/src/man-getopt.h b/src/man-getopt.h new file mode 100644 index 0000000..35ad35b --- /dev/null +++ b/src/man-getopt.h @@ -0,0 +1,6 @@ +extern void man_getopt (int argc, char **argv); + +extern int global_apropos; +extern int alt_system; +extern char *alt_system_name; +extern char *opt_manpath; diff --git a/src/man-iconv.c b/src/man-iconv.c new file mode 100644 index 0000000..9ce1236 --- /dev/null +++ b/src/man-iconv.c @@ -0,0 +1,163 @@ +/* + * From andy@pylesos.asp-linux.com.ua Tue Dec 3 14:17:38 2002 + * (polished, aeb) + * + * Manpages for a given language have a traditional character set. + * E.g., for Russian this is koi8r. + * If the user uses a different locale, throw in an invocation of iconv. + * + * Exports: + * const char *get_converter (const char *path); + * Conversion is to the users locale. Conversion is from the + * manpage charset, found in environment variables, or in + * PATH/.charset, where PATH is the directory (below that) containing + * the man page. + * + * TODO: adapt this to man.conf way + */ + +/* + * By default iconv is not used - this is the wrong interface. + * But if you want it, define USE_ICONV. + */ +#undef USE_ICONV + +#include /* NULL */ + +#if defined __GLIBC__ && __GLIBC__ >= 2 && defined USE_ICONV +#include /* getenv */ +#include /* access */ +#include /* strcmp */ +#include /* setlocale */ +#include /* nl_langinfo */ +#include /* iconv_open */ +#include "man-iconv.h" /* get_converter */ +#include "util.h" /* my_strdup */ +#include "man.h" /* debug */ + +static char * +find_iconv(void) { + static char *iconv_path = NULL; + static int inited = 0; + + if (!inited) { + char *file = getenv("MAN_ICONV_PATH"); + if (!file) + file = "/usr/bin/iconv"; + if (access(file, X_OK) == 0) + iconv_path = my_strdup(file); + inited = 1; + } + return iconv_path; +} + +static char * +iconv_extra_flags(void) { + static char *iconv_flags = "-cs"; + static int inited = 0; + + if (!inited) { + char *opt = getenv("MAN_ICONV_OPT"); + if (opt) + iconv_flags = my_strdup(opt); + inited = 1; + } + return iconv_flags; +} + +static char * +get_locale_charset (void) { + char *old_lc_ctype, *charset; + + if ((charset = getenv("MAN_ICONV_OUTPUT_CHARSET")) == NULL) { + old_lc_ctype = setlocale(LC_CTYPE, ""); + charset = nl_langinfo(CODESET); + setlocale(LC_CTYPE, old_lc_ctype); + } + return charset; +} + +static char * +get_man_charset (const char *path) { + char *charset_env, *file, *path2, *p; + FILE *f = NULL; + + charset_env = getenv("MAN_ICONV_INPUT_CHARSET"); + if (charset_env) + return charset_env; + + if (!path || !*path) + return NULL; + + if (debug) + fprintf(stderr, "get_man_charset: path=%s\n", path); + + /* strip trailing "/.." and try that directory first */ + path2 = my_strdup(path); + p = strrchr(path2, '/'); + if (p && !strcmp(p, "/..")) { + *p = 0; + file = my_xsprintf("%s/.charset", path2); + f = fopen(file, "r"); + free(file); + } + free(path2); + + /* if that fails, try path itself */ + if (f == NULL) { + file = my_xsprintf("%s/.charset", path); + f = fopen(file, "r"); + free(file); + } + + if (f) { + char charset[100], *p; + + fgets(charset, sizeof(charset), f); + fclose(f); + fprintf(stderr, "read %s\n", charset); + p = strchr(charset, '\n'); + if (p) { + *p = 0; + return my_strdup(charset); + } + } + return NULL; +} + +static int +is_conversion_supported (char *from, char *to) { + iconv_t cd; + + if (!from || !*from || !to || !*to || !strcmp(from,to)) + return 0; + if ((cd = iconv_open(to, from)) != (iconv_t) -1) { + iconv_close(cd); + return 1; + } + return 0; +} + +const char * +get_converter (const char *path) { + char *from, *to, *iconv_path; + + iconv_path = find_iconv(); + from = get_man_charset(path); + to = get_locale_charset(); + if (debug) + fprintf(stderr, "get_converter: iconv_path=%s from=%s to=%s\n", + iconv_path, from, to); + if (iconv_path && is_conversion_supported(from, to)) + return my_xsprintf("%s %s -f %s -t %s", + iconv_path, iconv_extra_flags(), from, to); + return NULL; +} +#else +#include "man-iconv.h" + +const char * +get_converter (const char *path) { + return NULL; +} +#endif /* __GLIBC__ && __GLIBC__ >= 2 */ diff --git a/src/man-iconv.h b/src/man-iconv.h new file mode 100644 index 0000000..40c9ad8 --- /dev/null +++ b/src/man-iconv.h @@ -0,0 +1 @@ +extern const char *get_converter (const char *path); diff --git a/src/man.c b/src/man.c new file mode 100644 index 0000000..62eaee4 --- /dev/null +++ b/src/man.c @@ -0,0 +1,1366 @@ +/* + * man.c + * + * Copyright (c) 1990, 1991, John W. Eaton. + * + * You may distribute under the terms of the GNU General Public + * License as specified in the file COPYING that comes with the man + * distribution. + * + * John W. Eaton + * jwe@che.utexas.edu + * Department of Chemical Engineering + * The University of Texas at Austin + * Austin, Texas 78712 + * + * Some manpath, compression and locale related changes - aeb - 940320 + * Some suid related changes - aeb - 941008 + * Some more fixes, Pauline Middelink & aeb, Oct 1994 + * man -K: aeb, Jul 1995 + * Split off of manfile for man2html, aeb, New Year's Eve 1997 + */ + +#include +#include +#include +#include +#include +#include /* for chmod */ +#include +#include +#include +#include +#ifdef TERMIOS_HEADER +#include +#endif + +#ifndef R_OK +#define R_OK 4 +#endif + +extern char *index (const char *, int); /* not always in */ +extern char *rindex (const char *, int); /* not always in */ + +#include "defs.h" +#include "gripes.h" +#include "man.h" +#include "manfile.h" +#include "manpath.h" +#include "man-config.h" +#include "man-getopt.h" +#include "man-iconv.h" +#include "to_cat.h" +#include "util.h" +#include "glob.h" +#include "different.h" +#include "man-iconv.h" + +#define SIZE(x) (sizeof(x)/sizeof((x)[0])) + +const char *progname; +const char *pager, *browser, *htmlpager; +char *colon_sep_section_list; +char *roff_directive; +char *dohp = 0; +int do_irix; +int do_win32; +int apropos; +int whatis; +int nocats; /* set by -c option: do not use cat page */ + /* this means that cat pages must not be used, + perhaps because the user knows they are + old or corrupt or so */ +int can_use_cache; /* output device is a tty, width 80 */ + /* this means that the result may be written + in /var/cache, and may be read from there */ +int findall; +int print_where; +int one_per_line; +int do_troff; +int preformat; +int debug; +int fhs; +int fsstnd; +int noautopath; +int nocache; +static int is_japanese; +static char *language; +static char **section_list; + +#ifdef DO_COMPRESS +int do_compress = 1; +#else +int do_compress = 0; +#endif + +#define BUFSIZE 8192 + +/* + * Try to determine the line length to use. + * Preferences: 1. MANWIDTH, 2. ioctl, 3. COLUMNS, 4. 80 + * + * joey, 950902 + */ + +#include + +int line_length = 80; +int ll = 0; + +static void +get_line_length(void){ + char *cp; + int width; + + if (preformat) { + line_length = 80; + return; + } + if ((cp = getenv ("MANWIDTH")) != NULL && (width = atoi(cp)) > 0) { + line_length = width; + return; + } +#ifdef TIOCGWINSZ + if (isatty(0) && isatty(1)) { /* Jon Tombs */ + struct winsize wsz; + + if(ioctl(0, TIOCGWINSZ, &wsz)) + perror("TIOCGWINSZ failed\n"); + else if(wsz.ws_col) { + line_length = wsz.ws_col; + return; + } + } +#endif + if ((cp = getenv ("COLUMNS")) != NULL && (width = atoi(cp)) > 0) + line_length = width; + else + line_length = 80; +} + +static int +setll(void) { + return + (!do_troff && (line_length < 66 || line_length > 80)) ? + line_length*9/10 : 0; +} + +/* People prefer no page headings in their man screen output; + now ".pl 0" has a bad effect on .SH etc, so we need ".pl N" + for some large number N, like 1100i (a hundred pages). */ +#define VERY_LONG_PAGE "1100i" + +static char * +setpl(void) { + char *pl; + if (do_troff) + return NULL; + if (preformat) + pl = VERY_LONG_PAGE; + else + if ((pl = getenv("MANPL")) == 0) { + if (isatty(0) && isatty(1)) + pl = VERY_LONG_PAGE; + else + pl = "11i"; /* old troff default */ + } + return pl; +} + +/* + * Check to see if the argument is a valid section number. If the + * first character of name is a numeral, or 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 char * +is_section (char *name) { + char **vs; + + /* 3Xt may be a section, but 3DBorder is a man page */ + if (isdigit (name[0]) && !isdigit (name[1]) && strlen(name) < 5) + return my_strdup (name); + + for (vs = section_list; *vs != NULL; vs++) + if (strcmp (*vs, name) == 0) + return my_strdup (name); + + return NULL; +} + + +static void +remove_file (char *file) { + int i; + + i = unlink (file); + + if (debug) { + if (i) + perror(file); + else + gripe (UNLINKED, file); + } +} + +static void +remove_other_catfiles (const char *catfile) { + char *pathname; + char *t; + char **gf; + int offset; + + pathname = my_strdup(catfile); + t = rindex(pathname, '.'); + if (t == NULL || strcmp(t, getval("COMPRESS_EXT"))) + return; + offset = t - pathname; + strcpy(t, "*"); + gf = glob_filename (pathname); + + if (gf != (char **) -1 && gf != NULL) { + for ( ; *gf; gf++) { + /* + * Only remove files with a known extension, like .Z + * (otherwise we might kill a lot when called with + * catfile = ".gz" ...) + */ + if (strlen (*gf) <= offset) { + if (strlen (*gf) == offset) /* uncompressed version */ + remove_file (*gf); + continue; + } + + if (!strcmp (*gf + offset, getval("COMPRESS_EXT"))) + continue; + + if (get_expander (*gf) != NULL) + remove_file (*gf); + } + } +} + +/* + * Simply display the preformatted page. + */ +static int +display_cat_file (const char *file) { + int found; + + if (preformat) + return 1; /* nothing to do - preformat only */ + + found = 0; + + if (access (file, R_OK) == 0 && different_cat_file(file)) { + char *command = NULL; + const char *expander = get_expander (file); + + if (expander != NULL && expander[0] != 0) { + if (isatty(1)) + command = my_xsprintf("%s %S | %s", expander, file, pager); + else + command = my_xsprintf("%s %S", expander, file); + } else { + if (isatty(1)) { + command = my_xsprintf("%s %S", pager, file); + } else { + const char *cat = getval("CAT"); + command = my_xsprintf("%s %S", cat[0] ? cat : "cat", file); + } + } + found = !do_system_command (command, 0); + } + return found; +} + +/* + * Simply display the preformatted page. + */ +static int +display_html_file (const char *file) { + int found; + + found = 0; + + if (access (file, R_OK) == 0 && different_cat_file(file)) { + char *command = NULL; + + if (isatty(1)) { + command = my_xsprintf("%s %S", browser, file); + } else { + command = my_xsprintf("%s %S", htmlpager, file); + } + found = !do_system_command (command, 0); + } + return found; + + return 1; +} + +/* + * Try to find the ultimate source file. If the first line of the + * current file is not of the form + * + * .so man3/printf.3s + * + * the input file name is returned. + * + * For /cd/usr/src/usr.bin/util-linux-1.5/mount/umount.8.gz + * (which contains `.so man8/mount.8') + * we return /cd/usr/src/usr.bin/util-linux-1.5/mount/mount.8.gz . + * + * For /usr/man/man3/TIFFScanlineSize.3t + * (which contains `.so TIFFsize.3t') + * we return /usr/man/man3/TIFFsize.3t . + */ +static const char * +ultimate_source (const char *name0) { + FILE *fp; + char *name; + const char *expander; + int expfl = 0; + char *fgr; + char *beg; + char *end; + char *cp; + char buf[BUFSIZE]; + static char ultname[BUFSIZE]; + + if (strlen(name0) >= sizeof(ultname)) + return name0; + strcpy(ultname, name0); + name = ultname; + +again: + expander = get_expander (name); + if (expander && *expander) { + char *command; + + command = my_xsprintf ("%s %S", expander, name); + fp = my_popen (command, "r"); + if (fp == NULL) { + perror("popen"); + gripe (EXPANSION_FAILED, command); + return (NULL); + } + fgr = fgets (buf, sizeof(buf), fp); + + #ifdef __APPLE__ + /* Man 1.5x randomly freezes under Mac OS X 10.4.7 when the + man page is compressed (with either gzip or bzip2), and + only with large pages. + The freeze occurs at the pclose function, and a ps shows + that gunzip is still running. + + The problem is the specification of pclose(): The pclose() + function waits for the associated process to terminate + and returns the exit status of the command as returned by + wait4(). + + So, if gunzip is started to look at the start of a file and + the file is larger than the buffer used by stdio then the + first read does not read everything, and the pclose hangs. */ + + /* Reading loop insures lockup cannot occur */ + char dummy[BUFSIZE]; + while (fgets (dummy,sizeof(dummy),fp) ); + #endif // __APPLE__ + + pclose (fp); + expfl = 1; + } else { + fp = fopen (name, "r"); + if (fp == NULL && expfl) { + char *extp = rindex (name0, '.'); + if (extp && *extp && strlen(name)+strlen(extp) < BUFSIZE) { + strcat(name, extp); + fp = fopen (name, "r"); + } + } + /* + * Some people have compressed man pages, but uncompressed + * .so files - we could glob for all possible extensions, + * for now: only try .gz + */ + else if (fp == NULL && get_expander(".gz") && + strlen(name)+strlen(".gz") < BUFSIZE) { + strcat(name, ".gz"); + fp = fopen (name, "r"); + } + + if (fp == NULL) { + perror("fopen"); + gripe (OPEN_ERROR, name); + return (NULL); + } + fgr = fgets (buf, sizeof(buf), fp); + fclose (fp); + } + + if (fgr == NULL) { + perror("fgets"); + gripe (READ_ERROR, name); + return (NULL); + } + + if (strncmp(buf, ".so", 3)) + return (my_strdup(name)); + + beg = buf+3; + while (*beg == ' ' || *beg == '\t') + beg++; + + end = beg; + while (*end != ' ' && *end != '\t' && *end != '\n' && *end != '\0') + end++; /* note that buf is NUL-terminated */ + *end = '\0'; + + /* If name ends in path/manx/foo.9x then use path, otherwise + try same directory. */ + if ((cp = rindex(name, '/')) == NULL) /* very strange ... */ + return 0; + *cp = 0; + + /* allow "man ./foo.3" where foo.3 contains ".so man2/bar.2" */ + if ((cp = rindex(name, '/')) != NULL && !strcmp(cp+1, ".")) + *cp = 0; + + /* In all cases, the new name will be something from name + followed by something from beg. */ + if (strlen(name) + strlen(beg) + 1 >= BUFSIZ) + return 0; /* very long names, ignore */ + + if (!index(beg, '/')) { + /* strange.. try same directory as the .so file */ + strcat(name, "/"); + strcat(name, beg); + } else if((cp = rindex(name, '/')) != NULL && !strncmp(cp+1, "man", 3)) { + strcpy(cp+1, beg); + } else if((cp = rindex(beg, '/')) != NULL) { + strcat(name, cp); + } else { + strcat(name, "/"); + strcat(name, beg); + } + + goto again; +} + +static void +add_directive (const char *d, const char *file, char *buf, int buflen) { + if ((d = getval(d)) != 0 && *d) { + if (*buf == 0) { + if (strlen(d) + strlen(file) + 2 > buflen) + return; + strcpy (buf, d); + strcat (buf, " "); + strcat (buf, file); + } else { + if (strlen(d) + strlen(buf) + 4 > buflen) + return; + strcat (buf, " | "); + strcat (buf, d); + } + } +} + +static int +is_lang_page (char *lang, const char *file) { + char lang_path[16] = ""; + + snprintf(lang_path, sizeof(lang_path), "/%s/", lang); + if (strstr(file, lang_path)) + return 1; + if (strlen(lang) > 2) { + lang_path[3] = '/'; + lang_path[4] = 0; + if (strstr(file, lang_path)) + return 1; + } + return 0; +} + +static int +parse_roff_directive (char *cp, const char *file, char *buf, int buflen) { + char c; + int tbl_found = 0; + int use_jroff; + + use_jroff = (is_japanese && + (strstr(file, "/jman/") || is_lang_page(language, file))); + + while ((c = *cp++) != '\0') { + switch (c) { + case 'e': + if (debug) + gripe (FOUND_EQN); + add_directive((do_troff ? "EQN" : use_jroff ? "JNEQN": "NEQN"), + file, buf, buflen); + break; + + case 'g': + if (debug) + gripe (FOUND_GRAP); + add_directive ("GRAP", file, buf, buflen); + break; + + case 'p': + if (debug) + gripe (FOUND_PIC); + add_directive ("PIC", file, buf, buflen); + break; + + case 't': + if (debug) + gripe (FOUND_TBL); + tbl_found++; + add_directive ("TBL", file, buf, buflen); + break; + + case 'v': + if (debug) + gripe (FOUND_VGRIND); + add_directive ("VGRIND", file, buf, buflen); + break; + + case 'r': + if (debug) + gripe (FOUND_REFER); + add_directive ("REFER", file, buf, buflen); + break; + + case ' ': + case '\t': + case '\n': + goto done; + + default: + return -1; + } + } + +done: + if (*buf == 0) + return 1; + + add_directive (do_troff ? "TROFF" : use_jroff ? "JNROFF" : "NROFF", + "", buf, buflen); + + if (tbl_found && !do_troff && *getval("COL")) + add_directive ("COL", "", buf, buflen); + + return 0; +} + +static char * +eos(char *s) { + while(*s) s++; + return s; +} + +/* + * Create command to format FILE, in the directory PATH/manX + */ +static char * +make_roff_command (const char *path, const char *file) { + FILE *fp; + static char buf [BUFSIZE]; + char line [BUFSIZE], bufh [BUFSIZE], buft [BUFSIZE]; + int status, ll; + char *cp, *fgr, *pl; + char *command = ""; + const char *expander; + const char *converter; + + /* if window size differs much from 80, try to adapt */ + /* (but write only standard formatted files to the cat directory, + see can_use_cache) */ + ll = setll(); + pl = setpl(); + if (ll && debug) + gripe (NO_CAT_FOR_NONSTD_LL); + + expander = get_expander (file); + converter = get_converter (path); + + /* head */ + bufh[0] = 0; + if (ll || pl) { + /* some versions of echo do not accept the -e flag, + so we just use two echo calls when needed */ + strcat(bufh, "("); + if (ll) { + /* + * We should set line length and title line length. + * However, a .lt command here fails, only + * .ev 1; .lt ...; .ev helps for my version of groff. + * The LL assignment is needed by the mandoc macros. + */ + sprintf(eos(bufh), "echo \".ll %d.%di\"; ", ll/10, ll%10); + sprintf(eos(bufh), "echo \".nr LL %d.%di\"; ", ll/10, ll%10); +#if 0 + sprintf(eos(bufh), "echo \".lt %d.%di\"; ", ll/10, ll%10); +#endif + } + if (pl) + sprintf(eos(bufh), "echo \".pl %.128s\"; ", pl); + } + + /* tail */ + buft[0] = 0; + if (ll || pl) { + if (pl && !strcmp(pl, VERY_LONG_PAGE)) + /* At end of the nroff source, set the page length to + the current position plus 10 lines. This plus setpl() + gives us a single page that just contains the whole + man page. (William Webber, wew@cs.rmit.edu.au) */ + strcat(buft, "; echo \".\\\\\\\"\"; echo \".pl \\n(nlu+10\""); +#if 0 + /* In case this doesnt work for some reason, + michaelkjohnson suggests: I've got a simple + awk invocation that I throw into the pipeline: */ + + awk 'BEGIN {RS="\n\n\n\n*"} /.*/ {print}' +#endif + strcat(buft, ")"); + } + + if (expander && *expander) { + if (converter && *converter) + command = my_xsprintf("%s%s '%S' | %s%s", + bufh, expander, file, converter, buft); + else + command = my_xsprintf("%s%s '%S'%s", + bufh, expander, file, buft); + } else if (ll || pl) { + const char *cat = getval("CAT"); + if (!cat || !*cat) + cat = "cat"; + + if (converter && *converter) + command = my_xsprintf("%s%s '%S' | %s%s", + bufh, cat, file, converter, buft); + else + command = my_xsprintf("%s%s '%S'%s", + bufh, cat, file, buft); + } + + if (strlen(command) >= sizeof(buf)) + exit(1); + strcpy(buf, command); + + if (roff_directive != NULL) { + if (debug) + gripe (ROFF_FROM_COMMAND_LINE); + + status = parse_roff_directive (roff_directive, file, + buf, sizeof(buf)); + + if (status == 0) + return buf; + + if (status == -1) + gripe (ROFF_CMD_FROM_COMMANDLINE_ERROR); + } + + if (expander && *expander) { + char *cmd = my_xsprintf ("%s %S", expander, file); + fp = my_popen (cmd, "r"); + if (fp == NULL) { + perror("popen"); + gripe (EXPANSION_FAILED, cmd); + return (NULL); + } + fgr = fgets (line, sizeof(line), fp); + pclose (fp); + } else { + fp = fopen (file, "r"); + if (fp == NULL) { + perror("fopen"); + gripe (OPEN_ERROR, file); + return (NULL); + } + fgr = fgets (line, sizeof(line), fp); + fclose (fp); + } + + if (fgr == NULL) { + perror("fgets"); + gripe (READ_ERROR, file); + return (NULL); + } + + cp = &line[0]; + if (*cp++ == '\'' && *cp++ == '\\' && *cp++ == '"' && *cp++ == ' ') { + if (debug) + gripe (ROFF_FROM_FILE, file); + + status = parse_roff_directive (cp, file, buf, sizeof(buf)); + + if (status == 0) + return buf; + + if (status == -1) + gripe (ROFF_CMD_FROM_FILE_ERROR, file); + } + + if ((cp = getenv ("MANROFFSEQ")) != NULL) { + if (debug) + gripe (ROFF_FROM_ENV); + + status = parse_roff_directive (cp, file, buf, sizeof(buf)); + + if (status == 0) + return buf; + + if (status == -1) + gripe (MANROFFSEQ_ERROR); + } + + if (debug) + gripe (USING_DEFAULT); + + (void) parse_roff_directive ("t", file, buf, sizeof(buf)); + + return buf; +} + +/* + * Try to format the man page and create a new formatted file. Return + * 1 for success and 0 for failure. + */ +static int +make_cat_file (const char *path, const char *man_file, const char *cat_file) { + int mode; + FILE *fp; + char *roff_command; + char *command = NULL; + struct stat statbuf; + + /* _Before_ first, make sure we will write to a regular file. */ + if (stat(cat_file, &statbuf) == 0) { + if(!S_ISREG(statbuf.st_mode)) { + if (debug) + gripe (CAT_OPEN_ERROR, cat_file); + return 0; + } + } + + /* First make sure we can write the file; create an empty file. */ + /* If we are suid it must get mode 0666. */ + if ((fp = fopen (cat_file, "w")) == NULL) { + if (errno == ENOENT) /* directory does not exist */ + return 0; + + /* If we cannot write the file, maybe we can delete it */ + if(unlink (cat_file) != 0 || (fp = fopen (cat_file, "w")) == NULL) { + if (errno == EROFS) /* possibly a CDROM */ + return 0; + if (debug) + gripe (CAT_OPEN_ERROR, cat_file); + if (!suid) + return 0; + + /* maybe the real user can write it */ + /* note: just doing "> %s" gives the wrong exit status */ + command = my_xsprintf("cp /dev/null %S 2>/dev/null", cat_file); + if (do_system_command(command, 1)) { + if (debug) + gripe (USER_CANNOT_OPEN_CAT); + return 0; + } + if (debug) + gripe (USER_CAN_OPEN_CAT); + } + } else { + /* we can write it - good */ + fclose (fp); + + /* but maybe the real user cannot - let's allow everybody */ + /* the mode is reset below */ + if (suid) { + if (chmod (cat_file, 0666)) { + /* probably we are sgid but not owner; + just delete the file and create it again */ + if(unlink(cat_file) != 0) { + command = my_xsprintf("rm %S", cat_file); + (void) do_system_command (command, 1); + } + if ((fp = fopen (cat_file, "w")) != NULL) + fclose (fp); + } + } + } + + roff_command = make_roff_command (path, man_file); + if (roff_command == NULL) + return 0; + if (do_compress) + /* The cd is necessary, because of .so commands, + like .so man1/bash.1 in bash_builtins.1. + But it changes the meaning of man_file and cat_file, + if these are not absolute. */ + + command = my_xsprintf("(cd %S && %s | %S > %S)", path, + roff_command, getval("COMPRESS"), cat_file); + else + command = my_xsprintf ("(cd %S && %s > %S)", path, + roff_command, cat_file); + + /* + * Don't let the user interrupt the system () call and screw up + * the formatted man page if we're not done yet. + */ + signal (SIGINT, SIG_IGN); + + gripe (PLEASE_WAIT); + + if (!do_system_command (command, 0)) { + /* success */ + mode = ((ruid != euid) ? 0644 : (rgid != egid) ? 0464 : 0444); + if(chmod (cat_file, mode) != 0 && suid) { + command = my_xsprintf ("chmod 0%o %S", mode, cat_file); + (void) do_system_command (command, 1); + } + /* be silent about the success of chmod - it is not important */ + if (debug) + gripe (CHANGED_MODE, cat_file, mode); + } else { + /* something went wrong - remove garbage */ + if(unlink(cat_file) != 0 && suid) { + command = my_xsprintf ("rm %S", cat_file); + (void) do_system_command (command, 1); + } + } + + signal (SIGINT, SIG_DFL); + + return 1; +} + +static int +display_man_file(const char *path, const char *man_file) { + char *roff_command; + char *command; + + if (!different_man_file (man_file)) + return 0; + roff_command = make_roff_command (path, man_file); + if (roff_command == NULL) + return 0; + if (do_troff) + command = my_xsprintf ("(cd \"%S\" && %s)", path, roff_command); + else + command = my_xsprintf ("(cd \"%S\" && %s | %s)", path, + roff_command, pager); + + return !do_system_command (command, 0); +} + +/* + * make and display the cat file - return 0 if something went wrong + */ +static int +make_and_display_cat_file (const char *path, const char *man_file) { + const char *cat_file; + const char *ext; + int status; + int standards; + + ext = (do_compress ? getval("COMPRESS_EXT") : 0); + + standards = (fhs ? FHS : 0) | (fsstnd ? FSSTND : 0) | (dohp ? DO_HP : 0); + + if ((cat_file = convert_to_cat(man_file, ext, standards)) == NULL) + return 0; + + if (debug) + gripe (PROPOSED_CATFILE, cat_file); + + /* + * If cat_file exists, check whether it is more recent. + * Otherwise, check for other cat files (maybe there are + * old .Z files that should be removed). + */ + + status = ((nocats | preformat) ? -2 : is_newer (man_file, cat_file)); + if (debug) + gripe (IS_NEWER_RESULT, status); + if (status == -1 || status == -3) { + /* what? man_file does not exist anymore? */ + gripe (CANNOT_STAT, man_file); + return(0); + } + + if (status != 0 || access (cat_file, R_OK) != 0) { + /* + * Cat file is out of date (status = 1) or does not exist or is + * empty or is to be rewritten (status = -2) or is unreadable. + * Try to format and save it. + */ + if (print_where) { + printf ("%s\n", man_file); + return 1; + } + + if (!make_cat_file (path, man_file, cat_file)) + return 0; + + /* + * If we just created this cat file, unlink any others. + */ + if (status == -2 && do_compress) + remove_other_catfiles(cat_file); + } else { + /* + * Formatting not necessary. Cat file is newer than source + * file, or source file is not present but cat file is. + */ + if (print_where) { + if (one_per_line) { + /* addition by marty leisner - leisner@sdsp.mc.xerox.com */ + printf("%s\n", cat_file); + printf("%s\n", man_file); + } else + printf ("%s (<-- %s)\n", cat_file, man_file); + return 1; + } + } + (void) display_cat_file (cat_file); + return 1; +} + +/* + * Try to format the man page source and save it, then display it. If + * that's not possible, try to format the man page source and display + * it directly. + */ +static int +format_and_display (const char *man_file) { + const char *path; + + if (access (man_file, R_OK) != 0) + return 0; + + path = mandir_of(man_file); + if (path == NULL) + return 0; + + /* first test for contents .so man1/xyzzy.1 */ + /* (in that case we do not want to make a cat file identical + to cat1/xyzzy.1) */ + man_file = ultimate_source (man_file); + if (man_file == NULL) + return 0; + + if (do_troff) { + char *command; + char *roff_command = make_roff_command (path, man_file); + + if (roff_command == NULL) + return 0; + + command = my_xsprintf("(cd \"%S\" && %s)", path, roff_command); + return !do_system_command (command, 0); + } + + if (can_use_cache && make_and_display_cat_file (path, man_file)) + return 1; + + /* line length was wrong or could not display cat_file */ + if (print_where) { + printf ("%s\n", man_file); + return 1; + } + + return display_man_file (path, man_file); +} + +/* + * 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. + * + * Note that globbing is necessary also if the section is given, + * since a preformatted man page might be compressed. + * + */ +static int +man (const char *name, const char *section) { + int found, type, flags; + struct manpage *mp; + + found = 0; + + /* allow man ./manpage for formatting explicitly given man pages */ + if (index(name, '/')) { + char fullname[BUFSIZE]; + char fullpath[BUFSIZE]; + char *path; + char *cp; + FILE *fp = fopen(name, "r"); + + if (!fp) { + perror(name); + return 0; + } + fclose (fp); + if (*name != '/' && getcwd(fullname, sizeof(fullname)) + && strlen(fullname) + strlen(name) + 3 < sizeof(fullname)) { + strcat (fullname, "/"); + strcat (fullname, name); + } else if (strlen(name) + 2 < sizeof(fullname)) { + strcpy (fullname, name); + } else { + fprintf(stderr, "%s: name too long\n", name); + return 0; + } + + strcpy (fullpath, fullname); + if ((cp = rindex(fullpath, '/')) != NULL + && cp-fullpath+4 < sizeof(fullpath)) { + strcpy(cp+1, ".."); + path = fullpath; + } else + path = "."; + + name = ultimate_source (fullname); + if (!name) + return 0; + + if (print_where) { + printf("%s\n", name); + return 1; + } + return display_man_file (path, name); + } + + fflush (stdout); + init_manpath(); + + can_use_cache = nocache ? 0 : (preformat || print_where || + (isatty(0) && isatty(1) && !setll())); + + if (do_troff) { + const char *t = getval("TROFF"); + if (!t || !*t) + return 0; /* don't know how to format */ + type = TYPE_MAN; + } else { + const char *n = getval("NROFF"); + type = 0; + if (can_use_cache) + type |= TYPE_CAT; + if (n && *n) + type |= TYPE_MAN; + if (fhs || fsstnd) + type |= TYPE_SCAT; + + n = getval("BROWSER"); + if (n && *n) + type |= TYPE_HTML; + } + + flags = type; + if (!findall) + flags |= ONLY_ONE; + if (fsstnd) + flags |= FSSTND; + else if (fhs) + flags |= FHS; + if (dohp) + flags |= DO_HP; + if (do_irix) + flags |= DO_IRIX; + if (do_win32) + flags |= DO_WIN32; + + mp = manfile(name, section, flags, section_list, mandirlist, + convert_to_cat); + found = 0; + while (mp) { + if (mp->type == TYPE_MAN) { + found = format_and_display(mp->filename); + } else if (mp->type == TYPE_CAT || mp->type == TYPE_SCAT) { + if (print_where) { + printf ("%s\n", mp->filename); + found = 1; + } else + found = display_cat_file(mp->filename); + } else if (mp->type == TYPE_HTML) { + if (print_where) { + printf ("%s\n", mp->filename); + found = 1; + } else + found = display_html_file(mp->filename); + } else + /* internal error */ + break; + if (found && !findall) + break; + mp = mp->next; + } + return found; +} + +static char ** +get_section_list (void) { + int i; + const char *p; + char *end; + static char *tmp_section_list[100]; + + if (colon_sep_section_list == NULL) { + if ((p = getenv ("MANSECT")) == NULL) + p = getval ("MANSECT"); + colon_sep_section_list = my_strdup (p); + } + + i = 0; + for (p = colon_sep_section_list; ; p = end+1) { + if ((end = strchr (p, ':')) != NULL) + *end = '\0'; + + tmp_section_list[i++] = my_strdup (p); + + if (end == NULL || i+1 == SIZE(tmp_section_list)) + break; + } + + tmp_section_list [i] = NULL; + return tmp_section_list; +} + +/* return 0 when all was OK */ +static int +do_global_apropos (char *name, char *section) { + char **dp, **gf; + char *pathname; + char *command; + int status, res; + + status = 0; + init_manpath(); + if (mandirlist) + for (dp = mandirlist; *dp; dp++) { + if (debug) + gripe(SEARCHING, *dp); + pathname = my_xsprintf("%s/man%s/*", *dp, section ? section : "*"); + gf = glob_filename (pathname); + free(pathname); + + if (gf != (char **) -1 && gf != NULL) { + for( ; *gf; gf++) { + const char *expander = get_expander (*gf); + if (expander) + command = my_xsprintf("%s %S | grep '%Q'" + "> /dev/null 2> /dev/null", + expander, *gf, name); + else + command = my_xsprintf("grep '%Q' %S" + "> /dev/null 2> /dev/null", + name, *gf); + res = do_system_command (command, 1); + status |= res; + free (command); + if (res == 0) { + if (print_where) + printf("%s\n", *gf); + else { + /* should read LOCALE, but libc 4.6.27 doesn't + seem to handle LC_RESPONSE yet */ + int answer, c; + char path[BUFSIZE]; + + printf("%s? [ynq] ", *gf); + fflush(stdout); + answer = c = getchar(); + while (c != '\n' && c != EOF) + c = getchar(); + if(index("QqXx", answer)) + exit(0); + if(index("YyJj", answer)) { + char *ri; + + strcpy(path, *gf); + ri = rindex(path, '/'); + if (ri) + *ri = 0; + format_and_display(*gf); + } + } + } + } + } + } + return status; +} + +/* Special code for Japanese (to pick jnroff instead of nroff, etc.) */ +static void +setlang(void) { + char *lang; + + /* We use getenv() instead of setlocale(), because of + glibc 2.1.x security policy for SetUID/SetGID binary. */ + if ((lang = getenv("LANG")) == NULL && + (lang = getenv("LC_ALL")) == NULL && + (lang = getenv("LC_CTYPE")) == NULL) + /* nothing */; + + language = lang; + is_japanese = (lang && strncmp(lang, "ja", 2) == 0); +} + +/* + * Handle the apropos option. Cheat by using another program. + */ +static int +do_apropos (char *name) { + char *command; + + command = my_xsprintf("'%s' '%Q'", getval("APROPOS"), name); + return do_system_command (command, 0); +} + +/* + * Handle the whatis option. Cheat by using another program. + */ +static int +do_whatis (char *name) { + char *command; + + command = my_xsprintf("'%s' '%Q'", getval("WHATIS"), name); + return do_system_command (command, 0); +} + +int +main (int argc, char **argv) { + int status = 0; + char *nextarg; + char *tmp; + char *section = 0; + +#ifdef __CYGWIN__ + extern int optind; +#endif + + +#if 0 + { + /* There are no known cases of buffer overflow caused by + excessively long environment variables. In case you find one, + the simplistic way to fix is to enable this stopgap. */ + char *s; +#define CHECK(p,l) s=getenv(p); if(s && strlen(s)>(l)) { fprintf(stderr, "ERROR: Environment variable %s too long!\n", p); exit(1); } + CHECK("LANG", 32); + CHECK("LANGUAGE", 128); + CHECK("LC_MESSAGES", 128); + CHECK("MANPAGER", 128); + CHECK("MANPL", 128); + CHECK("MANROFFSEQ", 128); + CHECK("MANSECT", 128); + CHECK("MAN_HP_DIREXT", 128); + CHECK("PAGER", 128); + CHECK("SYSTEM", 64); + CHECK("BROWSER", 64); + CHECK("HTMLPAGER", 64); + /* COLUMNS, LC_ALL, LC_CTYPE, MANPATH, MANWIDTH, MAN_IRIX_CATNAMES, + MAN_ICONV_PATH, MAN_ICONV_OPT, MAN_ICONV_INPUT_CHARSET, + MAN_ICONV_OUTPUT_CHARSET, NLSPATH, PATH */ + } +#endif + + +#ifndef __FreeBSD__ + /* Slaven Rezif: FreeBSD-2.2-SNAP does not recognize LC_MESSAGES. */ + setlocale(LC_CTYPE, ""); /* used anywhere? maybe only isdigit()? */ + setlocale(LC_MESSAGES, ""); +#endif + + /* No doubt we'll need some generic language code here later. + For the moment only Japanese support. */ + setlang(); + + /* Handle /usr/man/man1.Z/name.1 nonsense from HP */ + dohp = getenv("MAN_HP_DIREXT"); /* .Z */ + + /* Handle ls.z (instead of ls.1.z) cat page naming from IRIX */ + if (getenv("MAN_IRIX_CATNAMES")) + do_irix = 1; + + /* Handle lack of ':' in NTFS file names */ +#if defined(_WIN32) || defined(__CYGWIN__) + do_win32 = 1; +#endif + + progname = mkprogname (argv[0]); + + get_permissions (); + get_line_length(); + + /* + * read command line options and man.conf + */ + man_getopt (argc, argv); + + /* + * manpath or man --path or man -w will only print the manpath + */ + if (!strcmp (progname, "manpath") || (optind == argc && print_where)) { + init_manpath(); + prmanpath(); + exit(0); + } + + if (optind == argc) + gripe(NO_NAME_NO_SECTION); + + section_list = get_section_list (); + + while (optind < argc) { + nextarg = argv[optind++]; + + /* is_section correctly accepts 3Xt as section, but also 9wm, + so we should not believe is_section() for the last arg. */ + tmp = is_section (nextarg); + if (tmp && optind < argc) { + section = tmp; + if (debug) + gripe (SECTION, section); + continue; + } + + if (global_apropos) + status = !do_global_apropos (nextarg, section); + else if (apropos) + status = !do_apropos (nextarg); + else if (whatis) + status = !do_whatis (nextarg); + else { + status = man (nextarg, section); + + if (status == 0) { + if (section) + gripe (NO_SUCH_ENTRY_IN_SECTION, nextarg, section); + else + gripe (NO_SUCH_ENTRY, nextarg); + } + } + + /* reset duplicate search - + fixes Fedora#542852 "man cut cut throws an error" */ + free_catman_filelists (); + } + return status ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/man.conf b/src/man.conf new file mode 100644 index 0000000..88073e4 --- /dev/null +++ b/src/man.conf @@ -0,0 +1,144 @@ +# +# Generated automatically from man.conf.in by the +# configure script. +# +# man.conf from man-1.6g +# +# For more information about this file, see the man pages man(1) +# and man.conf(5). +# +# This file is read by man to configure the default manpath (also used +# when MANPATH contains an empty substring), to find out where the cat +# pages corresponding to given man pages should be stored, +# and to map each PATH element to a manpath element. +# It may also record the pathname of the man binary. [This is unused.] +# The format is: +# +# MANBIN pathname +# MANPATH manpath_element [corresponding_catdir] +# MANPATH_MAP path_element manpath_element +# +# If no catdir is given, it is assumed to be equal to the mandir +# (so that this dir has both man1 etc. and cat1 etc. subdirs). +# This is the traditional Unix setup. +# Certain versions of the FSSTND recommend putting formatted versions +# of /usr/.../man/manx/page.x into /var/catman/.../catx/page.x. +# The keyword FSSTND will cause this behaviour. +# Certain versions of the FHS recommend putting formatted versions of +# /usr/.../share/man/[locale/]manx/page.x into +# /var/cache/man/.../[locale/]catx/page.x. +# The keyword FHS will cause this behaviour (and overrides FSSTND). +# Explicitly given catdirs override. +# +# FSSTND +FHS +# +# This file is also read by man in order to find how to call nroff, less, etc., +# and to determine the correspondence between extensions and decompressors. +# +# MANBIN /usr/local/bin/man +# +# Every automatically generated MANPATH includes these fields +# +MANPATH /usr/man +MANPATH /usr/share/man +MANPATH /usr/local/man +MANPATH /usr/local/share/man +MANPATH /usr/X11R6/man +# +# Uncomment if you want to include one of these by default +# +# MANPATH /opt/*/man +# MANPATH /usr/lib/*/man +# MANPATH /usr/share/*/man +# MANPATH /usr/kerberos/man +# +# Set up PATH to MANPATH mapping +# +# If people ask for "man foo" and have "/dir/bin/foo" in their PATH +# and the docs are found in "/dir/man", then no mapping is required. +# +# The below mappings are superfluous when the right hand side is +# in the mandatory manpath already, but will keep man from statting +# lots of other nearby files and directories. +# +MANPATH_MAP /bin /usr/share/man +MANPATH_MAP /sbin /usr/share/man +MANPATH_MAP /usr/bin /usr/share/man +MANPATH_MAP /usr/sbin /usr/share/man +MANPATH_MAP /usr/local/bin /usr/local/share/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/bin/mh /usr/share/man +# +# NOAUTOPATH keeps man from automatically adding directories that look like +# manual page directories to the path. +# +#NOAUTOPATH +# +# NOCACHE keeps man from creating cache pages ("cat pages") +# (generally one enables/disable cat page creation by creating/deleting +# the directory they would live in - man never does mkdir) +# +#NOCACHE +# +# Useful paths - note that COL should not be defined when +# NROFF is defined as "groff -Tascii" or "groff -Tlatin1"; +# not only is it superfluous, but it actually damages the output. +# For use with utf-8, NROFF should be "nroff -mandoc" without -T option. +# (Maybe - but today I need -Tlatin1 to prevent double conversion to utf8.) +# +# If you have a new troff (version 1.18.1?) and its colored output +# causes problems, add the -c option to TROFF, NROFF, JNROFF. +# +TROFF /usr/bin/groff -Tps -mandoc +NROFF /usr/bin/nroff -Tlatin1 -mandoc +JNROFF /usr/bin/groff -Tnippon -mandocj +EQN /usr/bin/geqn -Tps +NEQN /usr/bin/geqn -Tlatin1 +JNEQN /usr/bin/geqn -Tnippon +TBL /usr/bin/gtbl +# COL /usr/bin/col +REFER +PIC /usr/bin/gpic +VGRIND +GRAP +PAGER /bin/less -is +BROWSER /bin/less -is +HTMLPAGER /bin/cat +CAT /bin/cat +# +# The command "man -a xyzzy" will show all man pages for xyzzy. +# When CMP is defined man will try to avoid showing the same +# text twice. (But compressed pages compare unequal.) +# +CMP /usr/bin/cmp -s +# +# Compress cat pages +# +COMPRESS /usr/bin/xz +COMPRESS_EXT .xz +# +# Default manual sections (and order) to search if -S is not specified +# and the MANSECT environment variable is not set. +# +MANSECT 1:1p:8:2:3:3p:4:5:6:7:9:0p:tcl:n:l:p:o +# +# Default options to use when man is invoked without options +# This is mainly for the benefit of those that think -a should be the default +# Note that some systems have /usr/man/allman, causing pages to be shown twice. +# +#MANDEFOPTIONS -a +# +# Decompress with given decompressor when input file has given extension +# The command given must act as a filter. +# +.gz /bin/gunzip -c +.bz2 /bin/bzip2 -c -d +.lzma /usr/bin/unlzma -c -d +.z +.Z /bin/zcat +.F +.Y +.xz /usr/bin/unxz -c diff --git a/src/man.conf.in b/src/man.conf.in new file mode 100644 index 0000000..ce73ffc --- /dev/null +++ b/src/man.conf.in @@ -0,0 +1,140 @@ +# man.conf from @version@ +# +# For more information about this file, see the man pages man(1) +# and man.conf(5). +# +# This file is read by man to configure the default manpath (also used +# when MANPATH contains an empty substring), to find out where the cat +# pages corresponding to given man pages should be stored, +# and to map each PATH element to a manpath element. +# It may also record the pathname of the man binary. [This is unused.] +# The format is: +# +# MANBIN pathname +# MANPATH manpath_element [corresponding_catdir] +# MANPATH_MAP path_element manpath_element +# +# If no catdir is given, it is assumed to be equal to the mandir +# (so that this dir has both man1 etc. and cat1 etc. subdirs). +# This is the traditional Unix setup. +# Certain versions of the FSSTND recommend putting formatted versions +# of /usr/.../man/manx/page.x into /var/catman/.../catx/page.x. +# The keyword FSSTND will cause this behaviour. +# Certain versions of the FHS recommend putting formatted versions of +# /usr/.../share/man/[locale/]manx/page.x into +# /var/cache/man/.../[locale/]catx/page.x. +# The keyword FHS will cause this behaviour (and overrides FSSTND). +# Explicitly given catdirs override. +# +@fsstnd@FSSTND +@fhs@FHS +# +# This file is also read by man in order to find how to call nroff, less, etc., +# and to determine the correspondence between extensions and decompressors. +# +# MANBIN /usr/local/bin/man +# +# Every automatically generated MANPATH includes these fields +# +MANPATH /usr/man +MANPATH /usr/share/man +MANPATH /usr/local/man +MANPATH /usr/local/share/man +MANPATH /usr/X11R6/man +# +# Uncomment if you want to include one of these by default +# +# MANPATH /opt/*/man +# MANPATH /usr/lib/*/man +# MANPATH /usr/share/*/man +# MANPATH /usr/kerberos/man +# +# Set up PATH to MANPATH mapping +# +# If people ask for "man foo" and have "/dir/bin/foo" in their PATH +# and the docs are found in "/dir/man", then no mapping is required. +# +# The below mappings are superfluous when the right hand side is +# in the mandatory manpath already, but will keep man from statting +# lots of other nearby files and directories. +# +MANPATH_MAP /bin /usr/share/man +MANPATH_MAP /sbin /usr/share/man +MANPATH_MAP /usr/bin /usr/share/man +MANPATH_MAP /usr/sbin /usr/share/man +MANPATH_MAP /usr/local/bin /usr/local/share/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/bin/mh /usr/share/man +# +# NOAUTOPATH keeps man from automatically adding directories that look like +# manual page directories to the path. +# +#NOAUTOPATH +# +# NOCACHE keeps man from creating cache pages ("cat pages") +# (generally one enables/disable cat page creation by creating/deleting +# the directory they would live in - man never does mkdir) +# +#NOCACHE +# +# Useful paths - note that COL should not be defined when +# NROFF is defined as "groff -Tascii" or "groff -Tlatin1"; +# not only is it superfluous, but it actually damages the output. +# For use with utf-8, NROFF should be "nroff -mandoc" without -T option. +# (Maybe - but today I need -Tlatin1 to prevent double conversion to utf8.) +# +# If you have a new troff (version 1.18.1?) and its colored output +# causes problems, add the -c option to TROFF, NROFF, JNROFF. +# +TROFF @troff@ +NROFF @nroff@ +JNROFF @jnroff@ +EQN @eqn@ +NEQN @neqn@ +JNEQN @jneqn@ +TBL @tbl@ +@nocol@COL @col@ +REFER @refer@ +PIC @pic@ +VGRIND @vgrind@ +GRAP @grap@ +PAGER @pager@ +BROWSER @browser@ +HTMLPAGER @htmlpager@ +CAT @cat@ +# +# The command "man -a xyzzy" will show all man pages for xyzzy. +# When CMP is defined man will try to avoid showing the same +# text twice. (But compressed pages compare unequal.) +# +CMP @cmp@ +# +# Compress cat pages +# +COMPRESS @compress@ +COMPRESS_EXT @compress_ext@ +# +# Default manual sections (and order) to search if -S is not specified +# and the MANSECT environment variable is not set. +# +MANSECT @sections@ +# +# Default options to use when man is invoked without options +# This is mainly for the benefit of those that think -a should be the default +# Note that some systems have /usr/man/allman, causing pages to be shown twice. +# +#MANDEFOPTIONS -a +# +# Decompress with given decompressor when input file has given extension +# The command given must act as a filter. +# +.gz @gunzip@ +.bz2 @bzip2@ +.lzma @unlzma@ +.z @pcat@ +.Z @zcat@ +.F @fcat@ +.Y @unyabba@ +.xz @unxz@ diff --git a/src/man.h b/src/man.h new file mode 100644 index 0000000..285aac7 --- /dev/null +++ b/src/man.h @@ -0,0 +1,22 @@ +extern int debug; +extern int do_compress; +extern int fhs; +extern int fsstnd; +extern int noautopath; +extern int nocache; +extern int findall; +extern int nocats; +extern int preformat; +extern int do_troff; +extern int apropos; +extern int whatis; +extern int print_where; +extern int one_per_line; +extern int do_irix; +extern char *dohp; +extern const char *progname; +extern const char *pager; +extern const char *browser; +extern const char *htmlpager; +extern char *colon_sep_section_list; +extern char *roff_directive; diff --git a/src/man2dvi b/src/man2dvi new file mode 100755 index 0000000..f9cb52e --- /dev/null +++ b/src/man2dvi @@ -0,0 +1,36 @@ +#! /bin/sh +# +# Script to format manpages to dvi. +# Copyright (c) 1997 Tobias Begalke (tb@lst.de) +# +# Part of release 1.6g of the man suite. +# + +groff="groff -Tdvi -mandoc" + +if [ ! $# = 1 ]; then + echo "$0: usage:" + echo " $0 [topic] > topic.dvi" + exit 1 +fi + +location=`man -c -w $1` + +if [ "$location" = "" ]; then + exit 1 +fi + +case `file $location` in + *gzip* ) + zcat $location | $groff + ;; + + *bzip2* ) + bzcat $location | $groff + ;; + + *troff* ) + $groff $location + ;; +esac + diff --git a/src/manfile.c b/src/manfile.c new file mode 100644 index 0000000..0df62ea --- /dev/null +++ b/src/manfile.c @@ -0,0 +1,337 @@ +/* + * manfile.c - aeb, 971231 + * + * Used both by man and man2html - be careful with printing! + */ +#include +#include +#include +#include +#include + +#include "glob.h" +#include "util.h" +#include "manfile.h" +#include "gripes.h" +#include "man.h" /* for debug */ + +static int standards; +static const char *((*to_cat_filename)(const char *man_filename, + const char *ext, int flags)); + +/* + * Append the struct or chain A to the chain HEAD. + */ +static void +append(struct manpage **head, struct manpage *a) { + struct manpage *p; + + if (a) { + if (*head) { + p = *head; + while(p->next) + p = p->next; + p->next = a; + } else + *head = a; + } +} + + +static int +my_lth(const char *s) { + return s ? strlen(s) : 0; +} + +/* + * Find the files of the form DIR/manSEC/NAME.EXT etc. + * Use "man" for TYPE_MAN, "cat" for TYPE_SCAT, and + * apply convert_to_cat() to the man version for TYPE_CAT. + * + * Some HP systems use /usr/man/man1.Z/name.1, where name.1 is + * compressed - yuk. We can handle this by using section 1.Z + * instead of 1 and assuming that the man page is compressed + * if the directory name ends in .Z. + * + * Some Sun systems use /usr/share/man/sman1/man.1 and + * /usr/share/man/sman1m/mkfs.1m. + * + * We support HTML filenames of the following form: + * /usr/share/man/sman1m/mkfs.1m.html, optionally followed + * by a compression suffix. + * + * Returns an array with pathnames, or 0 if out-of-memory or error. + */ +static char ** +glob_for_file_ext_glob (const char *dir, const char *sec, + const char *name, const char *ext, char *hpx, + int glob, int type) { + char *pathname; + const char *p; + char **names; + int len; +#define MANFORM "%s/%s%s%s/%s.%s" +#define GLOB "*" +#define LENGTHOF(s) (sizeof(s)-1) +/* This must be long enough to hold the format-directory name. + * The basic type-directory names are 'cat' and 'man'; this needs to + * allocate space for those or any others such as html or sman. + */ +#define TYPELEN 8 + + len = my_lth(dir) + my_lth(sec) + my_lth(hpx) + my_lth(name) + my_lth(ext) + + TYPELEN + + LENGTHOF(".html") + LENGTHOF(MANFORM) + LENGTHOF(GLOB); + + if (debug >= 2) + gripe(CALLTRACE3, dir, sec, name, ext, hpx, glob, type); + + pathname = (char *) malloc(len); + if (!pathname) + return 0; + + sprintf (pathname, MANFORM, + dir, + (type==TYPE_HTML) ? "html" : (type==TYPE_XML) ? "sman" : (type==TYPE_SCAT) ? "cat" : "man", + sec, hpx, name, ext); + if (type == TYPE_HTML) + strcat(pathname, ".html"); + if (glob) + strcat(pathname, GLOB); + + if (type == TYPE_CAT) { + p = to_cat_filename(pathname, 0, standards); + if (p) { + free(pathname); + } else { + sprintf (pathname, "%s/cat%s%s/%s.%s%s", + dir, sec, hpx, name, ext, glob ? GLOB : ""); + p = pathname; + } + } else + p = pathname; + + if (debug >=2) + gripe(ABOUT_TO_GLOB, p); + names = glob_filename (p); + if (names == (char **) -1) /* file system error; print msg? */ + names = 0; + return names; +} + +static char ** +glob_for_file_ext (const char *dir, const char *sec, + const char *name, const char *ext, int type) { + char **names, **namesglob; + char *hpx = ((standards & DO_HP) ? ".Z" : ""); + + namesglob = glob_for_file_ext_glob(dir,sec,name,ext,hpx,1,type); + if (!namesglob && *hpx) { + hpx = ""; + namesglob = glob_for_file_ext_glob(dir,sec,name,ext,hpx,1,type); + } + if (!namesglob) + return 0; + if (*namesglob) { + /* we found something - try to get a more precise match */ + names = glob_for_file_ext_glob(dir,sec,name,ext,hpx,0,type); + if (names && *names) + namesglob = names; + } + return namesglob; +} + +/* + * Find the files of the form DIR/manSEC/NAME.SEC etc. + */ +static char ** +glob_for_file (const char *dir, const char *sec, const char *name, int type) { + char **names; + + if (debug >= 2) + gripe(CALLTRACE2, dir, sec, name, type); + + if (standards & DO_IRIX) { + /* try first without `sec' extension */ + /* maybe this should be done only for cat pages? */ + return glob_for_file_ext (dir, sec, name, "", type); + } + + /* try /usr/X11R6/man/man3x/XSetFont.3x */ + names = glob_for_file_ext (dir, sec, name, sec, type); + + if (!names) + return 0; /* out-of-memory or error */ + + /* sometimes the extension is only a single digit */ + if (!*names && isdigit(sec[0]) && sec[1] != 0) { + char ext[2]; + ext[0] = sec[0]; + ext[1] = 0; + names = glob_for_file_ext (dir, sec, name, ext, type); + } + + if (!names) + return 0; /* out-of-memory or error */ + + /* or the extension could be .man */ + if (!*names) + names = glob_for_file_ext (dir, sec, name, "man", type); + + if (debug >= 2) { + if (!names[0]) + gripe(NO_MATCH); + else { + char **np; + for (np = names; *np; np++) + gripe(GLOB_FOR_FILE, *np); + } + } + + return names; +} + +/* + * Find a man page of the given NAME under the directory DIR, + * in section SEC. Only types (man, cat, scat, html) permitted in FLAGS + * are allowed, and priorities are in this order. + */ +static struct manpage * +manfile_from_sec_and_dir(const char *dir, + const char *sec, const char *name, int flags) { + struct manpage *res = 0; + struct manpage *p; + char **names, **np; + int i, type; + int types[] = {TYPE_HTML, TYPE_MAN, TYPE_CAT, TYPE_SCAT}; + + if (debug >= 2) + gripe(CALLTRACE1, dir, sec, name, flags); + + for (i=0; i<(sizeof(types)/sizeof(types[0])); i++) { + type = types[i]; + + /* If convert_to_cat() is trivial, TYPE_CAT and TYPE_SCAT + are the same thing. */ + if ((type == TYPE_CAT) && (flags & TYPE_SCAT) && !standards) + continue; + + if (flags & type) { + names = glob_for_file (dir, sec, name, type); + if (names) { + for (np = names; *np; np++) { +#if 1 + /* Keep looking if we encounter a file + we can't access */ + if (access(*np, R_OK)) + continue; + + if (debug >= 2) + gripe(FOUND_FILE, *np); + /* disadvantage: no error message when permissions + are wrong, the page just silently becomes + invisible */ +#endif + p = (struct manpage *) malloc(sizeof(*p)); + if (!p) + break; /* %% perhaps print msg, free names */ + p->filename = *np; + p->type = type; + p->next = 0; + append(&res, p); + if (res && (flags & ONLY_ONE_PERSEC)) + break; + } + free(names); + } + } + + if (res) + return res; + } + + return res; +} + +/* + * Find a man page of the given NAME, searching in the specified SECTION. + * Searching is done in all directories of MANPATH. + */ +static struct manpage * +manfile_from_section(const char *name, const char *section, + int flags, char **manpath) { + char **mp; + struct manpage *res = 0; + + for (mp = manpath; *mp; mp++) { + append(&res, manfile_from_sec_and_dir(*mp, section, name, flags)); + if (res && (flags & ONLY_ONE_PERSEC)) + break; + } +#if 0 + /* Someone wants section 1p - better not to give 1 */ + if (res == NULL && isdigit(section[0]) && section[1]) { + char sec[2]; + + sec[0] = section[0]; + sec[1] = 0; + for (mp = manpath; *mp; mp++) { + append(&res, manfile_from_sec_and_dir(*mp, sec, name, flags)); + if (res && (flags & ONLY_ONE_PERSEC)) + break; + } + } +#endif + return res; +} + +/* + * Find a man page of the given NAME, searching in the specified + * SECTION, or, if that is 0, in all sections in SECTIONLIST. + * Searching is done in all directories of MANPATH. + * If FLAGS contains the ONLY_ONE bits, only the first matching + * page is returned; otherwise all matching pages are found. + * Only types (man, cat, scat) permitted in FLAGS are allowed. + */ +struct manpage * +manfile(const char *name, const char *section, int flags, + char **sectionlist, char **manpath, + const char *((*tocat)(const char *man_filename, const char *ext, + int flags))) { + char **sl; + struct manpage *res; + + standards = (flags & (FHS | FSSTND | DO_HP | DO_IRIX)); + to_cat_filename = tocat; + + if (name && (flags & DO_WIN32)) { /* Convert : sequences to a ? */ + char *n = my_malloc(strlen(name) + 1); + const char *p = name; + char *q = n; + + while (*p) { + if (*p == ':') { + *q++ = '?'; + while (*p == ':') + p++; + } else + *q++ = *p++; + } + *q = 0; + name = n; + } + + if (!name || !manpath) /* error msg? */ + res = 0; + else if (section) + res = manfile_from_section(name, section, flags, manpath); + else if (sectionlist) { + res = 0; + for (sl = sectionlist; *sl; sl++) { + append(&res, manfile_from_section(name, *sl, flags, manpath)); + if (res && (flags & ONLY_ONE)) + break; + } + } + return res; +} diff --git a/src/manfile.h b/src/manfile.h new file mode 100644 index 0000000..cae3add --- /dev/null +++ b/src/manfile.h @@ -0,0 +1,36 @@ +struct manpage { + struct manpage *next; + char *filename; + int type; +}; + +#define TYPE_MAN 0x0001 +#define TYPE_CAT 0x0002 +#define TYPE_SCAT 0x0004 +#define TYPE_HTML 0x0008 +#define TYPE_XML 0x0010 /* not presently used */ + +#define ONLY_ONE_PERSEC 0x0020 /* do not return more pages from one section */ +#define ONLY_ONE 0x0040 /* return only a single page */ + +/* various standards have various ideas about where the cat pages + ought to live */ +#define FSSTND 0x0080 +#define FHS 0x0100 + +/* HP has a peculiar way to indicate that pages are compressed */ +#define DO_HP 0x0200 /* compressed file in man1.Z/ls.1 */ + +/* IRIX has a peculiar cat page naming */ +#define DO_IRIX 0x0400 /* cat page ls.z, not ls.1.z */ + +/* Sun uses both man and sman, where sman contains XML */ +#define DO_SUN 0x0800 /* unused today */ + +/* NTFS cannot handle : in filenames */ +#define DO_WIN32 0x1000 /* turn :: into ? */ + +extern struct manpage * +manfile(const char *name, const char *section, int flags, + char **sectionlist, char **manpath, + const char *(*tocat)(const char *, const char *, int)); diff --git a/src/manpath.c b/src/manpath.c new file mode 100644 index 0000000..90d520e --- /dev/null +++ b/src/manpath.c @@ -0,0 +1,412 @@ +/* + * manpath.c + * + * Copyright (c) 1990, 1991, John W. Eaton. + * + * You may distribute under the terms of the GNU General Public + * License as specified in the file COPYING that comes with the man + * distribution. + * + * John W. Eaton + * jwe@che.utexas.edu + * Department of Chemical Engineering + * The University of Texas at Austin + * Austin, Texas 78712 + * + * Changed PATH->manpath algorithm + * Added: an empty string in MANPATH denotes the system path + * Added: use LANG to search in /usr/man/ + * Lots of other minor things, including spoiling the indentation. + * aeb - 940315 + */ + +#include +#include +#include +#include +#include +#include + +/* not always in */ +extern char *index(const char *, int); +extern char *rindex(const char *, int); + +#include "defs.h" +#include "gripes.h" +#include "man.h" /* for debug */ +#include "man-config.h" /* for cfdirlist */ +#include "man-getopt.h" /* for alt_system, opt_manpath */ +#include "manpath.h" +#include "util.h" /* my_malloc, my_strdup */ + +char **mandirlist; +static int mandirlistlth = 0; +static int mandirlistmax = 0; + +/* + * Input: a string, with : as separator + * For each entry in the string, call fn. + */ +static void +split (char *string, void (*fn)(char *, int), int perrs) { + char *p, *q, *r; + + if (string) { + p = my_strdup(string); + for (q = p; ; ) { + if ((r = index(q, ':'))==(char*)0) + r=index(q,'\01'); + if (r) { + *r = 0; + fn (q, perrs); + q = r+1; + } else { + fn (q, perrs); + break; + } + } + free (p); + } +} + +static void +split2 (char *s, char *string, void (*fn)(char *, char *, int), int perrs) { + char *p, *q, *r; + + if (string) { + p = my_strdup(string); + for (q = p; ; ) { + r = index(q, ':'); + if (r) { + *r = 0; + fn (s, q, perrs); + q = r+1; + } else { + fn (s, q, perrs); + break; + } + } + free (p); + } +} + +/* + * Is path a directory? + * -1: error, 0: no, 1: yes. + */ +static int +is_directory (char *path) { + struct stat sb; + + if (stat (path, &sb) != 0) + return -1; + + return ((sb.st_mode & S_IFDIR) == S_IFDIR); +} + +/* + * Check to see if the current directory has man or MAN + * or ../man or ../man1 or ../man8 subdirectories. + */ +static char * +find_man_subdir (char *p) { + int len; + char *t, *sp; + + len = strlen (p); + + t = my_malloc ((unsigned) len + 20); + + memcpy (t, p, len); + strcpy (t + len, "/man"); + + if (is_directory (t) == 1) + return t; + + strcpy (t + len, "/MAN"); + + if (is_directory (t) == 1) + return t; + + /* find parent directory */ + t[len] = 0; + if ((sp = rindex (t, '/')) != NULL) { + *sp = 0; + len = sp - t; + } else { + strcpy (t + len, "/.."); + len += 3; + } + + /* look for the situation with packagedir/bin and packagedir/man */ + strcpy (t + len, "/man"); + + if (is_directory (t) == 1) + return t; + + /* look for the situation with pkg/bin and pkg/man1 or pkg/man8 */ + /* (looking for all man[1-9] would probably be a waste of stats) */ + strcpy (t + len, "/man1"); + + if (is_directory (t) == 1) { + t[len] = 0; + return t; + } + + strcpy (t + len, "/man8"); + + if (is_directory (t) == 1) { + t[len] = 0; + return t; + } + + free (t); + return NULL; +} + +/* + * Add a directory to the manpath list if it isn't already there. + */ +static void +add_to_list (char *dir, char *lang, int perrs) { + int status; + char cwd[BUFSIZ]; + char **dp; + + if (!lang) + lang = ""; + + /* only add absolute paths */ + if (*dir != '/') { + if (!getcwd(cwd, sizeof(cwd))) + return; /* cwd not readable, or pathname very long */ + if (cwd[0] != '/') + return; /* strange.. */ + if (strlen(dir) + strlen(lang) + strlen(cwd) + 3 > sizeof(cwd)) + return; + if (!strncmp (dir, "./", 2)) + dir += 2; + while (!strncmp (dir, "../", 3)) { + char *p = rindex (cwd, '/'); + if (p > cwd) + *p = 0; + else + cwd[1] = 0; + dir += 3; + } + strcat (cwd, "/"); + strcat (cwd, dir); + if (*lang) { + strcat (cwd, "/"); + strcat (cwd, lang); + } + dir = cwd; + } else if (*lang) { + if (strlen(dir) + strlen(lang) + 2 > sizeof(cwd)) + return; + strcpy (cwd, dir); + strcat (cwd, "/"); + strcat (cwd, lang); + dir = cwd; + } + + if (mandirlist) { + for (dp = mandirlist; *dp; dp++) { + if (!strcmp (*dp, dir)) + return; + } + } + + /* + * Avoid trickery: no /../ in path. + */ + if (strstr(dir, "/../")) + return; + + /* + * Not found -- add it. + */ + status = is_directory(dir); + + if (status < 0 && perrs) { + gripe (CANNOT_STAT, dir); + } else if (status == 0 && perrs) { + gripe (IS_NO_DIR, dir); + } else if (status == 1) { + if (debug) + gripe (ADDING_TO_MANPATH, dir); + + if (!mandirlist || mandirlistlth+1 >= mandirlistmax) { + int i, ct = mandirlistmax + 100; + char **p = (char **) my_malloc(ct * sizeof(char *)); + + if (mandirlist) { + for (i=0; i 5 && lang[5] == '.') { + char lang2[6]; /* e.g. zh_CN from zh_CN.GB2312 */ + + strncpy(lang2,lang,5); + lang2[5] = 0; + add_to_list(dir, lang2, perrs); + } + if (lang && strlen(lang) > 2) { + char lang2[3]; + + strncpy(lang2,lang,2); + lang2[2] = 0; + add_to_list(dir, lang2, perrs); + } +} + +static void +add_to_mandirlist (char *dir, int perrs) { + char *lang; + + if (alt_system) { + add_to_list(dir, alt_system_name, perrs); + } else { + /* We cannot use "lang = setlocale(LC_MESSAGES, NULL)" or so: + the return value of setlocale is an opaque string. */ + /* POSIX prescribes the order: LC_ALL, LC_MESSAGES, LANG */ + if((lang = getenv("LC_ALL")) != NULL) + split2(dir, lang, add_to_mandirlist_x, perrs); + if((lang = getenv("LC_MESSAGES")) != NULL) + split2(dir, lang, add_to_mandirlist_x, perrs); + if((lang = getenv("LANG")) != NULL) + split2(dir, lang, add_to_mandirlist_x, perrs); + if((lang = getenv("LANGUAGE")) != NULL) + split2(dir, lang, add_to_mandirlist_x, perrs); + add_to_mandirlist_x(dir, 0, perrs); + } +} + +/* + * For each directory in the user's path, see if it is one of the + * directories listed in the man.conf file. If so, and it is + * not already in the manpath, add it. If the directory is not listed + * in the man.conf file, see if there is a subdirectory `man' or + * `MAN'. If so, and it is not already in the manpath, add it. + * + * Example: user has /bin in his path and the directory + * /bin/man exists -- the directory /bin/man will be added + * to the manpath. + * Try also /man ?and ?, and, if LANG is set, /$LANG/man. + * aeb - 940320 + */ +static void +get_manpath_from_pathdir (char *dir, int perrs) { + char *t; + struct dirs *dlp; + + if (debug) + gripe (PATH_DIR, dir); + + /* + * The directory we're working on is in the config file. + * If we haven't added it to the list yet, do. + */ + if (*dir) { + for (dlp = cfdirlist.nxt; dlp; dlp = dlp->nxt) { + if (!strcmp (dir, dlp->bindir)) { + if (debug) + gripe (IS_IN_CONFIG); + + add_to_mandirlist (dlp->mandir, perrs); + return; + } + } + } + + if (!noautopath) { + /* + * The directory we're working on isn't in the config file. + * See if it has man or MAN subdirectories. If so, and this + * subdirectory hasn't been added to the list, do. (Try also + * a few other places nearby.) + */ + if (debug) + gripe (IS_NOT_IN_CONFIG); + + t = find_man_subdir (dir); + if (t != NULL) { + if (debug) + gripe (MAN_NEARBY); + + add_to_mandirlist (t, perrs); + free (t); + } else { + if (debug) + gripe (NO_MAN_NEARBY); + } + } +} + +static void +add_default_manpath (int perrs) { + struct dirs *dlp; + + if (debug) + gripe (ADDING_MANDIRS); + + for (dlp = cfdirlist.nxt; dlp; dlp = dlp->nxt) + if (dlp->mandatory) + add_to_mandirlist (dlp->mandir, perrs); +} + +static void +to_mandirlist(char *s, int perrs) { + char *path; + + if (*s) { + add_to_mandirlist (s, perrs); + } else { + /* empty substring: insert default path */ + if((path = getenv ("PATH")) != NULL) + split (path, get_manpath_from_pathdir, perrs); + add_default_manpath (perrs); + } +} + +void +init_manpath () { + static int done = 0; + + if (!done) { + char *manp; + + if ((manp = opt_manpath) == NULL && + (manp = getenv ("manpath")) == NULL && + (manp = getenv ("MANPATH")) == NULL) + manp = ""; /* default path */ + split (manp, to_mandirlist, 0); + done = 1; + } +} + +void +prmanpath () { + char **dp, **dp0; + + if (mandirlist) { + for (dp0 = dp = mandirlist; *dp; dp++) { + if (dp != dp0) + printf(":"); + printf("%s", *dp); + } + } + printf("\n"); +} diff --git a/src/manpath.h b/src/manpath.h new file mode 100644 index 0000000..5232938 --- /dev/null +++ b/src/manpath.h @@ -0,0 +1,5 @@ +/* functions and variables exported by manpath.c */ +void prmanpath (void); +void init_manpath (void); + +extern char ** mandirlist; diff --git a/src/msg.c b/src/msg.c new file mode 100644 index 0000000..874e22f --- /dev/null +++ b/src/msg.c @@ -0,0 +1,106 @@ +char *msg[] = { + "", +/* 1 */ "unable to make sense of the file %s\n", +/* 2 */ "Warning: cannot open configuration file %s\n", +/* 3 */ "Error parsing config file\n", +/* 4 */ "incompatible options %s and %s\n", +/* 5 */ "Sorry - no support for alternate systems compiled in\n", +/* 6 */ "Man was compiled with automatic cat page compression,\n\ +but the configuration file does not define COMPRESS.\n", +/* 7 */ "What manual page do you want from section %s?\n", +/* 8 */ "What manual page do you want?\n", +/* 9 */ "No entry for %s in section %s of the manual\n", +/* 10 */ "No manual entry for %s\n", +/* 11 */ "\nusing %s as pager\n", +/* 12 */ "Error executing formatting or display command.\n\ +System command %s exited with status %d.\n", +/* 13 */ "%s, version %s\n\n", +/* 14 */ "Out of memory - can't malloc %d bytes\n", +/* 15 */ "Error parsing *roff command from file %s\n", +/* 16 */ "Error parsing MANROFFSEQ. Using system defaults.\n", +/* 17 */ "Error parsing *roff command from command line.\n", +/* 18 */ "Unrecognized line in config file (ignored)\n%s\n", +/* 19 */ "man-config.c: internal error: string %s not found\n", +/* 20 */ "found man directory %s\n", +/* 21 */ "found manpath map %s --> %s\n", +/* 22 */ "corresponding catdir is %s\n", +/* 23 */ "Line too long in config file\n", +/* 24 */ "\nsection: %s\n", +/* 25 */ "unlinked %s\n", +/* 26 */ "globbing %s\n", +/* 27 */ "Attempt [%s] to expand man page failed\n", +/* 28 */ "Cannot open man page %s\n", +/* 29 */ "Error reading man page %s\n", +/* 30 */ "found eqn(1) directive\n", +/* 31 */ "found grap(1) directive\n", +/* 32 */ "found pic(1) directive\n", +/* 33 */ "found tbl(1) directive\n", +/* 34 */ "found vgrind(1) directive\n", +/* 35 */ "found refer(1) directive\n", +/* 36 */ "parsing directive from command line\n", +/* 37 */ "parsing directive from file %s\n", +/* 38 */ "parsing directive from environment\n", +/* 39 */ "using default preprocessor sequence\n", +/* 40 */ "Formatting page, please wait...\n", +/* 41 */ "changed mode of %s to %o\n", +/* 42 */ "Couldn't open %s for writing.\n", +/* 43 */ "will try to write %s if needed\n", +/* 44 */ "status from is_newer() = %d\n", +/* 45 */ "trying section %s\n", +/* 46 */ "\nsearching in %s\n", +/* 47 */ "but %s is already in the manpath\n", +/* 48 */ "Warning: cannot stat file %s!\n", +/* 49 */ "Warning: %s isn't a directory!\n", +/* 50 */ "adding %s to manpath\n", +/* 51 */ "\npath directory %s ", +/* 52 */ "is in the config file\n", +/* 53 */ "is not in the config file\n", +/* 54 */ "but there is a man directory nearby\n", +/* 55 */ "and we found no man directory nearby\n", +/* 56 */ "\nadding mandatory man directories\n\n", +/* 57 */ "cat_name in convert_to_cat () is: %s\n", +/* 58 */ "\nnot executing command:\n %s\n", +/* 59 */ "usage: %s [-adfhktwW] [section] [-M path] [-P pager] [-S list]\n\t", +/* 60 */ "[-m system] ", +/* 61 */ "[-p string] name ...\n\n", +/* 62 */ " a : find all matching entries\n\ + c : do not use cat file\n\ + d : print gobs of debugging information\n\ + D : as for -d, but also display the pages\n\ + f : same as whatis(1)\n\ + h : print this help message\n\ + k : same as apropos(1)\n\ + K : search for a string in all pages\n", +/* 63 */ " t : use troff to format pages for printing\n", +/* 64 */ "\ + w : print location of man page(s) that would be displayed\n\ + (if no name given: print directories that would be searched)\n\ + W : as for -w, but display filenames only\n\n\ + C file : use `file' as configuration file\n\ + M path : set search path for manual pages to `path'\n\ + P pager : use program `pager' to display pages\n\ + S list : colon separated section list\n", +/* 65 */ " m system : search for alternate system's man pages\n", +/* 66 */ " p string : string tells which preprocessors to run\n\ + e - [n]eqn(1) p - pic(1) t - tbl(1)\n\ + g - grap(1) r - refer(1) v - vgrind(1)\n", +/* 67 */ "and the real user cannot open the cat file either\n", +/* 68 */ "but the real user can open the cat file\n", +/* 69 */ "failed to fork off the command _%s_\n", +/* 70 */ "error while waiting for child _%s_\n", +/* 71 */ "very strange ..., got wrong pid while waiting for my child\n", +/* 72 */ "fatal error: the command _%s_ terminated abnormally\n", +/* 73 */ "Man page %s is identical to %s\n", +/* 74 */ "Found the man page(s):\n", +/* 75 */ "error: no TROFF command specified in %s\n", +/* 76 */ "no cat page stored because of nonstandard line length\n", +/* 77 */ "\nusing %s as browser\n", +/* 78 */ "\nusing %s to dump HTML pages as text\n", +/* 79 */ "manfile_from_sec_and_dir() found %s\n", +/* 80 */ "manfile_from_sec_and_dir(dir=%s, sec=%s, name=%s, flags=0x%0x)\n", +/* 81 */ "glob_for_file(dir=%s, sec=%s, name=%s, type=0x%0x, ...)\n", +/* 82 */ "glob_for_file found no matches.\n", +/* 83 */ "glob_for_file returns %s.\n", +/* 84 */ "glob_for_file_ext_glob(dir=%s, sec=%s, name=%s, ext=%s, hpx=%s, glob=%d, type=0x%0x);\n", +/* 85 */ "glob_for_file_ext_glob will expand %s\n", +}; diff --git a/src/mwi b/src/mwi new file mode 100755 index 0000000..3b52feb --- /dev/null +++ b/src/mwi @@ -0,0 +1,19 @@ +#!/bin/sh +# test which words in all caps end a NAME section in a (compressed) cat page +# +# Found so far: +# +# SYNOPSIS +# SYNOPOSIS +# SYSTEM V SYNOPSIS +# SYNTAX +# DESCRIPTION +# COMMAND +# OVERVIEW +# STRUCTURES +# INTRODUCTION +# + for i in * + do + zcat $i | col -bx | sed '1,/^NAME/d; /^[A-Z][A-Z]/q' | tail -1 + done diff --git a/src/ndir.h b/src/ndir.h new file mode 100644 index 0000000..438d5c2 --- /dev/null +++ b/src/ndir.h @@ -0,0 +1,51 @@ +/* + -- definitions for 4.2BSD-compatible directory access + + last edit: 09-Jul-1983 D A Gwyn +*/ + +#ifdef VMS +#ifndef FAB$C_BID +#include +#endif +#ifndef NAM$C_BID +#include +#endif +#ifndef RMS$_SUC +#include +#endif +#include "dir.h" +#endif /* VMS */ + +#define DIRBLKSIZ 512 /* size of directory block */ +#ifdef VMS +#define MAXNAMLEN (DIR$S_NAME + 7) /* 80 plus room for version #. */ +#define MAXFULLSPEC NAM$C_MAXRSS /* Maximum full spec */ +#else +#define MAXNAMLEN 15 /* maximum filename length */ +#endif /* VMS */ + /* NOTE: MAXNAMLEN must be one less than a multiple of 4 */ + +struct direct /* data from readdir() */ + { + long d_ino; /* inode number of entry */ + unsigned short d_reclen; /* length of this record */ + unsigned short d_namlen; /* length of string in d_name */ + char d_name[MAXNAMLEN+1]; /* name of file */ + }; + +typedef struct + { + int dd_fd; /* file descriptor */ + int dd_loc; /* offset in block */ + int dd_size; /* amount of valid data */ + char dd_buf[DIRBLKSIZ]; /* directory block */ + } DIR; /* stream data from opendir() */ + +extern DIR *opendir(); +extern struct direct *readdir(); +extern long telldir(); +extern void seekdir(); +extern void closedir(); + +#define rewinddir( dirp ) seekdir( dirp, 0L ) diff --git a/src/paths.h b/src/paths.h new file mode 100644 index 0000000..723731d --- /dev/null +++ b/src/paths.h @@ -0,0 +1,43 @@ +/* + * Generated automatically from paths.h.in by the + * configure script. + */ +/* paths.h - included in man-config.c */ +/* + * Define the absolute path to the configuration file and programs used. + * (If no configuration file is found then the preset values are used.) + */ +#ifndef CONFIG_FILE +#define CONFIG_FILE "/usr/share/misc/man.conf" +#endif + +static struct paths { + char *name; + char *path; /* path plus command options - never NULL */ +} paths[] = { + { "MANBIN", "" }, /* value unused */ + { "APROPOS", "/usr/bin/apropos" }, + { "WHATIS", "/usr/bin/whatis" }, + { "TROFF", "/usr/bin/groff -Tps -mandoc" }, + { "NROFF", "/usr/bin/nroff -Tlatin1 -mandoc" }, + { "JNROFF", "/usr/bin/groff -Tnippon -mandocj" }, + { "EQN", "/usr/bin/geqn -Tps" }, + { "NEQN", "/usr/bin/geqn -Tlatin1" }, + { "JNEQN", "/usr/bin/geqn -Tnippon" }, + { "TBL", "/usr/bin/gtbl" }, + { "COL", "" }, + { "REFER", "" }, + { "PIC", "/usr/bin/gpic" }, + { "VGRIND", "" }, + { "GRAP", "" }, + { "PAGER", "/bin/less -is" }, + { "BROWSER","/bin/less -is" }, + { "HTMLPAGER", "/bin/cat" }, + { "CMP", "/usr/bin/cmp -s" }, + { "CAT", "/bin/cat" }, + { "COMPRESS", "/usr/bin/xz" }, + { "COMPRESS_EXT", ".xz" }, /* not a path, just a string variable */ + { "DECOMPRESS", "/usr/bin/unxz -c" }, + { "MANSECT", "1:1p:8:2:3:3p:4:5:6:7:9:0p:tcl:n:l:p:o"}, /* idem */ + { "MANDEFOPTIONS", ""} /* idem */ +}; diff --git a/src/paths.h.in b/src/paths.h.in new file mode 100644 index 0000000..ee9ec75 --- /dev/null +++ b/src/paths.h.in @@ -0,0 +1,39 @@ +/* paths.h - included in man-config.c */ +/* + * Define the absolute path to the configuration file and programs used. + * (If no configuration file is found then the preset values are used.) + */ +#ifndef CONFIG_FILE +#define CONFIG_FILE "@man_config_file@" +#endif + +static struct paths { + char *name; + char *path; /* path plus command options - never NULL */ +} paths[] = { + { "MANBIN", "" }, /* value unused */ + { "APROPOS", "@apropos@" }, + { "WHATIS", "@whatis@" }, + { "TROFF", "@troff@" }, + { "NROFF", "@nroff@" }, + { "JNROFF", "@jnroff@" }, + { "EQN", "@eqn@" }, + { "NEQN", "@neqn@" }, + { "JNEQN", "@jneqn@" }, + { "TBL", "@tbl@" }, + { "COL", "@pcol@" }, + { "REFER", "@refer@" }, + { "PIC", "@pic@" }, + { "VGRIND", "@vgrind@" }, + { "GRAP", "@grap@" }, + { "PAGER", "@pager@" }, + { "BROWSER","@browser@" }, + { "HTMLPAGER", "@htmlpager@" }, + { "CMP", "@cmp@" }, + { "CAT", "@cat@" }, + { "COMPRESS", "@compress@" }, + { "COMPRESS_EXT", "@compress_ext@" }, /* not a path, just a string variable */ + { "DECOMPRESS", "@decompress@" }, + { "MANSECT", "@sections@"}, /* idem */ + { "MANDEFOPTIONS", ""} /* idem */ +}; diff --git a/src/to_cat.c b/src/to_cat.c new file mode 100644 index 0000000..c6aeb5f --- /dev/null +++ b/src/to_cat.c @@ -0,0 +1,171 @@ +#include +#include +#include + +extern char *rindex (const char *, int); /* not always in */ + +#include "defs.h" +#include "manfile.h" +#include "man-config.h" +#include "to_cat.h" +#include "util.h" + +/* + * Given PATH/man1/name.1, return a pointer to the '/' following PATH. + */ +static char * +mantail_of(char *name) { + char *s0, *s1, *s; + + s0 = s1 = 0; + for (s = name; *s; s++) { + if (*s == '/') { + s0 = s1; + s1 = s; + } + } + return s0; +} + +/* + * Given PATH/man1/name.1, return PATH, newly allocated. + * The argument must be writable, not a constant string. + */ +const char * +mandir_of(const char *name) { + char *p, *q; + + q = my_strdup(name); + p = mantail_of(q); + if (p) { + *p = 0; + return q; + } + free(q); + return NULL; +} + +/* + * Change a name of the form PATH/man1/name.1[.Z] + * into PATH/cat1/name.1.EXT + * or (FSSTND) change /usr/PA/man/PB/man1/name.1 + * into /var/catman/PA/PB/cat1/name.1.EXT + * or (FHS) change /usr/PATH/share/man/LOC/man1/name.1 + * into /var/cache/man/PATH/LOC/cat1/name.1.EXT + * (here the /LOC part is absent or a single [locale] dir). + * + * Returns 0 on failure. + */ + +const char * +convert_to_cat (const char *name0, const char *ext, int standards) { + char *name, *freename, *cat_name = 0; + char *t0, *t2, *t3, *t4; + struct dirs *dlp; + int len; + + freename = name = my_strdup (name0); + + t0 = rindex (name, '.'); + if (t0 && get_expander(t0)) /* remove compressee extension */ + *t0 = 0; + + t2 = mantail_of (name); + if (t2 == NULL) + return 0; + *t2 = 0; /* remove man1/name.1 part */ + + if (strncmp(t2+1, "man", 3) != 0) + return 0; + t2[1] = 'c'; + t2[3] = 't'; + + len = (ext ? strlen(ext) : 0); + + /* Explicitly given cat file? */ + for (dlp = cfdirlist.nxt; dlp; dlp = dlp->nxt) { + if (!strcmp (name, dlp->mandir)) { + if (!dlp->catdir[0]) + break; + *t2 = '/'; + len += strlen (dlp->catdir) + strlen (t2) + 1; + cat_name = (char *) my_malloc (len); + strcpy (cat_name, dlp->catdir); + strcat (cat_name, t2); + goto gotit; + } + } + + if (standards & FHS) { + if (*name != '/') + return 0; + + /* possibly strip locale part */ + t3 = t2; + if ((t4 = rindex(name,'/')) != NULL && strcmp(t4, "/man")) { + *t3 = '/'; + t3 = t4; + *t3 = 0; + } + + if(t3 - name >= 4 && !strcmp(t3 - 4, "/man")) { + /* fhs is applicable; strip leading /usr and trailing share */ + if(!strncmp(name, "/usr/", 5)) + name += 4; + t4 = t3 - 4; + *t4 = 0; + if(t4 - name >= 6 && !strcmp(t4 - 6, "/share")) + t4[-6] = 0; + *t3 = '/'; + + len += strlen("/var/cache/man") + strlen(name) + strlen(t3) + 1; + cat_name = (char *) my_malloc (len); + strcpy (cat_name, "/var/cache/man"); + strcat (cat_name, name); + strcat (cat_name, t3); + goto gotit; + } + + return 0; + } + + if ((standards & FSSTND) && !strncmp(name, "/usr/", 5)) { + /* search, starting at the end, for a part `man' to delete */ + t3 = t2; + while ((t4 = rindex(name, '/')) != NULL && strcmp(t4, "/man")) { + *t3 = '/'; + t3 = t4; + *t3 = 0; + } + *t3 = '/'; + if (t4) { + *t4 = 0; + len += strlen("/var/catman") + strlen (name+4) + strlen (t3) + 1; + cat_name = (char *) my_malloc (len); + strcpy (cat_name, "/var/catman"); + strcat (cat_name, name+4); + strcat (cat_name, t3); + goto gotit; + } + } else + *t2 = '/'; + + if (ext) { /* allocate room for extension */ + len += strlen(name) + 1; + cat_name = (char *) my_malloc (len); + strcpy (cat_name, name); + } else + cat_name = name; + +gotit: + + if ((standards & DO_HP) && get_expander(cat_name)) { + /* nothing - we have cat1.Z/file.1 */ + } else if (ext) + strcat (cat_name, ext); + + if (name != cat_name) + free (freename); + + return cat_name; +} diff --git a/src/to_cat.h b/src/to_cat.h new file mode 100644 index 0000000..c9cc7e9 --- /dev/null +++ b/src/to_cat.h @@ -0,0 +1,3 @@ +extern const char *mandir_of (const char *name); +extern const char *convert_to_cat (const char *name, const char *ext, + int standards); diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..d074451 --- /dev/null +++ b/src/util.c @@ -0,0 +1,305 @@ +/* + * util.c + * + * Copyright (c) 1990, 1991, John W. Eaton. + * + * You may distribute under the terms of the GNU General Public + * License as specified in the file COPYING that comes with the man + * distribution. + * + * John W. Eaton + * jwe@che.utexas.edu + * Department of Chemical Engineering + * The University of Texas at Austin + * Austin, Texas 78712 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "gripes.h" +#include "man.h" /* for debug */ + +/* + * Extract last element of a name like /foo/bar/baz. + */ +const char * +mkprogname (const char *s) { + const char *t; + + t = strrchr (s, '/'); + if (t == (char *)NULL) + t = s; + else + t++; + + return my_strdup (t); +} + +/* + * Is file a nonempty and newer than file b? + * + * case: + * + * a newer than b returns 1 + * a older than b returns 0 + * stat on a fails or a empty returns -1 + * stat on b fails or b empty returns -2 + * both fail or empty returns -3 + */ +int +is_newer (const char *fa, const char *fb) { + struct stat fa_sb; + struct stat fb_sb; + register int fa_stat; + register int fb_stat; + register int status = 0; + + fa_stat = stat (fa, &fa_sb); + if (fa_stat != 0 || fa_sb.st_size == 0) + status = 1; + + fb_stat = stat (fb, &fb_sb); + if (fb_stat != 0 || fb_sb.st_size == 0) + status |= 2; + + if (status != 0) + return -status; + + return (fa_sb.st_mtime > fb_sb.st_mtime); +} + +int ruid, rgid, euid, egid, suid; + +void +get_permissions (void) { + ruid = getuid(); + euid = geteuid(); + rgid = getgid(); + egid = getegid(); + suid = (ruid != euid || rgid != egid); +} + +void +no_privileges (void) { + if (suid) { +#if !defined (__CYGWIN__) && !defined (__BEOS__) + setreuid(ruid, ruid); + setregid(rgid, rgid); +#endif + suid = 0; + } +} + +/* + * What to do upon an interrupt? Experience shows that + * if we exit immediately, sh notices that its child has + * died and will try to fiddle with the tty. + * Simultaneously, also less will fiddle with the tty, + * resetting the mode before exiting. + * This leads to undesirable races. So, we catch SIGINT here + * and exit after the child has exited. + */ +static int interrupted = 0; +static void catch_int(int a) { + interrupted = 1; +} + +static int +system1 (const char *command) { + void (*prev_handler)(int) = signal (SIGINT,catch_int); + int ret = system(command); + + /* child terminated with signal? */ + if (WIFSIGNALED(ret) && + (WTERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT)) + exit(1); + + /* or we caught an interrupt? */ + if (interrupted) + exit(1); + + signal(SIGINT,prev_handler); + return ret; +} + +static int +my_system (const char *command) { + int pid, pid2, status, stat; + + if (!suid) + return system1 (command); + +#ifdef _POSIX_SAVED_IDS + + /* we need not fork */ + setuid(ruid); + setgid(rgid); + status = system1(command); + setuid(euid); + setgid(egid); + return (WIFEXITED(status) ? WEXITSTATUS(status) : 127); +#endif + + fflush(stdout); fflush(stderr); + pid = fork(); + if (pid == -1) { + perror(progname); + fatal (CANNOT_FORK, command); + } + if (pid == 0) { + setuid(ruid); + setgid(rgid); + status = system1 (command); + exit(WIFEXITED(status) ? WEXITSTATUS(status) : 127); + } + pid2 = wait (&stat); + if (pid2 == -1) { + perror(progname); + fatal (WAIT_FAILED, command); /* interrupted? */ + } + if (pid2 != pid) + fatal (GOT_WRONG_PID); + if (WIFEXITED(stat) && WEXITSTATUS(stat) != 127) + return WEXITSTATUS(stat); + fatal (CHILD_TERMINATED_ABNORMALLY, command); + return -1; /* not reached */ +} + +FILE * +my_popen(const char *command, const char *type) { + FILE *r; + + if (!suid) + return popen(command, type); + +#ifdef _POSIX_SAVED_IDS + setuid(ruid); + setgid(rgid); + r = popen(command, type); + setuid(euid); + setgid(egid); + return r; +#endif + + no_privileges(); + return popen(command, type); +} + +#define NOT_SAFE "/unsafe/" + +/* + * Attempt a system () call. + */ +int +do_system_command (const char *command, int silent) { + int status = 0; + + /* + * If we're debugging, don't really execute the command + */ + if ((debug & 1) || !strncmp(command, NOT_SAFE, strlen(NOT_SAFE))) + fatal (NO_EXEC, command); + else + status = my_system (command); + + if (status && !silent) + gripe (SYSTEM_FAILED, command, status); + + return status; +} + +char * +my_malloc (int n) { + char *s = malloc(n); + if (!s) + fatal (OUT_OF_MEMORY, n); + return s; +} + +char * +my_strdup (const char *s) { + char *t = my_malloc(strlen(s) + 1); + strcpy(t, s); + return t; +} + +/* + * Call: my_xsprintf(format,s1,s2,...) where format only contains %s/%S/%Q + * (or %d or %o) and all %s/%S/%Q parameters are strings. + * Result: allocates a new string containing the sprintf result. + * The %S parameters are checked for being shell safe. + * The %Q parameters are checked for being shell safe inside single quotes. + */ + +static int +is_shell_safe(const char *ss, int quoted) { + char *bad = " ;'\\\"<>|&"; + char *p; + + if (quoted) + bad++; /* allow a space inside quotes */ + for (p = bad; *p; p++) + if (strchr(ss, *p)) + return 0; + return 1; +} + +static void +nothing(int x) {} + +char * +my_xsprintf (char *format, ...) { + va_list p; + char *s, *ss, *fm; + int len; + + len = strlen(format) + 1; + fm = my_strdup(format); + + va_start(p, format); + for (s = fm; *s; s++) { + if (*s == '%') { + switch (s[1]) { + case 'Q': + case 'S': /* check and turn into 's' */ + ss = va_arg(p, char *); + if (!is_shell_safe(ss, (s[1] == 'Q'))) + return NOT_SAFE; + len += strlen(ss); + s[1] = 's'; + break; + case 's': + len += strlen(va_arg(p, char *)); + break; + case 'd': + case 'o': + case 'c': + len += 20; + nothing(va_arg(p, int)); /* advance */ + break; + default: + fprintf(stderr, + "my_xsprintf called with %s\n", + format); + exit(1); + } + } + } + va_end(p); + + s = my_malloc(len); + va_start(p, format); + vsprintf(s, fm, p); + va_end(p); + + return s; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..c317bd9 --- /dev/null +++ b/src/util.h @@ -0,0 +1,13 @@ +/* functions and variables exported from util.c */ + +void get_permissions (void); +void no_privileges (void); +char *my_malloc (int n); +char *my_strdup (const char *s); +const char *mkprogname (const char *s); +int is_newer (const char *fa, const char *fb); +int do_system_command (const char *cmd, int silent); +FILE *my_popen(const char *cmd, const char *type); +char *my_xsprintf(char *f,...); + +extern int ruid, rgid, euid, egid, suid; diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..b575ffd --- /dev/null +++ b/src/version.h @@ -0,0 +1 @@ +static char version[] = "1.6g"; diff --git a/src/whatis b/src/whatis new file mode 100755 index 0000000..22da854 --- /dev/null +++ b/src/whatis @@ -0,0 +1,88 @@ +#!/bin/sh +# +# apropos -- search the whatis database for keywords. +# whatis -- idem, but match only commands (as whole words). +# +# Copyright (c) 1990, 1991, John W. Eaton. +# Copyright (c) 1994-1999, Andries E. Brouwer. +# +# You may distribute under the terms of the GNU General Public +# License as specified in the README file that comes with the man +# distribution. +# +# apropos/whatis-1.5m aeb 2003-08-01 (from man-1.6g) +# +# keep old PATH - 000323 - Bryan Henderson +# also look in /var/cache/man - 030801 - aeb + +program=`basename $0` + +# When man pages in your favorite locale look to grep like binary files +# (and you use GNU grep) you may want to add the 'a' option to *grepopt1. +aproposgrepopt1='i' +aproposgrepopt2='' +whatisgrepopt1='iw' +whatisgrepopt2='^' +grepopt1=$whatisgrepopt1 +grepopt2=$whatisgrepopt2 + +if [ $# = 0 ] +then + echo "usage: $program keyword ..." + exit 1 +fi + +manpath=`man --path | tr : '\040'` + +if [ "$manpath" = "" ] +then + echo "$program: manpath is null" + exit 1 +fi + +args= +for arg in $*; do + case $arg in + --version|-V|-v) + echo "$program from man-1.6g" + exit 0 + ;; + --help|-h) + echo "usage: $program keyword ..." + exit 0 + ;; + -*) + echo "$program: $arg: unknown option" + exit 1 + ;; + *) + args="$args $arg" + esac +done + +while [ "$1" != "" ] +do + found=0 + for d in /var/cache/man $manpath /usr/lib + do + if [ -f $d/whatis ] + then + if grep -"$grepopt1" "$grepopt2""$1" $d/whatis + then + found=1 +# Some people are satisfied with a single occurrence +# But it is better to give all +# break + fi + fi + done + + if [ $found = 0 ] + then + echo "$1: nothing appropriate" + fi + + shift +done + +exit -- cgit v1.2.3