pkgsrc-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

pkg/59049: pkgtools/mtree is way out of date and fails to build on modern systems



>Number:         59049
>Category:       pkg
>Synopsis:       pkgtools/mtree is way out of date and fails to build on modern systems
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    pkg-manager
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Feb 06 16:25:00 +0000 2025
>Originator:     Taylor R Campbell
>Release:        current
>Organization:
The MtreeBSD Foundapackage
>Environment:
>Description:
pkgtools/mtree hasn't been updated in a long time and has failures like this:

cc -DHAVE_CONFIG_H -DHAVE_NBCOMPAT_H=1 -I/home/brian/pkgs/pkgsrc/pkgtools/mtree/work/libnbcompat -I. -I. -O2 -c compare.c
compare.c: In function 'compare':
compare.c:220:37: warning: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'dev_t' {aka 'long unsigned int'} [-Wformat=]
  220 |                 printf("%sdevice (%#x, %#x",
      |                                   ~~^
      |                                     |
      |                                     unsigned int
      |                                   %#lx
  221 |                     tab, s->st_rdev, p->fts_statp->st_rdev);
      |                          ~~~~~~~~~~
      |                           |
      |                           dev_t {aka long unsigned int}
compare.c:220:42: warning: format '%x' expects argument of type 'unsigned int', but argument 4 has type '__dev_t' {aka 'long unsigned int'} [-Wformat=]

>How-To-Repeat:
build pkgtools/mtree on a modern linux system
>Fix:
Yes, please!  I drafted this patch but since NetBSD's stat_flags.h got merged into util.h I think it needs some more work to disentangle that -- I don't think this patch will work out of the box on non-NetBSD systems.

diff --git a/pkgtools/mtree/Makefile b/pkgtools/mtree/Makefile
index 12faf51d71de..05f1fa1ea873 100644
--- a/pkgtools/mtree/Makefile
+++ b/pkgtools/mtree/Makefile
@@ -1,12 +1,13 @@
 # $NetBSD: Makefile,v 1.29 2020/01/18 23:33:26 rillig Exp $
 #
 
-PKGNAME=		mtree-20130908
+PKGNAME=		mtree-20250206
 CATEGORIES=		pkgtools sysutils
 
 MAINTAINER=		sbd%NetBSD.org@localhost
 HOMEPAGE=		https://www.NetBSD.org/
 COMMENT=		Utility for mapping and checking directory hierarchies
+LICENSE=		2-clause-bsd
 
 GNU_CONFIGURE=		yes
 
@@ -15,6 +16,6 @@ USE_FEATURES=		nbcompat
 .include "../../mk/bsd.prefs.mk"
 
 do-extract:
-	@${CP} -R ${FILESDIR} ${WRKSRC}
+	${RUN}${CP} -R ${FILESDIR} ${WRKSRC}
 
 .include "../../mk/bsd.pkg.mk"
diff --git a/pkgtools/mtree/files/Makefile.in b/pkgtools/mtree/files/Makefile.in
index ebcaad9f33f7..69c4de1843e6 100644
--- a/pkgtools/mtree/files/Makefile.in
+++ b/pkgtools/mtree/files/Makefile.in
@@ -21,8 +21,21 @@ INSTALL=	@INSTALL@
 
 PROG=		mtree
 
-OBJS=	compare.o crc.o create.o excludes.o misc.o mtree.o spec.o verify.o \
-	getid.o stat_flags.o pack_dev.o
+OBJS=	\
+	compare.o \
+	crc.o \
+	create.o \
+	excludes.o \
+	getid.o \
+	misc.o \
+	mtree.o \
+	only.o \
+	pack_dev.o \
+	spec.o \
+	specspec.o \
+	stat_flags.o \
+	verify.o \
+	# end of OBJS
 
 all: $(PROG)
 
diff --git a/pkgtools/mtree/files/compare.c b/pkgtools/mtree/files/compare.c
index 0c9234a98709..91e221cc0441 100644
--- a/pkgtools/mtree/files/compare.c
+++ b/pkgtools/mtree/files/compare.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: compare.c,v 1.7 2013/09/08 16:20:10 ryoon Exp $	*/
+/*	$NetBSD: compare.c,v 1.61 2024/12/05 17:17:43 christos Exp $	*/
 
 /*-
  * Copyright (c) 1989, 1993
@@ -44,13 +44,16 @@
 #if 0
 static char sccsid[] = "@(#)compare.c	8.1 (Berkeley) 6/6/93";
 #else
-__RCSID("$NetBSD: compare.c,v 1.7 2013/09/08 16:20:10 ryoon Exp $");
+__RCSID("$NetBSD: compare.c,v 1.61 2024/12/05 17:17:43 christos Exp $");
 #endif
 #endif /* not lint */
 
 #if HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
 
 #if HAVE_ERRNO_H
 #include <errno.h>
@@ -61,6 +64,12 @@ __RCSID("$NetBSD: compare.c,v 1.7 2013/09/08 16:20:10 ryoon Exp $");
 #if HAVE_STDIO_H
 #include <stdio.h>
 #endif
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
 #if HAVE_STRING_H
 #include <string.h>
 #endif
@@ -87,26 +96,28 @@ __RCSID("$NetBSD: compare.c,v 1.7 2013/09/08 16:20:10 ryoon Exp $");
 #endif
 #endif
 #ifndef NO_SHA2
-#if HAVE_SHA2_H && HAVE_SHA512_FILE
+#if HAVE_SHA2_H
 #include <sha2.h>
-#else
-#include <nbcompat/sha2.h>
 #endif
 #endif
 
-
 #include "extern.h"
 
 #define	INDENTNAMELEN	8
 #define MARK								\
 do {									\
-	len = printf("%s: ", RP(p));					\
-	if (len > INDENTNAMELEN) {					\
+	if (flavor == F_FREEBSD9) {					\
+		len = printf("%s changed\n", RP(p));			\
 		tab = "\t";						\
-		printf("\n");						\
 	} else {							\
-		tab = "";						\
-		printf("%*s", INDENTNAMELEN - (int)len, "");		\
+		len = printf("%s: ", RP(p));				\
+		if (len > INDENTNAMELEN) {				\
+			tab = "\t";					\
+			printf("\n");					\
+		} else {						\
+			tab = "";					\
+			printf("%*s", INDENTNAMELEN - (int)len, "");	\
+		}							\
 	}								\
 } while (0)
 #define	LABEL if (!label++) MARK
@@ -116,18 +127,22 @@ do {									\
 
 #define CHANGEFLAGS							\
 	if (flags != p->fts_statp->st_flags) {				\
+		char *sf;						\
 		if (!label) {						\
 			MARK;						\
-			printf("%sflags (\"%s\"", tab,			\
-			    flags_to_string(p->fts_statp->st_flags, "none")); \
+			sf = flags_to_string(p->fts_statp->st_flags, "none"); \
+			printf("%sflags (\"%s\"", tab, sf);		\
+			free(sf);					\
 		}							\
 		if (lchflags(p->fts_accpath, flags)) {			\
 			label++;					\
 			printf(", not modified: %s)\n",			\
 			    strerror(errno));				\
-		} else							\
-			printf(", modified to \"%s\")\n",		\
-			     flags_to_string(flags, "none"));		\
+		} else {						\
+			sf = flags_to_string(flags, "none");		\
+			printf(", modified to \"%s\")\n", sf);		\
+			free(sf);					\
+		}							\
 	}
 
 /* SETFLAGS:
@@ -156,13 +171,15 @@ compare(NODE *s, FTSENT *p)
 {
 	uint32_t len, val, flags;
 	int fd, label;
+	bool was_unlinked;
 	const char *cp, *tab;
 #if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2)
-	char digestbuf[MAXHASHLEN + 1];
+	char *digestbuf;
 #endif
 
 	tab = NULL;
 	label = 0;
+	was_unlinked = false;
 	switch(s->type) {
 	case F_BLOCK:
 		if (!S_ISBLK(p->fts_statp->st_mode))
@@ -195,7 +212,8 @@ compare(NODE *s, FTSENT *p)
 		break;
 #endif
 typeerr:		LABEL;
-		printf("\ttype (%s, %s)\n",
+		printf(flavor == F_FREEBSD9 ?
+		    "\ttype expected %s found %s\n" : "\ttype (%s, %s)\n",
 		    nodetype(s->type), inotype(p->fts_statp->st_mode));
 		return (label);
 	}
@@ -217,56 +235,73 @@ typeerr:		LABEL;
 	    (s->type == F_BLOCK || s->type == F_CHAR) &&
 	    s->st_rdev != p->fts_statp->st_rdev) {
 		LABEL;
-		printf("%sdevice (%#x, %#x",
-		    tab, s->st_rdev, p->fts_statp->st_rdev);
+		printf(flavor == F_FREEBSD9 ?
+		    "%sdevice expected %#jx found %#jx" :
+		    "%sdevice (%#jx, %#jx",
+		    tab, (uintmax_t)s->st_rdev,
+		    (uintmax_t)p->fts_statp->st_rdev);
 		if (uflag) {
 			if ((unlink(p->fts_accpath) == -1) ||
 			    (mknod(p->fts_accpath,
 			      s->st_mode | nodetoino(s->type),
 			      s->st_rdev) == -1) ||
 			    (lchown(p->fts_accpath, p->fts_statp->st_uid,
-			      p->fts_statp->st_gid) == -1) )
-				printf(", not modified: %s)\n",
-				    strerror(errno));
-			 else
-				printf(", modified)\n");
+			      p->fts_statp->st_gid) == -1) ) {
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
+			} else {
+				printf(", modified%s\n",
+				    flavor == F_FREEBSD9 ? "" : ")");
+				was_unlinked = true;
+			}
 		} else
 			printf(")\n");
 		tab = "\t";
 	}
 	/* Set the uid/gid first, then set the mode. */
-	if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
+	if (s->flags & (F_UID | F_UNAME) &&
+	    (was_unlinked || s->st_uid != p->fts_statp->st_uid)) {
 		LABEL;
-		printf("%suser (%lu, %lu",
+		printf(flavor == F_FREEBSD9 ?
+		    "%suser expected %lu found %lu" : "%suser (%lu, %lu",
 		    tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
 		if (uflag) {
-			if (lchown(p->fts_accpath, s->st_uid, -1))
-				printf(", not modified: %s)\n",
-				    strerror(errno));
+			if (lchown(p->fts_accpath, s->st_uid, (gid_t)-1))
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
 			else
-				printf(", modified)\n");
+				printf(", modified%s%s\n",
+				    was_unlinked ? " by unlink" : "",
+				    flavor == F_FREEBSD9 ? "" : ")");
 		} else
 			printf(")\n");
 		tab = "\t";
 	}
-	if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
+	if (s->flags & (F_GID | F_GNAME) &&
+	    (was_unlinked || s->st_gid != p->fts_statp->st_gid)) {
 		LABEL;
-		printf("%sgid (%lu, %lu",
+		printf(flavor == F_FREEBSD9 ?
+		    "%sgid expected %lu found %lu" : "%sgid (%lu, %lu",
 		    tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
 		if (uflag) {
-			if (lchown(p->fts_accpath, -1, s->st_gid))
-				printf(", not modified: %s)\n",
-				    strerror(errno));
+			if (lchown(p->fts_accpath, (uid_t)-1, s->st_gid))
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
 			else
-				printf(", modified)\n");
+				printf(", modified%s%s\n",
+				    was_unlinked ? " by unlink" : "",
+				    flavor == F_FREEBSD9 ? "" : ")");
 		}
 		else
 			printf(")\n");
 		tab = "\t";
 	}
 	if (s->flags & F_MODE &&
-	    s->st_mode != (p->fts_statp->st_mode & MBITS)) {
-		if (lflag) {
+	    (was_unlinked || s->st_mode != (p->fts_statp->st_mode & MBITS))) {
+		if (lflag && !was_unlinked) {
 			mode_t tmode, mode;
 
 			tmode = s->st_mode;
@@ -283,15 +318,20 @@ typeerr:		LABEL;
 		}
 
 		LABEL;
-		printf("%spermissions (%#lo, %#lo",
+		printf(flavor == F_FREEBSD9 ?
+		    "%spermissions expcted %#lo found %#lo" :
+		    "%spermissions (%#lo, %#lo",
 		    tab, (u_long)s->st_mode,
 		    (u_long)p->fts_statp->st_mode & MBITS);
 		if (uflag) {
 			if (lchmod(p->fts_accpath, s->st_mode))
-				printf(", not modified: %s)\n",
-				    strerror(errno));
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
 			else
-				printf(", modified)\n");
+				printf(", modified%s%s\n",
+				    was_unlinked ? " by unlink" : "",
+				    flavor == F_FREEBSD9 ? "" : ")");
 		}
 		else
 			printf(")\n");
@@ -301,15 +341,18 @@ typeerr:		LABEL;
 	if (s->flags & F_NLINK && s->type != F_DIR &&
 	    s->st_nlink != p->fts_statp->st_nlink) {
 		LABEL;
-		printf("%slink count (%lu, %lu)\n",
+		printf(flavor == F_FREEBSD9 ?
+		    "%slink count expected %lu found %lu\n" :
+		    "%slink count (%lu, %lu)\n",
 		    tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
 		tab = "\t";
 	}
 	if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
 		LABEL;
-		printf("%ssize (%lld, %lld)\n",
-		    tab, (long long)s->st_size,
-		    (long long)p->fts_statp->st_size);
+		printf(flavor == F_FREEBSD9 ?
+		    "%ssize expected %ju found %ju\n" : "%ssize (%ju, %ju)\n",
+		    tab, (uintmax_t)s->st_size,
+		    (uintmax_t)p->fts_statp->st_size);
 		tab = "\t";
 	}
 	/*
@@ -342,18 +385,22 @@ typeerr:		LABEL;
 		if (tv[0].tv_sec != tv[1].tv_sec ||
 		    tv[0].tv_usec != tv[1].tv_usec) {
 			LABEL;
-			printf("%smodification time (%.24s, ",
+			printf(flavor == F_FREEBSD9 ?
+			    "%smodification time expected %.24s found " :
+			    "%smodification time (%.24s, ",
 			    tab, ctime(&smtime));
 			printf("%.24s", ctime(&pmtime));
 			if (tflag) {
 				tv[1] = tv[0];
 				if (utimes(p->fts_accpath, tv))
-					printf(", not modified: %s)\n",
-					    strerror(errno));
+					printf(", not modified: %s%s\n",
+					    strerror(errno),
+					    flavor == F_FREEBSD9 ? "" : ")");
 				else
-					printf(", modified)\n");
+					printf(", modified%s\n",
+					    flavor == F_FREEBSD9 ? "" : ")");
 			} else
-				printf(")\n");
+				printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
 			tab = "\t";
 		}
 	}
@@ -367,11 +414,16 @@ typeerr:		LABEL;
         if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags)
 	    || mflag || iflag)) {
 		if (s->st_flags != p->fts_statp->st_flags) {
+			char *f_s;
 			LABEL;
-			printf("%sflags (\"%s\" is not ", tab,
-			    flags_to_string(s->st_flags, "none"));
-			printf("\"%s\"",
-			    flags_to_string(p->fts_statp->st_flags, "none"));
+			f_s = flags_to_string(s->st_flags, "none");
+			printf(flavor == F_FREEBSD9 ?
+			    "%sflags expected \"%s\" found " :
+			    "%sflags (\"%s\" is not ", tab, f_s);
+			free(f_s);
+			f_s = flags_to_string(p->fts_statp->st_flags, "none");
+			printf("\"%s\"", f_s);
+			free(f_s);
 		}
 		if (uflag) {
 			if (iflag)
@@ -381,7 +433,7 @@ typeerr:		LABEL;
 			else
 				SETFLAGS(0, (~SP_FLGS & CH_MASK));
 		} else
-			printf(")\n");
+			printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
 		tab = "\t";
 	}
 #endif	/* HAVE_STRUCT_STAT_ST_FLAGS */
@@ -407,7 +459,9 @@ typeerr:		LABEL;
 			close(fd);
 			if (s->cksum != val) {
 				LABEL;
-				printf("%scksum (%lu, %lu)\n",
+				printf(flavor == F_FREEBSD9 ?
+				    "%scksum expected %lu found %lu\n" :
+				    "%scksum (%lu, %lu)\n",
 				    tab, s->cksum, (unsigned long)val);
 			}
 			tab = "\t";
@@ -415,115 +469,139 @@ typeerr:		LABEL;
 	}
 #ifndef NO_MD5
 	if (s->flags & F_MD5) {
-		if (MD5File(p->fts_accpath, digestbuf) == NULL) {
+		if ((digestbuf = MD5File(p->fts_accpath, NULL)) == NULL) {
 			LABEL;
-			printf("%smd5: %s: %s\n",
-			    tab, p->fts_accpath, strerror(errno));
+			printf("%s%s: %s: %s\n",
+			    tab, MD5KEY, p->fts_accpath, strerror(errno));
 			tab = "\t";
 		} else {
 			if (strcmp(s->md5digest, digestbuf)) {
 				LABEL;
-				printf("%smd5 (0x%s, 0x%s)\n",
-				    tab, s->md5digest, digestbuf);
+				printf(flavor == F_FREEBSD9 ?
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, MD5KEY, s->md5digest, digestbuf);
 			}
 			tab = "\t";
+			free(digestbuf);
 		}
 	}
 #endif	/* ! NO_MD5 */
 #ifndef NO_RMD160
 	if (s->flags & F_RMD160) {
-		if (RMD160File(p->fts_accpath, digestbuf) == NULL) {
+		if ((digestbuf = RMD160File(p->fts_accpath, NULL)) == NULL) {
 			LABEL;
-			printf("%srmd160: %s: %s\n",
-			    tab, p->fts_accpath, strerror(errno));
+			printf("%s%s: %s: %s\n",
+			    tab, RMD160KEY, p->fts_accpath, strerror(errno));
 			tab = "\t";
 		} else {
 			if (strcmp(s->rmd160digest, digestbuf)) {
 				LABEL;
-				printf("%srmd160 (0x%s, 0x%s)\n",
-				    tab, s->rmd160digest, digestbuf);
+				printf(flavor == F_FREEBSD9 ?
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, RMD160KEY, s->rmd160digest, digestbuf);
 			}
 			tab = "\t";
+			free(digestbuf);
 		}
 	}
 #endif	/* ! NO_RMD160 */
 #ifndef NO_SHA1
 	if (s->flags & F_SHA1) {
-		if (SHA1File(p->fts_accpath, digestbuf) == NULL) {
+		if ((digestbuf = SHA1File(p->fts_accpath, NULL)) == NULL) {
 			LABEL;
-			printf("%ssha1: %s: %s\n",
-			    tab, p->fts_accpath, strerror(errno));
+			printf("%s%s: %s: %s\n",
+			    tab, SHA1KEY, p->fts_accpath, strerror(errno));
 			tab = "\t";
 		} else {
 			if (strcmp(s->sha1digest, digestbuf)) {
 				LABEL;
-				printf("%ssha1 (0x%s, 0x%s)\n",
-				    tab, s->sha1digest, digestbuf);
+				printf(flavor == F_FREEBSD9 ? 
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, SHA1KEY, s->sha1digest, digestbuf);
 			}
 			tab = "\t";
+			free(digestbuf);
 		}
 	}
 #endif	/* ! NO_SHA1 */
 #ifndef NO_SHA2
 	if (s->flags & F_SHA256) {
-		if (SHA256_File(p->fts_accpath, digestbuf) == NULL) {
+		if ((digestbuf = SHA256_File(p->fts_accpath, NULL)) == NULL) {
 			LABEL;
-			printf("%ssha256: %s: %s\n",
-			    tab, p->fts_accpath, strerror(errno));
+			printf("%s%s: %s: %s\n",
+			    tab, SHA256KEY, p->fts_accpath, strerror(errno));
 			tab = "\t";
 		} else {
 			if (strcmp(s->sha256digest, digestbuf)) {
 				LABEL;
-				printf("%ssha256 (0x%s, 0x%s)\n",
-				    tab, s->sha256digest, digestbuf);
+				printf(flavor == F_FREEBSD9 ? 
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, SHA256KEY, s->sha256digest, digestbuf);
 			}
 			tab = "\t";
+			free(digestbuf);
 		}
 	}
+#ifdef SHA384_BLOCK_LENGTH
 	if (s->flags & F_SHA384) {
-		if (SHA384_File(p->fts_accpath, digestbuf) == NULL) {
+		if ((digestbuf = SHA384_File(p->fts_accpath, NULL)) == NULL) {
 			LABEL;
-			printf("%ssha384: %s: %s\n",
-			    tab, p->fts_accpath, strerror(errno));
+			printf("%s%s: %s: %s\n",
+			    tab, SHA384KEY, p->fts_accpath, strerror(errno));
 			tab = "\t";
 		} else {
 			if (strcmp(s->sha384digest, digestbuf)) {
 				LABEL;
-				printf("%ssha384 (0x%s, 0x%s)\n",
-				    tab, s->sha384digest, digestbuf);
+				printf(flavor == F_FREEBSD9 ? 
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, SHA384KEY, s->sha384digest, digestbuf);
 			}
 			tab = "\t";
+			free(digestbuf);
 		}
 	}
+#endif
 	if (s->flags & F_SHA512) {
-		if (SHA512_File(p->fts_accpath, digestbuf) == NULL) {
+		if ((digestbuf = SHA512_File(p->fts_accpath, NULL)) == NULL) {
 			LABEL;
-			printf("%ssha512: %s: %s\n",
-			    tab, p->fts_accpath, strerror(errno));
+			printf("%s%s: %s: %s\n",
+			    tab, SHA512KEY, p->fts_accpath, strerror(errno));
 			tab = "\t";
 		} else {
 			if (strcmp(s->sha512digest, digestbuf)) {
 				LABEL;
-				printf("%ssha512 (0x%s, 0x%s)\n",
-				    tab, s->sha512digest, digestbuf);
+				printf(flavor == F_FREEBSD9 ? 
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, SHA512KEY, s->sha512digest, digestbuf);
 			}
 			tab = "\t";
+			free(digestbuf);
 		}
 	}
 #endif	/* ! NO_SHA2 */
 	if (s->flags & F_SLINK &&
 	    strcmp(cp = rlink(p->fts_accpath), s->slink)) {
 		LABEL;
-		printf("%slink ref (%s, %s", tab, cp, s->slink);
+		printf(flavor == F_FREEBSD9 ? 
+		    "%slink ref expected %s found %s" :
+		    "%slink ref (%s, %s", tab, cp, s->slink);
 		if (uflag) {
 			if ((unlink(p->fts_accpath) == -1) ||
 			    (symlink(s->slink, p->fts_accpath) == -1) )
-				printf(", not modified: %s)\n",
-				    strerror(errno));
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
 			else
-				printf(", modified)\n");
+				printf(", modified%s\n",
+				    flavor == F_FREEBSD9 ? "" : ")");
 		} else
-			printf(")\n");
+			printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
 	}
 	return (label);
 }
@@ -532,7 +610,7 @@ const char *
 rlink(const char *name)
 {
 	static char lbuf[MAXPATHLEN];
-	int len;
+	ssize_t len;
 
 	if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
 		mtree_err("%s: %s", name, strerror(errno));
diff --git a/pkgtools/mtree/files/crc.c b/pkgtools/mtree/files/crc.c
index 548584b707ff..22966d5fa2de 100644
--- a/pkgtools/mtree/files/crc.c
+++ b/pkgtools/mtree/files/crc.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: crc.c,v 1.5 2010/03/21 16:30:17 joerg Exp $	*/
+/*	$NetBSD: crc.c,v 1.11 2024/12/05 17:17:43 christos Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -47,7 +47,7 @@
 #if 0
 static char sccsid[] = "@(#)crc.c	8.1 (Berkeley) 6/17/93";
 #else
-__RCSID("$NetBSD: crc.c,v 1.5 2010/03/21 16:30:17 joerg Exp $");
+__RCSID("$NetBSD: crc.c,v 1.11 2024/12/05 17:17:43 christos Exp $");
 #endif
 #endif /* not lint */
 
@@ -128,14 +128,13 @@ static const uint32_t crctab[] = {
  * locations to store the crc and the number of bytes read.  It returns 0 on
  * success and 1 on failure.  Errno is set on failure.
  */
-extern int sflag;
-uint32_t crc_total = ~0;		/* The crc over a number of files. */
+uint32_t crc_total = ~0u;		/* The crc over a number of files. */
 
 int
 crc(int fd, uint32_t *cval, uint32_t *clen)
 {
 	u_char *p;
-	int nr;
+	ssize_t nr;
 	uint32_t thecrc, len;
 	uint32_t crctot;
 	u_char buf[16 * 1024];
@@ -147,12 +146,12 @@ crc(int fd, uint32_t *cval, uint32_t *clen)
 		crctot = ~crc_total;
 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
 		if (sflag) {
-			for (len += nr, p = buf; nr--; ++p) {
+			for (len += (uint32_t)nr, p = buf; nr--; ++p) {
 				COMPUTE(thecrc, *p);
 				COMPUTE(crctot, *p);
 			}
 		} else {
-			for (len += nr, p = buf; nr--; ++p)
+			for (len += (uint32_t)nr, p = buf; nr--; ++p)
 				COMPUTE(thecrc, *p);
 		}
 	if (nr < 0)
diff --git a/pkgtools/mtree/files/create.c b/pkgtools/mtree/files/create.c
index 0dc51d523902..6ffe3768b77d 100644
--- a/pkgtools/mtree/files/create.c
+++ b/pkgtools/mtree/files/create.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: create.c,v 1.9 2013/09/08 16:20:10 ryoon Exp $	*/
+/*	$NetBSD: create.c,v 1.79 2024/12/05 17:17:43 christos Exp $	*/
 
 /*-
  * Copyright (c) 1989, 1993
@@ -44,7 +44,7 @@
 #if 0
 static char sccsid[] = "@(#)create.c	8.1 (Berkeley) 6/6/93";
 #else
-__RCSID("$NetBSD: create.c,v 1.9 2013/09/08 16:20:10 ryoon Exp $");
+__RCSID("$NetBSD: create.c,v 1.79 2024/12/05 17:17:43 christos Exp $");
 #endif
 #endif /* not lint */
 
@@ -79,6 +79,9 @@ __RCSID("$NetBSD: create.c,v 1.9 2013/09/08 16:20:10 ryoon Exp $");
 #if HAVE_STDARG_H
 #include <stdarg.h>
 #endif
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
 #if HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -108,10 +111,8 @@ __RCSID("$NetBSD: create.c,v 1.9 2013/09/08 16:20:10 ryoon Exp $");
 #endif
 #endif
 #ifndef NO_SHA2
-#if HAVE_SHA2_H && HAVE_SHA512_FILE
+#if HAVE_SHA2_H
 #include <sha2.h>
-#else
-#include <nbcompat/sha2.h>
 #endif
 #endif
 
@@ -125,47 +126,74 @@ static uid_t uid;
 static mode_t mode;
 static u_long flags;
 
-static int	dsort(const FTSENT **, const FTSENT **);
-static void	output(int *, const char *, ...)
-	__attribute__((__format__(__printf__, 2, 3)));
-static int	statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *);
-static void	statf(FTSENT *);
+static void	output(FILE *, int, int *, const char *, ...)
+    __printflike(4, 5);
+static int	statd(FILE *, FTS *, FTSENT *, uid_t *, gid_t *, mode_t *,
+    u_long *);
+static void	statf(FILE *, int, FTSENT *);
 
 void
-cwalk(void)
+cwalk(FILE *fp)
 {
 	FTS *t;
 	FTSENT *p;
 	time_t clocktime;
 	char host[MAXHOSTNAMELEN + 1];
+	const char *user;
 	char *argv[2];
 	char  dot[] = ".";
+	int indent = 0;
+
 	argv[0] = dot;
 	argv[1] = NULL;
 
 	time(&clocktime);
 	gethostname(host, sizeof(host));
 	host[sizeof(host) - 1] = '\0';
-	printf(
-	    "#\t   user: %s\n#\tmachine: %s\n#\t   tree: %s\n#\t   date: %s",
-	    getlogin(), host, fullpath, ctime(&clocktime));
+	if ((user = getlogin()) == NULL) {
+		struct passwd *pw;
+		user = (pw = getpwuid(getuid())) != NULL ? pw->pw_name :
+		    "<unknown>";
+	}
 
-	if ((t = fts_open(argv, ftsoptions, dsort)) == NULL)
+	if (!nflag)
+		fprintf(fp,
+	    	    "#\t   user: %s\n#\tmachine: %s\n#\t   tree: %s\n"
+		    "#\t   date: %s",
+		    user, host, fullpath, ctime(&clocktime));
+
+	if ((t = fts_open(argv, ftsoptions, dcmp)) == NULL)
 		mtree_err("fts_open: %s", strerror(errno));
 	while ((p = fts_read(t)) != NULL) {
+		if (jflag)
+			indent = p->fts_level * 4;
 		if (check_excludes(p->fts_name, p->fts_path)) {
 			fts_set(t, p, FTS_SKIP);
 			continue;
 		}
+		if (!find_only(p->fts_path)) {
+			fts_set(t, p, FTS_SKIP);
+			continue;
+		}
 		switch(p->fts_info) {
 		case FTS_D:
-			printf("\n# %s\n", p->fts_path);
-			statd(t, p, &uid, &gid, &mode, &flags);
-			statf(p);
+			if (!bflag)
+				fprintf(fp, "\n");
+			if (!nflag)
+				fprintf(fp, "# %s\n", p->fts_path);
+			statd(fp, t, p, &uid, &gid, &mode, &flags);
+			statf(fp, indent, p);
 			break;
 		case FTS_DP:
 			if (p->fts_level > 0)
-				printf("# %s\n..\n\n", p->fts_path);
+				if (!nflag)
+					fprintf(fp, "%*s# %s\n", indent, "",
+					    p->fts_path);
+			if (p->fts_level > 0 || flavor == F_FREEBSD9) {
+				fprintf(fp, "%*s..\n", indent, "");
+				if (!bflag)
+					fprintf(fp, "\n");
+			}
 			break;
 		case FTS_DNR:
 		case FTS_ERR:
@@ -175,120 +203,152 @@ cwalk(void)
 			break;
 		default:
 			if (!dflag)
-				statf(p);
+				statf(fp, indent, p);
 			break;
 
 		}
 	}
+	if (errno != 0)
+		mtree_err("fts_read: %s", strerror(errno));
 	fts_close(t);
 	if (sflag && keys & F_CKSUM)
 		mtree_err("%s checksum: %u", fullpath, crc_total);
 }
 
 static void
-statf(FTSENT *p)
+dosum(FILE *fp, int indent, FTSENT *p, int *offset, int flag,
+    char * (*func)(const char *, char *), const char *key)
 {
-	uint32_t len, val;
-	int fd, indent;
-	const char *name;
-#if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2)
-	char digestbuf[MAXHASHLEN + 1];
-#endif
+	char *digestbuf;
+
+	if ((keys & flag) == 0)
+		return;
+
+	digestbuf = (*func)(p->fts_accpath, NULL);
+	if (digestbuf != NULL) {
+		output(fp, indent, offset, "%s=%s", key, digestbuf);
+		free(digestbuf);
+		return;
+	}
+
+	if (qflag) {
+		warn("%s: %s failed", p->fts_path, key);
+		return;
+	}
+
+	mtree_err("%s: %s failed: %s", p->fts_path, key, strerror(errno));
+}
+
+static char *
+crcFile(const char *fname, char *dummy __unused)
+{
+	char *ptr;
+	uint32_t val, len;
+	int fd, e;
+
+	if ((fd = open(fname, O_RDONLY)) == -1)
+		goto out;
+
+	e = crc(fd, &val, &len);
+	close(fd);
+	if (e)
+		goto out;
+
+	if (asprintf(&ptr, "%u", val) < 0)
+		goto out;
+
+	return ptr;
+out:
+	mtree_err("%s: %s", fname, strerror(errno));
+	return NULL;
+}
+
+static void
+statf(FILE *fp, int indent, FTSENT *p)
+{
+	int offset;
+	const char *name = NULL;
 
-	indent = printf("%s%s",
+	offset = fprintf(fp, "%*s%s%s", indent, "",
 	    S_ISDIR(p->fts_statp->st_mode) ? "" : "    ", vispath(p->fts_name));
 
-	if (indent > INDENTNAMELEN)
-		indent = MAXLINELEN;
+	if (offset > (INDENTNAMELEN + indent))
+		offset = MAXLINELEN;
 	else
-		indent += printf("%*s", INDENTNAMELEN - indent, "");
+		offset += fprintf(fp, "%*s",
+		    (INDENTNAMELEN + indent) - offset, "");
 
-	if (!S_ISREG(p->fts_statp->st_mode))
-		output(&indent, "type=%s", inotype(p->fts_statp->st_mode));
+	if (!S_ISREG(p->fts_statp->st_mode) && (flavor == F_NETBSD6 || !dflag))
+		output(fp, indent, &offset, "type=%s",
+		    inotype(p->fts_statp->st_mode));
 	if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) {
 		if (keys & F_UNAME &&
 		    (name = user_from_uid(p->fts_statp->st_uid, 1)) != NULL)
-			output(&indent, "uname=%s", name);
-		else /* if (keys & F_UID) */
-			output(&indent, "uid=%u", p->fts_statp->st_uid);
+			output(fp, indent, &offset, "uname=%s", name);
+		if (keys & F_UID || (keys & F_UNAME && name == NULL))
+			output(fp, indent, &offset, "uid=%u",
+			    p->fts_statp->st_uid);
 	}
 	if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) {
 		if (keys & F_GNAME &&
 		    (name = group_from_gid(p->fts_statp->st_gid, 1)) != NULL)
-			output(&indent, "gname=%s", name);
-		else /* if (keys & F_GID) */
-			output(&indent, "gid=%u", p->fts_statp->st_gid);
+			output(fp, indent, &offset, "gname=%s", name);
+		if (keys & F_GID || (keys & F_GNAME && name == NULL))
+			output(fp, indent, &offset, "gid=%u",
+			    p->fts_statp->st_gid);
 	}
 	if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode)
-		output(&indent, "mode=%#o", p->fts_statp->st_mode & MBITS);
+		output(fp, indent, &offset, "mode=%#o",
+		    p->fts_statp->st_mode & MBITS);
 	if (keys & F_DEV &&
 	    (S_ISBLK(p->fts_statp->st_mode) || S_ISCHR(p->fts_statp->st_mode)))
-		output(&indent, "device=%#x", p->fts_statp->st_rdev);
+		output(fp, indent, &offset, "device=%#jx",
+		    (uintmax_t)p->fts_statp->st_rdev);
 	if (keys & F_NLINK && p->fts_statp->st_nlink != 1)
-		output(&indent, "nlink=%u", p->fts_statp->st_nlink);
-	if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode))
-		output(&indent, "size=%lld", (long long)p->fts_statp->st_size);
-#if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H)
+		output(fp, indent, &offset, "nlink=%ju",
+		    (uintmax_t)p->fts_statp->st_nlink);
+	if (keys & F_SIZE &&
+	    (flavor == F_FREEBSD9 || S_ISREG(p->fts_statp->st_mode)))
+		output(fp, indent, &offset, "size=%ju",
+		    (uintmax_t)p->fts_statp->st_size);
 	if (keys & F_TIME)
-		output(&indent, "time=%ld.%ld",
-		    (long)p->fts_statp->st_mtimespec.tv_sec,
+#if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H)
+		output(fp, indent, &offset, "time=%jd.%09ld",
+		    (intmax_t)p->fts_statp->st_mtimespec.tv_sec,
 		    p->fts_statp->st_mtimespec.tv_nsec);
 #else
-		output(&indent, "time=%ld.%ld",
-		    p->fts_statp->st_mtime, 0);
-#endif
-	if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) {
-		if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 ||
-		    crc(fd, &val, &len))
-			mtree_err("%s: %s", p->fts_accpath, strerror(errno));
-		close(fd);
-		output(&indent, "cksum=%lu", (long)val);
-	}
+		output(fp, indent, &offset, "time=%jd.%09ld",
+		    (intmax_t)p->fts_statp->st_mtime, (long)0);
+#endif
+	if (S_ISREG(p->fts_statp->st_mode))  {
+		dosum(fp, indent, p, &offset, F_CKSUM, crcFile, "cksum");
 #ifndef NO_MD5
-	if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) {
-		if (MD5File(p->fts_accpath, digestbuf) == NULL)
-			mtree_err("%s: %s", p->fts_accpath, "MD5File");
-		output(&indent, "md5=%s", digestbuf);
-	}
+		dosum(fp, indent, p, &offset, F_MD5, MD5File, MD5KEY);
 #endif	/* ! NO_MD5 */
 #ifndef NO_RMD160
-	if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) {
-		if (RMD160File(p->fts_accpath, digestbuf) == NULL)
-			mtree_err("%s: %s", p->fts_accpath, "RMD160File");
-		output(&indent, "rmd160=%s", digestbuf);
-	}
+		dosum(fp, indent, p, &offset, F_RMD160, RMD160File, RMD160KEY);
 #endif	/* ! NO_RMD160 */
 #ifndef NO_SHA1
-	if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) {
-		if (SHA1File(p->fts_accpath, digestbuf) == NULL)
-			mtree_err("%s: %s", p->fts_accpath, "SHA1File");
-		output(&indent, "sha1=%s", digestbuf);
-	}
+		dosum(fp, indent, p, &offset, F_SHA1, SHA1File, SHA1KEY);
 #endif	/* ! NO_SHA1 */
 #ifndef NO_SHA2
-	if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) {
-		if (SHA256_File(p->fts_accpath, digestbuf) == NULL)
-			mtree_err("%s: %s", p->fts_accpath, "SHA256_File");
-		output(&indent, "sha256=%s", digestbuf);
-	}
-	if (keys & F_SHA384 && S_ISREG(p->fts_statp->st_mode)) {
-		if (SHA384_File(p->fts_accpath, digestbuf) == NULL)
-			mtree_err("%s: %s", p->fts_accpath, "SHA384_File");
-		output(&indent, "sha384=%s", digestbuf);
-	}
-	if (keys & F_SHA512 && S_ISREG(p->fts_statp->st_mode)) {
-		if (SHA512_File(p->fts_accpath, digestbuf) == NULL)
-			mtree_err("%s: %s", p->fts_accpath, "SHA512_File");
-		output(&indent, "sha512=%s", digestbuf);
-	}
+		dosum(fp, indent, p, &offset, F_SHA256, SHA256_File, SHA256KEY);
+#ifdef SHA384_BLOCK_LENGTH
+		dosum(fp, indent, p, &offset, F_SHA384, SHA384_File, SHA384KEY);
+#endif
+		dosum(fp, indent, p, &offset, F_SHA512, SHA512_File, SHA512KEY);
 #endif	/* ! NO_SHA2 */
+	}
 	if (keys & F_SLINK &&
 	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE))
-		output(&indent, "link=%s", vispath(rlink(p->fts_accpath)));
+		output(fp, indent, &offset, "link=%s",
+		    vispath(rlink(p->fts_accpath)));
 #if HAVE_STRUCT_STAT_ST_FLAGS
-	if (keys & F_FLAGS && p->fts_statp->st_flags != flags)
-		output(&indent, "flags=%s",
-		    flags_to_string(p->fts_statp->st_flags, "none"));
+	if (keys & F_FLAGS && p->fts_statp->st_flags != flags) {
+		char *str = flags_to_string(p->fts_statp->st_flags, "none");
+		output(fp, indent, &offset, "flags=%s", str);
+		free(str);
+	}
 #endif
 	putchar('\n');
 }
@@ -310,15 +370,15 @@ statf(FTSENT *p)
 #define	MTREE_MAXS 16
 
 static int
-statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode,
-      u_long *pflags)
+statd(FILE *fp, FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode,
+    u_long *pflags)
 {
 	FTSENT *p;
 	gid_t sgid;
 	uid_t suid;
 	mode_t smode;
 	u_long sflags = 0;
-	const char *name;
+	const char *name = NULL;
 	gid_t savegid;
 	uid_t saveuid;
 	mode_t savemode;
@@ -345,29 +405,32 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode,
 
 	maxuid = maxgid = maxmode = maxflags = 0;
 	for (; p; p = p->fts_link) {
-		smode = p->fts_statp->st_mode & MBITS;
-		if (smode < MTREE_MAXMODE && ++m[smode] > maxmode) {
-			savemode = smode;
-			maxmode = m[smode];
-		}
-		sgid = p->fts_statp->st_gid;
-		if (sgid < MTREE_MAXGID && ++g[sgid] > maxgid) {
-			savegid = sgid;
-			maxgid = g[sgid];
-		}
-		suid = p->fts_statp->st_uid;
-		if (suid < MTREE_MAXUID && ++u[suid] > maxuid) {
-			saveuid = suid;
-			maxuid = u[suid];
-		}
+		if (flavor == F_NETBSD6 || !dflag ||
+		    (dflag && S_ISDIR(p->fts_statp->st_mode))) {
+			smode = p->fts_statp->st_mode & MBITS;
+			if (smode < MTREE_MAXMODE && ++m[smode] > maxmode) {
+				savemode = smode;
+				maxmode = m[smode];
+			}
+			sgid = p->fts_statp->st_gid;
+			if (sgid < MTREE_MAXGID && ++g[sgid] > maxgid) {
+				savegid = sgid;
+				maxgid = g[sgid];
+			}
+			suid = p->fts_statp->st_uid;
+			if (suid < MTREE_MAXUID && ++u[suid] > maxuid) {
+				saveuid = suid;
+				maxuid = u[suid];
+			}
 
 #if HAVE_STRUCT_STAT_ST_FLAGS
-		sflags = FLAGS2INDEX(p->fts_statp->st_flags);
-		if (sflags < MTREE_MAXFLAGS && ++f[sflags] > maxflags) {
-			saveflags = p->fts_statp->st_flags;
-			maxflags = f[sflags];
-		}
+			sflags = FLAGS2INDEX(p->fts_statp->st_flags);
+			if (sflags < MTREE_MAXFLAGS && ++f[sflags] > maxflags) {
+				saveflags = p->fts_statp->st_flags;
+				maxflags = f[sflags];
+			}
 #endif
+		}
 	}
 	/*
 	 * If the /set record is the same as the last one we do not need to
@@ -380,29 +443,34 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode,
 	    ((keys & F_FLAGS) && (*pflags != saveflags)) ||
 	    first) {
 		first = 0;
-		printf("/set type=file");
+		if (flavor != F_NETBSD6 && dflag)
+			fprintf(fp, "/set type=dir");
+		else
+			fprintf(fp, "/set type=file");
 		if (keys & (F_UID | F_UNAME)) {
 			if (keys & F_UNAME &&
 			    (name = user_from_uid(saveuid, 1)) != NULL)
-				printf(" uname=%s", name);
-			else /* if (keys & F_UID) */
-				printf(" uid=%lu", (u_long)saveuid);
+				fprintf(fp, " uname=%s", name);
+			if (keys & F_UID || (keys & F_UNAME && name == NULL))
+				fprintf(fp, " uid=%lu", (u_long)saveuid);
 		}
 		if (keys & (F_GID | F_GNAME)) {
 			if (keys & F_GNAME &&
 			    (name = group_from_gid(savegid, 1)) != NULL)
-				printf(" gname=%s", name);
-			else /* if (keys & F_UID) */
-				printf(" gid=%lu", (u_long)savegid);
+				fprintf(fp, " gname=%s", name);
+			if (keys & F_GID || (keys & F_GNAME && name == NULL))
+				fprintf(fp, " gid=%lu", (u_long)savegid);
 		}
 		if (keys & F_MODE)
-			printf(" mode=%#lo", (u_long)savemode);
+			fprintf(fp, " mode=%#lo", (u_long)savemode);
 		if (keys & F_NLINK)
-			printf(" nlink=1");
-		if (keys & F_FLAGS)
-			printf(" flags=%s",
-			    flags_to_string(saveflags, "none"));
-		printf("\n");
+			fprintf(fp, " nlink=1");
+		if (keys & F_FLAGS) {
+			char *str = flags_to_string(saveflags, "none");
+			fprintf(fp, " flags=%s", str);
+			free(str);
+		}
+		fprintf(fp, "\n");
 		*puid = saveuid;
 		*pgid = savegid;
 		*pmode = savemode;
@@ -411,20 +479,8 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode,
 	return (0);
 }
 
-static int
-dsort(const FTSENT **a, const FTSENT **b)
-{
-
-	if (S_ISDIR((*a)->fts_statp->st_mode)) {
-		if (!S_ISDIR((*b)->fts_statp->st_mode))
-			return (1);
-	} else if (S_ISDIR((*b)->fts_statp->st_mode))
-		return (-1);
-	return (strcmp((*a)->fts_name, (*b)->fts_name));
-}
-
-static void
-output(int *offset, const char *fmt, ...)
+void
+output(FILE *fp, int indent, int *offset, const char *fmt, ...)
 {
 	va_list ap;
 	char buf[1024];
@@ -434,8 +490,8 @@ output(int *offset, const char *fmt, ...)
 	va_end(ap);
 
 	if (*offset + strlen(buf) > MAXLINELEN - 3) {
-		printf(" \\\n%*s", INDENTNAMELEN, "");
-		*offset = INDENTNAMELEN;
+		fprintf(fp, " \\\n%*s", INDENTNAMELEN + indent, "");
+		*offset = INDENTNAMELEN + indent;
 	}
-	*offset += printf(" %s", buf) + 1;
+	*offset += fprintf(fp, " %s", buf) + 1;
 }
diff --git a/pkgtools/mtree/files/excludes.c b/pkgtools/mtree/files/excludes.c
index 111da4e82c05..5df2dd0313de 100644
--- a/pkgtools/mtree/files/excludes.c
+++ b/pkgtools/mtree/files/excludes.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: excludes.c,v 1.4 2004/08/21 04:10:45 jlam Exp $	*/
+/*	$NetBSD: excludes.c,v 1.13 2004/06/20 22:20:18 jmc Exp $	*/
 
 /*
  * Copyright 2000 Massachusetts Institute of Technology
@@ -42,7 +42,7 @@
 #endif
 
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: excludes.c,v 1.4 2004/08/21 04:10:45 jlam Exp $");
+__RCSID("$NetBSD: excludes.c,v 1.13 2004/06/20 22:20:18 jmc Exp $");
 #endif
 
 #if HAVE_SYS_TYPES_H
diff --git a/pkgtools/mtree/files/extern.h b/pkgtools/mtree/files/extern.h
index c93cbaa8cc1d..4df267c12889 100644
--- a/pkgtools/mtree/files/extern.h
+++ b/pkgtools/mtree/files/extern.h
@@ -1,4 +1,4 @@
-/*	$NetBSD: extern.h,v 1.5 2011/07/27 15:31:00 seb Exp $	*/
+/*	$NetBSD: extern.h,v 1.41 2024/12/05 17:17:43 christos Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -36,13 +36,11 @@
 #endif
 #include "mtree.h"
 
-#if 0
 #if HAVE_NBTOOL_CONFIG_H
 #include "nbtool_config.h"
 #else 
 #define HAVE_STRUCT_STAT_ST_FLAGS 1
 #endif
-#endif
  
 #include <nbcompat.h>
 #if HAVE_ERR_H
@@ -51,6 +49,12 @@
 #if HAVE_FTS_H
 #include <fts.h>
 #endif
+#if HAVE_UTIL_H
+#include <util.h>
+#endif
+#if HAVE_STDBOOL_H
+#include <stdbool.h>
+#endif
 
 #if HAVE_NETDB_H
 /* For MAXHOSTNAMELEN on some platforms. */
@@ -59,35 +63,49 @@
 #endif
 #endif
 
+#if defined(__FreeBSD__) && !defined(HAVE_NBTOOL_CONFIG_H)
+#define    FTS_CONST const
+#else
+#define    FTS_CONST
+#endif
+
 #ifndef MAXHOSTNAMELEN
 #define MAXHOSTNAMELEN 256
 #endif
 
+enum flavor {
+	F_MTREE,
+	F_FREEBSD9,
+	F_NETBSD6
+};
+
 void	 addtag(slist_t *, char *);
 int	 check_excludes(const char *, const char *);
 int	 compare(NODE *, FTSENT *);
 int	 crc(int, uint32_t *, uint32_t *);
-void	 cwalk(void);
-void	 dump_nodes(const char *, NODE *, int);
+void	 cwalk(FILE *);
+int	dcmp(const FTSENT *FTS_CONST *, const FTSENT *FTS_CONST *);
+void	 dump_nodes(FILE *, const char *, NODE *, int);
 void	 init_excludes(void);
 int	 matchtags(NODE *);
-void	 mtree_err(const char *, ...)
-	    __attribute__((__format__(__printf__, 1, 2)));
+__dead __printflike(1,2) void	 mtree_err(const char *, ...);
 const char *nodetype(u_int);
 u_int	 parsekey(const char *, int *);
 void	 parsetags(slist_t *, char *);
 u_int	 parsetype(const char *);
 void	 read_excludes_file(const char *);
 const char *rlink(const char *);
-int	 verify(void);
+int	 verify(FILE *);
+void	 load_only(const char *fname);
+bool	 find_only(const char *path);
 
-extern int	dflag, eflag, iflag, lflag, mflag, rflag, sflag, tflag, uflag;
-extern int	mtree_Mflag, mtree_Wflag;
+extern int	bflag, dflag, eflag, iflag, jflag, lflag, mflag,
+		nflag, qflag, rflag, sflag, tflag, uflag;
+extern int	mtree_Mflag, mtree_Sflag, mtree_Wflag;
 extern size_t	mtree_lineno;
+extern enum flavor	flavor;
 extern uint32_t crc_total;
 extern int	ftsoptions, keys;
 extern char	fullpath[];
 extern slist_t	includetags, excludetags;
 
-
-#include "stat_flags.h"
diff --git a/pkgtools/mtree/files/getid.c b/pkgtools/mtree/files/getid.c
index 769b2303fb28..04aec810493e 100644
--- a/pkgtools/mtree/files/getid.c
+++ b/pkgtools/mtree/files/getid.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: getid.c,v 1.4 2008/11/06 02:14:52 jschauma Exp $	*/
+/*	$NetBSD: getid.c,v 1.10 2014/10/27 21:46:45 christos Exp $	*/
 /*	from: NetBSD: getpwent.c,v 1.48 2000/10/03 03:22:26 enami Exp */
 /*	from: NetBSD: getgrent.c,v 1.41 2002/01/12 23:51:30 lukem Exp */
 
@@ -46,13 +46,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *        This product includes software developed by the NetBSD
- *        Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -75,6 +68,11 @@
 #endif
 
 #include <nbcompat.h>
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+__RCSID("$NetBSD: getid.c,v 1.10 2014/10/27 21:46:45 christos Exp $");
+
 #if HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
@@ -232,7 +230,12 @@ grstart(void)
 	}
 	if (grfile[0] == '\0')			/* sanity check */
 		return 0;
-	return (_gr_fp = fopen(grfile, "r")) ? 1 : 0;
+
+	_gr_fp = fopen(grfile, "r");
+	if (_gr_fp != NULL)
+		return 1;
+	warn("Can't open `%s'", grfile);
+	return 0;
 }
 
 
@@ -256,6 +259,9 @@ grscan(int search, gid_t gid, const char *name)
 				;
 			continue;
 		}
+		/* skip comments */
+		if (grline[0] == '#')
+			continue;
 		if (grmatchline(search, gid, name))
 			return 1;
 	}
@@ -373,7 +379,11 @@ pwstart(void)
 	}
 	if (pwfile[0] == '\0')			/* sanity check */
 		return 0;
-	return (_pw_fp = fopen(pwfile, "r")) ? 1 : 0;
+	_pw_fp = fopen(pwfile, "r");
+	if (_pw_fp != NULL)
+		return 1;
+	warn("Can't open `%s'", pwfile);
+	return 0;
 }
 
 
@@ -397,6 +407,9 @@ pwscan(int search, uid_t uid, const char *name)
 				;
 			continue;
 		}
+		/* skip comments */
+		if (pwline[0] == '#')
+			continue;
 		if (pwmatchline(search, uid, name))
 			return 1;
 	}
diff --git a/pkgtools/mtree/files/misc.c b/pkgtools/mtree/files/misc.c
index fff571e37123..e6670c69525d 100644
--- a/pkgtools/mtree/files/misc.c
+++ b/pkgtools/mtree/files/misc.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: misc.c,v 1.3 2008/11/06 02:14:52 jschauma Exp $	*/
+/*	$NetBSD: misc.c,v 1.35 2024/12/05 17:17:43 christos Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -43,7 +43,7 @@
 #include <sys/cdefs.h>
 #endif
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: misc.c,v 1.3 2008/11/06 02:14:52 jschauma Exp $");
+__RCSID("$NetBSD: misc.c,v 1.35 2024/12/05 17:17:43 christos Exp $");
 #endif /* not lint */
 
 #if HAVE_SYS_TYPES_H
@@ -68,6 +68,8 @@ __RCSID("$NetBSD: misc.c,v 1.3 2008/11/06 02:14:52 jschauma Exp $");
 
 #include "extern.h"
 
+enum flavor	flavor = F_MTREE;
+
 typedef struct _key {
 	const char	*name;		/* key name */
 	u_int		val;		/* value */
@@ -89,7 +91,9 @@ static KEY keylist[] = {
 	{"md5digest",	F_MD5,		NEEDVALUE},
 	{"mode",	F_MODE,		NEEDVALUE},
 	{"nlink",	F_NLINK,	NEEDVALUE},
+	{"nochange",	F_NOCHANGE,	0},
 	{"optional",	F_OPT,		0},
+	{"ripemd160digest", F_RMD160,	NEEDVALUE},
 	{"rmd160",	F_RMD160,	NEEDVALUE},
 	{"rmd160digest",F_RMD160,	NEEDVALUE},
 	{"sha1",	F_SHA1,		NEEDVALUE},
@@ -125,7 +129,7 @@ slist_t	excludetags, includetags;
 int	keys = KEYDEFAULT;
 
 
-int keycompare(const void *, const void *);
+static int keycompare(const void *, const void *);
 
 u_int
 parsekey(const char *name, int *needvaluep)
@@ -134,7 +138,7 @@ parsekey(const char *name, int *needvaluep)
 	KEY *k, tmp;
 
 	if (allbits == 0) {
-		int i;
+		size_t i;
 
 		for (i = 0; i < sizeof(keylist) / sizeof(KEY); i++)
 			allbits |= keylist[i].val;
@@ -167,7 +171,7 @@ parsetype(const char *name)
 	return (k->val);
 }
 
-int
+static int
 keycompare(const void *a, const void *b)
 {
 
@@ -212,7 +216,7 @@ void
 parsetags(slist_t *list, char *args)
 {
 	char	*p, *e;
-	int	len;
+	size_t	len;
 
 	if (args == NULL) {
 		addtag(list, NULL);
diff --git a/pkgtools/mtree/files/mtree.8 b/pkgtools/mtree/files/mtree.8
index 9f91aa1be0bd..99e3199de943 100644
--- a/pkgtools/mtree/files/mtree.8
+++ b/pkgtools/mtree/files/mtree.8
@@ -1,4 +1,4 @@
-.\"	$NetBSD: mtree.8,v 1.4 2008/11/06 02:14:52 jschauma Exp $
+.\"	$NetBSD: mtree.8,v 1.78 2023/12/02 13:26:09 christos Exp $
 .\"
 .\" Copyright (c) 1989, 1990, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -41,13 +41,6 @@
 .\" 2. Redistributions in binary form must reproduce the above copyright
 .\"    notice, this list of conditions and the following disclaimer in the
 .\"    documentation and/or other materials provided with the distribution.
-.\" 3. All advertising materials mentioning features or use of this software
-.\"    must display the following acknowledgement:
-.\"        This product includes software developed by the NetBSD
-.\"        Foundation, Inc. and its contributors.
-.\" 4. Neither the name of The NetBSD Foundation nor the names of its
-.\"    contributors may be used to endorse or promote products derived
-.\"    from this software without specific prior written permission.
 .\"
 .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -63,7 +56,7 @@
 .\"
 .\"     @(#)mtree.8	8.2 (Berkeley) 12/11/93
 .\"
-.Dd September 12, 2006
+.Dd December 2, 2023
 .Dt MTREE 8
 .Os
 .Sh NAME
@@ -71,143 +64,244 @@
 .Nd map a directory hierarchy
 .Sh SYNOPSIS
 .Nm
-.Op Fl cCdDelLMPruUWx
-.Bk -words
+.Op Fl bCcDdejLlMnPqrStUuWx
 .Op Fl i | Fl m
-.Ek
-.Bk -words
-.Op Fl f Ar spec
-.Ek
-.Bk -words
-.Op Fl p Ar path
-.Ek
-.Bk -words
-.Op Fl k Ar keywords
-.Ek
-.Bk -words
-.Op Fl K Ar keywords
-.Ek
-.Bk -words
-.Op Fl R Ar keywords
-.Ek
-.Bk -words
 .Op Fl E Ar tags
-.Ek
-.Bk -words
+.Op Fl F Ar flavor
+.Op Fl f Ar spec
 .Op Fl I Ar tags
-.Ek
-.Bk -words
+.Op Fl K Ar keywords
+.Op Fl k Ar keywords
 .Op Fl N Ar dbdir
-.Ek
-.Bk -words
+.Op Fl O Ar onlyfile
+.Op Fl p Ar path
+.Op Fl R Ar keywords
 .Op Fl s Ar seed
-.Ek
-.Bk -words
 .Op Fl X Ar exclude-file
-.Ek
 .Sh DESCRIPTION
 The
 .Nm
-utility compares the file hierarchy rooted in the current directory
+utility compares a file hierarchy against a specification,
+creates a specification for a file hierarchy, or modifies
+a specification.
+.Pp
+The default action, if not overridden by command line options,
+is to compare the file hierarchy rooted in the current directory
 against a specification read from the standard input.
 Messages are written to the standard output for any files whose
 characteristics do not match the specification, or which are
 missing from either the file hierarchy or the specification.
 .Pp
 The options are as follows:
-.Bl -tag -width flag
-.It Fl c
-Print a specification for the file hierarchy to the standard output.
-.It Fl d
-Ignore everything except directory type files.
+.Bl -tag -width Fl
+.
+.It Fl b
+Suppress blank lines before entering and after exiting directories.
+.
 .It Fl C
-Print
-.Pq Sq dump
-the specification as provided by
-.Fl f Ar spec
-in a format that's easier to parse with various tools.
-The full path name is always printed as the first field, and
-.Fl k ,
+Convert a specification into
+a format that's easier to parse with various tools.
+The input specification is read from standard input or
+from the file given by
+.Fl f Ar spec .
+In the output, each file or directory is represented using a single line
+(which might be very long).
+The full path name
+.Pq beginning with Ql \&./
+is always printed as the first field;
 .Fl K ,
+.Fl k ,
 and
 .Fl R
-can be used to control which other keywords are printed,
-and
+can be used to control which other keywords are printed;
 .Fl E
 and
 .Fl I
-can be used to control which files are printed.
+can be used to control which files are printed;
+and the
+.Fl S
+option can be used to sort the output.
+.
+.It Fl c
+Print a specification for the file hierarchy originating at
+the current working directory
+.Po or the directory provided by
+.Fl p Ar path
+.Pc
+to the standard output.
+The output is in a style using relative path names.
+.
 .It Fl D
 As per
 .Fl C ,
 except that the path name is always printed as the last field instead of
 the first.
+.
+.It Fl d
+Ignore everything except directory type files.
+.
 .It Fl E Ar tags
 Add the comma separated tags to the
 .Dq exclusion
 list.
 Non-directories with tags which are in the exclusion list are not printed with
+.Fl C
+and
 .Fl D .
+.
 .It Fl e
 Don't complain about files that are in the file hierarchy, but not in the
 specification.
+.
+.It Fl F Ar flavor
+Set the compatibility flavor of the
+.Nm
+utility.
+The
+.Ar flavor
+can be one of
+.Cm mtree ,
+.Cm freebsd9 ,
+or
+.Cm netbsd6 .
+The default is
+.Cm mtree .
+The
+.Cm freebsd9
+and
+.Cm netbsd6
+flavors attempt to preserve output compatibility and command line option
+backward compatibility with
+.Fx 9.0
+and
+.Nx 6.0
+respectively.
+.
 .It Fl f Ar spec
 Read the specification from
 .Ar file  ,
 instead of from the standard input.
+.Pp
+If this option is specified twice, the two specifications are compared
+to each other rather than to the file hierarchy.
+The specifications will be sorted like output generated using
+.Fl c .
+The output format in this case is somewhat reminiscent of
+.Xr comm 1 ,
+having
+.Dq in first spec only ,
+.Dq in second spec only ,
+and
+.Dq different
+columns, prefixed by zero, one and two
+.Tn TAB
+characters respectively.
+Each entry in the
+.Dq different
+column occupies two lines, one from each specification.
+.
 .It Fl I Ar tags
 Add the comma separated tags to the
 .Dq inclusion
 list.
 Non-directories with tags which are in the inclusion list are printed with
+.Fl C
+and
 .Fl D .
 If no inclusion list is provided, the default is to display all files.
+.
 .It Fl i
-If specified, set the schg and/or sappnd flags.
+If specified, set the
+.Ql schg
+and/or
+.Ql sappnd
+flags.
+.
+.It Fl j
+Indent the output 4 spaces each time a directory level is descended when
+creating a specification with the
+.Fl c
+option.
+This does not affect either the
+.Ql /set
+statements or the comment before each
+directory.
+It does however affect the comment before the close of each directory.
+This is the equivalent of the
+.Fl i
+option in the
+.Fx
+version of
+.Nm .
+.
 .It Fl K Ar keywords
 Add the specified (whitespace or comma separated) keywords to the current
 set of keywords.
 If
 .Ql all
 is specified, add all of the other keywords.
+.
 .It Fl k Ar keywords
 Use the
 .Sy type
 keyword plus the specified (whitespace or comma separated)
-keywords instead of the current set of keywords.
+.Ar keywords
+instead of the current set of keywords.
 If
 .Ql all
 is specified, use all of the other keywords.
 If the
 .Sy type
 keyword is not desired, suppress it with
-.Fl R Ar type .
+.Fl R Cm type .
+.
+.It Fl L
+Follow all symbolic links in the file hierarchy.
+.
 .It Fl l
 Do
 .Dq loose
 permissions checks, in which more stringent permissions
-will match less stringent ones. For example, a file marked mode 0444
+will match less stringent ones.
+For example, a file marked mode 0444
 will pass a check for mode 0644.
 .Dq Loose
-checks apply only to read, write and execute permissions -- in
+checks apply only to read, write and execute permissions \(em in
 particular, if other bits like the sticky bit or suid/sgid bits are
 set either in the specification or the file, exact checking will be
-performed. This flag may not be set at the same time as the
-.Fl u
-or
+performed.
+This option may not be set at the same time as the
 .Fl U
-flags.
-.It Fl L
-Follow all symbolic links in the file hierarchy.
+or
+.Fl u
+option.
+.
+.It Fl M
+Permit merging of specification entries with different types,
+with the last entry taking precedence.
+.
 .It Fl m
-If the schg and/or sappnd flags are specified, reset these flags. Note that
-this is only possible with securelevel less than 1 (i.e. in single user mode
-or while the system is running in insecure mode). See
+If the
+.Ql schg
+and/or
+.Ql sappnd
+flags are specified, reset these flags.
+Note that this is only possible with securelevel less than 1
+.Po
+i.e., in single user mode or while the system is running in insecure mode
+.Pc .
+See
 .Xr init 8
 for information on security levels.
-.It Fl M
-Permit merging of specification entries with different types,
-with the last entry take precedence.
+.
+.It Fl n
+Do not emit pathname comments when creating a specification.
+Normally
+a comment is emitted before each directory and before the close of that
+directory when using the
+.Fl c
+option.
+.
 .It Fl N Ar dbdir
 Use the user database text file
 .Pa master.passwd
@@ -220,29 +314,80 @@ rather than using the results from the system's
 and
 .Xr getgrnam 3
 (and related) library calls.
-.It Fl p Ar path
-Use the file hierarchy rooted in
-.Ar path  ,
-instead of the current directory.
+.
+.It Fl O Ar onlypaths
+Only include files included in this list of pathnames.
+.
 .It Fl P
 Don't follow symbolic links in the file hierarchy, instead consider
 the symbolic link itself in any comparisons.
 This is the default.
-.It Fl r
-Remove any files in the file hierarchy that are not described in the
-specification.
+.
+.It Fl p Ar path
+Use the file hierarchy rooted in
+.Ar path  ,
+instead of the current directory.
+.
+.It Fl q
+Quiet mode.
+Do not complain when a
+.Dq missing
+directory cannot be created because it already exists.
+This occurs when the directory is a symbolic link.
+.
 .It Fl R Ar keywords
 Remove the specified (whitespace or comma separated) keywords from the current
 set of keywords.
 If
 .Ql all
 is specified, remove all of the other keywords.
+.
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+Repeating the flag more than once will attempt to reset all the
+file flags via
+.Xr lchflags 2
+before attempting to remove the file in case the file was immutable.
+.
+.It Fl S
+When reading a specification into an internal data structure,
+sort the entries.
+Sorting will affect the order of the output produced by the
+.Fl C
+or
+.Fl D
+options, and will also affect the order in which
+missing entries are created or reported when a directory tree is checked
+against a specification.
+.Pp
+The sort order is the same as that used by the
+.Fl c
+option, which is that entries within the same directory are
+sorted in the order used by
+.Xr strcmp 3 ,
+except that entries for subdirectories sort after other entries.
+By default, if the
+.Fl S
+option is not used, entries within the same directory are collected
+together (separated from entries for other directories), but not sorted.
+.
 .It Fl s Ar seed
 Display a single checksum to the standard error output that represents all
 of the files for which the keyword
 .Sy cksum
 was specified.
 The checksum is seeded with the specified value.
+.
+.It Fl t
+Modify the modified time of existing files, the device type of devices, and
+symbolic link targets, to match the specification.
+.
+.It Fl U
+Same as
+.Fl u
+except that a mismatch is not considered to be an error if it was corrected.
+.
 .It Fl u
 Modify the owner, group, permissions, and flags of existing files,
 the device type of devices, and symbolic link targets,
@@ -253,26 +398,23 @@ to be created.
 Note that unless the
 .Fl i
 option is given, the schg and sappnd flags will not be set, even if
-specified. If
+specified.
+If
 .Fl m
 is given, these flags will be reset.
 Exit with a status of 0 on success,
 2 if the file hierarchy did not match the specification, and
 1 if any other error occurred.
-.It Fl U
-Same as
-.Fl u
-except that a mismatch is not considered to be an error if it was corrected.
+.
 .It Fl W
 Don't attempt to set various file attributes such as the
 ownership, mode, flags, or time
 when creating new directories or changing existing entries.
 This option will be most useful when used in conjunction with
-.Fl u
+.Fl U
 or
-.Fl U .
-.It Fl x
-Don't descend below mount points in the file hierarchy.
+.Fl u .
+.
 .It Fl X Ar exclude-file
 The specified file contains
 .Xr fnmatch 3
@@ -285,9 +427,14 @@ the starting directory); otherwise,
 it will be matched against basenames only.
 Comments are permitted in
 the
-.Ar exclude-list
+.Ar exclude-file
 file.
+.
+.It Fl x
+Don't descend below mount points in the file hierarchy.
+.
 .El
+.
 .Pp
 Specifications are mostly composed of
 .Dq keywords ,
@@ -297,6 +444,7 @@ No keywords have default values, and if a keyword has no value set, no
 checks based on it are performed.
 .Pp
 Currently supported keywords are as follows:
+.
 .Bl -tag -width Sy
 .It Sy cksum
 The checksum of the file using the default algorithm specified by
@@ -310,15 +458,8 @@ or
 .Sy char
 file types.
 The argument must be one of the following forms:
-.Pp
 .Bl -tag -width 4n
-.It Xo
-.Sm off
-.Ar format ,
-.Ar major ,
-.Ar minor
-.Sm on
-.Xc
+.It Ar format , Ns Ar major , Ns Ar minor
 A device with
 .Ar major
 and
@@ -326,14 +467,7 @@ and
 fields, for an operating system specified with
 .Ar format .
 See below for valid formats.
-.It Xo
-.Sm off
-.Ar format ,
-.Ar major ,
-.Ar unit ,
-.Ar subunit
-.Sm on
-.Xc
+.It Ar format , Ns Ar major , Ns Ar unit , Ns Ar subunit
 A device with
 .Ar major ,
 .Ar unit ,
@@ -347,6 +481,7 @@ format.)
 .It Ar number
 Opaque number (as stored on the file system).
 .El
+.
 .Pp
 The following values for
 .Ar format
@@ -373,9 +508,11 @@ See
 .Xr mknod 8
 for more details.
 .It Sy flags
-The file flags as a symbolic name.  See
+The file flags as a symbolic name.
+See
 .Xr chflags 1
-for information on these names.  If no flags are to be set the string
+for information on these names.
+If no flags are to be set the string
 .Ql none
 may be used to override the current default.
 Note that the schg and sappnd flags are treated specially (see the
@@ -403,9 +540,14 @@ The current file's permissions as a numeric (octal) or symbolic
 value.
 .It Sy nlink
 The number of hard links the file is expected to have.
+.It Sy nochange
+Make sure this file or directory exists but otherwise ignore all attributes.
 .It Sy optional
 The file is optional; don't complain about the file if it's
 not in the file hierarchy.
+.It Sy ripemd160digest
+Synonym for
+.Sy rmd160 .
 .It Sy rmd160
 The
 .Tn RMD-160
@@ -451,7 +593,10 @@ and
 These may be specified without leading or trailing commas, but will be
 stored internally with them.
 .It Sy time
-The last modification time of the file.
+The last modification time of the file,
+in second and nanoseconds.
+The value should include a period character and exactly nine digits after
+the period.
 .It Sy type
 The type of the file; may be set to any one of the following:
 .Pp
@@ -490,7 +635,6 @@ and
 .Sy uid .
 .Pp
 There are four types of lines in a specification:
-.Pp
 .Bl -enum
 .It
 Set global values for a keyword.
@@ -532,8 +676,24 @@ they match.
 .Nm
 uses
 .Xr strsvis 3
-(in VIS_CSTYLE format) to encode path names containing
-non-printable characters. Whitespace characters are encoded as
+(in
+.Dv VIS_OCTAL
+format) to encode path names containing
+non-printable characters.
+Whitespace characters are encoded as
+.Ql \e040
+(space),
+.Ql \e011
+(tab), and
+.Ql \e012
+(new line).
+When flavor
+.Sy netbsd6
+is selected,
+.Xr strsvis 3
+(in
+.Dv VIS_CSTYLE
+format) is used and whitespace characters are encoded as
 .Ql \es
 (space),
 .Ql \et
@@ -572,7 +732,7 @@ appropriately.
 Multiple entries for the same full path are permitted if the types
 are the same (unless
 .Fl M
-is given, and then the types may differ);
+is given, in which case the types may differ);
 in this case the settings for the last entry take precedence.
 .Pp
 A path name that does not contain a slash will be treated as a relative path.
@@ -595,7 +755,7 @@ The
 utility exits with a status of 0 on success, 1 if any error occurred,
 and 2 if the file hierarchy did not match the specification.
 .Sh FILES
-.Bl -tag -width /etc/mtree -compact
+.Bl -tag -width Pa -compact
 .It Pa /etc/mtree
 system specification directory
 .El
@@ -622,10 +782,40 @@ can be used to detect which of the binaries have actually been modified.
 .Pp
 The
 .Fl d
-and
+option can be used in combination with
+.Fl U
+or
 .Fl u
-options can be used in combination to create directory hierarchies
-for distributions and other such things.
+to create directory hierarchies for, for example, distributions.
+.Sh COMPATIBILITY
+The compatibility shims provided by the
+.Fl F
+option are incomplete by design.
+Known limitations are described below.
+.Pp
+The
+.Sy freebsd9
+flavor retains the default handling of lookup failures for the
+.Sy uname
+and
+.Sy group
+keywords by replacing them with appropriate
+.Sy uid
+and
+.Sy gid
+keywords rather than failing and reporting an error.
+The related
+.Fl w
+flag is a no-op rather than causing a warning to be printed and no
+keyword to be emitted.
+The latter behavior is not emulated as it is potentially dangerous in
+the face of /set statements.
+.Pp
+The
+.Sy netbsd6
+flavor does not replicate the historical bug that reported time as
+seconds.nanoseconds without zero padding nanosecond values less than
+100000000.
 .Sh SEE ALSO
 .Xr chflags 1 ,
 .Xr chgrp 1 ,
@@ -635,6 +825,7 @@ for distributions and other such things.
 .Xr fnmatch 3 ,
 .Xr fts 3 ,
 .Xr strsvis 3 ,
+.Xr mtree 5 ,
 .Xr chown 8 ,
 .Xr mknod 8
 .Sh HISTORY
@@ -648,7 +839,7 @@ keyword appeared in
 .Nx 1.2 .
 The
 .Fl U
-flag appeared in
+option appeared in
 .Nx 1.3 .
 The
 .Sy flags
@@ -658,7 +849,7 @@ keywords, and
 .Fl i
 and
 .Fl m
-flags
+options
 appeared in
 .Nx 1.4 .
 The
@@ -672,15 +863,15 @@ keywords,
 .Fl D ,
 .Fl E ,
 .Fl I ,
-.Fl l ,
 .Fl L ,
+.Fl l ,
 .Fl N ,
 .Fl P ,
 .Fl R ,
 .Fl W ,
 and
 .Fl X
-flags, and support for full paths appeared in
+options, and support for full paths appeared in
 .Nx 1.6 .
 The
 .Sy sha256 ,
@@ -689,3 +880,7 @@ and
 .Sy sha512
 keywords appeared in
 .Nx 3.0 .
+The
+.Fl S
+option appeared in
+.Nx 6.0 .
diff --git a/pkgtools/mtree/files/mtree.c b/pkgtools/mtree/files/mtree.c
index 77c4d710b7aa..5cca72d09b09 100644
--- a/pkgtools/mtree/files/mtree.c
+++ b/pkgtools/mtree/files/mtree.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: mtree.c,v 1.3 2004/08/21 04:10:45 jlam Exp $	*/
+/*	$NetBSD: mtree.c,v 1.51 2024/12/05 17:17:15 christos Exp $	*/
 
 /*-
  * Copyright (c) 1989, 1990, 1993
@@ -41,15 +41,15 @@
 #include <sys/cdefs.h>
 #endif
 #if defined(__COPYRIGHT) && !defined(lint)
-__COPYRIGHT("@(#) Copyright (c) 1989, 1990, 1993\n\
-	The Regents of the University of California.  All rights reserved.\n");
+__COPYRIGHT("@(#) Copyright (c) 1989, 1990, 1993\
+ The Regents of the University of California.  All rights reserved.");
 #endif /* not lint */
 
 #if defined(__RCSID) && !defined(lint)
 #if 0
 static char sccsid[] = "@(#)mtree.c	8.1 (Berkeley) 6/6/93";
 #else
-__RCSID("$NetBSD: mtree.c,v 1.3 2004/08/21 04:10:45 jlam Exp $");
+__RCSID("$NetBSD: mtree.c,v 1.51 2024/12/05 17:17:15 christos Exp $");
 #endif
 #endif /* not lint */
 
@@ -79,27 +79,45 @@ __RCSID("$NetBSD: mtree.c,v 1.3 2004/08/21 04:10:45 jlam Exp $");
 #include "extern.h"
 
 int	ftsoptions = FTS_PHYSICAL;
-int	cflag, Cflag, dflag, Dflag, eflag, iflag, lflag, mflag,
-    	rflag, sflag, tflag, uflag, Uflag;
+int	bflag, dflag, eflag, iflag, jflag, lflag, mflag, nflag, qflag, rflag,
+	sflag, tflag, uflag;
 char	fullpath[MAXPATHLEN];
 
-	int	main(int, char **);
-static	void	usage(void);
+static struct {
+	enum flavor flavor;
+	const char name[9];
+} flavors[] = {
+	{F_MTREE, "mtree"},
+	{F_FREEBSD9, "freebsd9"},
+	{F_NETBSD6, "netbsd6"},
+};
+
+__dead static	void	usage(void);
 
 int
 main(int argc, char **argv)
 {
 	int	ch, status;
+	unsigned int	i;
+	int	cflag, Cflag, Dflag, Uflag, wflag;
 	char	*dir, *p;
+	FILE	*spec1, *spec2;
 
 	setprogname(argv[0]);
 
+	cflag = Cflag = Dflag = Uflag = wflag = 0;
 	dir = NULL;
 	init_excludes();
+	spec1 = stdin;
+	spec2 = NULL;
 
-	while ((ch = getopt(argc, argv, "cCdDeE:f:I:ik:K:lLmMN:p:PrR:s:tuUWxX:"))
+	while ((ch = getopt(argc, argv,
+	    "bcCdDeE:f:F:I:ijk:K:lLmMnN:O:p:PqrR:s:StuUwWxX:"))
 	    != -1) {
 		switch((char)ch) {
+		case 'b':
+			bflag = 1;
+			break;
 		case 'c':
 			cflag = 1;
 			break;
@@ -119,8 +137,27 @@ main(int argc, char **argv)
 			eflag = 1;
 			break;
 		case 'f':
-			if (!(freopen(optarg, "r", stdin)))
-				mtree_err("%s: %s", optarg, strerror(errno));
+			if (spec1 == stdin) {
+				spec1 = fopen(optarg, "r");
+				if (spec1 == NULL)
+					mtree_err("%s: %s", optarg,
+					    strerror(errno));
+			} else if (spec2 == NULL) {
+				spec2 = fopen(optarg, "r");
+				if (spec2 == NULL)
+					mtree_err("%s: %s", optarg,
+					    strerror(errno));
+			} else
+				usage();
+			break;
+		case 'F':
+			for (i = 0; i < __arraycount(flavors); i++)
+				if (strcmp(optarg, flavors[i].name) == 0) {
+					flavor = flavors[i].flavor;
+					break;
+				}
+			if (i == __arraycount(flavors))
+				usage();
 			break;
 		case 'i':
 			iflag = 1;
@@ -128,6 +165,9 @@ main(int argc, char **argv)
 		case 'I':
 			parsetags(&includetags, optarg);
 			break;
+		case 'j':
+			jflag = 1;
+			break;
 		case 'k':
 			keys = F_TYPE;
 			while ((p = strsep(&optarg, " \t,")) != NULL)
@@ -152,12 +192,18 @@ main(int argc, char **argv)
 		case 'M':
 			mtree_Mflag = 1;
 			break;
+		case 'n':
+			nflag = 1;
+			break;
 		case 'N':
 			if (! setup_getid(optarg))
 				mtree_err(
 			    "Unable to use user and group databases in `%s'",
 				    optarg);
 			break;
+		case 'O':
+			load_only(optarg);
+			break;
 		case 'p':
 			dir = optarg;
 			break;
@@ -165,8 +211,11 @@ main(int argc, char **argv)
 			ftsoptions &= ~FTS_LOGICAL;
 			ftsoptions |= FTS_PHYSICAL;
 			break;
+		case 'q':
+			qflag = 1;
+			break;
 		case 'r':
-			rflag = 1;
+			rflag++;
 			break;
 		case 'R':
 			while ((p = strsep(&optarg, " \t,")) != NULL)
@@ -175,10 +224,13 @@ main(int argc, char **argv)
 			break;
 		case 's':
 			sflag = 1;
-			crc_total = ~strtol(optarg, &p, 0);
+			crc_total = (uint32_t)~strtol(optarg, &p, 0);
 			if (*p)
 				mtree_err("illegal seed value -- %s", optarg);
 			break;
+		case 'S':
+			mtree_Sflag = 1;
+			break;
 		case 't':
 			tflag = 1;
 			break;
@@ -188,6 +240,9 @@ main(int argc, char **argv)
 		case 'U':
 			Uflag = uflag = 1;
 			break;
+		case 'w':
+			wflag = 1;
+			break;
 		case 'W':
 			mtree_Wflag = 1;
 			break;
@@ -208,6 +263,43 @@ main(int argc, char **argv)
 	if (argc)
 		usage();
 
+	switch (flavor) {
+	case F_FREEBSD9:
+		if (cflag && iflag) {
+			warnx("-c and -i passed, replacing -i with -j for "
+			    "FreeBSD compatibility");
+			iflag = 0;
+			jflag = 1;
+		}
+		if (dflag && !bflag) {
+			warnx("Adding -b to -d for FreeBSD compatibility");
+			bflag = 1;
+		}
+		if (uflag && !iflag) {
+			warnx("Adding -i to -%c for FreeBSD compatibility",
+			    Uflag ? 'U' : 'u');
+			iflag = 1;
+		}
+		if (uflag && !tflag) {
+			warnx("Adding -t to -%c for FreeBSD compatibility",
+			    Uflag ? 'U' : 'u');
+			tflag = 1;
+		}
+		if (wflag)
+			warnx("The -w flag is a no-op");
+		break;
+	default:
+		if (wflag)
+			usage();
+	}
+
+	if (spec2 && (cflag || Cflag || Dflag))
+		mtree_err("Double -f, -c, -C and -D flags are mutually "
+		    "exclusive");
+
+	if (dir && spec2)
+		mtree_err("Double -f and -p flags are mutually exclusive");
+
 	if (dir && chdir(dir))
 		mtree_err("%s: %s", dir, strerror(errno));
 
@@ -224,15 +316,18 @@ main(int argc, char **argv)
 		mtree_err("-l and -u flags are mutually exclusive");
 
 	if (cflag) {
-		cwalk();
+		cwalk(stdout);
 		exit(0);
 	}
 	if (Cflag || Dflag) {
-		dump_nodes("", spec(stdin), Dflag);
+		dump_nodes(stdout, "", spec(spec1), Dflag);
 		exit(0);
 	}
-	status = verify();
-	if (Uflag & (status == MISMATCHEXIT))
+	if (spec2 != NULL)
+		status = mtree_specspec(spec1, spec2);
+	else
+		status = verify(spec1);
+	if (Uflag && (status == MISMATCHEXIT))
 		status = 0;
 	exit(status);
 }
@@ -240,11 +335,18 @@ main(int argc, char **argv)
 static void
 usage(void)
 {
+	unsigned int i;
 
 	fprintf(stderr,
-	    "usage: %s [-cCdDelLMPruUWx] [-i|-m] [-f spec] [-k key]\n"
-	    "\t\t[-K addkey] [-R removekey] [-I inctags] [-E exctags]\n"
-	    "\t\t[-N userdbdir] [-X exclude-file] [-p path] [-s seed]\n",
+	    "usage: %s [-bCcDdejLlMnPqrStUuWx] [-i|-m] [-E tags]\n"
+	    "\t\t[-f spec] [-f spec]\n"
+	    "\t\t[-I tags] [-K keywords] [-k keywords] [-N dbdir] [-p path]\n"
+	    "\t\t[-R keywords] [-s seed] [-X exclude-file]\n"
+	    "\t\t[-F flavor]\n",
 	    getprogname());
+	fprintf(stderr, "\nflavors:");
+	for (i = 0; i < __arraycount(flavors); i++)
+		fprintf(stderr, " %s", flavors[i].name);
+	fprintf(stderr, "\n");
 	exit(1);
 }
diff --git a/pkgtools/mtree/files/mtree.cat8 b/pkgtools/mtree/files/mtree.cat8
index ebc2b8939af3..de9a2f8243f2 100644
--- a/pkgtools/mtree/files/mtree.cat8
+++ b/pkgtools/mtree/files/mtree.cat8
@@ -1,156 +1,222 @@
-MTREE(8)                NetBSD System Manager's Manual                MTREE(8)
+MTREE(8)                NetBSD System Manager?s Manual                MTREE(8)
 
 NNAAMMEE
-     mmttrreeee -- map a directory hierarchy
+     mmttrreeee ? map a directory hierarchy
 
 SSYYNNOOPPSSIISS
-     mmttrreeee [--ccCCddDDeellLLMMPPrruuUUWWxx] [--ii | --mm] [--ff _s_p_e_c] [--pp _p_a_t_h] [--kk _k_e_y_w_o_r_d_s]
-           [--KK _k_e_y_w_o_r_d_s] [--RR _k_e_y_w_o_r_d_s] [--EE _t_a_g_s] [--II _t_a_g_s] [--NN _d_b_d_i_r]
-           [--ss _s_e_e_d] [--XX _e_x_c_l_u_d_e_-_f_i_l_e]
+     mmttrreeee [&#8722;&#8722;bbCCccDDddeejjLLllMMnnPPqqrrSSttUUuuWWxx] [&#8722;&#8722;ii | &#8722;&#8722;mm] [&#8722;&#8722;EE _t_a_g_s] [&#8722;&#8722;FF _f_l_a_v_o_r] [&#8722;&#8722;ff _s_p_e_c]
+           [&#8722;&#8722;II _t_a_g_s] [&#8722;&#8722;KK _k_e_y_w_o_r_d_s] [&#8722;&#8722;kk _k_e_y_w_o_r_d_s] [&#8722;&#8722;NN _d_b_d_i_r] [&#8722;&#8722;OO _o_n_l_y_f_i_l_e]
+           [&#8722;&#8722;pp _p_a_t_h] [&#8722;&#8722;RR _k_e_y_w_o_r_d_s] [&#8722;&#8722;ss _s_e_e_d] [&#8722;&#8722;XX _e_x_c_l_u_d_e_&#8208;_f_i_l_e]
 
 DDEESSCCRRIIPPTTIIOONN
-     The mmttrreeee utility compares the file hierarchy rooted in the current
-     directory against a specification read from the standard input.  Messages
-     are written to the standard output for any files whose characteristics do
-     not match the specification, or which are missing from either the file
-     hierarchy or the specification.
+     The mmttrreeee utility compares a file hierarchy against a specification, cre&#8208;
+     ates a specification for a file hierarchy, or modifies a specification.
+
+     The default action, if not overridden by command line options, is to com&#8208;
+     pare the file hierarchy rooted in the current directory against a speci&#8208;
+     fication read from the standard input.  Messages are written to the stan&#8208;
+     dard output for any files whose characteristics do not match the specifi&#8208;
+     cation, or which are missing from either the file hierarchy or the speci&#8208;
+     fication.
 
      The options are as follows:
 
-     --cc    Print a specification for the file hierarchy to the standard out-
-           put.
-
-     --dd    Ignore everything except directory type files.
-
-     --CC    Print (`dump') the specification as provided by --ff _s_p_e_c in a format
-           that's easier to parse with various tools.  The full path name is
-           always printed as the first field, and --kk, --KK, and --RR can be used
-           to control which other keywords are printed, and --EE and --II can be
-           used to control which files are printed.
-
-     --DD    As per --CC, except that the path name is always printed as the last
-           field instead of the first.
-
-     --EE _t_a_g_s
-           Add the comma separated tags to the ``exclusion'' list.  Non-direc-
-           tories with tags which are in the exclusion list are not printed
-           with --DD.
-
-     --ee    Don't complain about files that are in the file hierarchy, but not
-           in the specification.
-
-     --ff _s_p_e_c
-           Read the specification from _f_i_l_e, instead of from the standard
-           input.
-
-     --II _t_a_g_s
-           Add the comma separated tags to the ``inclusion'' list.  Non-direc-
-           tories with tags which are in the inclusion list are printed with
-           --DD.  If no inclusion list is provided, the default is to display
-           all files.
-
-     --ii    If specified, set the schg and/or sappnd flags.
-
-     --KK _k_e_y_w_o_r_d_s
-           Add the specified (whitespace or comma separated) keywords to the
-           current set of keywords.  If `all' is specified, add all of the
-           other keywords.
-
-     --kk _k_e_y_w_o_r_d_s
-           Use the ttyyppee keyword plus the specified (whitespace or comma sepa-
-           rated) keywords instead of the current set of keywords.  If `all'
-           is specified, use all of the other keywords.  If the ttyyppee keyword
-           is not desired, suppress it with --RR _t_y_p_e.
-
-     --ll    Do ``loose'' permissions checks, in which more stringent permis-
-           sions will match less stringent ones. For example, a file marked
-           mode 0444 will pass a check for mode 0644.  ``Loose'' checks apply
-           only to read, write and execute permissions -- in particular, if
-           other bits like the sticky bit or suid/sgid bits are set either in
-           the specification or the file, exact checking will be performed.
-           This flag may not be set at the same time as the --uu or --UU flags.
-
-     --LL    Follow all symbolic links in the file hierarchy.
-
-     --mm    If the schg and/or sappnd flags are specified, reset these flags.
-           Note that this is only possible with securelevel less than 1 (i.e.
-           in single user mode or while the system is running in insecure
-           mode). See init(8) for information on security levels.
-
-     --MM    Permit merging of specification entries with different types, with
-           the last entry take precedence.
-
-     --NN _d_b_d_i_r
-           Use the user database text file _m_a_s_t_e_r_._p_a_s_s_w_d and group database
-           text file _g_r_o_u_p from _d_b_d_i_r, rather than using the results from the
-           system's getpwnam(3) and getgrnam(3) (and related) library calls.
-
-     --pp _p_a_t_h
-           Use the file hierarchy rooted in _p_a_t_h, instead of the current
-           directory.
-
-     --PP    Don't follow symbolic links in the file hierarchy, instead consider
-           the symbolic link itself in any comparisons.  This is the default.
-
-     --rr    Remove any files in the file hierarchy that are not described in
-           the specification.
-
-     --RR _k_e_y_w_o_r_d_s
-           Remove the specified (whitespace or comma separated) keywords from
-           the current set of keywords.  If `all' is specified, remove all of
-           the other keywords.
-
-     --ss _s_e_e_d
-           Display a single checksum to the standard error output that repre-
-           sents all of the files for which the keyword cckkssuumm was specified.
-           The checksum is seeded with the specified value.
-
-     --uu    Modify the owner, group, permissions, and flags of existing files,
-           the device type of devices, and symbolic link targets, to match the
-           specification.  Create any missing directories, devices or symbolic
-           links.  User, group, and permissions must all be specified for
-           missing directories to be created.  Note that unless the --ii option
-           is given, the schg and sappnd flags will not be set, even if speci-
-           fied. If --mm is given, these flags will be reset.  Exit with a sta-
-           tus of 0 on success, 2 if the file hierarchy did not match the
-           specification, and 1 if any other error occurred.
-
-     --UU    Same as --uu except that a mismatch is not considered to be an error
-           if it was corrected.
-
-     --WW    Don't attempt to set various file attributes such as the ownership,
-           mode, flags, or time when creating new directories or changing
-           existing entries.  This option will be most useful when used in
-           conjunction with --uu or --UU.
-
-     --xx    Don't descend below mount points in the file hierarchy.
-
-     --XX _e_x_c_l_u_d_e_-_f_i_l_e
-           The specified file contains fnmatch(3) patterns matching files to
-           be excluded from the specification, one to a line.  If the pattern
-           contains a `/' character, it will be matched against entire path-
-           names (relative to the starting directory); otherwise, it will be
-           matched against basenames only.  Comments are permitted in the
-           _e_x_c_l_u_d_e_-_l_i_s_t file.
-
-     Specifications are mostly composed of ``keywords'', i.e. strings that
-     that specify values relating to files.  No keywords have default values,
-     and if a keyword has no value set, no checks based on it are performed.
+     &#8722;&#8722;bb          Suppress blank lines before entering and after exiting direc&#8208;
+                 tories.
+
+     &#8722;&#8722;CC          Convert a specification into a format that?s easier to parse
+                 with various tools.  The input specification is read from
+                 standard input or from the file given by &#8722;&#8722;ff _s_p_e_c.  In the
+                 output, each file or directory is represented using a single
+                 line (which might be very long).  The full path name
+                 (beginning with ?./?) is always printed as the first field;
+                 &#8722;&#8722;KK, &#8722;&#8722;kk, and &#8722;&#8722;RR can be used to control which other keywords
+                 are printed; &#8722;&#8722;EE and &#8722;&#8722;II can be used to control which files are
+                 printed; and the &#8722;&#8722;SS option can be used to sort the output.
+
+     &#8722;&#8722;cc          Print a specification for the file hierarchy originating at
+                 the current working directory (or the directory provided by
+                 &#8722;&#8722;pp _p_a_t_h) to the standard output.  The output is in a style
+                 using relative path names.
+
+     &#8722;&#8722;DD          As per &#8722;&#8722;CC, except that the path name is always printed as the
+                 last field instead of the first.
+
+     &#8722;&#8722;dd          Ignore everything except directory type files.
+
+     &#8722;&#8722;EE _t_a_g_s     Add the comma separated tags to the ?exclusion? list.  Non&#8208;
+                 directories with tags which are in the exclusion list are not
+                 printed with &#8722;&#8722;CC and &#8722;&#8722;DD.
+
+     &#8722;&#8722;ee          Don?t complain about files that are in the file hierarchy,
+                 but not in the specification.
+
+     &#8722;&#8722;FF _f_l_a_v_o_r   Set the compatibility flavor of the mmttrreeee utility.  The
+                 _f_l_a_v_o_r can be one of mmttrreeee, ffrreeeebbssdd99, or nneettbbssdd66.  The
+                 default is mmttrreeee.  The ffrreeeebbssdd99 and nneettbbssdd66 flavors attempt
+                 to preserve output compatibility and command line option
+                 backward compatibility with FreeBSD 9.0 and NetBSD 6.0
+                 respectively.
+
+     &#8722;&#8722;ff _s_p_e_c     Read the specification from _f_i_l_e, instead of from the stan&#8208;
+                 dard input.
+
+                 If this option is specified twice, the two specifications are
+                 compared to each other rather than to the file hierarchy.
+                 The specifications will be sorted like output generated using
+                 &#8722;&#8722;cc.  The output format in this case is somewhat reminiscent
+                 of comm(1), having ?in first spec only?, ?in second spec
+                 only?, and ?different? columns, prefixed by zero, one and two
+                 TAB characters respectively.  Each entry in the ?different?
+                 column occupies two lines, one from each specification.
+
+     &#8722;&#8722;II _t_a_g_s     Add the comma separated tags to the ?inclusion? list.  Non&#8208;
+                 directories with tags which are in the inclusion list are
+                 printed with &#8722;&#8722;CC and &#8722;&#8722;DD.  If no inclusion list is provided,
+                 the default is to display all files.
+
+     &#8722;&#8722;ii          If specified, set the ?schg? and/or ?sappnd? flags.
+
+     &#8722;&#8722;jj          Indent the output 4 spaces each time a directory level is
+                 descended when creating a specification with the &#8722;&#8722;cc option.
+                 This does not affect either the ?/set? statements or the com&#8208;
+                 ment before each directory.  It does however affect the com&#8208;
+                 ment before the close of each directory.  This is the equiva&#8208;
+                 lent of the &#8722;&#8722;ii option in the FreeBSD version of mmttrreeee.
+
+     &#8722;&#8722;KK _k_e_y_w_o_r_d_s
+                 Add the specified (whitespace or comma separated) keywords to
+                 the current set of keywords.  If ?all? is specified, add all
+                 of the other keywords.
+
+     &#8722;&#8722;kk _k_e_y_w_o_r_d_s
+                 Use the ttyyppee keyword plus the specified (whitespace or comma
+                 separated) _k_e_y_w_o_r_d_s instead of the current set of keywords.
+                 If ?all? is specified, use all of the other keywords.  If the
+                 ttyyppee keyword is not desired, suppress it with &#8722;&#8722;RR ttyyppee.
+
+     &#8722;&#8722;LL          Follow all symbolic links in the file hierarchy.
+
+     &#8722;&#8722;ll          Do ?loose? permissions checks, in which more stringent per&#8208;
+                 missions will match less stringent ones.  For example, a file
+                 marked mode 0444 will pass a check for mode 0644.  ?Loose?
+                 checks apply only to read, write and execute permissions ? in
+                 particular, if other bits like the sticky bit or suid/sgid
+                 bits are set either in the specification or the file, exact
+                 checking will be performed.  This option may not be set at
+                 the same time as the &#8722;&#8722;UU or &#8722;&#8722;uu option.
+
+     &#8722;&#8722;MM          Permit merging of specification entries with different types,
+                 with the last entry taking precedence.
+
+     &#8722;&#8722;mm          If the ?schg? and/or ?sappnd? flags are specified, reset
+                 these flags.  Note that this is only possible with
+                 securelevel less than 1 (i.e., in single user mode or while
+                 the system is running in insecure mode).  See init(8) for
+                 information on security levels.
+
+     &#8722;&#8722;nn          Do not emit pathname comments when creating a specification.
+                 Normally a comment is emitted before each directory and
+                 before the close of that directory when using the &#8722;&#8722;cc option.
+
+     &#8722;&#8722;NN _d_b_d_i_r    Use the user database text file _m_a_s_t_e_r_._p_a_s_s_w_d and group data&#8208;
+                 base text file _g_r_o_u_p from _d_b_d_i_r, rather than using the
+                 results from the system?s getpwnam(3) and getgrnam(3) (and
+                 related) library calls.
+
+     &#8722;&#8722;OO _o_n_l_y_p_a_t_h_s
+                 Only include files included in this list of pathnames.
+
+     &#8722;&#8722;PP          Don?t follow symbolic links in the file hierarchy, instead
+                 consider the symbolic link itself in any comparisons.  This
+                 is the default.
+
+     &#8722;&#8722;pp _p_a_t_h     Use the file hierarchy rooted in _p_a_t_h, instead of the current
+                 directory.
+
+     &#8722;&#8722;qq          Quiet mode.  Do not complain when a ?missing? directory can&#8208;
+                 not be created because it already exists.  This occurs when
+                 the directory is a symbolic link.
+
+     &#8722;&#8722;RR _k_e_y_w_o_r_d_s
+                 Remove the specified (whitespace or comma separated) keywords
+                 from the current set of keywords.  If ?all? is specified,
+                 remove all of the other keywords.
+
+     &#8722;&#8722;rr          Remove any files in the file hierarchy that are not described
+                 in the specification.  Repeating the flag more than once will
+                 attempt to reset all the file flags via lchflags(2) before
+                 attempting to remove the file in case the file was immutable.
+
+     &#8722;&#8722;SS          When reading a specification into an internal data structure,
+                 sort the entries.  Sorting will affect the order of the out&#8208;
+                 put produced by the &#8722;&#8722;CC or &#8722;&#8722;DD options, and will also affect
+                 the order in which missing entries are created or reported
+                 when a directory tree is checked against a specification.
+
+                 The sort order is the same as that used by the &#8722;&#8722;cc option,
+                 which is that entries within the same directory are sorted in
+                 the order used by strcmp(3), except that entries for subdi&#8208;
+                 rectories sort after other entries.  By default, if the &#8722;&#8722;SS
+                 option is not used, entries within the same directory are
+                 collected together (separated from entries for other directo&#8208;
+                 ries), but not sorted.
+
+     &#8722;&#8722;ss _s_e_e_d     Display a single checksum to the standard error output that
+                 represents all of the files for which the keyword cckkssuumm was
+                 specified.  The checksum is seeded with the specified value.
+
+     &#8722;&#8722;tt          Modify the modified time of existing files, the device type
+                 of devices, and symbolic link targets, to match the specifi&#8208;
+                 cation.
+
+     &#8722;&#8722;UU          Same as &#8722;&#8722;uu except that a mismatch is not considered to be an
+                 error if it was corrected.
+
+     &#8722;&#8722;uu          Modify the owner, group, permissions, and flags of existing
+                 files, the device type of devices, and symbolic link targets,
+                 to match the specification.  Create any missing directories,
+                 devices or symbolic links.  User, group, and permissions must
+                 all be specified for missing directories to be created.  Note
+                 that unless the &#8722;&#8722;ii option is given, the schg and sappnd flags
+                 will not be set, even if specified.  If &#8722;&#8722;mm is given, these
+                 flags will be reset.  Exit with a status of 0 on success, 2
+                 if the file hierarchy did not match the specification, and 1
+                 if any other error occurred.
+
+     &#8722;&#8722;WW          Don?t attempt to set various file attributes such as the own&#8208;
+                 ership, mode, flags, or time when creating new directories or
+                 changing existing entries.  This option will be most useful
+                 when used in conjunction with &#8722;&#8722;UU or &#8722;&#8722;uu.
+
+     &#8722;&#8722;XX _e_x_c_l_u_d_e_&#8208;_f_i_l_e
+                 The specified file contains fnmatch(3) patterns matching
+                 files to be excluded from the specification, one to a line.
+                 If the pattern contains a ?/? character, it will be matched
+                 against entire pathnames (relative to the starting direc&#8208;
+                 tory); otherwise, it will be matched against basenames only.
+                 Comments are permitted in the _e_x_c_l_u_d_e_&#8208;_f_i_l_e file.
+
+     &#8722;&#8722;xx          Don?t descend below mount points in the file hierarchy.
+
+     Specifications are mostly composed of ?keywords?, i.e. strings that that
+     specify values relating to files.  No keywords have default values, and
+     if a keyword has no value set, no checks based on it are performed.
 
      Currently supported keywords are as follows:
 
      cckkssuumm   The checksum of the file using the default algorithm specified by
              the cksum(1) utility.
 
-     ddeevviiccee  The device number to use for bblloocckk or cchhaarr file types.  The argu-
+     ddeevviiccee  The device number to use for bblloocckk or cchhaarr file types.  The argu&#8208;
              ment must be one of the following forms:
 
              _f_o_r_m_a_t,_m_a_j_o_r,_m_i_n_o_r
-                   A device with _m_a_j_o_r and _m_i_n_o_r fields, for an operating sys-
+                   A device with _m_a_j_o_r and _m_i_n_o_r fields, for an operating sys&#8208;
                    tem specified with _f_o_r_m_a_t.  See below for valid formats.
 
              _f_o_r_m_a_t,_m_a_j_o_r,_u_n_i_t,_s_u_b_u_n_i_t
-                   A device with _m_a_j_o_r, _u_n_i_t, and _s_u_b_u_n_i_t fields, for an oper-
+                   A device with _m_a_j_o_r, _u_n_i_t, and _s_u_b_u_n_i_t fields, for an oper&#8208;
                    ating system specified with _f_o_r_m_a_t.  (Currently this is
                    only supported by the bbssddooss format.)
 
@@ -163,10 +229,10 @@ DDEESSCCRRIIPPTTIIOONN
 
              See mknod(8) for more details.
 
-     ffllaaggss   The file flags as a symbolic name.  See chflags(1) for informa-
-             tion on these names.  If no flags are to be set the string `none'
+     ffllaaggss   The file flags as a symbolic name.  See chflags(1) for informa&#8208;
+             tion on these names.  If no flags are to be set the string ?none?
              may be used to override the current default.  Note that the schg
-             and sappnd flags are treated specially (see the --ii and --mm
+             and sappnd flags are treated specially (see the &#8722;&#8722;ii and &#8722;&#8722;mm
              options).
 
      iiggnnoorree  Ignore any file hierarchy below this file.
@@ -182,47 +248,56 @@ DDEESSCCRRIIPPTTIIOONN
      mmdd55ddiiggeesstt
              Synonym for mmdd55.
 
-     mmooddee    The current file's permissions as a numeric (octal) or symbolic
+     mmooddee    The current file?s permissions as a numeric (octal) or symbolic
              value.
 
      nnlliinnkk   The number of hard links the file is expected to have.
 
+     nnoocchhaannggee
+             Make sure this file or directory exists but otherwise ignore all
+             attributes.
+
      ooppttiioonnaall
-             The file is optional; don't complain about the file if it's not
+             The file is optional; don?t complain about the file if it?s not
              in the file hierarchy.
 
-     rrmmdd116600  The RMD-160 cryptographic message digest of the file.
+     rriippeemmdd116600ddiiggeesstt
+             Synonym for rrmmdd116600.
+
+     rrmmdd116600  The RMD&#8208;160 cryptographic message digest of the file.
 
      rrmmdd116600ddiiggeesstt
              Synonym for rrmmdd116600.
 
-     sshhaa11    The SHA-1 cryptographic message digest of the file.
+     sshhaa11    The SHA&#8208;1 cryptographic message digest of the file.
 
      sshhaa11ddiiggeesstt
              Synonym for sshhaa11.
 
-     sshhaa225566  The 256-bits SHA-2 cryptographic message digest of the file.
+     sshhaa225566  The 256&#8208;bits SHA&#8208;2 cryptographic message digest of the file.
 
      sshhaa225566ddiiggeesstt
              Synonym for sshhaa225566.
 
-     sshhaa338844  The 384-bits SHA-2 cryptographic message digest of the file.
+     sshhaa338844  The 384&#8208;bits SHA&#8208;2 cryptographic message digest of the file.
 
      sshhaa338844ddiiggeesstt
              Synonym for sshhaa338844.
 
-     sshhaa551122  The 512-bits SHA-2 cryptographic message digest of the file.
+     sshhaa551122  The 512&#8208;bits SHA&#8208;2 cryptographic message digest of the file.
 
      sshhaa551122ddiiggeesstt
              Synonym for sshhaa551122.
 
      ssiizzee    The size, in bytes, of the file.
 
-     ttaaggss    Comma delimited tags to be matched with --EE and --II.  These may be
+     ttaaggss    Comma delimited tags to be matched with &#8722;&#8722;EE and &#8722;&#8722;II.  These may be
              specified without leading or trailing commas, but will be stored
              internally with them.
 
-     ttiimmee    The last modification time of the file.
+     ttiimmee    The last modification time of the file, in second and nanosec&#8208;
+             onds.  The value should include a period character and exactly
+             nine digits after the period.
 
      ttyyppee    The type of the file; may be set to any one of the following:
 
@@ -243,95 +318,113 @@ DDEESSCCRRIIPPTTIIOONN
 
      There are four types of lines in a specification:
 
-     1.   Set global values for a keyword.  This consists of the string `/set'
+     1.   Set global values for a keyword.  This consists of the string ?/set?
           followed by whitespace, followed by sets of keyword/value pairs,
           separated by whitespace.  Keyword/value pairs consist of a keyword,
-          followed by an equals sign (`='), followed by a value, without
+          followed by an equals sign (?=?), followed by a value, without
           whitespace characters.  Once a keyword has been set, its value
           remains unchanged until either reset or unset.
 
      2.   Unset global values for a keyword.  This consists of the string
-          `/unset', followed by whitespace, followed by one or more keywords,
-          separated by whitespace.  If `all' is specified, unset all of the
+          ?/unset?, followed by whitespace, followed by one or more keywords,
+          separated by whitespace.  If ?all? is specified, unset all of the
           keywords.
 
-     3.   A file specification, consisting of a path name, followed by white-
+     3.   A file specification, consisting of a path name, followed by white&#8208;
           space, followed by zero or more whitespace separated keyword/value
           pairs.
 
           The path name may be preceded by whitespace characters.  The path
           name may contain any of the standard path name matching characters
-          (`[', `]', `?' or `*'), in which case files in the hierarchy will be
+          (?[?, ?]?, ??? or ?*?), in which case files in the hierarchy will be
           associated with the first pattern that they match.  mmttrreeee uses
-          strsvis(3) (in VIS_CSTYLE format) to encode path names containing
-          non-printable characters. Whitespace characters are encoded as `\s'
-          (space), `\t' (tab), and `\n' (new line).  `#' characters in path
-          names are escaped by a preceding backslash `\' to distinguish them
-          from comments.
+          strsvis(3) (in VIS_OCTAL format) to encode path names containing
+          non&#8208;printable characters.  Whitespace characters are encoded as
+          ?\040? (space), ?\011? (tab), and ?\012? (new line).  When flavor
+          nneettbbssdd66 is selected, strsvis(3) (in VIS_CSTYLE format) is used and
+          whitespace characters are encoded as ?\s? (space), ?\t? (tab), and
+          ?\n? (new line).  ?#? characters in path names are escaped by a pre&#8208;
+          ceding backslash ?\? to distinguish them from comments.
 
           Each of the keyword/value pairs consist of a keyword, followed by an
-          equals sign (`='), followed by the keyword's value, without white-
+          equals sign (?=?), followed by the keyword?s value, without white&#8208;
           space characters.  These values override, without changing, the
           global value of the corresponding keyword.
 
-          The first path name entry listed must be a directory named `.', as
+          The first path name entry listed must be a directory named ?.?, as
           this ensures that intermixing full and relative path names will work
           consistently and correctly.  Multiple entries for a directory named
-          `.' are permitted; the settings for the last such entry override
+          ?.? are permitted; the settings for the last such entry override
           those of the existing entry.
 
-          A path name that contains a slash (`/') that is not the first char-
+          A path name that contains a slash (?/?) that is not the first char&#8208;
           acter will be treated as a full path (relative to the root of the
           tree).  All parent directories referenced in the path name must
           exist.  The current directory path used by relative path names will
           be updated appropriately.  Multiple entries for the same full path
-          are permitted if the types are the same (unless --MM is given, and
-          then the types may differ); in this case the settings for the last
-          entry take precedence.
+          are permitted if the types are the same (unless &#8722;&#8722;MM is given, in
+          which case the types may differ); in this case the settings for the
+          last entry take precedence.
 
-          A path name that does not contain a slash will be treated as a rela-
+          A path name that does not contain a slash will be treated as a rela&#8208;
           tive path.  Specifying a directory will cause subsequent files to be
           searched for in that directory hierarchy.
 
-     4.   A line containing only the string `..' which causes the current
+     4.   A line containing only the string ?..? which causes the current
           directory path (used by relative paths) to ascend one level.
 
-     Empty lines and lines whose first non-whitespace character is a hash mark
-     (`#') are ignored.
+     Empty lines and lines whose first non&#8208;whitespace character is a hash mark
+     (?#?) are ignored.
 
      The mmttrreeee utility exits with a status of 0 on success, 1 if any error
      occurred, and 2 if the file hierarchy did not match the specification.
 
 FFIILLEESS
-     /etc/mtree  system specification directory
+     /etc/mtree                        system specification directory
 
 EEXXAAMMPPLLEESS
-     To detect system binaries that have been ``trojan horsed'', it is recom-
+     To detect system binaries that have been ?trojan horsed?, it is recom&#8208;
      mended that mmttrreeee be run on the file systems, and a copy of the results
      stored on a different machine, or, at least, in encrypted form.  The seed
-     for the --ss option should not be an obvious value and the final checksum
-     should not be stored on-line under any circumstances!  Then, periodi-
-     cally, mmttrreeee should be run against the on-line specifications and the
+     for the &#8722;&#8722;ss option should not be an obvious value and the final checksum
+     should not be stored on&#8208;line under any circumstances!  Then, periodi&#8208;
+     cally, mmttrreeee should be run against the on&#8208;line specifications and the
      final checksum compared with the previous value.  While it is possible
-     for the bad guys to change the on-line specifications to conform to their
-     modified binaries, it shouldn't be possible for them to make it produce
+     for the bad guys to change the on&#8208;line specifications to conform to their
+     modified binaries, it shouldn?t be possible for them to make it produce
      the same final checksum value.  If the final checksum value changes, the
-     off-line copies of the specification can be used to detect which of the
+     off&#8208;line copies of the specification can be used to detect which of the
      binaries have actually been modified.
 
-     The --dd and --uu options can be used in combination to create directory
-     hierarchies for distributions and other such things.
+     The &#8722;&#8722;dd option can be used in combination with &#8722;&#8722;UU or &#8722;&#8722;uu to create direc&#8208;
+     tory hierarchies for, for example, distributions.
+
+CCOOMMPPAATTIIBBIILLIITTYY
+     The compatibility shims provided by the &#8722;&#8722;FF option are incomplete by
+     design.  Known limitations are described below.
+
+     The ffrreeeebbssdd99 flavor retains the default handling of lookup failures for
+     the uunnaammee and ggrroouupp keywords by replacing them with appropriate uuiidd and
+     ggiidd keywords rather than failing and reporting an error.  The related &#8722;&#8722;ww
+     flag is a no&#8208;op rather than causing a warning to be printed and no key&#8208;
+     word to be emitted.  The latter behavior is not emulated as it is poten&#8208;
+     tially dangerous in the face of /set statements.
+
+     The nneettbbssdd66 flavor does not replicate the historical bug that reported
+     time as seconds.nanoseconds without zero padding nanosecond values less
+     than 100000000.
 
 SSEEEE AALLSSOO
      chflags(1), chgrp(1), chmod(1), cksum(1), stat(2), fnmatch(3), fts(3),
-     strsvis(3), chown(8), mknod(8)
+     strsvis(3), mtree(5), chown(8), mknod(8)
 
 HHIISSTTOORRYY
-     The mmttrreeee utility appeared in 4.3BSD-Reno.  The ooppttiioonnaall keyword appeared
-     in NetBSD 1.2.  The --UU flag appeared in NetBSD 1.3.  The ffllaaggss and mmdd55
-     keywords, and --ii and --mm flags appeared in NetBSD 1.4.  The ddeevviiccee,
-     rrmmdd116600, sshhaa11, ttaaggss, and aallll keywords, --DD, --EE, --II, --ll, --LL, --NN, --PP, --RR, --WW,
-     and --XX flags, and support for full paths appeared in NetBSD 1.6.  The
-     sshhaa225566, sshhaa338844, and sshhaa551122 keywords appeared in NetBSD 3.0.
-
-NetBSD 3.0                    September 12, 2006                    NetBSD 3.0
+     The mmttrreeee utility appeared in 4.3BSD&#8722;Reno.  The ooppttiioonnaall keyword appeared
+     in NetBSD 1.2.  The &#8722;&#8722;UU option appeared in NetBSD 1.3.  The ffllaaggss and mmdd55
+     keywords, and &#8722;&#8722;ii and &#8722;&#8722;mm options appeared in NetBSD 1.4.  The ddeevviiccee,
+     rrmmdd116600, sshhaa11, ttaaggss, and aallll keywords, &#8722;&#8722;DD, &#8722;&#8722;EE, &#8722;&#8722;II, &#8722;&#8722;LL, &#8722;&#8722;ll, &#8722;&#8722;NN, &#8722;&#8722;PP, &#8722;&#8722;RR, &#8722;&#8722;WW,
+     and &#8722;&#8722;XX options, and support for full paths appeared in NetBSD 1.6.  The
+     sshhaa225566, sshhaa338844, and sshhaa551122 keywords appeared in NetBSD 3.0.  The &#8722;&#8722;SS
+     option appeared in NetBSD 6.0.
+
+NetBSD 9.2_STABLE              December 2, 2023              NetBSD 9.2_STABLE
diff --git a/pkgtools/mtree/files/mtree.h b/pkgtools/mtree/files/mtree.h
index 4675a873c8f7..93d6cdfb0e7b 100644
--- a/pkgtools/mtree/files/mtree.h
+++ b/pkgtools/mtree/files/mtree.h
@@ -1,4 +1,4 @@
-/*	$NetBSD: mtree.h,v 1.3 2008/11/06 02:14:52 jschauma Exp $	*/
+/*	$NetBSD: mtree.h,v 1.31 2012/10/05 09:17:29 wiz Exp $	*/
 
 /*-
  * Copyright (c) 1990, 1993
@@ -39,9 +39,6 @@
 
 #define	MISMATCHEXIT	2
 
-/* Max. length of hash -- update this if needed when adding a new algorithm. */
-#define	MAXHASHLEN	128 /* SHA512 */
-
 typedef struct _node {
 	struct _node	*parent, *child;	/* up, down */
 	struct _node	*prev, *next;		/* left, right */
@@ -62,7 +59,9 @@ typedef struct _node {
 	char	*sha256digest;			/* SHA256 digest */
 	char	*sha384digest;			/* SHA384 digest */
 	char	*sha512digest;			/* SHA512 digest */
-	char	*tags;				/* tags, comma delimited */
+	char	*tags;				/* tags, comma delimited,
+						 * also with leading and
+						 * trailing commas */
 	size_t	lineno;				/* line # entry came from */
 
 #define	F_CKSUM		0x00000001		/* cksum(1) check sum */
@@ -87,6 +86,8 @@ typedef struct _node {
 #define	F_UID		0x00080000		/* uid */
 #define	F_UNAME		0x00100000		/* user name */
 #define	F_VISIT		0x00200000		/* file visited */
+#define	F_NOCHANGE	0x00400000		/* check existence, but not */
+						/* other properties */
 #define	F_SHA256	0x00800000		/* SHA256 digest */
 #define	F_SHA384	0x01000000		/* SHA384 digest */
 #define	F_SHA512	0x02000000		/* SHA512 digest */
@@ -121,9 +122,26 @@ const char	*inotype(u_int);
 u_int		 nodetoino(u_int);
 int		 setup_getid(const char *);
 NODE		*spec(FILE *);
+int		 mtree_specspec(FILE *, FILE *);
 void		 free_nodes(NODE *);
 char		*vispath(const char *);
 
+#ifdef __FreeBSD__
+#define KEY_DIGEST "digest"
+#else
+#define KEY_DIGEST
+#endif
+
+#define	MD5KEY		"md5"		KEY_DIGEST
+#ifdef __FreeBSD__
+#define	RMD160KEY	"ripemd160"	KEY_DIGEST
+#else
+#define	RMD160KEY	"rmd160"	KEY_DIGEST
+#endif
+#define	SHA1KEY		"sha1"		KEY_DIGEST
+#define	SHA256KEY	"sha256"	KEY_DIGEST
+#define	SHA384KEY	"sha384"
+#define	SHA512KEY	"sha512"
 
 #define	RP(p)	\
 	((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \
diff --git a/pkgtools/mtree/files/mtree2nbcompat b/pkgtools/mtree/files/mtree2nbcompat
index 357520706962..23742caca262 100755
--- a/pkgtools/mtree/files/mtree2nbcompat
+++ b/pkgtools/mtree/files/mtree2nbcompat
@@ -44,7 +44,6 @@ src2nbcompat="${PKGSRCDIR}/pkgtools/libnbcompat/files/src2nbcompat"
 dest=$1
 
 $src2nbcompat ${BSDSRCDIR}/usr.sbin/mtree		$dest
-$src2nbcompat ${BSDSRCDIR}/bin/ls/stat_flags.c		$dest/stat_flags.c
-$src2nbcompat ${BSDSRCDIR}/bin/ls/stat_flags.h		$dest/stat_flags.h
+$src2nbcompat ${BSDSRCDIR}/lib/libutil/stat_flags.c	$dest/stat_flags.c
 $src2nbcompat ${BSDSRCDIR}/sbin/mknod/pack_dev.c	$dest/pack_dev.c
 $src2nbcompat ${BSDSRCDIR}/sbin/mknod/pack_dev.h	$dest/pack_dev.h
diff --git a/pkgtools/mtree/files/pack_dev.c b/pkgtools/mtree/files/pack_dev.c
index f91e894ffdc2..505866593ba2 100644
--- a/pkgtools/mtree/files/pack_dev.c
+++ b/pkgtools/mtree/files/pack_dev.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: pack_dev.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $	*/
+/*	$NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
@@ -15,13 +15,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *        This product includes software developed by the NetBSD
- *        Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -48,7 +41,7 @@
 #include <sys/cdefs.h>
 #endif
 #if !defined(lint)
-__RCSID("$NetBSD: pack_dev.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $");
+__RCSID("$NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $");
 #endif /* not lint */
 
 #if HAVE_SYS_TYPES_H
@@ -90,16 +83,16 @@ static const char iMinorError[] = "invalid minor number";
 static const char tooManyFields[] = "too many fields for format";
 
 	/* exported */
-portdev_t
+dev_t
 pack_native(int n, u_long numbers[], const char **error)
 {
-	portdev_t dev = 0;
+	dev_t dev = 0;
 
 	if (n == 2) {
 		dev = makedev(numbers[0], numbers[1]);
-		if (major(dev) != numbers[0])
+		if ((u_long)major(dev) != numbers[0])
 			*error = iMajorError;
-		else if (minor(dev) != numbers[1])
+		else if ((u_long)minor(dev) != numbers[1])
 			*error = iMinorError;
 	} else
 		*error = tooManyFields;
@@ -107,16 +100,16 @@ pack_native(int n, u_long numbers[], const char **error)
 }
 
 
-static portdev_t
+static dev_t
 pack_netbsd(int n, u_long numbers[], const char **error)
 {
-	portdev_t dev = 0;
+	dev_t dev = 0;
 
 	if (n == 2) {
 		dev = makedev_netbsd(numbers[0], numbers[1]);
-		if (major_netbsd(dev) != numbers[0])
+		if ((u_long)major_netbsd(dev) != numbers[0])
 			*error = iMajorError;
-		else if (minor_netbsd(dev) != numbers[1])
+		else if ((u_long)minor_netbsd(dev) != numbers[1])
 			*error = iMinorError;
 	} else
 		*error = tooManyFields;
@@ -126,19 +119,19 @@ pack_netbsd(int n, u_long numbers[], const char **error)
 
 #define	major_freebsd(x)	((int32_t)(((x) & 0x0000ff00) >> 8))
 #define	minor_freebsd(x)	((int32_t)(((x) & 0xffff00ff) >> 0))
-#define	makedev_freebsd(x,y)	((portdev_t)((((x) << 8) & 0x0000ff00) | \
+#define	makedev_freebsd(x,y)	((dev_t)((((x) << 8) & 0x0000ff00) | \
 					 (((y) << 0) & 0xffff00ff)))
 
-static portdev_t
+static dev_t
 pack_freebsd(int n, u_long numbers[], const char **error)
 {
-	portdev_t dev = 0;
+	dev_t dev = 0;
 
 	if (n == 2) {
 		dev = makedev_freebsd(numbers[0], numbers[1]);
-		if (major_freebsd(dev) != numbers[0])
+		if ((u_long)major_freebsd(dev) != numbers[0])
 			*error = iMajorError;
-		if (minor_freebsd(dev) != numbers[1])
+		if ((u_long)minor_freebsd(dev) != numbers[1])
 			*error = iMinorError;
 	} else
 		*error = tooManyFields;
@@ -148,19 +141,19 @@ pack_freebsd(int n, u_long numbers[], const char **error)
 
 #define	major_8_8(x)		((int32_t)(((x) & 0x0000ff00) >> 8))
 #define	minor_8_8(x)		((int32_t)(((x) & 0x000000ff) >> 0))
-#define	makedev_8_8(x,y)	((portdev_t)((((x) << 8) & 0x0000ff00) | \
+#define	makedev_8_8(x,y)	((dev_t)((((x) << 8) & 0x0000ff00) | \
 					 (((y) << 0) & 0x000000ff)))
 
-static portdev_t
+static dev_t
 pack_8_8(int n, u_long numbers[], const char **error)
 {
-	portdev_t dev = 0;
+	dev_t dev = 0;
 
 	if (n == 2) {
 		dev = makedev_8_8(numbers[0], numbers[1]);
-		if (major_8_8(dev) != numbers[0])
+		if ((u_long)major_8_8(dev) != numbers[0])
 			*error = iMajorError;
-		if (minor_8_8(dev) != numbers[1])
+		if ((u_long)minor_8_8(dev) != numbers[1])
 			*error = iMinorError;
 	} else
 		*error = tooManyFields;
@@ -170,19 +163,19 @@ pack_8_8(int n, u_long numbers[], const char **error)
 
 #define	major_12_20(x)		((int32_t)(((x) & 0xfff00000) >> 20))
 #define	minor_12_20(x)		((int32_t)(((x) & 0x000fffff) >>  0))
-#define	makedev_12_20(x,y)	((portdev_t)((((x) << 20) & 0xfff00000) | \
+#define	makedev_12_20(x,y)	((dev_t)((((x) << 20) & 0xfff00000) | \
 					 (((y) <<  0) & 0x000fffff)))
 
-static portdev_t
+static dev_t
 pack_12_20(int n, u_long numbers[], const char **error)
 {
-	portdev_t dev = 0;
+	dev_t dev = 0;
 
 	if (n == 2) {
 		dev = makedev_12_20(numbers[0], numbers[1]);
-		if (major_12_20(dev) != numbers[0])
+		if ((u_long)major_12_20(dev) != numbers[0])
 			*error = iMajorError;
-		if (minor_12_20(dev) != numbers[1])
+		if ((u_long)minor_12_20(dev) != numbers[1])
 			*error = iMinorError;
 	} else
 		*error = tooManyFields;
@@ -192,19 +185,19 @@ pack_12_20(int n, u_long numbers[], const char **error)
 
 #define	major_14_18(x)		((int32_t)(((x) & 0xfffc0000) >> 18))
 #define	minor_14_18(x)		((int32_t)(((x) & 0x0003ffff) >>  0))
-#define	makedev_14_18(x,y)	((portdev_t)((((x) << 18) & 0xfffc0000) | \
+#define	makedev_14_18(x,y)	((dev_t)((((x) << 18) & 0xfffc0000) | \
 					 (((y) <<  0) & 0x0003ffff)))
 
-static portdev_t
+static dev_t
 pack_14_18(int n, u_long numbers[], const char **error)
 {
-	portdev_t dev = 0;
+	dev_t dev = 0;
 
 	if (n == 2) {
 		dev = makedev_14_18(numbers[0], numbers[1]);
-		if (major_14_18(dev) != numbers[0])
+		if ((u_long)major_14_18(dev) != numbers[0])
 			*error = iMajorError;
-		if (minor_14_18(dev) != numbers[1])
+		if ((u_long)minor_14_18(dev) != numbers[1])
 			*error = iMinorError;
 	} else
 		*error = tooManyFields;
@@ -214,19 +207,19 @@ pack_14_18(int n, u_long numbers[], const char **error)
 
 #define	major_8_24(x)		((int32_t)(((x) & 0xff000000) >> 24))
 #define	minor_8_24(x)		((int32_t)(((x) & 0x00ffffff) >>  0))
-#define	makedev_8_24(x,y)	((portdev_t)((((x) << 24) & 0xff000000) | \
+#define	makedev_8_24(x,y)	((dev_t)((((x) << 24) & 0xff000000) | \
 					 (((y) <<  0) & 0x00ffffff)))
 
-static portdev_t
+static dev_t
 pack_8_24(int n, u_long numbers[], const char **error)
 {
-	portdev_t dev = 0;
+	dev_t dev = 0;
 
 	if (n == 2) {
 		dev = makedev_8_24(numbers[0], numbers[1]);
-		if (major_8_24(dev) != numbers[0])
+		if ((u_long)major_8_24(dev) != numbers[0])
 			*error = iMajorError;
-		if (minor_8_24(dev) != numbers[1])
+		if ((u_long)minor_8_24(dev) != numbers[1])
 			*error = iMinorError;
 	} else
 		*error = tooManyFields;
@@ -237,28 +230,28 @@ pack_8_24(int n, u_long numbers[], const char **error)
 #define	major_12_12_8(x)	((int32_t)(((x) & 0xfff00000) >> 20))
 #define	unit_12_12_8(x)		((int32_t)(((x) & 0x000fff00) >>  8))
 #define	subunit_12_12_8(x)	((int32_t)(((x) & 0x000000ff) >>  0))
-#define	makedev_12_12_8(x,y,z)	((portdev_t)((((x) << 20) & 0xfff00000) | \
+#define	makedev_12_12_8(x,y,z)	((dev_t)((((x) << 20) & 0xfff00000) | \
 					 (((y) <<  8) & 0x000fff00) | \
 					 (((z) <<  0) & 0x000000ff)))
 
-static portdev_t
+static dev_t
 pack_bsdos(int n, u_long numbers[], const char **error)
 {
-	portdev_t dev = 0;
+	dev_t dev = 0;
 
 	if (n == 2) {
 		dev = makedev_12_20(numbers[0], numbers[1]);
-		if (major_12_20(dev) != numbers[0])
+		if ((u_long)major_12_20(dev) != numbers[0])
 			*error = iMajorError;
-		if (minor_12_20(dev) != numbers[1])
+		if ((u_long)minor_12_20(dev) != numbers[1])
 			*error = iMinorError;
 	} else if (n == 3) {
 		dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]);
-		if (major_12_12_8(dev) != numbers[0])
+		if ((u_long)major_12_12_8(dev) != numbers[0])
 			*error = iMajorError;
-		if (unit_12_12_8(dev) != numbers[1])
+		if ((u_long)unit_12_12_8(dev) != numbers[1])
 			*error = "invalid unit number";
-		if (subunit_12_12_8(dev) != numbers[2])
+		if ((u_long)subunit_12_12_8(dev) != numbers[2])
 			*error = "invalid subunit number";
 	} else
 		*error = tooManyFields;
@@ -268,7 +261,7 @@ pack_bsdos(int n, u_long numbers[], const char **error)
 
 		/* list of formats and pack functions */
 		/* this list must be sorted lexically */
-struct format {
+static struct format {
 	const char	*name;
 	pack_t		*pack;
 } formats[] = {
diff --git a/pkgtools/mtree/files/pack_dev.h b/pkgtools/mtree/files/pack_dev.h
index 7398b125102d..d0cb75e6dc6d 100644
--- a/pkgtools/mtree/files/pack_dev.h
+++ b/pkgtools/mtree/files/pack_dev.h
@@ -1,4 +1,4 @@
-/*	$NetBSD: pack_dev.h,v 1.4 2008/11/06 02:14:52 jschauma Exp $	*/
+/*	$NetBSD: pack_dev.h,v 1.8 2013/06/14 16:28:20 tsutsui Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
@@ -15,13 +15,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *        This product includes software developed by the NetBSD
- *        Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -39,12 +32,7 @@
 #ifndef	_PACK_DEV_H
 #define	_PACK_DEV_H
 
-#ifdef __CYGWIN__
-typedef	__dev32_t	portdev_t;
-#else
-typedef	dev_t		portdev_t;
-#endif
-typedef	portdev_t pack_t(int, u_long [], const char **);
+typedef	dev_t pack_t(int, u_long [], const char **);
 
 pack_t	*pack_find(const char *);
 pack_t	 pack_native;
diff --git a/pkgtools/mtree/files/spec.c b/pkgtools/mtree/files/spec.c
index 4d402b10d09e..b123f35df26e 100644
--- a/pkgtools/mtree/files/spec.c
+++ b/pkgtools/mtree/files/spec.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: spec.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $	*/
+/*	$NetBSD: spec.c,v 1.92 2024/12/05 17:17:43 christos Exp $	*/
 
 /*-
  * Copyright (c) 1989, 1993
@@ -44,13 +44,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *        This product includes software developed by the NetBSD
- *        Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -80,7 +73,7 @@
 #if 0
 static char sccsid[] = "@(#)spec.c	8.2 (Berkeley) 4/28/95";
 #else
-__RCSID("$NetBSD: spec.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $");
+__RCSID("$NetBSD: spec.c,v 1.92 2024/12/05 17:17:43 christos Exp $");
 #endif
 #endif /* not lint */
 
@@ -91,6 +84,9 @@ __RCSID("$NetBSD: spec.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $");
 #include <sys/stat.h>
 #endif
 
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
 #if HAVE_CTYPE_H
 #include <ctype.h>
 #endif
@@ -103,9 +99,15 @@ __RCSID("$NetBSD: spec.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $");
 #if HAVE_PWD_H
 #include <pwd.h>
 #endif
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#endif
 #if HAVE_STDIO_H
 #include <stdio.h>
 #endif
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
 #if HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -128,11 +130,15 @@ __RCSID("$NetBSD: spec.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $");
 size_t	mtree_lineno;			/* Current spec line number */
 int	mtree_Mflag;			/* Merge duplicate entries */
 int	mtree_Wflag;			/* Don't "whack" permissions */
+int	mtree_Sflag;			/* Sort entries */
 
 static	dev_t	parsedev(char *);
 static	void	replacenode(NODE *, NODE *);
 static	void	set(char *, NODE *);
 static	void	unset(char *, NODE *);
+static	void	addchild(NODE *, NODE *);
+static	int	nodecmp(const NODE *, const NODE *);
+static	int	appendfield(FILE *, int, const char *, ...) __printflike(3, 4);
 
 #define REPLACEPTR(x,v)	do { if ((x)) free((x)); (x) = (v); } while (0)
 
@@ -224,7 +230,7 @@ noparent:		mtree_err("no parent node");
 				}
 				if (cur == NULL || cur->type != F_DIR) {
 					mtree_err("%s: %s", tname,
-					    strerror(ENOENT));
+					"missing directory in specification");
 				}
 				*e = '/';
 				pathparent = cur;
@@ -248,40 +254,29 @@ noparent:		mtree_err("no parent node");
 				/*
 				 * empty tree
 				 */
-			if (strcmp(centry->name, ".") != 0 ||
-			    centry->type != F_DIR)
+			/*
+			 * Allow a bare "." root node by forcing it to
+			 * type=dir for compatibility with FreeBSD.
+			 */
+			if (strcmp(centry->name, ".") == 0 && centry->type == 0)
+				centry->type = F_DIR;
+			if (strcmp(centry->name, ".") != 0)
+				mtree_err(
+				    "root node must be the directory `.',"
+				    " found `%s'", centry->name);
+			if (centry->type != F_DIR)
 				mtree_err(
-				    "root node must be the directory `.'");
+				    "root node must type %#x != %#x",
+				    F_DIR, centry->type);
 			last = root = centry;
 			root->parent = root;
 		} else if (pathparent != NULL) {
 				/*
-				 * full path entry
+				 * full path entry; add or replace
 				 */
 			centry->parent = pathparent;
-			cur = pathparent->child;
-			if (cur == NULL) {
-				pathparent->child = centry;
-				last = centry;
-			} else {
-				for (; cur != NULL; cur = cur->next) {
-					if (strcmp(cur->name, centry->name)
-					    == 0) {
-						/* existing entry; replace */
-						replacenode(cur, centry);
-						break;
-					}
-					if (cur->next == NULL) {
-						/* last entry; add new */
-						cur->next = centry;
-						centry->prev = cur;
-						break;
-					}
-				}
-				last = cur;
-				while (last->next != NULL)
-					last = last->next;
-			}
+			addchild(pathparent, centry);
+			last = centry;
 		} else if (strcmp(centry->name, ".") == 0) {
 				/*
 				 * duplicate "." entry; always replace
@@ -289,19 +284,21 @@ noparent:		mtree_err("no parent node");
 			replacenode(root, centry);
 		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
 				/*
-				 * new relative child
-				 * (no duplicate check)
+				 * new relative child in current dir;
+				 * add or replace
 				 */
 			centry->parent = last;
-			last = last->child = centry;
+			addchild(last, centry);
+			last = centry;
 		} else {
 				/*
-				 * relative entry, up one directory
-				 * (no duplicate check)
+				 * new relative child in parent dir
+				 * (after encountering ".." entry);
+				 * add or replace
 				 */
 			centry->parent = last->parent;
-			centry->prev = last;
-			last = last->next = centry;
+			addchild(last->parent, centry);
+			last = centry;
 		}
 	}
 	return (root);
@@ -331,6 +328,27 @@ free_nodes(NODE *root)
 	}
 }
 
+/*
+ * appendfield --
+ *	Like fprintf(), but output a space either before or after
+ *	the regular output, according to the pathlast flag.
+ */
+static int
+appendfield(FILE *fp, int pathlast, const char *fmt, ...)
+{
+	va_list ap;
+	int result;
+
+	va_start(ap, fmt);
+	if (!pathlast)
+		fprintf(fp, " ");
+	result = vprintf(fmt, ap);
+	if (pathlast)
+		fprintf(fp, " ");
+	va_end(ap);
+	return result;
+}
+
 /*
  * dump_nodes --
  *	dump the NODEs from `cur', based in the directory `dir'.
@@ -338,11 +356,13 @@ free_nodes(NODE *root)
  *	it first.
  */
 void
-dump_nodes(const char *dir, NODE *root, int pathlast)
+dump_nodes(FILE *fp, const char *dir, NODE *root, int pathlast)
 {
 	NODE	*cur;
 	char	path[MAXPATHLEN];
 	const char *name;
+	char	*str;
+	char	*p, *q;
 
 	for (cur = root; cur != NULL; cur = cur->next) {
 		if (cur->type != F_DIR && !matchtags(cur))
@@ -350,70 +370,94 @@ dump_nodes(const char *dir, NODE *root, int pathlast)
 
 		if (snprintf(path, sizeof(path), "%s%s%s",
 		    dir, *dir ? "/" : "", cur->name)
-		    >= sizeof(path))
+		    >= (int)sizeof(path))
 			mtree_err("Pathname too long.");
 
 		if (!pathlast)
-			printf("%s ", vispath(path));
+			fprintf(fp, "%s", vispath(path));
 
 #define MATCHFLAG(f)	((keys & (f)) && (cur->flags & (f)))
 		if (MATCHFLAG(F_TYPE))
-			printf("type=%s ", nodetype(cur->type));
+			appendfield(fp, pathlast, "type=%s",
+			    nodetype(cur->type));
 		if (MATCHFLAG(F_UID | F_UNAME)) {
 			if (keys & F_UNAME &&
 			    (name = user_from_uid(cur->st_uid, 1)) != NULL)
-				printf("uname=%s ", name);
+				appendfield(fp, pathlast, "uname=%s", name);
 			else
-				printf("uid=%u ", cur->st_uid);
+				appendfield(fp, pathlast, "uid=%u",
+				    cur->st_uid);
 		}
 		if (MATCHFLAG(F_GID | F_GNAME)) {
 			if (keys & F_GNAME &&
 			    (name = group_from_gid(cur->st_gid, 1)) != NULL)
-				printf("gname=%s ", name);
+				appendfield(fp, pathlast, "gname=%s", name);
 			else
-				printf("gid=%u ", cur->st_gid);
+				appendfield(fp, pathlast, "gid=%u",
+				    cur->st_gid);
 		}
 		if (MATCHFLAG(F_MODE))
-			printf("mode=%#o ", cur->st_mode);
+			appendfield(fp, pathlast, "mode=%#o", cur->st_mode);
 		if (MATCHFLAG(F_DEV) &&
 		    (cur->type == F_BLOCK || cur->type == F_CHAR))
-			printf("device=%#x ", cur->st_rdev);
+			appendfield(fp, pathlast, "device=%#jx",
+			    (uintmax_t)cur->st_rdev);
 		if (MATCHFLAG(F_NLINK))
-			printf("nlink=%d ", cur->st_nlink);
+			appendfield(fp, pathlast, "nlink=%ju",
+			    (uintmax_t)cur->st_nlink);
 		if (MATCHFLAG(F_SLINK))
-			printf("link=%s ", vispath(cur->slink));
+			appendfield(fp, pathlast, "link=%s",
+			    vispath(cur->slink));
 		if (MATCHFLAG(F_SIZE))
-			printf("size=%lld ", (long long)cur->st_size);
+			appendfield(fp, pathlast, "size=%ju",
+			    (uintmax_t)cur->st_size);
 		if (MATCHFLAG(F_TIME))
-			printf("time=%ld.%ld ", (long)cur->st_mtimespec.tv_sec,
+			appendfield(fp, pathlast, "time=%jd.%09ld",
+			    (intmax_t)cur->st_mtimespec.tv_sec,
 			    cur->st_mtimespec.tv_nsec);
 		if (MATCHFLAG(F_CKSUM))
-			printf("cksum=%lu ", cur->cksum);
+			appendfield(fp, pathlast, "cksum=%lu", cur->cksum);
 		if (MATCHFLAG(F_MD5))
-			printf("md5=%s ", cur->md5digest);
+			appendfield(fp, pathlast, "%s=%s", MD5KEY,
+			    cur->md5digest);
 		if (MATCHFLAG(F_RMD160))
-			printf("rmd160=%s ", cur->rmd160digest);
+			appendfield(fp, pathlast, "%s=%s", RMD160KEY,
+			    cur->rmd160digest);
 		if (MATCHFLAG(F_SHA1))
-			printf("sha1=%s ", cur->sha1digest);
+			appendfield(fp, pathlast, "%s=%s", SHA1KEY,
+			    cur->sha1digest);
 		if (MATCHFLAG(F_SHA256))
-			printf("sha256=%s ", cur->sha256digest);
+			appendfield(fp, pathlast, "%s=%s", SHA256KEY,
+			    cur->sha256digest);
 		if (MATCHFLAG(F_SHA384))
-			printf("sha384=%s ", cur->sha384digest);
+			appendfield(fp, pathlast, "%s=%s", SHA384KEY,
+			    cur->sha384digest);
 		if (MATCHFLAG(F_SHA512))
-			printf("sha512=%s ", cur->sha512digest);
-		if (MATCHFLAG(F_FLAGS))
-			printf("flags=%s ",
-			    flags_to_string(cur->st_flags, "none"));
+			appendfield(fp, pathlast, "%s=%s", SHA512KEY,
+			    cur->sha512digest);
+		if (MATCHFLAG(F_FLAGS)) {
+			str = flags_to_string(cur->st_flags, "none");
+			appendfield(fp, pathlast, "flags=%s", str);
+			free(str);
+		}
 		if (MATCHFLAG(F_IGN))
-			printf("ignore ");
+			appendfield(fp, pathlast, "ignore");
 		if (MATCHFLAG(F_OPT))
-			printf("optional ");
-		if (MATCHFLAG(F_TAGS))
-			printf("tags=%s ", cur->tags);
+			appendfield(fp, pathlast, "optional");
+		if (MATCHFLAG(F_TAGS)) {
+			/* don't output leading or trailing commas */
+			p = cur->tags;
+			while (*p == ',')
+				p++;
+			q = p + strlen(p);
+			while(q > p && q[-1] == ',')
+				q--;
+			appendfield(fp, pathlast, "tags=%.*s", (int)(q - p), p);
+		}
 		puts(pathlast ? vispath(path) : "");
 
 		if (cur->child)
-			dump_nodes(path, cur->child, pathlast);
+			dump_nodes(fp, path, cur->child, pathlast);
 	}
 }
 
@@ -426,11 +470,16 @@ dump_nodes(const char *dir, NODE *root, int pathlast)
 char *
 vispath(const char *path)
 {
-	const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
+	static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
+	static const char extra_glob[] = { ' ', '\t', '\n', '\\', '#', '*',
+	    '?', '[', '\0' };
 	static char pathbuf[4*MAXPATHLEN + 1];
 
-	strsvis(pathbuf, path, VIS_CSTYLE, extra);
-	return(pathbuf);
+	if (flavor == F_NETBSD6)
+		strsvis(pathbuf, path, VIS_CSTYLE, extra);
+	else
+		strsvis(pathbuf, path, VIS_OCTAL, extra_glob);
+	return pathbuf;
 }
 
 
@@ -464,7 +513,7 @@ parsedev(char *arg)
 			mtree_err("not enough arguments");
 		result = (*pack)(argc, numbers, &error);
 		if (error != NULL)
-			mtree_err(error);
+			mtree_err("%s", error);
 	} else {
 		result = (dev_t)strtoul(arg, &ep, 0);
 		if (*ep != '\0')
@@ -530,7 +579,8 @@ replacenode(NODE *cur, NODE *new)
 static void
 set(char *t, NODE *ip)
 {
-	int	type, value, len;
+	int	type, value;
+	size_t	len;
 	gid_t	gid;
 	uid_t	uid;
 	char	*kw, *val, *md, *ep;
@@ -632,11 +682,11 @@ set(char *t, NODE *ip)
 			break;
 		case F_TIME:
 			ip->st_mtimespec.tv_sec =
-			    (time_t)strtoul(val, &ep, 10);
+			    (time_t)strtoll(val, &ep, 10);
 			if (*ep != '.')
 				mtree_err("invalid time `%s'", val);
 			val = ep + 1;
-			ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
+			ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10);
 			if (*ep)
 				mtree_err("invalid time `%s'", val);
 			break;
@@ -699,3 +749,169 @@ unset(char *t, NODE *ip)
 		ip->flags &= ~parsekey(p, NULL);
 	}
 }
+
+/*
+ * addchild --
+ *	Add the centry node as a child of the pathparent node.	If
+ *	centry is a duplicate, call replacenode().  If centry is not
+ *	a duplicate, insert it into the linked list referenced by
+ *	pathparent->child.  Keep the list sorted if Sflag is set.
+ */
+static void
+addchild(NODE *pathparent, NODE *centry)
+{
+	NODE *samename;      /* node with the same name as centry */
+	NODE *replacepos;    /* if non-NULL, centry should replace this node */
+	NODE *insertpos;     /* if non-NULL, centry should be inserted
+			      * after this node */
+	NODE *cur;           /* for stepping through the list */
+	NODE *last;          /* the last node in the list */
+	int cmp;
+
+	samename = NULL;
+	replacepos = NULL;
+	insertpos = NULL;
+	last = NULL;
+	cur = pathparent->child;
+	if (cur == NULL) {
+		/* centry is pathparent's first and only child node so far */
+		pathparent->child = centry;
+		return;
+	}
+
+	/*
+	 * pathparent already has at least one other child, so add the
+	 * centry node to the list.
+	 *
+	 * We first scan through the list looking for an existing node
+	 * with the same name (setting samename), and also looking
+	 * for the correct position to replace or insert the new node
+	 * (setting replacepos and/or insertpos).
+	 */
+	for (; cur != NULL; last = cur, cur = cur->next) {
+		if (strcmp(centry->name, cur->name) == 0) {
+			samename = cur;
+		}
+		if (mtree_Sflag) {
+			cmp = nodecmp(centry, cur);
+			if (cmp == 0) {
+				replacepos = cur;
+			} else if (cmp > 0) {
+				insertpos = cur;
+			}
+		}
+	}
+	if (! mtree_Sflag) {
+		if (samename != NULL) {
+			/* replace node with same name */
+			replacepos = samename;
+		} else {
+			/* add new node at end of list */
+			insertpos = last;
+		}
+	}
+
+	if (samename != NULL) {
+		/*
+		 * We found a node with the same name above.  Call
+		 * replacenode(), which will either exit with an error,
+		 * or replace the information in the samename node and
+		 * free the information in the centry node.
+		 */
+		replacenode(samename, centry);
+		if (samename == replacepos) {
+			/* The just-replaced node was in the correct position */
+			return;
+		}
+		if (samename == insertpos || samename->prev == insertpos) {
+			/*
+			 * We thought the new node should be just before
+			 * or just after the replaced node, but that would
+			 * be equivalent to just retaining the replaced node.
+			 */
+			return;
+		}
+
+		/*
+		 * The just-replaced node is in the wrong position in
+		 * the list.  This can happen if sort order depends on
+		 * criteria other than the node name.
+		 *
+		 * Make centry point to the just-replaced node.	 Unlink
+		 * the just-replaced node from the list, and allow it to
+		 * be insterted in the correct position later.
+		 */
+		centry = samename;
+		if (centry->prev)
+			centry->prev->next = centry->next;
+		else {
+			/* centry->next is the new head of the list */
+			pathparent->child = centry->next;
+			assert(centry->next != NULL);
+		}
+		if (centry->next)
+			centry->next->prev = centry->prev;
+		centry->prev = NULL;
+		centry->next = NULL;
+	}
+
+	if (insertpos == NULL) {
+		/* insert centry at the beginning of the list */
+		pathparent->child->prev = centry;
+		centry->next = pathparent->child;
+		centry->prev = NULL;
+		pathparent->child = centry;
+	} else {
+		/* insert centry into the list just after insertpos */
+		centry->next = insertpos->next;
+		insertpos->next = centry;
+		centry->prev = insertpos;
+		if (centry->next)
+			centry->next->prev = centry;
+	}
+	return;
+}
+
+/*
+ * nodecmp --
+ *	used as a comparison function by addchild() to control the order
+ *	in which entries appear within a list of sibling nodes.	 We make
+ *	directories sort after non-directories, but otherwise sort in
+ *	strcmp() order.
+ *
+ * Keep this in sync with dcmp() below.
+ */
+static int
+nodecmp(const NODE *a, const NODE *b)
+{
+
+	if ((a->type & F_DIR) != 0) {
+		if ((b->type & F_DIR) == 0)
+			return 1;
+	} else if ((b->type & F_DIR) != 0) {
+		return -1;
+	}
+	return strcmp(a->name, b->name);
+}
+
+/*
+ * dcmp --
+ *    used as a comparison function passed to fts_open() to control
+ *    the order in which fts_read() returns results.    We make
+ *    directories sort after non-directories, but otherwise sort in
+ *    strcmp() order.
+ *
+ * Keep this in sync with nodecmp() above.
+ */
+int
+dcmp(const FTSENT *FTS_CONST *a, const FTSENT *FTS_CONST *b)
+{
+
+	if (S_ISDIR((*a)->fts_statp->st_mode)) {
+		if (!S_ISDIR((*b)->fts_statp->st_mode))
+			return 1;
+	} else if (S_ISDIR((*b)->fts_statp->st_mode)) {
+		return -1;
+	}
+	return strcmp((*a)->fts_name, (*b)->fts_name);
+}
diff --git a/pkgtools/mtree/files/stat_flags.c b/pkgtools/mtree/files/stat_flags.c
index 0ef284a4c74a..364e83a413c7 100644
--- a/pkgtools/mtree/files/stat_flags.c
+++ b/pkgtools/mtree/files/stat_flags.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: stat_flags.c,v 1.5 2011/07/27 15:31:00 seb Exp $	*/
+/*	$NetBSD: stat_flags.c,v 1.3 2022/04/19 20:32:17 rillig Exp $	*/
 
 /*-
  * Copyright (c) 1993
@@ -32,13 +32,11 @@
 #if HAVE_CONFIG_H
 #include "config.h"
 #endif
-#if 0
 #if HAVE_NBTOOL_CONFIG_H
 #include "nbtool_config.h"
 #else
 #define HAVE_STRUCT_STAT_ST_FLAGS 1
 #endif
-#endif
 
 #include <nbcompat.h>
 #if HAVE_SYS_CDEFS_H
@@ -48,7 +46,7 @@
 #if 0
 static char sccsid[] = "@(#)stat_flags.c	8.2 (Berkeley) 7/28/94";
 #else
-__RCSID("$NetBSD: stat_flags.c,v 1.5 2011/07/27 15:31:00 seb Exp $");
+__RCSID("$NetBSD: stat_flags.c,v 1.3 2022/04/19 20:32:17 rillig Exp $");
 #endif
 #endif /* not lint */
 
@@ -67,15 +65,18 @@ __RCSID("$NetBSD: stat_flags.c,v 1.5 2011/07/27 15:31:00 seb Exp $");
 #if HAVE_STRING_H
 #include <string.h>
 #endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
 
-#include "stat_flags.h"
+#include "util.h"
 
 #define	SAPPEND(s) do {							\
 	if (prefix != NULL)						\
 		(void)strlcat(string, prefix, sizeof(string));		\
 	(void)strlcat(string, s, sizeof(string));			\
 	prefix = ",";							\
-} while (/* CONSTCOND */ 0)
+} while (0)
 
 /*
  * flags_to_string --
@@ -85,7 +86,7 @@ __RCSID("$NetBSD: stat_flags.c,v 1.5 2011/07/27 15:31:00 seb Exp $");
 char *
 flags_to_string(u_long flags, const char *def)
 {
-	static char string[128];
+	char string[128];
 	const char *prefix;
 
 	string[0] = '\0';
@@ -110,9 +111,9 @@ flags_to_string(u_long flags, const char *def)
 		SAPPEND("snap");
 #endif
 #endif
-	if (prefix == NULL)
-		strlcpy(string, def, sizeof(string));
-	return (string);
+	if (prefix != NULL)
+		return strdup(string);
+	return strdup(def);
 }
 
 #define	TEST(a, b, f) {							\
diff --git a/pkgtools/mtree/files/stat_flags.h b/pkgtools/mtree/files/stat_flags.h
deleted file mode 100644
index bf8b30fb5366..000000000000
--- a/pkgtools/mtree/files/stat_flags.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*	$NetBSD: stat_flags.h,v 1.1 2003/09/05 18:39:00 jlam Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *	@(#)extern.h	8.1 (Berkeley) 5/31/93
- */
-
-char	*flags_to_string(u_long, const char *);
-int	 string_to_flags(char **, u_long *, u_long *);
diff --git a/pkgtools/mtree/files/verify.c b/pkgtools/mtree/files/verify.c
index 478e10fe3068..561c8948a48d 100644
--- a/pkgtools/mtree/files/verify.c
+++ b/pkgtools/mtree/files/verify.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: verify.c,v 1.6 2010/03/21 16:30:17 joerg Exp $	*/
+/*	$NetBSD: verify.c,v 1.50 2024/12/11 14:52:26 christos Exp $	*/
 
 /*-
  * Copyright (c) 1990, 1993
@@ -44,7 +44,7 @@
 #if 0
 static char sccsid[] = "@(#)verify.c	8.1 (Berkeley) 6/6/93";
 #else
-__RCSID("$NetBSD: verify.c,v 1.6 2010/03/21 16:30:17 joerg Exp $");
+__RCSID("$NetBSD: verify.c,v 1.50 2024/12/11 14:52:26 christos Exp $");
 #endif
 #endif /* not lint */
 
@@ -86,11 +86,11 @@ static void	miss(NODE *, char *);
 static int	vwalk(void);
 
 int
-verify(void)
+verify(FILE *fi)
 {
 	int rval;
 
-	root = spec(stdin);
+	root = spec(fi);
 	rval = vwalk();
 	miss(root, path);
 	return (rval);
@@ -108,7 +108,7 @@ vwalk(void)
 	argv[0] = dot;
 	argv[1] = NULL;
 
-	if ((t = fts_open(argv, ftsoptions, NULL)) == NULL)
+	if ((t = fts_open(argv, ftsoptions, dcmp)) == NULL)
 		mtree_err("fts_open: %s", strerror(errno));
 	level = root;
 	specdepth = rval = 0;
@@ -117,6 +117,10 @@ vwalk(void)
 			fts_set(t, p, FTS_SKIP);
 			continue;
 		}
+		if (!find_only(p->fts_path)) {
+			fts_set(t, p, FTS_SKIP);
+			continue;
+		}
 		switch(p->fts_info) {
 		case FTS_D:
 		case FTS_SL:
@@ -146,7 +150,8 @@ vwalk(void)
 			    !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) ||
 			    !strcmp(ep->name, p->fts_name)) {
 				ep->flags |= F_VISIT;
-				if (compare(ep, p))
+				if ((ep->flags & F_NOCHANGE) == 0 &&
+				    compare(ep, p))
 					rval = MISMATCHEXIT;
 				if (!(ep->flags & F_IGN) &&
 				    ep->type == F_DIR &&
@@ -163,9 +168,16 @@ vwalk(void)
 		if (ep)
 			continue;
  extra:
-		if (!eflag) {
-			printf("extra: %s", RP(p));
+		if (!eflag && !(dflag && p->fts_info == FTS_SL)) {
+			printf(flavor == F_FREEBSD9 ? "%s extra" : "extra: %s",
+			    RP(p));
 			if (rflag) {
+#if HAVE_STRUCT_STAT_ST_FLAGS
+				if (rflag > 1 &&
+				    lchflags(p->fts_accpath, 0) == -1)
+					printf(" (chflags %s)",
+					    strerror(errno));
+#endif
 				if ((S_ISDIR(p->fts_statp->st_mode)
 				    ? rmdir : unlink)(p->fts_accpath)) {
 					printf(", not removed: %s",
@@ -177,6 +189,8 @@ vwalk(void)
 		}
 		fts_set(t, p, FTS_SKIP);
 	}
+	if (errno != 0)
+		mtree_err("fts_read: %s", strerror(errno));
 	fts_close(t);
 	if (sflag)
 		warnx("%s checksum: %u", fullpath, crc_total);
@@ -189,7 +203,7 @@ miss(NODE *p, char *tail)
 	int create;
 	char *tp;
 	const char *type;
-	uint32_t flags;
+	u_long flags;
 
 	for (; p; p = p->next) {
 		if (p->flags & F_OPT && !(p->flags & F_VISIT))
@@ -197,8 +211,17 @@ miss(NODE *p, char *tail)
 		if (p->type != F_DIR && (dflag || p->flags & F_VISIT))
 			continue;
 		strcpy(tail, p->name);
-		if (!(p->flags & F_VISIT))
-			printf("missing: %s", path);
+		if (!(p->flags & F_VISIT)) {
+			/* Don't print missing message if file exists as a 
+			   symbolic link and the -q flag is set. */
+			struct stat statbuf;
+
+			if (qflag && stat(path, &statbuf) == 0 &&
+			    S_ISDIR(statbuf.st_mode))
+				p->flags |= F_VISIT;
+			else
+				(void)printf("%s missing", path);
+		}
 		switch (p->type) {
 		case F_BLOCK:
 		case F_CHAR:




Home | Main Index | Thread Index | Old Index