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

Reply via email to