Replacement for man(1) (part 2 of 2)
John W. Eaton
jwe at che.utexas.edu
Mon Jan 7 08:26:25 AEST 1991
-------------------------------cut here-------------------------------
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 2 (of 2)."
# Contents: man-1.0/glob.c man-1.0/man.c
# Wrapped by jwe at andy.che.utexas.edu on Sun Jan 6 15:10:31 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'man-1.0/glob.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'man-1.0/glob.c'\"
else
echo shar: Extracting \"'man-1.0/glob.c'\" \(12628 characters\)
sed "s/^X//" >'man-1.0/glob.c' <<'END_OF_FILE'
X/* File-name wildcard pattern matching for GNU.
X Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 1, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* To whomever it may concern: I have never seen the code which most
X Unix programs use to perform this function. I wrote this from scratch
X based on specifications for the pattern matching. --RMS. */
X
X#if defined (SYSV) && !defined (Xenix)
X# define SYSVr3
X#endif
X
X#include <sys/types.h>
X
X#if defined (SYSVr3) || defined (DIRENT)
X# include <dirent.h>
X# define direct dirent
X# define D_NAMLEN(d) strlen((d)->d_name)
X#else
X# define D_NAMLEN(d) ((d)->d_namlen)
X# if defined (xenix)
X# include <sys/ndir.h>
X# else
X# if defined (SYSV)
X# include "ndir.h"
X# else
X# include <sys/dir.h>
X# endif
X# endif
X#endif /* SYSVr3 or DIRENT. */
X
X#if defined (NeXT)
X#include <string.h>
X#else
X#if defined (SYSV)
X#include <memory.h>
X#include <string.h>
X#define bcopy(s, d, n) ((void) memcpy ((d), (s), (n)))
X#define rindex strrchr
X
X#else /* not SYSV */
X#include <strings.h>
X
Xextern void bcopy ();
X#endif /* not SYSV */
X#endif /* not NeXT */
X
Xextern char *malloc (), *realloc ();
Xextern void free ();
X
X#ifndef NULL
X#define NULL 0
X#endif
X
X/* Global variable which controls whether or not * matches .*.
X Non-zero means don't match .*. */
Xint noglob_dot_filenames = 1;
X
X
Xstatic int glob_match_after_star ();
X
X/* Return nonzero if PATTERN has any special globbing chars in it. */
Xint
Xglob_pattern_p (pattern)
X char *pattern;
X{
X register char *p = pattern;
X register char c;
X
X while ((c = *p++) != '\0')
X switch (c)
X {
X case '?':
X case '[':
X case '*':
X return 1;
X
X case '\\':
X if (*p++ == '\0')
X return 0;
X }
X
X return 0;
X}
X
X/* Match the pattern PATTERN against the string TEXT;
X return 1 if it matches, 0 otherwise.
X
X A match means the entire string TEXT is used up in matching.
X
X In the pattern string, `*' matches any sequence of characters,
X `?' matches any character, [SET] matches any character in the specified set,
X [!SET] matches any character not in the specified set.
X
X A set is composed of characters or ranges; a range looks like
X character hyphen character (as in 0-9 or A-Z).
X [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
X Any other character in the pattern must be matched exactly.
X
X To suppress the special syntactic significance of any of `[]*?!-\',
X and match the character exactly, precede it with a `\'.
X
X If DOT_SPECIAL is nonzero,
X `*' and `?' do not match `.' at the beginning of TEXT. */
X
Xint
Xglob_match (pattern, text, dot_special)
X char *pattern, *text;
X int dot_special;
X{
X register char *p = pattern, *t = text;
X register char c;
X
X while ((c = *p++) != '\0')
X switch (c)
X {
X case '?':
X if (*t == '\0' || (dot_special && t == text && *t == '.'))
X return 0;
X else
X ++t;
X break;
X
X case '\\':
X if (*p++ != *t++)
X return 0;
X break;
X
X case '*':
X if (dot_special && t == text && *t == '.')
X return 0;
X return glob_match_after_star (p, t);
X
X case '[':
X {
X register char c1 = *t++;
X int invert;
X
X if (!c1)
X return (0);
X
X invert = ((*p == '!') || (*p == '^'));
X if (invert)
X p++;
X
X c = *p++;
X while (1)
X {
X register char cstart = c, cend = c;
X
X if (c == '\\')
X {
X cstart = *p++;
X cend = cstart;
X }
X
X if (c == '\0')
X return 0;
X
X c = *p++;
X if (c == '-')
X {
X cend = *p++;
X if (cend == '\\')
X cend = *p++;
X if (cend == '\0')
X return 0;
X c = *p++;
X }
X if (c1 >= cstart && c1 <= cend)
X goto match;
X if (c == ']')
X break;
X }
X if (!invert)
X return 0;
X break;
X
X match:
X /* Skip the rest of the [...] construct that already matched. */
X while (c != ']')
X {
X if (c == '\0')
X return 0;
X c = *p++;
X if (c == '\0')
X return 0;
X else if (c == '\\')
X ++p;
X }
X if (invert)
X return 0;
X break;
X }
X
X default:
X if (c != *t++)
X return 0;
X }
X
X return *t == '\0';
X}
X
X/* Like glob_match, but match PATTERN against any final segment of TEXT. */
X
Xstatic int
Xglob_match_after_star (pattern, text)
X char *pattern, *text;
X{
X register char *p = pattern, *t = text;
X register char c, c1;
X
X while ((c = *p++) == '?' || c == '*')
X if (c == '?' && *t++ == '\0')
X return 0;
X
X if (c == '\0')
X return 1;
X
X if (c == '\\')
X c1 = *p;
X else
X c1 = c;
X
X while (1)
X {
X if ((c == '[' || *t == c1) && glob_match (p - 1, t, 0))
X return 1;
X if (*t++ == '\0')
X return 0;
X }
X}
X
X/* Return a vector of names of files in directory DIR
X whose names match glob pattern PAT.
X The names are not in any particular order.
X Wildcards at the beginning of PAT do not match an initial period.
X
X The vector is terminated by an element that is a null pointer.
X
X To free the space allocated, first free the vector's elements,
X then free the vector.
X
X Return 0 if cannot get enough memory to hold the pointer
X and the names.
X
X Return -1 if cannot access directory DIR.
X Look in errno for more information. */
X
Xchar **
Xglob_vector (pat, dir)
X char *pat;
X char *dir;
X{
X struct globval
X {
X struct globval *next;
X char *name;
X };
X
X DIR *d;
X register struct direct *dp;
X struct globval *lastlink;
X register struct globval *nextlink;
X register char *nextname;
X unsigned int count;
X int lose;
X register char **name_vector;
X register unsigned int i;
X
X d = opendir (dir);
X if (d == NULL)
X return (char **) -1;
X
X lastlink = 0;
X count = 0;
X lose = 0;
X
X /* Scan the directory, finding all names that match.
X For each name that matches, allocate a struct globval
X on the stack and store the name in it.
X Chain those structs together; lastlink is the front of the chain. */
X while (1)
X {
X dp = readdir (d);
X if (dp == NULL)
X break;
X if (dp->d_ino != 0
X && glob_match (pat, dp->d_name, noglob_dot_filenames))
X {
X nextlink = (struct globval *) alloca (sizeof (struct globval));
X nextlink->next = lastlink;
X nextname = (char *) malloc (D_NAMLEN(dp) + 1);
X if (nextname == NULL)
X {
X lose = 1;
X break;
X }
X lastlink = nextlink;
X nextlink->name = nextname;
X bcopy (dp->d_name, nextname, D_NAMLEN(dp) + 1);
X ++count;
X }
X }
X (void) closedir (d);
X
X if (!lose)
X {
X name_vector = (char **) malloc ((count + 1) * sizeof (char *));
X lose |= name_vector == NULL;
X }
X
X /* Have we run out of memory? */
X if (lose)
X {
X /* Here free the strings we have got. */
X while (lastlink)
X {
X free (lastlink->name);
X lastlink = lastlink->next;
X }
X return NULL;
X }
X
X /* Copy the name pointers from the linked list into the vector. */
X for (i = 0; i < count; ++i)
X {
X name_vector[i] = lastlink->name;
X lastlink = lastlink->next;
X }
X
X name_vector[count] = NULL;
X return name_vector;
X}
X
X/* Return a new array which is the concatenation
X of each string in ARRAY to DIR. */
X
Xstatic char **
Xglob_dir_to_array (dir, array)
X char *dir, **array;
X{
X register unsigned int i, l;
X int add_slash;
X char **result;
X
X l = strlen (dir);
X if (l == 0)
X return array;
X
X add_slash = dir[l - 1] != '/';
X
X i = 0;
X while (array[i] != NULL)
X ++i;
X
X result = (char **) malloc ((i + 1) * sizeof (char *));
X if (result == NULL)
X return NULL;
X
X for (i = 0; array[i] != NULL; i++)
X {
X result[i] = (char *) malloc (l + (add_slash ? 1 : 0)
X + strlen (array[i]) + 1);
X if (result[i] == NULL)
X return NULL;
X sprintf (result[i], "%s%s%s", dir, add_slash ? "/" : "", array[i]);
X }
X result[i] = NULL;
X
X /* Free the input array. */
X for (i = 0; array[i] != NULL; i++)
X free (array[i]);
X free ((char *) array);
X
X return result;
X}
X
X/* Do globbing on PATHNAME. Return an array of pathnames that match,
X marking the end of the array with a null-pointer as an element.
X If no pathnames match, then the array is empty (first element is null).
X If there isn't enough memory, then return NULL.
X If a file system error occurs, return -1; `errno' has the error code. */
Xchar **
Xglob_filename (pathname)
X char *pathname;
X{
X char **result;
X unsigned int result_size;
X char *directory_name, *filename;
X unsigned int directory_len;
X
X result = (char **) malloc (sizeof (char *));
X result_size = 1;
X if (result == NULL)
X return NULL;
X
X result[0] = NULL;
X
X /* Find the filename. */
X filename = rindex (pathname, '/');
X if (filename == NULL)
X {
X filename = pathname;
X directory_name = "";
X directory_len = 0;
X }
X else
X {
X directory_len = (filename - pathname) + 1;
X directory_name = (char *) alloca (directory_len + 1);
X
X bcopy (pathname, directory_name, directory_len);
X directory_name[directory_len] = '\0';
X ++filename;
X }
X
X /* If directory_name contains globbing characters, then we
X have to expand the previous levels. Just recurse. */
X if (glob_pattern_p (directory_name))
X {
X char **directories;
X register unsigned int i;
X
X if (directory_name[directory_len - 1] == '/')
X directory_name[directory_len - 1] = '\0';
X
X directories = glob_filename (directory_name);
X
X if (directories == NULL)
X goto memory_error;
X else if ((int) directories == -1)
X return (char **) -1;
X else if (*directories == NULL)
X {
X free ((char *) directories);
X return (char **) -1;
X }
X
X /* We have successfully globbed the preceding directory name.
X For each name in DIRECTORIES, call glob_vector on it and
X FILENAME. Concatenate the results together. */
X for (i = 0; directories[i] != NULL; ++i)
X {
X char **temp_results = glob_vector (filename, directories[i]);
X
X /* Handle error cases. */
X if (temp_results == NULL)
X goto memory_error;
X else if (temp_results == (char **)-1)
X /* This filename is probably not a directory. Ignore it. */
X ;
X else
X {
X char **array = glob_dir_to_array (directories[i], temp_results);
X register unsigned int l;
X
X l = 0;
X while (array[l] != NULL)
X ++l;
X
X result =
X (char **)realloc (result, (result_size + l) * sizeof (char *));
X
X if (result == NULL)
X goto memory_error;
X
X for (l = 0; array[l] != NULL; ++l)
X result[result_size++ - 1] = array[l];
X
X result[result_size - 1] = NULL;
X
X /* Note that the elements of ARRAY are not freed. */
X free ((char *) array);
X }
X }
X /* Free the directories. */
X for (i = 0; directories[i]; i++)
X free (directories[i]);
X
X free ((char *) directories);
X
X return result;
X }
X
X /* If there is only a directory name, return it. */
X if (*filename == '\0')
X {
X result = (char **) realloc ((char *) result, 2 * sizeof (char *));
X if (result == NULL)
X return NULL;
X result[0] = (char *) malloc (directory_len + 1);
X if (result[0] == NULL)
X goto memory_error;
X bcopy (directory_name, result[0], directory_len + 1);
X result[1] = NULL;
X return result;
X }
X else
X {
X /* Otherwise, just return what glob_vector
X returns appended to the directory name. */
X char **temp_results = glob_vector (filename,
X (directory_len == 0
X ? "." : directory_name));
X
X if (temp_results == NULL || temp_results == (char **)-1)
X return temp_results;
X
X return glob_dir_to_array (directory_name, temp_results);
X }
X
X memory_error:;
X if (result != NULL)
X {
X register unsigned int i;
X for (i = 0; result[i] != NULL; ++i)
X free (result[i]);
X free ((char *) result);
X }
X return NULL;
X}
X
X#ifdef TEST
X
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X unsigned int i;
X
X for (i = 1; i < argc; ++i)
X {
X char **value = glob_filename (argv[i]);
X if (value == NULL)
X puts ("Out of memory.");
X else if ((int) value == -1)
X perror (argv[i]);
X else
X for (i = 0; value[i] != NULL; i++)
X puts (value[i]);
X }
X
X exit (0);
X}
X#endif /* TEST. */
X
X
END_OF_FILE
if test 12628 -ne `wc -c <'man-1.0/glob.c'`; then
echo shar: \"'man-1.0/glob.c'\" unpacked with wrong size!
fi
# end of 'man-1.0/glob.c'
fi
if test -f 'man-1.0/man.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'man-1.0/man.c'\"
else
echo shar: Extracting \"'man-1.0/man.c'\" \(19322 characters\)
sed "s/^X//" >'man-1.0/man.c' <<'END_OF_FILE'
X/*
X * man.c
X *
X * Copyright (c) 1991, John W. Eaton.
X *
X * You may distribute under the terms of the GNU General Public
X * License as specified in the README file that comes with the man 1.0
X * distribution.
X *
X * John W. Eaton
X * jwe at che.utexas.edu
X * Department of Chemical Engineering
X * The University of Texas at Austin
X * Austin, Texas 78712
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X#include <signal.h>
X#include "config.h"
X#include "gripes.h"
X#include "version.h"
X
X#ifdef STD_HEADERS
X#include <stdlib.h>
X#else
Xextern char *malloc ();
Xextern char *getenv ();
Xextern void free ();
Xextern int system ();
Xextern int strcmp ();
Xextern int strncmp ();
Xextern int exit ();
Xextern int fflush ();
Xextern int fprintf ();
Xextern FILE *fopen ();
Xextern int fclose ();
X#ifdef CHARSPRINTF
Xextern char *sprintf ();
X#else
Xextern int sprintf ();
X#endif
X#endif
X
Xextern char *strdup ();
X
Xextern char **glob_vector ();
Xextern int access ();
Xextern int unlink ();
Xextern int system ();
Xextern int stat ();
X
Xchar *prognam;
Xstatic char *pager;
Xstatic char *manp;
Xstatic char *manpathlist[MAXDIRS];
Xstatic char *section;
Xstatic int apropos;
Xstatic int whatis;
Xstatic int findall;
X
X#ifdef ALT_SYSTEMS
Xstatic int alt_system;
Xstatic char *alt_system_name;
X#endif
X
X#ifdef HAS_TROFF
Xstatic int troff;
X#endif
X
Xint debug;
X
Xint
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X int status = 0;
X char *nextarg;
X char *tmp;
X extern int optind;
X extern char *mkprogname ();
X char *is_section ();
X void man_getopt ();
X void do_apropos ();
X void do_whatis ();
X int man ();
X
X prognam = mkprogname (argv[0]);
X
X man_getopt (argc, argv);
X
X if (optind == argc)
X gripe_no_name (NULL);
X
X if (optind == argc - 1)
X {
X tmp = is_section (argv[optind]);
X
X if (tmp != NULL)
X gripe_no_name (tmp);
X }
X
X while (optind < argc)
X {
X nextarg = argv[optind++];
X
X /*
X * See if this argument is a valid section name. If not,
X * is_section returns NULL.
X */
X tmp = is_section (nextarg);
X
X if (tmp != NULL)
X {
X section = tmp;
X
X if (debug)
X fprintf (stderr, "\nsection: %s\n", section);
X
X continue;
X }
X
X if (apropos)
X do_apropos (nextarg);
X else if (whatis)
X do_whatis (nextarg);
X else
X {
X status = man (nextarg);
X
X if (status == 0)
X gripe_not_found (nextarg, section);
X }
X }
X return status;
X}
X
X/*
X * Get options from the command line and user environment.
X */
Xvoid
Xman_getopt (argc, argv)
X register int argc;
X register char **argv;
X{
X register int c;
X register char *p;
X register char *end;
X register char **mp;
X extern char *optarg;
X extern int getopt ();
X extern void downcase ();
X extern char *manpath ();
X void usage ();
X
X#ifdef HAS_TROFF
X#ifdef ALT_SYSTEMS
X while ((c = getopt (argc, argv, "M:P:S:adfhkt?")) != EOF)
X#else
X while ((c = getopt (argc, argv, "M:P:adfhkt?")) != EOF)
X#endif
X#else
X#ifdef ALT_SYSTEMS
X while ((c = getopt (argc, argv, "M:P:S:adfhk?")) != EOF)
X#else
X while ((c = getopt (argc, argv, "M:P:adfhk?")) != EOF)
X#endif
X#endif
X {
X switch (c)
X {
X case 'M':
X manp = strdup (optarg);
X break;
X case 'P':
X pager = strdup (optarg);
X break;
X#ifdef ALT_SYSTEMS
X case 'S':
X alt_system++;
X alt_system_name = strdup (optarg);
X break;
X#endif
X case 'a':
X findall++;
X break;
X case 'd':
X debug++;
X break;
X case 'f':
X#ifdef HAS_TROFF
X if (troff)
X gripe_incompatible ("-f and -t");
X#endif
X if (apropos)
X gripe_incompatible ("-f and -k");
X whatis++;
X break;
X case 'k':
X#ifdef HAS_TROFF
X if (troff)
X gripe_incompatible ("-t and -k");
X#endif
X if (whatis)
X gripe_incompatible ("-f and -k");
X apropos++;
X break;
X#ifdef HAS_TROFF
X case 't':
X if (apropos)
X gripe_incompatible ("-t and -k");
X if (whatis)
X gripe_incompatible ("-t and -f");
X troff++;
X break;
X#endif
X case 'h':
X case '?':
X default:
X usage();
X break;
X }
X }
X
X if (pager == NULL || *pager == NULL)
X if ((pager = getenv ("PAGER")) == NULL)
X pager = strdup (PAGER);
X
X if (debug)
X fprintf (stderr, "\nusing %s as pager\n", pager);
X
X if (manp == NULL)
X {
X if ((manp = manpath (0)) == NULL)
X gripe_manpath ();
X
X if (debug)
X fprintf (stderr,
X "\nsearch path for pages determined by manpath is\n%s\n\n",
X manp);
X }
X
X#ifdef ALT_SYSTEMS
X if (alt_system_name == NULL || *alt_system_name == NULL)
X if ((alt_system_name = getenv ("SYSTEM")) == NULL)
X alt_system_name = strdup (alt_system_name);
X
X downcase (alt_system_name);
X#endif
X
X /*
X * Expand the manpath into a list for easier handling.
X */
X mp = manpathlist;
X for (p = manp ; ; p = end+1)
X {
X if ((end = strchr (p, ':')) != NULL)
X *end = '\0';
X
X if (debug)
X fprintf (stderr, "adding %s to manpathlist\n", p);
X
X#ifdef ALT_SYSTEMS
X if (alt_system)
X {
X char buf[BUFSIZ];
X
X strcpy (buf, p);
X strcat (buf, "/");
X strcat (buf, alt_system_name);
X
X *mp++ = strdup (buf);
X }
X#else
X *mp++ = strdup (p);
X#endif
X
X if (end == NULL)
X break;
X }
X *mp = NULL;
X
X}
X
Xvoid
Xusage ()
X{
X static char usage_string[1024] = "%s, version %s\n\n";
X
X#ifdef HAS_TROFF
X#ifdef ALT_SYSTEMS
X static char s1[] = "usage: %s [-afhkt] [section] [-M path] [-P pager] [-S system] name ...\n\n";
X#else
X static char s1[] = "usage: %s [-afhkt] [section] [-M path] [-P pager] name ...\n\n";
X#endif
X#else
X#ifdef ALT_SYSTEMS
X static char s1[] = "usage: %s [-afhk] [section] [-M path] [-P pager] [-S system] name ...\n\n";
X#else
X static char s1[] = "usage: %s [-afhk] [section] [-M path] [-P pager] name ...\n\n";
X#endif
X#endif
X
Xstatic char s2[] = " a : find all matching entries\n\
X d : print gobs of debugging information\n\
X f : same as whatis(1)\n\
X h : print this help message\n\
X k : same as apropos(1)\n";
X
X#ifdef HAS_TROFF
X static char s3[] = " t : use troff to format pages for printing\n";
X#endif
X
X static char s4[] = "\n M path : set search path for manual pages to `path'\n\
X P pager : use program `pager' to display pages\n";
X
X#ifdef ALT_SYSTEMS
X static char s5[] = " S system : search for alternate system's man pages\n";
X#endif
X
X strcat (usage_string, s1);
X strcat (usage_string, s2);
X
X#ifdef HAS_TROFF
X strcat (usage_string, s3);
X#endif
X
X strcat (usage_string, s4);
X
X#ifdef ALT_SYSTEMS
X strcat (usage_string, s5);
X#endif
X
X fprintf (stderr, usage_string, prognam, version, prognam);
X exit(1);
X}
X
X/*
X * Check to see if the argument is a valid section number. If the
X * first character of name is a numeral, or the name matches one of
X * the sections listed in config.h, we'll assume that it's a section.
X * The list of sections in config.h simply allows us to specify oddly
X * named directories like .../man3f. Yuk.
X */
Xchar *
Xis_section (name)
X register char *name;
X{
X register char **vs;
X
X for (vs = valid_sections; *vs != NULL; vs++)
X if ((strcmp (*vs, name) == NULL) || (isdigit (name[0])))
X return strdup (name);
X
X return NULL;
X}
X
X/*
X * Handle the apropos option. Cheat by using another program.
X */
Xvoid
Xdo_apropos (name)
X register char *name;
X{
X int status;
X register int len;
X register char *command;
X
X len = strlen (APROPOS) + strlen (name) + 2;
X
X if ((command = malloc(len)) == NULL)
X gripe_alloc (len, "command");
X
X sprintf (command, "%s %s", APROPOS, name);
X
X status = 0;
X if (debug)
X fprintf (stderr, "\ntrying command: %s\n", command);
X else
X status = system (command);
X
X if (status == 127)
X gripe_system_command (status);
X
X free (command);
X}
X
X/*
X * Handle the whatis option. Cheat by using another program.
X */
Xvoid
Xdo_whatis (name)
X register char *name;
X{
X int status;
X register int len;
X register char *command;
X
X len = strlen (WHATIS) + strlen (name) + 2;
X
X if ((command = malloc(len)) == NULL)
X gripe_alloc (len, "command");
X
X sprintf (command, "%s %s", WHATIS, name);
X
X status = 0;
X if (debug)
X fprintf (stderr, "\ntrying command: %s\n", command);
X else
X status = system (command);
X
X if (status == 127)
X gripe_system_command (status);
X
X free (command);
X}
X
X/*
X * Search for manual pages.
X *
X * If preformatted manual pages are supported, look for the formatted
X * file first, then the man page source file. If they both exist and
X * the man page source file is newer, or only the source file exists,
X * try to reformat it and write the results in the cat directory. If
X * it is not possible to write the cat file, simply format and display
X * the man file.
X *
X * If preformatted pages are not supported, or the troff option is
X * being used, only look for the man page source file.
X *
X */
Xint
Xman (name)
X char *name;
X{
X register int found;
X register int glob;
X register char **mp;
X register char **sp;
X int try_section ();
X
X found = 0;
X
X fflush (stdout);
X if (section != NULL)
X {
X for (mp = manpathlist; *mp != NULL; mp++)
X {
X if (debug)
X fprintf (stderr, "\nsearching in %s\n", *mp);
X
X glob = 0;
X
X found += try_section (*mp, section, name, glob);
X
X if (found && !findall) /* i.e. only do this section... */
X return found;
X }
X }
X else
X {
X for (sp = valid_sections; *sp != NULL; sp++)
X {
X for (mp = manpathlist; *mp != NULL; mp++)
X {
X if (debug)
X fprintf (stderr, "\nsearching in %s\n", *mp);
X
X glob = 1;
X
X found += try_section (*mp, *sp, name, glob);
X
X if (found && !findall) /* i.e. only do this section... */
X return found;
X
X }
X }
X }
X return found;
X}
X
X/*
X * See if the preformatted man page or the source exists in the given
X * section.
X */
Xint
Xtry_section (path, section, name, glob)
X register char *path;
X register char *section;
X register char *name;
X register int glob;
X{
X register int found = 0;
X register int to_cat;
X register int cat;
X register char **names;
X register char **np;
X char **glob_for_file ();
X char **make_name ();
X char *convert_name ();
X char *ultimate_source ();
X int display_cat_file ();
X int format_and_display ();
X
X if (debug)
X {
X if (glob)
X fprintf (stderr, "trying section %s with globbing\n", section);
X else
X fprintf (stderr, "trying section %s without globbing\n", section);
X }
X
X /*
X * Look for man page source files.
X */
X cat = 0;
X if (glob)
X names = glob_for_file (path, section, name, cat);
X else
X names = make_name (path, section, name, cat);
X
X if ((int) names == -1 || *names == NULL)
X /*
X * No files match. If we're not using troff and we're supporting
X * preformatted pages, see if there's one around that we can
X * display.
X */
X {
X#ifdef HAS_TROFF
X if (cat_support && !troff)
X#else
X if (cat_support)
X#endif
X {
X cat = 1;
X if (glob)
X names = glob_for_file (path, section, name, cat);
X else
X names = make_name (path, section, name, cat);
X
X if ((int) names != -1 && *names != NULL)
X {
X for (np = names; *np != NULL; np++)
X found+= display_cat_file (*np);
X }
X }
X }
X else
X {
X for (np = names; *np != NULL; np++)
X {
X register char *cat_file = NULL;
X register char *man_file;
X
X man_file = ultimate_source (*np, path);
X
X#ifdef HAS_TROFF
X if (cat_support && !troff)
X#else
X if (cat_support)
X#endif
X {
X to_cat = 1;
X
X cat_file = convert_name (man_file, to_cat);
X if (debug)
X fprintf (stderr, "will try to write %s if needed\n", cat_file);
X }
X
X found += format_and_display (path, man_file, cat_file);
X }
X }
X
X return found;
X}
X
X/*
X * Change a name of the form ...man/man1/name.1 to ...man/cat1/name.1
X * or a name of the form ...man/cat1/name.1 to ...man/man1/name.1
X */
Xchar *
Xconvert_name (name, to_cat)
X register char *name;
X register int to_cat;
X{
X register char *to_name;
X register char *t1;
X register char *t2 = NULL;
X
X to_name = strdup (name);
X
X t1 = strrchr (to_name, '/');
X if (t1 != NULL)
X {
X *t1 = NULL;
X t2 = strrchr (to_name, '/');
X *t1 = '/';
X }
X
X if (t2 == NULL)
X gripe_converting_name (name, to_cat);
X
X if (to_cat)
X {
X *(++t2) = 'c';
X *(t2+2) = 't';
X }
X else
X {
X *(++t2) = 'm';
X *(t2+2) = 'n';
X }
X
X return to_name;
X}
X
X
X/*
X * Try to find the man page corresponding to the given name. The
X * reason we do this with globbing is because some systems have man
X * page directories named man3 which contain files with names like
X * XtPopup.3Xt. Rather than requiring that this program know about
X * all those possible names, we simply try to match things like
X * .../man[sect]/name[sect]*. This is *much* easier.
X *
X * Note that globbing is only done when the section is unspecified.
X */
Xchar **
Xglob_for_file (path, section, name, cat)
X register char *path;
X register char *section;
X register char *name;
X register int cat;
X{
X char pathname[BUFSIZ];
X char **glob_filename ();
X char **gf;
X
X if (cat)
X sprintf (pathname, "%s/cat%s/%s.%s*", path, section, name, section);
X else
X sprintf (pathname, "%s/man%s/%s.%s*", path, section, name, section);
X
X if (debug)
X fprintf (stderr, "globbing %s\n", pathname);
X
X gf = glob_filename (pathname);
X
X if (((int) gf == -1 || *gf == NULL) && isdigit (*section))
X {
X if (cat)
X sprintf (pathname, "%s/cat%s/%s.%c*", path, section, name, *section);
X else
X sprintf (pathname, "%s/man%s/%s.%c*", path, section, name, *section);
X
X gf = glob_filename (pathname);
X }
X return gf;
X}
X
X/*
X * Return an un-globbed name in the same form as if we were doing
X * globbing.
X */
Xchar **
Xmake_name (path, section, name, cat)
X register char *path;
X register char *section;
X register char *name;
X register int cat;
X{
X register int i = 0;
X static char *names[3];
X char buf[BUFSIZ];
X
X if (cat)
X sprintf (buf, "%s/cat%s/%s.%s", path, section, name, section);
X else
X sprintf (buf, "%s/man%s/%s.%s", path, section, name, section);
X
X if (access (buf, R_OK) == 0)
X names[i++] = strdup (buf);
X
X /*
X * If we're given a section that looks like `3f', we may want to try
X * file names like .../man3/foo.3f as well. This seems a bit
X * kludgey to me, but what the hey...
X */
X if (section[1] != '\0')
X {
X if (cat)
X sprintf (buf, "%s/cat%c/%s.%s", path, section[0], name, section);
X else
X sprintf (buf, "%s/man%c/%s.%s", path, section[0], name, section);
X
X if (access (buf, R_OK) == 0)
X names[i++] = strdup (buf);
X }
X
X names[i] = NULL;
X
X return &names[0];
X}
X
X/*
X * Simply display the preformmated page.
X */
Xint
Xdisplay_cat_file (file)
X register char *file;
X{
X int status;
X register int found;
X char command[BUFSIZ];
X
X found = 0;
X
X if (access (file, R_OK) == NULL)
X {
X sprintf (command, "%s %s", pager, file);
X
X status = 0;
X if (debug)
X fprintf (stderr, "\ntrying command: %s\n", command);
X else
X status = system (command);
X
X if (status == 127)
X gripe_system_command (status);
X else
X found++;
X }
X return found;
X}
X
X/*
X * Try to find the ultimate source file. If the first line of the
X * current file is not of the form
X *
X * .so man3/printf.3s
X *
X * the input file name is returned.
X */
Xchar *
Xultimate_source (name, path)
X char *name;
X char *path;
X{
X FILE *fp;
X char buf[BUFSIZ];
X char ult[BUFSIZ];
X char *beg;
X char *end;
X
X strcpy (ult, name);
X strcpy (buf, name);
X
X next:
X
X if ((fp = fopen (ult, "r")) == NULL)
X return buf;
X
X if (fgets (buf, BUFSIZ, fp) == NULL)
X return ult;
X
X if (strlen (buf) < 5)
X return ult;
X
X beg = buf;
X if (*beg++ == '.' && *beg++ == 's' && *beg++ == 'o')
X {
X while ((*beg == ' ' || *beg == '\t') && *beg != '\0')
X beg++;
X
X end = beg;
X while (*end != ' ' && *end != '\t' && *end != '\n' && *end != '\0')
X end++;
X
X *end = '\0';
X
X strcpy (ult, path);
X strcat (ult, "/");
X strcat (ult, beg);
X
X strcpy (buf, ult);
X
X goto next;
X }
X
X if (debug)
X fprintf (stderr, "found ultimate source file %s\n", ult);
X
X return ult;
X}
X
X/*
X * Try to format the man page source and save it, then display it. If
X * that's not possible, try to format the man page source and display
X * it directly.
X *
X * Note that in the commands below, the cd is necessary because some
X * man pages are one liners like my version of sprintf.3s:
X *
X * .so man3/printf.3s
X */
Xint
Xformat_and_display (path, man_file, cat_file)
X register char *path;
X register char *man_file;
X register char *cat_file;
X{
X int status;
X int mode;
X register int found;
X FILE *fp;
X char command[BUFSIZ];
X int is_newer ();
X
X found = 0;
X
X if (access (man_file, R_OK) != 0)
X return found;
X
X#ifdef HAS_TROFF
X if (troff || !cat_support)
X {
X if (troff)
X sprintf (command, "(cd %s ; %s %s)", path, troff_command, man_file);
X else
X sprintf (command, "(cd %s ; %s %s | %s)", path, nroff_command,
X man_file, pager);
X#else
X if (!cat_support)
X {
X sprintf (command, "(cd %s ; %s %s | %s)", path, nroff_command,
X man_file, pager);
X#endif
X status = 0;
X if (debug)
X fprintf (stderr, "\ntrying command: %s\n", command);
X else
X status = system (command);
X
X if (status == 127)
X gripe_system_command (status);
X else
X found++;
X }
X else
X {
X if ((status = is_newer (man_file, cat_file)) == 1 || status == -2)
X {
X if ((fp = fopen (cat_file, "w")) != NULL)
X {
X fclose (fp);
X unlink (cat_file);
X
X fprintf (stderr, "Formatting page, please wait...\n");
X
X sprintf (command, "(cd %s ; %s %s > %s)", path,
X nroff_command, man_file, cat_file);
X
X signal (SIGINT, SIG_IGN);
X
X status = 0;
X if (debug)
X fprintf (stderr, "\ntrying command: %s\n", command);
X else
X status = system (command);
X
X if (status == 127)
X gripe_system_command (status);
X else
X found++;
X
X mode = CATMODE;
X chmod (cat_file, mode);
X
X if (debug)
X fprintf (stderr, "mode of %s is now %o\n", cat_file, mode);
X
X signal (SIGINT, SIG_DFL);
X
X found = display_cat_file (cat_file);
X }
X else
X {
X sprintf (command, "(cd %s ; %s %s | %s)", path, nroff_command,
X man_file, pager);
X
X status = 0;
X if (debug)
X fprintf (stderr, "\ntrying command: %s\n", command);
X else
X status = system (command);
X
X if (status == 127)
X gripe_system_command (status);
X else
X found++;
X }
X }
X else if (access (cat_file, R_OK) == 0)
X {
X found = display_cat_file (cat_file);
X }
X }
X return found;
X}
X
X/*
X * Is file a newer than file b?
X *
X * case:
X *
X * a newer than b returns 1
X * a older than b returns 0
X * stat on a fails returns -1
X * stat on b fails returns -2
X * stat on a and b fails returns -3
X */
Xint
Xis_newer (fa, fb)
X register char *fa;
X register char *fb;
X{
X struct stat fa_sb;
X struct stat fb_sb;
X register int fa_stat;
X register int fb_stat;
X register int status = 0;
X
X fa_stat = stat (fa, &fa_sb);
X if (fa_stat != 0)
X status = 1;
X
X fb_stat = stat (fb, &fb_sb);
X if (fb_stat != 0)
X status |= 2;
X
X if (status != 0)
X return -status;
X
X if (fa_sb.st_mtime > fb_sb.st_mtime)
X {
X status = 1;
X
X if (debug)
X fprintf (stderr, "%s is newer than %s\n", fa, fb);
X }
X else
X {
X status = 0;
X
X if (debug)
X fprintf (stderr, "%s is older than %s\n", fa, fb);
X }
X return status;
X}
END_OF_FILE
if test 19322 -ne `wc -c <'man-1.0/man.c'`; then
echo shar: \"'man-1.0/man.c'\" unpacked with wrong size!
fi
# end of 'man-1.0/man.c'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked both archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
More information about the Alt.sources
mailing list