Hi, following the recent switch from installing preformatted manuals to installing source manuals, the following issue needs to be fixed:
If somebody ugrades a machine the standard way to a new snapshot or to a new release, the old preformatted manuals will stay, and man(1) will typically prefer them over the new source manuals. The following solutions are not acceptable: - Tell people to delete the old manuals. Many people will miss or forget that. We don't want people to inadvertently use outdated manuals. Manuals are too important to take that risk. - Update /etc/man.conf. Again, people might miss that and get bitten. So, it has been decided that whenever man(1) finds BASEPATH/catN/PAGE.0 it will check whether the companion BASEPATH/manN/PAGE.N exists in the same base path and vice verse, and in case both exist, only the one with the more recent change time will be displayed, unless the -a or -w options are specified. The cost is a small amount of string manipulation and two additional stats per manual. No additional looping over paths is required, and no loop needs to be exited later. This leaves you with the following options: * Do nothing, get up-to-date manuals, be happy. * Delete the old manuals to reclaim space, get up-to-date manuals. * Create new preformatted manuals from the installed source manuals. The new preformatted ones will than be newer and will be used until you do the next upgrade and getter yet newer source. At some point, i'll probably provide a script to build preformatted manuals, as that script will be very simple. Note that for the companion to be found and used, it is *not* necessary that the companion itself can be found via man.conf(5). Restricting the use of newer companions to those also accessible via man.conf(5) would not be useful: If you have a newer version in the same base tree, you certainly want it, no matter what man.conf(5) says. Besides, restricting would be very complicated and slow, and with the default /etc/man.conf or any other sane version, it doesn't make a difference, anyway. I'm looking for tests, comments and OKs regarding the following patch. Thanks, Ingo Index: man.c =================================================================== RCS file: /cvs/src/usr.bin/man/man.c,v retrieving revision 1.41 diff -u -p -r1.41 man.c --- man.c 25 May 2011 14:36:04 -0000 1.41 +++ man.c 25 Jun 2011 13:46:30 -0000 @@ -2,7 +2,7 @@ /* $NetBSD: man.c,v 1.7 1995/09/28 06:05:34 tls Exp $ */ /* - * Copyright (c) 2010 Ingo Schwarze <schwa...@openbsd.org> + * Copyright (c) 2010, 2011 Ingo Schwarze <schwa...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -46,8 +46,10 @@ * SUCH DAMAGE. */ +#include <sys/types.h> #include <sys/param.h> #include <sys/queue.h> +#include <sys/stat.h> #include <ctype.h> #include <err.h> @@ -81,6 +83,7 @@ static int cleanup(int); static void how(char *); static void jump(char **, char *, char *); static int manual(char *, TAG *, glob_t *); +static void check_companion(char **); static void onsig(int); static void usage(void); @@ -455,6 +458,9 @@ manual(char *page, TAG *tag, glob_t *pg) for (cnt = pg->gl_pathc - pg->gl_matchc; cnt < pg->gl_pathc; ++cnt) { + if (!f_all) + check_companion(pg->gl_pathv + cnt); + /* * Try the _suffix key words first. * @@ -541,6 +547,75 @@ next: anyfound = 1; sigprocmask(SIG_SETMASK, &osigs, NULL); } return (anyfound); +} + +/* + * check_companion -- + * Check for a companion [un]formatted page + * and use the newer one of the two. + */ +static void +check_companion(char **orig) { + struct stat sb_orig, sb_comp; + size_t len; + char *p, *pext, comp[MAXPATHLEN]; + + len = strlcpy(comp, *orig, sizeof(comp)); + p = comp + len; + + /* Locate the file name extension. */ + while (p > comp && *p != '.' && *p != '/') + p--; + if (*p == '.') + pext = p + 1; + else + return; + + /* Locate the last slash, the one before "page". */ + while (p > comp && *p != '/') + p--; + if (--p <= comp) + return; + + /* Locate the previous slash, the one before {cat,man}. */ + while (p > comp && *p != '/') + p--; + if (*p == '/') + p++; + else + return; + + /* Rewrite manN/page.N <-> catN/page.0. */ + if (!strncmp(p, "man", 3)) { + memcpy(p, "cat", 3); + *pext++ = '0'; + } else if (!strncmp(p, "cat", 3)) { + memcpy(p, "man", 3); + p += 3; + while (*p != '/') + *pext++ = *p++; + } else + return; + *pext = '\0'; + + /* Check whether both files exist. */ + if (stat(*orig, &sb_orig) || stat(comp, &sb_comp)) + return; + + /* No action if the companion file is older. */ + if (sb_orig.st_mtim.tv_sec > sb_comp.st_mtim.tv_sec || ( + sb_orig.st_mtim.tv_sec == sb_comp.st_mtim.tv_sec && + sb_orig.st_mtim.tv_nsec > sb_comp.st_mtim.tv_nsec)) + return; + + /* The companion file is newer, use it. */ + free(*orig); + if ((p = strdup(comp)) == NULL) { + warn(NULL); + (void)cleanup(0); + exit(1); + } + *orig = p; } /*