Logo Search packages:      
Sourcecode: man2html version File versions

man.c

/*
 * 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 <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>         /* for chmod */
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <locale.h>

#ifndef R_OK
#define R_OK 4
#endif

extern char *index (const char *, int);         /* not always in <string.h> */
extern char *rindex (const char *, int);  /* not always in <string.h> */

#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;
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 <sys/ioctl.h>

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;
}

/*
 * 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);
        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;
     }

     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
             /* 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);
      /* 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);
             }
        }
     }
     return status ? EXIT_SUCCESS : EXIT_FAILURE;
}

Generated by  Doxygen 1.6.0   Back to index