commit 863dc7f

shrub  ·  2026-06-04 11:22:40 +0000 UTC
parent 44ae0f1
add makefs
64 files changed,  +17218, -129
+1, -1
1@@ -1,7 +1,7 @@
2 .POSIX:
3 
4 #RECURSIVE MAKE CONSIDERED USEFUL?
5-SUBDIRS = apply col ctags finger jot look pr rs script shar
6+SUBDIRS = compat apply col ctags finger jot look makefs pr rs script shar
7 all:
8 	@set -e; \
9 	for dir in $(SUBDIRS); do \
+11, -0
 1@@ -3,6 +3,17 @@ netmisc
 2 
 3 misc programs from netbsd with some compatibilty shim and portability bits.
 4 
 5+- apply
 6+- col
 7+- ctags
 8+- finger
 9+- look
10+- makefs
11+- pr
12+- rs 
13+- script
14+- shar
15+
16 the shar implementation is not from netbsd, but a slightly improved version in C by me
17 
18 you need cc, make, yacc, etc to build, and a reasonably posix-compliant system. 
+7, -3
 1@@ -1,7 +1,8 @@
 2 .POSIX:
 3 
 4 PROG = apply
 5-SRCS = apply.c ../compat/getprogname.c ../compat/setprogname.c
 6+SRCS = apply.c
 7+COMPATLIB = ../compat/libnetcompat.a
 8 MAN = apply.1
 9 
10 CC = cc
11@@ -13,8 +14,11 @@ MANDIR = /usr/local/share/man
12 
13 all: $(PROG)
14 
15-$(PROG): $(SRCS)
16-	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
17+$(PROG): $(SRCS) $(COMPATLIB)
18+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
19+
20+$(COMPATLIB):
21+	(cd ../compat && $(MAKE) all)
22 
23 install: $(PROG)
24 	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
+7, -5
 1@@ -1,9 +1,8 @@
 2 .POSIX:
 3 
 4 PROG = col
 5-SRCS = col.c ../compat/getprogname.c ../compat/strtoi.c \
 6-	../compat/errc.c ../compat/verrc.c ../compat/warnc.c \
 7-	../compat/vwarnc.c
 8+SRCS = col.c
 9+COMPATLIB = ../compat/libnetcompat.a
10 MAN = col.1
11 
12 CC = cc
13@@ -15,8 +14,11 @@ MANDIR = /usr/local/share/man
14 
15 all: $(PROG)
16 
17-$(PROG): $(SRCS)
18-	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
19+$(PROG): $(SRCS) $(COMPATLIB)
20+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
21+
22+$(COMPATLIB):
23+	(cd ../compat && $(MAKE) all)
24 
25 install: $(PROG)
26 	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
+36, -0
 1@@ -0,0 +1,36 @@
 2+.POSIX:
 3+
 4+LIB = libnetcompat.a
 5+SRCS = \
 6+	db.c efun.c errc.c fgetln.c fparseln.c getmode.c getprogname.c \
 7+	pwcache.c reallocarr.c raise_default_signal.c setgroupent.c \
 8+	setpassent.c setprogname.c shquote.c strlcat.c strlcpy.c \
 9+	strsuftoll.c strtoi.c strtou.c unvis.c verrc.c vis.c vwarnc.c \
10+	warnc.c
11+OBJS = \
12+	db.o efun.o errc.o fgetln.o fparseln.o getmode.o getprogname.o \
13+	pwcache.o reallocarr.o raise_default_signal.o setgroupent.o \
14+	setpassent.o setprogname.o shquote.o strlcat.o strlcpy.o \
15+	strsuftoll.o strtoi.o strtou.o unvis.o verrc.o vis.o vwarnc.o \
16+	warnc.o
17+
18+CC = cc
19+AR = ar
20+RANLIB = ranlib
21+CFLAGS = -O2
22+CPPFLAGS = -DHAVE_NBTOOL_CONFIG_H=1 -I. -include netcompat.h
23+
24+all: $(LIB)
25+
26+.c.o:
27+	$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
28+
29+$(LIB): $(OBJS)
30+	rm -f $(LIB)
31+	$(AR) -rc $(LIB) $(OBJS)
32+	$(RANLIB) $(LIB)
33+
34+install:
35+
36+clean:
37+	rm -f $(LIB) $(OBJS)
+203, -0
  1@@ -0,0 +1,203 @@
  2+/*	$NetBSD: efun.c,v 1.12 2019/10/03 20:29:19 tnn Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
  6+ * All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to The NetBSD Foundation
  9+ * by Christos Zoulas.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or without
 12+ * modification, are permitted provided that the following conditions
 13+ * are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above copyright
 17+ *    notice, this list of conditions and the following disclaimer in the
 18+ *    documentation and/or other materials provided with the distribution.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 21+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 22+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 23+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 24+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 25+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 26+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 27+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 28+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 29+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 30+ * POSSIBILITY OF SUCH DAMAGE.
 31+ */
 32+
 33+#if HAVE_NBTOOL_CONFIG_H
 34+#include "nbtool_config.h"
 35+#endif
 36+
 37+#include <sys/cdefs.h>
 38+#ifdef __RCSID
 39+__RCSID("$NetBSD: efun.c,v 1.12 2019/10/03 20:29:19 tnn Exp $");
 40+#endif
 41+
 42+#include <err.h>
 43+#include <errno.h>
 44+#include <inttypes.h>
 45+#include <string.h>
 46+#include <stdlib.h>
 47+#include <stdio.h>
 48+#include <stdarg.h>
 49+#include <util.h>
 50+
 51+static void (*efunc)(int, const char *, ...) = err;
 52+
 53+static void __dead
 54+eexit(int e, const char *fmt NETCOMPAT_UNUSED, ...)
 55+{
 56+	exit(e);
 57+}
 58+
 59+void (*
 60+esetfunc(void (*ef)(int, const char *, ...)))(int, const char *, ...)
 61+{
 62+	void (*of)(int, const char *, ...) = efunc;
 63+	efunc = ef == NULL ? eexit : ef;
 64+	return of;
 65+}
 66+
 67+size_t
 68+estrlcpy(char *dst, const char *src, size_t len)
 69+{
 70+	size_t rv;
 71+	if ((rv = strlcpy(dst, src, len)) >= len) {
 72+		errno = ENAMETOOLONG;
 73+		(*efunc)(1,
 74+		    "Cannot copy string; %zu chars needed %zu provided",
 75+		    rv, len);
 76+	}
 77+	return rv;
 78+}
 79+
 80+size_t
 81+estrlcat(char *dst, const char *src, size_t len)
 82+{
 83+	size_t rv;
 84+	if ((rv = strlcat(dst, src, len)) >= len) {
 85+		errno = ENAMETOOLONG;
 86+		(*efunc)(1,
 87+		    "Cannot append to string; %zu chars needed %zu provided",
 88+		    rv, len);
 89+	}
 90+	return rv;
 91+}
 92+
 93+char *
 94+estrdup(const char *s)
 95+{
 96+	char *d = strdup(s);
 97+	if (d == NULL)
 98+		(*efunc)(1, "Cannot copy string");
 99+	return d;
100+}
101+
102+char *
103+estrndup(const char *s, size_t len)
104+{
105+	char *d = strndup(s, len);
106+	if (d == NULL)
107+		(*efunc)(1, "Cannot copy string");
108+	return d;
109+}
110+
111+void *
112+emalloc(size_t n)
113+{
114+	void *p = malloc(n);
115+	if (p == NULL && n != 0)
116+		(*efunc)(1, "Cannot allocate %zu bytes", n);
117+	return p;
118+}
119+
120+void *
121+ecalloc(size_t n, size_t s)
122+{
123+	void *p = calloc(n, s);
124+	if (p == NULL && n != 0 && s != 0)
125+		(*efunc)(1, "Cannot allocate %zu blocks of size %zu", n, s);
126+	return p;
127+}
128+
129+void *
130+erealloc(void *p, size_t n)
131+{
132+	void *q = realloc(p, n);
133+	if (q == NULL && n != 0)
134+		(*efunc)(1, "Cannot re-allocate %zu bytes", n);
135+	return q;
136+}
137+
138+void
139+ereallocarr(void *p, size_t n, size_t s)
140+{
141+	int rv = reallocarr(p, n, s);
142+	if (rv != 0) {
143+		errno = rv;
144+		(*efunc)(1, "Cannot re-allocate %zu * %zu bytes", n, s);
145+	}
146+}
147+
148+FILE *
149+efopen(const char *p, const char *m)
150+{
151+	FILE *fp = fopen(p, m);
152+	if (fp == NULL)
153+		(*efunc)(1, "Cannot open `%s'", p);
154+	return fp;
155+}
156+
157+int
158+easprintf(char ** __restrict ret, const char * __restrict format, ...)
159+{
160+	int rv;
161+	va_list ap;
162+	va_start(ap, format);
163+	if ((rv = vasprintf(ret, format, ap)) == -1)
164+		(*efunc)(1, "Cannot format string");
165+	va_end(ap);
166+	return rv;
167+}
168+
169+int
170+evasprintf(char ** __restrict ret, const char * __restrict format, va_list ap)
171+{
172+	int rv;
173+	if ((rv = vasprintf(ret, format, ap)) == -1)
174+		(*efunc)(1, "Cannot format string");
175+	return rv;
176+}
177+
178+intmax_t
179+estrtoi(const char * nptr, int base, intmax_t lo, intmax_t hi)
180+{
181+	int e;
182+	intmax_t rv = strtoi(nptr, NULL, base, lo, hi, &e);
183+	if (e != 0) {
184+		errno = e;
185+		(*efunc)(1,
186+		    "Cannot convert string value '%s' with base %d to a number in range [%jd .. %jd]",
187+		    nptr, base, lo, hi);
188+	}
189+	return rv;
190+}
191+
192+uintmax_t
193+estrtou(const char * nptr, int base, uintmax_t lo, uintmax_t hi)
194+{
195+	int e;
196+	uintmax_t rv = strtou(nptr, NULL, base, lo, hi, &e);
197+	if (e != 0) {
198+		errno = e;
199+		(*efunc)(1,
200+		    "Cannot convert string value '%s' with base %d to a number in range [%ju .. %ju]",
201+		    nptr, base, lo, hi);
202+	}
203+	return rv;
204+}
+78, -0
 1@@ -0,0 +1,78 @@
 2+/*	$NetBSD: fgetln.c,v 1.5 2003/10/27 00:12:43 lukem Exp $	*/
 3+
 4+/*
 5+ * Copyright 1999 Luke Mewburn <lukem@NetBSD.org>.
 6+ * All rights reserved.
 7+ *
 8+ * Redistribution and use in source and binary forms, with or without
 9+ * modification, are permitted provided that the following conditions
10+ * are met:
11+ * 1. Redistributions of source code must retain the above copyright
12+ *    notice, this list of conditions and the following disclaimer.
13+ * 2. Redistributions in binary form must reproduce the above copyright
14+ *    notice, this list of conditions and the following disclaimer in the
15+ *    documentation and/or other materials provided with the distribution.
16+ * 3. The name of the author may not be used to endorse or promote products
17+ *    derived from this software without specific prior written permission.
18+ *
19+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
28+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+ */
30+
31+#include "nbtool_config.h"
32+
33+#if !HAVE_FGETLN
34+#include <err.h>
35+#include <stdio.h>
36+#include <stdlib.h>
37+#include <string.h>
38+
39+#define BUFCHUNKS	BUFSIZ
40+
41+char *
42+fgetln(FILE *fp, size_t *len)
43+{
44+	static char *buf;
45+	static size_t bufsize;
46+	size_t buflen;
47+	char curbuf[BUFCHUNKS];
48+	char *p;
49+
50+	if (buf == NULL) {
51+		bufsize = BUFCHUNKS;
52+		buf = (char *)malloc(bufsize);
53+		if (buf == NULL)
54+			err(1, "Unable to allocate buffer for fgetln()");
55+	}
56+
57+	*buf = '\0';
58+	buflen = 0;
59+	while ((p = fgets(curbuf, sizeof(curbuf), fp)) != NULL) {
60+		size_t l;
61+
62+		l = strlen(p);
63+		if (bufsize < buflen + l) {
64+			bufsize += BUFCHUNKS;
65+			if ((buf = (char *)realloc(buf, bufsize)) == NULL)
66+				err(1, "Unable to allocate %ld bytes of memory",
67+				    (long)bufsize);
68+		}
69+		strcpy(buf + buflen, p);
70+		buflen += l;
71+		if (p[l - 1] == '\n')
72+			break;
73+	}
74+	if (p == NULL && *buf == '\0')
75+		return (NULL);
76+	*len = strlen(buf);
77+	return (buf);
78+}
79+#endif
+250, -0
  1@@ -0,0 +1,250 @@
  2+/*	$NetBSD: fparseln.c,v 1.5 2004/06/20 22:20:15 jmc Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1997 Christos Zoulas.  All rights reserved.
  6+ *
  7+ * Redistribution and use in source and binary forms, with or without
  8+ * modification, are permitted provided that the following conditions
  9+ * are met:
 10+ * 1. Redistributions of source code must retain the above copyright
 11+ *    notice, this list of conditions and the following disclaimer.
 12+ * 2. Redistributions in binary form must reproduce the above copyright
 13+ *    notice, this list of conditions and the following disclaimer in the
 14+ *    documentation and/or other materials provided with the distribution.
 15+ * 3. All advertising materials mentioning features or use of this software
 16+ *    must display the following acknowledgement:
 17+ *	This product includes software developed by Christos Zoulas.
 18+ * 4. The name of the author may not be used to endorse or promote products
 19+ *    derived from this software without specific prior written permission.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 22+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 23+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 24+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 25+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 26+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 27+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 28+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 29+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 30+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 31+ */
 32+
 33+#include <sys/cdefs.h>
 34+#if defined(LIBC_SCCS) && !defined(lint)
 35+__RCSID("$NetBSD: fparseln.c,v 1.5 2004/06/20 22:20:15 jmc Exp $");
 36+#endif /* LIBC_SCCS and not lint */
 37+
 38+#include "namespace.h"
 39+
 40+#include <assert.h>
 41+#include <errno.h>
 42+#include <stdio.h>
 43+#include <string.h>
 44+#include <stdlib.h>
 45+
 46+#include "util.h"
 47+
 48+#ifdef __weak_alias
 49+__weak_alias(fparseln,_fparseln)
 50+#endif
 51+
 52+#if ! HAVE_FPARSELN
 53+
 54+#ifndef HAVE_NBTOOL_CONFIG_H
 55+#include "reentrant.h"
 56+#include "local.h"
 57+#else
 58+#define FLOCKFILE(fp)
 59+#define FUNLOCKFILE(fp)
 60+#endif
 61+
 62+#if defined(_REENTRANT) && !HAVE_NBTOOL_CONFIG_H
 63+#define __fgetln(f, l) __fgetstr(f, l, '\n')
 64+#else
 65+#define __fgetln(f, l) fgetln(f, l)
 66+#endif
 67+
 68+static int isescaped(const char *, const char *, int);
 69+
 70+/* isescaped():
 71+ *	Return true if the character in *p that belongs to a string
 72+ *	that starts in *sp, is escaped by the escape character esc.
 73+ */
 74+static int
 75+isescaped(const char *sp, const char *p, int esc)
 76+{
 77+	const char     *cp;
 78+	size_t		ne;
 79+
 80+	_DIAGASSERT(sp != NULL);
 81+	_DIAGASSERT(p != NULL);
 82+
 83+	/* No escape character */
 84+	if (esc == '\0')
 85+		return 1;
 86+
 87+	/* Count the number of escape characters that precede ours */
 88+	for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
 89+		continue;
 90+
 91+	/* Return true if odd number of escape characters */
 92+	return (ne & 1) != 0;
 93+}
 94+
 95+
 96+/* fparseln():
 97+ *	Read a line from a file parsing continuations ending in \
 98+ *	and eliminating trailing newlines, or comments starting with
 99+ *	the comment char.
100+ */
101+char *
102+fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags)
103+{
104+	static const char dstr[3] = { '\\', '\\', '#' };
105+
106+	size_t	s, len;
107+	char   *buf;
108+	char   *ptr, *cp;
109+	int	cnt;
110+	char	esc, con, nl, com;
111+
112+	_DIAGASSERT(fp != NULL);
113+
114+	len = 0;
115+	buf = NULL;
116+	cnt = 1;
117+
118+	if (str == NULL)
119+		str = dstr;
120+
121+	esc = str[0];
122+	con = str[1];
123+	com = str[2];
124+	/*
125+	 * XXX: it would be cool to be able to specify the newline character,
126+	 * but unfortunately, fgetln does not let us
127+	 */
128+	nl  = '\n';
129+
130+	FLOCKFILE(fp);
131+
132+	while (cnt) {
133+		cnt = 0;
134+
135+		if (lineno)
136+			(*lineno)++;
137+
138+		if ((ptr = __fgetln(fp, &s)) == NULL)
139+			break;
140+
141+		if (s && com) {		/* Check and eliminate comments */
142+			for (cp = ptr; cp < ptr + s; cp++)
143+				if (*cp == com && !isescaped(ptr, cp, esc)) {
144+					s = cp - ptr;
145+					cnt = s == 0 && buf == NULL;
146+					break;
147+				}
148+		}
149+
150+		if (s && nl) { 		/* Check and eliminate newlines */
151+			cp = &ptr[s - 1];
152+
153+			if (*cp == nl)
154+				s--;	/* forget newline */
155+		}
156+
157+		if (s && con) {		/* Check and eliminate continuations */
158+			cp = &ptr[s - 1];
159+
160+			if (*cp == con && !isescaped(ptr, cp, esc)) {
161+				s--;	/* forget escape */
162+				cnt = 1;
163+			}
164+		}
165+
166+		if (s == 0 && buf != NULL)
167+			continue;
168+
169+		if ((cp = realloc(buf, len + s + 1)) == NULL) {
170+			FUNLOCKFILE(fp);
171+			free(buf);
172+			return NULL;
173+		}
174+		buf = cp;
175+
176+		(void) memcpy(buf + len, ptr, s);
177+		len += s;
178+		buf[len] = '\0';
179+	}
180+
181+	FUNLOCKFILE(fp);
182+
183+	if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
184+	    strchr(buf, esc) != NULL) {
185+		ptr = cp = buf;
186+		while (cp[0] != '\0') {
187+			int skipesc;
188+
189+			while (cp[0] != '\0' && cp[0] != esc)
190+				*ptr++ = *cp++;
191+			if (cp[0] == '\0' || cp[1] == '\0')
192+				break;
193+
194+			skipesc = 0;
195+			if (cp[1] == com)
196+				skipesc += (flags & FPARSELN_UNESCCOMM);
197+			if (cp[1] == con)
198+				skipesc += (flags & FPARSELN_UNESCCONT);
199+			if (cp[1] == esc)
200+				skipesc += (flags & FPARSELN_UNESCESC);
201+			if (cp[1] != com && cp[1] != con && cp[1] != esc)
202+				skipesc = (flags & FPARSELN_UNESCREST);
203+
204+			if (skipesc)
205+				cp++;
206+			else
207+				*ptr++ = *cp++;
208+			*ptr++ = *cp++;
209+		}
210+		*ptr = '\0';
211+		len = strlen(buf);
212+	}
213+
214+	if (size)
215+		*size = len;
216+	return buf;
217+}
218+
219+#ifdef TEST
220+
221+int main(int, char **);
222+
223+int
224+main(int argc, char **argv)
225+{
226+	char   *ptr;
227+	size_t	size, line;
228+
229+	line = 0;
230+	while ((ptr = fparseln(stdin, &size, &line, NULL,
231+	    FPARSELN_UNESCALL)) != NULL)
232+		printf("line %d (%d) |%s|\n", line, size, ptr);
233+	return 0;
234+}
235+
236+/*
237+
238+# This is a test
239+line 1
240+line 2 \
241+line 3 # Comment
242+line 4 \# Not comment \\\\
243+
244+# And a comment \
245+line 5 \\\
246+line 6
247+
248+*/
249+
250+#endif /* TEST */
251+#endif	/* ! HAVE_FPARSELN */
+64, -0
 1@@ -0,0 +1,64 @@
 2+/*	$NetBSD: getmode.c,v 1.6 2004/01/13 00:53:06 simonb Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
 6+ * All rights reserved.
 7+ *
 8+ * This code is derived from software contributed to The NetBSD Foundation
 9+ * by Todd Vierling.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
14+ * 1. Redistributions of source code must retain the above copyright
15+ *    notice, this list of conditions and the following disclaimer.
16+ * 2. Redistributions in binary form must reproduce the above copyright
17+ *    notice, this list of conditions and the following disclaimer in the
18+ *    documentation and/or other materials provided with the distribution.
19+ * 3. All advertising materials mentioning features or use of this software
20+ *    must display the following acknowledgement:
21+ *        This product includes software developed by the NetBSD
22+ *        Foundation, Inc. and its contributors.
23+ * 4. Neither the name of The NetBSD Foundation nor the names of its
24+ *    contributors may be used to endorse or promote products derived
25+ *    from this software without specific prior written permission.
26+ *
27+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37+ * POSSIBILITY OF SUCH DAMAGE.
38+ */
39+
40+#include "nbtool_config.h"
41+#include "netcompat.h"
42+#include <stdlib.h>
43+
44+void *
45+setmode(const char *str)
46+{
47+	mode_t *mp = malloc(sizeof(mode_t));
48+
49+	*mp = strtoul(str, NULL, 8);
50+
51+	return mp;
52+}
53+
54+mode_t
55+getmode(const void *mp, mode_t mode)
56+{
57+	mode_t m;
58+
59+	m = *((const mode_t *)mp);
60+
61+	mode &= ~ALLPERMS;	/* input mode less RWX permissions */
62+	m &= ALLPERMS;		/* new RWX permissions */
63+
64+	return m | mode;
65+}
+14, -0
 1@@ -0,0 +1,14 @@
 2+/*	$NetBSD: namespace.h,v 1.3 2003/10/27 00:12:43 lukem Exp $	*/
 3+
 4+/*
 5+ * Mainly empty header to make reachover bits of libc happy.
 6+ *
 7+ * Since all reachover bits will include this, it's a good place to pull
 8+ * in nbtool_config.h.
 9+ */
10+#include "nbtool_config.h"
11+
12+/* No aliases in reachover-based libc sources. */
13+#undef __indr_reference
14+#undef __weak_alias
15+#undef __warn_references
+31, -0
 1@@ -0,0 +1,31 @@
 2+#ifndef __NETBSD_NBTOOL_CONFIG_H__
 3+#define __NETBSD_NBTOOL_CONFIG_H__
 4+
 5+#define HAVE_NBTOOL_CONFIG_H 1
 6+
 7+#define PATH_BSHELL "/bin/sh"
 8+
 9+#define HAVE_FGETLN 0
10+#define HAVE_FPARSELN 0
11+#define HAVE_PWCACHE_USERDB 0
12+#define HAVE_SETPASSENT 0
13+#define HAVE_SETGROUPENT 0
14+#define HAVE_DECL_SETPASSENT 0
15+#define HAVE_DECL_SETGROUPENT 0
16+#define HAVE_SETPROGNAME 0
17+#define HAVE_STRSUFTOLL 0
18+#define HAVE_VIS 0
19+#define HAVE_SVIS 0
20+
21+#define HAVE_STRUCT_STAT_ST_FLAGS 0
22+#define HAVE_STRUCT_STAT_ST_GEN 0
23+#define HAVE_STRUCT_STAT_BIRTHTIME 0
24+#define HAVE_STRUCT_STAT_ST_MTIMENSEC 0
25+#define HAVE_STRUCT_STATVFS_F_IOSIZE 0
26+#define HAVE_FSTATVFS 1
27+#define HAVE_STRUCT_TM_TM_GMTOFF 1
28+
29+#define HAVE_SYS_ENDIAN_H 1
30+#define HAVE_STRTOLL 1
31+
32+#endif
+76, -2
  1@@ -22,9 +22,15 @@
  2 #include <inttypes.h>
  3 #include <stdint.h>
  4 #include <stdarg.h>
  5+#include <limits.h>
  6 #include <time.h>
  7+#include <sys/time.h>
  8+#include <sys/types.h>
  9+#include <grp.h>
 10 #include <pwd.h>
 11 #include <byteswap.h>
 12+#include <sys/stat.h>
 13+#include <sys/sysmacros.h>
 14 
 15 #ifndef __RCSID
 16 #define __RCSID(x)
 17@@ -67,14 +73,58 @@
 18 #define __weak_alias(sym, alias)
 19 #endif
 20 
 21+#ifndef NETCOMPAT_UNUSED
 22+#if defined(__GNUC__) || defined(__clang__)
 23+#define NETCOMPAT_UNUSED __attribute__((__unused__))
 24+#else
 25+#define NETCOMPAT_UNUSED
 26+#endif
 27+#endif
 28+
 29 #ifndef __UNCONST
 30 #define __UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
 31 #endif
 32 
 33+#ifndef __CAST
 34+#define __CAST(dt, st) ((dt)(st))
 35+#endif
 36+
 37+#ifndef __arraycount
 38+#define __arraycount(__x) (sizeof(__x) / sizeof((__x)[0]))
 39+#endif
 40+
 41+#ifndef __USE
 42+#define __USE(a) ((void)(a))
 43+#endif
 44+
 45 #ifndef sig_t
 46 typedef void (*sig_t)(int);
 47 #endif
 48 
 49+#ifndef u_int
 50+typedef unsigned int u_int;
 51+#endif
 52+
 53+#ifndef u_long
 54+typedef unsigned long u_long;
 55+#endif
 56+
 57+#ifndef daddr_t
 58+typedef int64_t daddr_t;
 59+#endif
 60+
 61+#ifndef UID_MAX
 62+#define UID_MAX UINT_MAX
 63+#endif
 64+
 65+#ifndef GID_MAX
 66+#define GID_MAX UINT_MAX
 67+#endif
 68+
 69+#ifndef ALLPERMS
 70+#define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
 71+#endif
 72+
 73 #ifndef timespeccmp
 74 #define timespeccmp(a, b, cmp) \
 75 	(((a)->tv_sec == (b)->tv_sec) ? \
 76@@ -90,18 +140,40 @@ typedef void (*sig_t)(int);
 77 	} while (0)
 78 #endif
 79 
 80-#ifndef setpassent
 81-#define setpassent(stayopen) ((void)(stayopen), setpwent())
 82+#ifndef timersub
 83+#define timersub(tvp, uvp, vvp) \
 84+	do { \
 85+		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
 86+		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
 87+		if ((vvp)->tv_usec < 0) { \
 88+			(vvp)->tv_sec--; \
 89+			(vvp)->tv_usec += 1000000; \
 90+		} \
 91+	} while (0)
 92 #endif
 93 
 94 #ifndef bswap32
 95 #define bswap32 bswap_32
 96 #endif
 97 
 98+#ifndef bswap16
 99+#define bswap16 bswap_16
100+#endif
101+
102 #ifndef bswap64
103 #define bswap64 bswap_64
104 #endif
105 
106+#ifndef UF_APPEND
107+#define UF_APPEND 0
108+#define UF_IMMUTABLE 0
109+#define UF_NODUMP 0
110+#define UF_OPAQUE 0
111+#define SF_APPEND 0
112+#define SF_ARCHIVED 0
113+#define SF_IMMUTABLE 0
114+#endif
115+
116 extern const char *netcompat_progname;
117 
118 const char *getprogname(void);
119@@ -116,5 +188,7 @@ void warnc(int, const char *, ...);
120 __dead void verrc(int, int, const char *, va_list);
121 __dead void errc(int, int, const char *, ...);
122 int raise_default_signal(int);
123+int setgroupent(int);
124+int setpassent(int);
125 
126 #endif
+646, -0
  1@@ -0,0 +1,646 @@
  2+/*	$NetBSD: pwcache.c,v 1.29 2004/06/20 22:20:14 jmc Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1992 Keith Muller.
  6+ * Copyright (c) 1992, 1993
  7+ *	The Regents of the University of California.  All rights reserved.
  8+ *
  9+ * This code is derived from software contributed to Berkeley by
 10+ * Keith Muller of the University of California, San Diego.
 11+ *
 12+ * Redistribution and use in source and binary forms, with or without
 13+ * modification, are permitted provided that the following conditions
 14+ * are met:
 15+ * 1. Redistributions of source code must retain the above copyright
 16+ *    notice, this list of conditions and the following disclaimer.
 17+ * 2. Redistributions in binary form must reproduce the above copyright
 18+ *    notice, this list of conditions and the following disclaimer in the
 19+ *    documentation and/or other materials provided with the distribution.
 20+ * 3. Neither the name of the University nor the names of its contributors
 21+ *    may be used to endorse or promote products derived from this software
 22+ *    without specific prior written permission.
 23+ *
 24+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 25+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 26+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 27+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 28+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 29+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 30+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 31+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 32+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 33+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 34+ * SUCH DAMAGE.
 35+ */
 36+
 37+/*-
 38+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
 39+ * All rights reserved.
 40+ *
 41+ * Redistribution and use in source and binary forms, with or without
 42+ * modification, are permitted provided that the following conditions
 43+ * are met:
 44+ * 1. Redistributions of source code must retain the above copyright
 45+ *    notice, this list of conditions and the following disclaimer.
 46+ * 2. Redistributions in binary form must reproduce the above copyright
 47+ *    notice, this list of conditions and the following disclaimer in the
 48+ *    documentation and/or other materials provided with the distribution.
 49+ * 3. All advertising materials mentioning features or use of this software
 50+ *    must display the following acknowledgement:
 51+ *        This product includes software developed by the NetBSD
 52+ *        Foundation, Inc. and its contributors.
 53+ * 4. Neither the name of The NetBSD Foundation nor the names of its
 54+ *    contributors may be used to endorse or promote products derived
 55+ *    from this software without specific prior written permission.
 56+ *
 57+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 58+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 59+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 60+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 61+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 62+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 63+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 64+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 65+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 66+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 67+ * POSSIBILITY OF SUCH DAMAGE.
 68+ */
 69+
 70+#if HAVE_NBTOOL_CONFIG_H
 71+#include "nbtool_config.h"
 72+/*
 73+ * XXX Undefine the renames of these functions so that we don't
 74+ * XXX rename the versions found in the host's <pwd.h> by mistake!
 75+ */
 76+#undef group_from_gid
 77+#undef user_from_uid
 78+#endif
 79+
 80+#include <sys/cdefs.h>
 81+#if defined(LIBC_SCCS) && !defined(lint)
 82+#if 0
 83+static char sccsid[] = "@(#)cache.c	8.1 (Berkeley) 5/31/93";
 84+#else
 85+__RCSID("$NetBSD: pwcache.c,v 1.29 2004/06/20 22:20:14 jmc Exp $");
 86+#endif
 87+#endif /* LIBC_SCCS and not lint */
 88+
 89+#include "netcompat.h"
 90+#include "namespace.h"
 91+
 92+#include <sys/types.h>
 93+#include <sys/param.h>
 94+
 95+#include <assert.h>
 96+#include <grp.h>
 97+#include <pwd.h>
 98+#include <stdio.h>
 99+#include <stdlib.h>
100+#include <string.h>
101+#include <unistd.h>
102+
103+#ifdef __weak_alias
104+__weak_alias(user_from_uid,_user_from_uid)
105+__weak_alias(group_from_gid,_group_from_gid)
106+__weak_alias(pwcache_userdb,_pwcache_userdb)
107+__weak_alias(pwcache_groupdb,_pwcache_groupdb)
108+#endif
109+
110+#if !HAVE_PWCACHE_USERDB || HAVE_NBTOOL_CONFIG_H
111+#include "pwcache.h"
112+
113+/*
114+ * routines that control user, group, uid and gid caches (for the archive
115+ * member print routine).
116+ * IMPORTANT:
117+ * these routines cache BOTH hits and misses, a major performance improvement
118+ */
119+
120+/*
121+ * function pointers to various name lookup routines.
122+ * these may be changed as necessary.
123+ */
124+static	int		(*_pwcache_setgroupent)(int)		= setgroupent;
125+static	void		(*_pwcache_endgrent)(void)		= endgrent;
126+static	struct group *	(*_pwcache_getgrnam)(const char *)	= getgrnam;
127+static	struct group *	(*_pwcache_getgrgid)(gid_t)		= getgrgid;
128+static	int		(*_pwcache_setpassent)(int)		= setpassent;
129+static	void		(*_pwcache_endpwent)(void)		= endpwent;
130+static	struct passwd *	(*_pwcache_getpwnam)(const char *)	= getpwnam;
131+static	struct passwd *	(*_pwcache_getpwuid)(uid_t)		= getpwuid;
132+
133+/*
134+ * internal state
135+ */
136+static	int	pwopn;		/* is password file open */
137+static	int	gropn;		/* is group file open */
138+static	UIDC	**uidtb;	/* uid to name cache */
139+static	GIDC	**gidtb;	/* gid to name cache */
140+static	UIDC	**usrtb;	/* user name to uid cache */
141+static	GIDC	**grptb;	/* group name to gid cache */
142+
143+static	int	uidtb_fail;	/* uidtb_start() failed ? */
144+static	int	gidtb_fail;	/* gidtb_start() failed ? */
145+static	int	usrtb_fail;	/* usrtb_start() failed ? */
146+static	int	grptb_fail;	/* grptb_start() failed ? */
147+
148+
149+static	u_int	st_hash(const char *, size_t, int);
150+static	int	uidtb_start(void);
151+static	int	gidtb_start(void);
152+static	int	usrtb_start(void);
153+static	int	grptb_start(void);
154+
155+
156+static u_int
157+st_hash(const char *name, size_t len, int tabsz)
158+{
159+	u_int key = 0;
160+
161+	_DIAGASSERT(name != NULL);
162+
163+	while (len--) {
164+		key += *name++;
165+		key = (key << 8) | (key >> 24);
166+	}
167+
168+	return (key % tabsz);
169+}
170+
171+/*
172+ * uidtb_start
173+ *	creates an an empty uidtb
174+ * Return:
175+ *	0 if ok, -1 otherwise
176+ */
177+static int
178+uidtb_start(void)
179+{
180+
181+	if (uidtb != NULL)
182+		return (0);
183+	if (uidtb_fail)
184+		return (-1);
185+	if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
186+		++uidtb_fail;
187+		return (-1);
188+	}
189+	return (0);
190+}
191+
192+/*
193+ * gidtb_start
194+ *	creates an an empty gidtb
195+ * Return:
196+ *	0 if ok, -1 otherwise
197+ */
198+static int
199+gidtb_start(void)
200+{
201+
202+	if (gidtb != NULL)
203+		return (0);
204+	if (gidtb_fail)
205+		return (-1);
206+	if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
207+		++gidtb_fail;
208+		return (-1);
209+	}
210+	return (0);
211+}
212+
213+/*
214+ * usrtb_start
215+ *	creates an an empty usrtb
216+ * Return:
217+ *	0 if ok, -1 otherwise
218+ */
219+static int
220+usrtb_start(void)
221+{
222+
223+	if (usrtb != NULL)
224+		return (0);
225+	if (usrtb_fail)
226+		return (-1);
227+	if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
228+		++usrtb_fail;
229+		return (-1);
230+	}
231+	return (0);
232+}
233+
234+/*
235+ * grptb_start
236+ *	creates an an empty grptb
237+ * Return:
238+ *	0 if ok, -1 otherwise
239+ */
240+static int
241+grptb_start(void)
242+{
243+
244+	if (grptb != NULL)
245+		return (0);
246+	if (grptb_fail)
247+		return (-1);
248+	if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
249+		++grptb_fail;
250+		return (-1);
251+	}
252+	return (0);
253+}
254+
255+/*
256+ * user_from_uid()
257+ *	caches the name (if any) for the uid. If noname clear, we always
258+ *	return the stored name (if valid or invalid match).
259+ *	We use a simple hash table.
260+ * Return
261+ *	Pointer to stored name (or a empty string)
262+ */
263+const char *
264+user_from_uid(uid_t uid, int noname)
265+{
266+	struct passwd *pw;
267+	UIDC *ptr, **pptr;
268+
269+	if ((uidtb == NULL) && (uidtb_start() < 0))
270+		return (NULL);
271+
272+	/*
273+	 * see if we have this uid cached
274+	 */
275+	pptr = uidtb + (uid % UID_SZ);
276+	ptr = *pptr;
277+
278+	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
279+		/*
280+		 * have an entry for this uid
281+		 */
282+		if (!noname || (ptr->valid == VALID))
283+			return (ptr->name);
284+		return (NULL);
285+	}
286+
287+	/*
288+	 * No entry for this uid, we will add it
289+	 */
290+	if (!pwopn) {
291+		if (_pwcache_setpassent != NULL)
292+			(*_pwcache_setpassent)(1);
293+		++pwopn;
294+	}
295+
296+	if (ptr == NULL)
297+		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
298+
299+	if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) {
300+		/*
301+		 * no match for this uid in the local password file
302+		 * a string that is the uid in numeric format
303+		 */
304+		if (ptr == NULL)
305+			return (NULL);
306+		ptr->uid = uid;
307+		(void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
308+		ptr->valid = INVALID;
309+		if (noname)
310+			return (NULL);
311+	} else {
312+		/*
313+		 * there is an entry for this uid in the password file
314+		 */
315+		if (ptr == NULL)
316+			return (pw->pw_name);
317+		ptr->uid = uid;
318+		(void)strlcpy(ptr->name, pw->pw_name, UNMLEN);
319+		ptr->valid = VALID;
320+	}
321+	return (ptr->name);
322+}
323+
324+/*
325+ * group_from_gid()
326+ *	caches the name (if any) for the gid. If noname clear, we always
327+ *	return the stored name (if valid or invalid match).
328+ *	We use a simple hash table.
329+ * Return
330+ *	Pointer to stored name (or a empty string)
331+ */
332+const char *
333+group_from_gid(gid_t gid, int noname)
334+{
335+	struct group *gr;
336+	GIDC *ptr, **pptr;
337+
338+	if ((gidtb == NULL) && (gidtb_start() < 0))
339+		return (NULL);
340+
341+	/*
342+	 * see if we have this gid cached
343+	 */
344+	pptr = gidtb + (gid % GID_SZ);
345+	ptr = *pptr;
346+
347+	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
348+		/*
349+		 * have an entry for this gid
350+		 */
351+		if (!noname || (ptr->valid == VALID))
352+			return (ptr->name);
353+		return (NULL);
354+	}
355+
356+	/*
357+	 * No entry for this gid, we will add it
358+	 */
359+	if (!gropn) {
360+		if (_pwcache_setgroupent != NULL)
361+			(*_pwcache_setgroupent)(1);
362+		++gropn;
363+	}
364+
365+	if (ptr == NULL)
366+		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
367+
368+	if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) {
369+		/*
370+		 * no match for this gid in the local group file, put in
371+		 * a string that is the gid in numberic format
372+		 */
373+		if (ptr == NULL)
374+			return (NULL);
375+		ptr->gid = gid;
376+		(void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
377+		ptr->valid = INVALID;
378+		if (noname)
379+			return (NULL);
380+	} else {
381+		/*
382+		 * there is an entry for this group in the group file
383+		 */
384+		if (ptr == NULL)
385+			return (gr->gr_name);
386+		ptr->gid = gid;
387+		(void)strlcpy(ptr->name, gr->gr_name, GNMLEN);
388+		ptr->valid = VALID;
389+	}
390+	return (ptr->name);
391+}
392+
393+/*
394+ * uid_from_user()
395+ *	caches the uid for a given user name. We use a simple hash table.
396+ * Return
397+ *	the uid (if any) for a user name, or a -1 if no match can be found
398+ */
399+int
400+uid_from_user(const char *name, uid_t *uid)
401+{
402+	struct passwd *pw;
403+	UIDC *ptr, **pptr;
404+	size_t namelen;
405+
406+	/*
407+	 * return -1 for mangled names
408+	 */
409+	if (name == NULL || ((namelen = strlen(name)) == 0))
410+		return (-1);
411+	if ((usrtb == NULL) && (usrtb_start() < 0))
412+		return (-1);
413+
414+	/*
415+	 * look up in hash table, if found and valid return the uid,
416+	 * if found and invalid, return a -1
417+	 */
418+	pptr = usrtb + st_hash(name, namelen, UNM_SZ);
419+	ptr = *pptr;
420+
421+	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
422+		if (ptr->valid == INVALID)
423+			return (-1);
424+		*uid = ptr->uid;
425+		return (0);
426+	}
427+
428+	if (!pwopn) {
429+		if (_pwcache_setpassent != NULL)
430+			(*_pwcache_setpassent)(1);
431+		++pwopn;
432+	}
433+
434+	if (ptr == NULL)
435+		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
436+
437+	/*
438+	 * no match, look it up, if no match store it as an invalid entry,
439+	 * or store the matching uid
440+	 */
441+	if (ptr == NULL) {
442+		if ((pw = (*_pwcache_getpwnam)(name)) == NULL)
443+			return (-1);
444+		*uid = pw->pw_uid;
445+		return (0);
446+	}
447+	(void)strlcpy(ptr->name, name, UNMLEN);
448+	if ((pw = (*_pwcache_getpwnam)(name)) == NULL) {
449+		ptr->valid = INVALID;
450+		return (-1);
451+	}
452+	ptr->valid = VALID;
453+	*uid = ptr->uid = pw->pw_uid;
454+	return (0);
455+}
456+
457+/*
458+ * gid_from_group()
459+ *	caches the gid for a given group name. We use a simple hash table.
460+ * Return
461+ *	the gid (if any) for a group name, or a -1 if no match can be found
462+ */
463+int
464+gid_from_group(const char *name, gid_t *gid)
465+{
466+	struct group *gr;
467+	GIDC *ptr, **pptr;
468+	size_t namelen;
469+
470+	/*
471+	 * return -1 for mangled names
472+	 */
473+	if (name == NULL || ((namelen = strlen(name)) == 0))
474+		return (-1);
475+	if ((grptb == NULL) && (grptb_start() < 0))
476+		return (-1);
477+
478+	/*
479+	 * look up in hash table, if found and valid return the uid,
480+	 * if found and invalid, return a -1
481+	 */
482+	pptr = grptb + st_hash(name, namelen, GID_SZ);
483+	ptr = *pptr;
484+
485+	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
486+		if (ptr->valid == INVALID)
487+			return (-1);
488+		*gid = ptr->gid;
489+		return (0);
490+	}
491+
492+	if (!gropn) {
493+		if (_pwcache_setgroupent != NULL)
494+			(*_pwcache_setgroupent)(1);
495+		++gropn;
496+	}
497+
498+	if (ptr == NULL)
499+		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
500+
501+	/*
502+	 * no match, look it up, if no match store it as an invalid entry,
503+	 * or store the matching gid
504+	 */
505+	if (ptr == NULL) {
506+		if ((gr = (*_pwcache_getgrnam)(name)) == NULL)
507+			return (-1);
508+		*gid = gr->gr_gid;
509+		return (0);
510+	}
511+
512+	(void)strlcpy(ptr->name, name, GNMLEN);
513+	if ((gr = (*_pwcache_getgrnam)(name)) == NULL) {
514+		ptr->valid = INVALID;
515+		return (-1);
516+	}
517+	ptr->valid = VALID;
518+	*gid = ptr->gid = gr->gr_gid;
519+	return (0);
520+}
521+
522+#define FLUSHTB(arr, len, fail)				\
523+	do {						\
524+		if (arr != NULL) {			\
525+			for (i = 0; i < len; i++)	\
526+				if (arr[i] != NULL)	\
527+					free(arr[i]);	\
528+			arr = NULL;			\
529+		}					\
530+		fail = 0;				\
531+	} while (/* CONSTCOND */0);
532+
533+int
534+pwcache_userdb(
535+	int		(*a_setpassent)(int),
536+	void		(*a_endpwent)(void),
537+	struct passwd *	(*a_getpwnam)(const char *),
538+	struct passwd *	(*a_getpwuid)(uid_t))
539+{
540+	int i;
541+
542+		/* a_setpassent and a_endpwent may be NULL */
543+	if (a_getpwnam == NULL || a_getpwuid == NULL)
544+		return (-1);
545+
546+	if (_pwcache_endpwent != NULL)
547+		(*_pwcache_endpwent)();
548+	FLUSHTB(uidtb, UID_SZ, uidtb_fail);
549+	FLUSHTB(usrtb, UNM_SZ, usrtb_fail);
550+	pwopn = 0;
551+	_pwcache_setpassent = a_setpassent;
552+	_pwcache_endpwent = a_endpwent;
553+	_pwcache_getpwnam = a_getpwnam;
554+	_pwcache_getpwuid = a_getpwuid;
555+
556+	return (0);
557+}
558+
559+int
560+pwcache_groupdb(
561+	int		(*a_setgroupent)(int),
562+	void		(*a_endgrent)(void),
563+	struct group *	(*a_getgrnam)(const char *),
564+	struct group *	(*a_getgrgid)(gid_t))
565+{
566+	int i;
567+
568+		/* a_setgroupent and a_endgrent may be NULL */
569+	if (a_getgrnam == NULL || a_getgrgid == NULL)
570+		return (-1);
571+
572+	if (_pwcache_endgrent != NULL)
573+		(*_pwcache_endgrent)();
574+	FLUSHTB(gidtb, GID_SZ, gidtb_fail);
575+	FLUSHTB(grptb, GNM_SZ, grptb_fail);
576+	gropn = 0;
577+	_pwcache_setgroupent = a_setgroupent;
578+	_pwcache_endgrent = a_endgrent;
579+	_pwcache_getgrnam = a_getgrnam;
580+	_pwcache_getgrgid = a_getgrgid;
581+
582+	return (0);
583+}
584+
585+
586+#ifdef TEST_PWCACHE
587+
588+struct passwd *
589+test_getpwnam(const char *name)
590+{
591+	static struct passwd foo;
592+
593+	memset(&foo, 0, sizeof(foo));
594+	if (strcmp(name, "toor") == 0) {
595+		foo.pw_uid = 666;
596+		return &foo;
597+	}
598+	return (getpwnam(name));
599+}
600+
601+int
602+main(int argc, char *argv[])
603+{
604+	uid_t	u;
605+	int	r, i;
606+
607+	printf("pass 1 (default userdb)\n");
608+	for (i = 1; i < argc; i++) {
609+		printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
610+		    i, pwopn, usrtb_fail, usrtb);
611+		r = uid_from_user(argv[i], &u);
612+		if (r == -1)
613+			printf("  uid_from_user %s: failed\n", argv[i]);
614+		else
615+			printf("  uid_from_user %s: %d\n", argv[i], u);
616+	}
617+	printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n",
618+		    pwopn, usrtb_fail, usrtb);
619+
620+	puts("");
621+	printf("pass 2 (replacement userdb)\n");
622+	printf("pwcache_userdb returned %d\n",
623+	    pwcache_userdb(setpassent, test_getpwnam, getpwuid));
624+	printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb);
625+
626+	for (i = 1; i < argc; i++) {
627+		printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
628+		    i, pwopn, usrtb_fail, usrtb);
629+		u = -1;
630+		r = uid_from_user(argv[i], &u);
631+		if (r == -1)
632+			printf("  uid_from_user %s: failed\n", argv[i]);
633+		else
634+			printf("  uid_from_user %s: %d\n", argv[i], u);
635+	}
636+	printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n",
637+		    pwopn, usrtb_fail, usrtb);
638+
639+	puts("");
640+	printf("pass 3 (null pointers)\n");
641+	printf("pwcache_userdb returned %d\n",
642+	    pwcache_userdb(NULL, NULL, NULL));
643+
644+	return (0);
645+}
646+#endif	/* TEST_PWCACHE */
647+#endif	/* !HAVE_PWCACHE_USERDB */
+72, -0
 1@@ -0,0 +1,72 @@
 2+/*	$NetBSD: pwcache.h,v 1.5 2003/11/10 08:51:51 wiz Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 1992 Keith Muller.
 6+ * Copyright (c) 1992, 1993
 7+ *	The Regents of the University of California.  All rights reserved.
 8+ *
 9+ * This code is derived from software contributed to Berkeley by
10+ * Keith Muller of the University of California, San Diego.
11+ *
12+ * Redistribution and use in source and binary forms, with or without
13+ * modification, are permitted provided that the following conditions
14+ * are met:
15+ * 1. Redistributions of source code must retain the above copyright
16+ *    notice, this list of conditions and the following disclaimer.
17+ * 2. Redistributions in binary form must reproduce the above copyright
18+ *    notice, this list of conditions and the following disclaimer in the
19+ *    documentation and/or other materials provided with the distribution.
20+ * 3. Neither the name of the University nor the names of its contributors
21+ *    may be used to endorse or promote products derived from this software
22+ *    without specific prior written permission.
23+ *
24+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34+ * SUCH DAMAGE.
35+ *
36+ *      @(#)cache.h	8.1 (Berkeley) 5/31/93
37+ */
38+
39+/*
40+ * Constants and data structures used to implement group and password file
41+ * caches. Traditional passwd/group cache routines perform quite poorly with
42+ * archives. The chances of hitting a valid lookup with an archive is quite a
43+ * bit worse than with files already resident on the file system. These misses
44+ * create a MAJOR performance cost. To address this problem, these routines
45+ * cache both hits and misses.
46+ *
47+ * NOTE:  name lengths must be as large as those stored in ANY PROTOCOL and
48+ * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME
49+ */
50+#define UNMLEN		32	/* >= user name found in any protocol */
51+#define GNMLEN		32	/* >= group name found in any protocol */
52+#define UID_SZ		317	/* size of uid to user_name cache */
53+#define UNM_SZ		317	/* size of user_name to uid cache */
54+#define GID_SZ		251	/* size of gid to group_name cache */
55+#define GNM_SZ		251	/* size of group_name to gid cache */
56+#define VALID		1	/* entry and name are valid */
57+#define INVALID		2	/* entry valid, name NOT valid */
58+
59+/*
60+ * Node structures used in the user, group, uid, and gid caches.
61+ */
62+
63+typedef struct uidc {
64+	int valid;		/* is this a valid or a miss entry */
65+	char name[UNMLEN];	/* uid name */
66+	uid_t uid;		/* cached uid */
67+} UIDC;
68+
69+typedef struct gidc {
70+	int valid;		/* is this a valid or a miss entry */
71+	char name[GNMLEN];	/* gid name */
72+	gid_t gid;		/* cached gid */
73+} GIDC;
+12, -0
 1@@ -0,0 +1,12 @@
 2+/*	$NetBSD: setgroupent.c,v 1.4 2003/10/27 00:12:43 lukem Exp $	*/
 3+
 4+#include "nbtool_config.h"
 5+
 6+#if !HAVE_SETGROUPENT || !HAVE_DECL_SETGROUPENT
 7+#include <grp.h>
 8+
 9+int setgroupent(int stayopen) {
10+	setgrent();
11+	return 1;
12+}
13+#endif
+12, -0
 1@@ -0,0 +1,12 @@
 2+/*	$NetBSD: setpassent.c,v 1.4 2003/10/27 00:12:43 lukem Exp $	*/
 3+
 4+#include "nbtool_config.h"
 5+
 6+#if !HAVE_SETPASSENT || !HAVE_DECL_SETPASSENT
 7+#include <pwd.h>
 8+
 9+int setpassent(int stayopen) {
10+	setpwent();
11+	return 1;
12+}
13+#endif
+101, -0
  1@@ -0,0 +1,101 @@
  2+/*	$NetBSD: strlcat.c,v 1.5 2024/11/01 21:11:37 riastradh Exp $	*/
  3+/*	$OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp $	*/
  4+
  5+/*
  6+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
  7+ *
  8+ * Permission to use, copy, modify, and distribute this software for any
  9+ * purpose with or without fee is hereby granted, provided that the above
 10+ * copyright notice and this permission notice appear in all copies.
 11+ *
 12+ * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
 13+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 14+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
 15+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 16+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 17+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 18+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 19+ */
 20+
 21+#if !defined(_KERNEL) && !defined(_STANDALONE)
 22+#if HAVE_NBTOOL_CONFIG_H
 23+#include "nbtool_config.h"
 24+#endif
 25+
 26+#include <sys/cdefs.h>
 27+#if defined(LIBC_SCCS) && !defined(lint)
 28+__RCSID("$NetBSD: strlcat.c,v 1.5 2024/11/01 21:11:37 riastradh Exp $");
 29+#endif /* LIBC_SCCS and not lint */
 30+
 31+#ifdef _LIBC
 32+#include "namespace.h"
 33+#endif
 34+#include <sys/types.h>
 35+#include <assert.h>
 36+#include <string.h>
 37+
 38+#ifdef _LIBC
 39+# ifdef __weak_alias
 40+__weak_alias(strlcat, _strlcat)
 41+# endif
 42+#endif
 43+
 44+#else
 45+#include <lib/libkern/libkern.h>
 46+#endif /* !_KERNEL && !_STANDALONE */
 47+
 48+#if !HAVE_STRLCAT
 49+/*
 50+ * Appends src to string dst of size siz (unlike strncat, siz is the
 51+ * full size of dst, not space left).  At most siz-1 characters
 52+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
 53+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
 54+ * If retval >= siz, truncation occurred.
 55+ */
 56+size_t
 57+strlcat(char *__restrict dst, const char *__restrict src, size_t siz)
 58+{
 59+#if 1
 60+	char *d = dst;
 61+	const char *s = src;
 62+	size_t n = siz;
 63+	size_t dlen;
 64+
 65+	_DIAGASSERT(dst != NULL);
 66+	_DIAGASSERT(src != NULL);
 67+
 68+	/* Find the end of dst and adjust bytes left but don't go past end */
 69+	while (n-- != 0 && *d != '\0')
 70+		d++;
 71+	dlen = d - dst;
 72+	n = siz - dlen;
 73+
 74+	if (n == 0)
 75+		return(dlen + strlen(s));
 76+	while (*s != '\0') {
 77+		if (n != 1) {
 78+			*d++ = *s;
 79+			n--;
 80+		}
 81+		s++;
 82+	}
 83+	*d = '\0';
 84+
 85+	return(dlen + (s - src));	/* count does not include NUL */
 86+#else
 87+	_DIAGASSERT(dst != NULL);
 88+	_DIAGASSERT(src != NULL);
 89+
 90+	/*
 91+	 * Find length of string in dst (maxing out at siz).
 92+	 */
 93+	size_t dlen = strnlen(dst, siz);
 94+
 95+	/*
 96+	 * Copy src into any remaining space in dst (truncating if needed).
 97+	 * Note strlcpy(dst, src, 0) returns strlen(src).
 98+	 */
 99+	return dlen + strlcpy(dst + dlen, src, siz - dlen);
100+#endif
101+}
102+#endif
+250, -0
  1@@ -0,0 +1,250 @@
  2+/*	$NetBSD: strsuftoll.c,v 1.6 2004/03/05 05:58:29 lukem Exp $	*/
  3+/*-
  4+ * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
  5+ * All rights reserved.
  6+ *
  7+ * This code is derived from software contributed to The NetBSD Foundation
  8+ * by Luke Mewburn.
  9+ *
 10+ * Redistribution and use in source and binary forms, with or without
 11+ * modification, are permitted provided that the following conditions
 12+ * are met:
 13+ * 1. Redistributions of source code must retain the above copyright
 14+ *    notice, this list of conditions and the following disclaimer.
 15+ * 2. Redistributions in binary form must reproduce the above copyright
 16+ *    notice, this list of conditions and the following disclaimer in the
 17+ *    documentation and/or other materials provided with the distribution.
 18+ * 3. All advertising materials mentioning features or use of this software
 19+ *    must display the following acknowledgement:
 20+ *        This product includes software developed by the NetBSD
 21+ *        Foundation, Inc. and its contributors.
 22+ * 4. Neither the name of The NetBSD Foundation nor the names of its
 23+ *    contributors may be used to endorse or promote products derived
 24+ *    from this software without specific prior written permission.
 25+ *
 26+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 27+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 28+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 29+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 30+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 31+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 32+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 33+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 34+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 35+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 36+ * POSSIBILITY OF SUCH DAMAGE.
 37+ */
 38+/*-
 39+ * Copyright (c) 1991, 1993, 1994
 40+ *	The Regents of the University of California.  All rights reserved.
 41+ *
 42+ * This code is derived from software contributed to Berkeley by
 43+ * Keith Muller of the University of California, San Diego and Lance
 44+ * Visser of Convex Computer Corporation.
 45+ *
 46+ * Redistribution and use in source and binary forms, with or without
 47+ * modification, are permitted provided that the following conditions
 48+ * are met:
 49+ * 1. Redistributions of source code must retain the above copyright
 50+ *    notice, this list of conditions and the following disclaimer.
 51+ * 2. Redistributions in binary form must reproduce the above copyright
 52+ *    notice, this list of conditions and the following disclaimer in the
 53+ *    documentation and/or other materials provided with the distribution.
 54+ * 3. Neither the name of the University nor the names of its contributors
 55+ *    may be used to endorse or promote products derived from this software
 56+ *    without specific prior written permission.
 57+ *
 58+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 59+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 60+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 61+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 62+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 63+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 64+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 65+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 66+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 67+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 68+ * SUCH DAMAGE.
 69+ */
 70+
 71+#if HAVE_NBTOOL_CONFIG_H
 72+#include "nbtool_config.h"
 73+#endif
 74+
 75+#include <sys/cdefs.h>
 76+
 77+#if defined(LIBC_SCCS) && !defined(lint)
 78+__RCSID("$NetBSD: strsuftoll.c,v 1.6 2004/03/05 05:58:29 lukem Exp $");
 79+#endif /* LIBC_SCCS and not lint */
 80+
 81+#ifdef _LIBC
 82+#include "namespace.h"
 83+#endif
 84+
 85+#if !HAVE_STRSUFTOLL
 86+
 87+#include <sys/types.h>
 88+#include <sys/time.h>
 89+
 90+#include <assert.h>
 91+#include <ctype.h>
 92+#include <err.h>
 93+#include <errno.h>
 94+#include <limits.h>
 95+#include <stdio.h>
 96+#include <stdlib.h>
 97+#include <string.h>
 98+
 99+#ifdef _LIBC
100+# ifdef __weak_alias
101+__weak_alias(strsuftoll, _strsuftoll)
102+__weak_alias(strsuftollx, _strsuftollx)
103+# endif
104+#endif /* LIBC */
105+
106+long long strsuftollx(const char *, const char *, long long, long long,
107+    char *, size_t);
108+
109+/*
110+ * Convert an expression of the following forms to a (u)int64_t.
111+ * 	1) A positive decimal number.
112+ *	2) A positive decimal number followed by a b (mult by 512).
113+ *	3) A positive decimal number followed by a k (mult by 1024).
114+ *	4) A positive decimal number followed by a m (mult by 1048576).
115+ *	5) A positive decimal number followed by a g (mult by 1073741824).
116+ *	6) A positive decimal number followed by a t (mult by 1099511627776).
117+ *	7) A positive decimal number followed by a w (mult by sizeof int)
118+ *	8) Two or more positive decimal numbers (with/without k,b or w).
119+ *	   separated by x (also * for backwards compatibility), specifying
120+ *	   the product of the indicated values.
121+ * Returns the result upon successful conversion, or exits with an
122+ * appropriate error.
123+ * 
124+ */
125+/* LONGLONG */
126+long long
127+strsuftoll(const char *desc, const char *val,
128+    long long min, long long max)
129+{
130+	long long result;
131+	char	errbuf[100];
132+
133+	result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf));
134+	if (*errbuf != '\0')
135+		errx(1, "%s", errbuf);
136+	return (result);
137+}
138+
139+/*
140+ * As strsuftoll(), but returns the error message into the provided buffer
141+ * rather than exiting with it.
142+ */
143+/* LONGLONG */
144+long long
145+strsuftollx(const char *desc, const char *val,
146+    long long min, long long max, char *ebuf, size_t ebuflen)
147+{
148+	long long num, t;
149+	char	*expr;
150+
151+	_DIAGASSERT(desc != NULL);
152+	_DIAGASSERT(val != NULL);
153+	_DIAGASSERT(ebuf != NULL);
154+
155+	errno = 0;
156+	ebuf[0] = '\0';
157+
158+	while (isspace((unsigned char)*val))	/* Skip leading space */
159+		val++;
160+
161+	num = strtoll(val, &expr, 10);
162+	if (errno == ERANGE)
163+		goto erange;			/* Overflow */
164+
165+	if (expr == val)			/* No digits */
166+		goto badnum;
167+
168+	switch (*expr) {
169+	case 'b':
170+		t = num;
171+		num *= 512;			/* 1 block */
172+		if (t > num)
173+			goto erange;
174+		++expr;
175+		break;
176+	case 'k':
177+		t = num;
178+		num *= 1024;			/* 1 kilobyte */
179+		if (t > num)
180+			goto erange;
181+		++expr;
182+		break;
183+	case 'm':
184+		t = num;
185+		num *= 1048576;			/* 1 megabyte */
186+		if (t > num)
187+			goto erange;
188+		++expr;
189+		break;
190+	case 'g':
191+		t = num;
192+		num *= 1073741824;		/* 1 gigabyte */
193+		if (t > num)
194+			goto erange;
195+		++expr;
196+		break;
197+	case 't':
198+		t = num;
199+		num *= 1099511627776LL;		/* 1 terabyte */
200+		if (t > num)
201+			goto erange;
202+		++expr;
203+		break;
204+	case 'w':
205+		t = num;
206+		num *= sizeof(int);		/* 1 word */
207+		if (t > num)
208+			goto erange;
209+		++expr;
210+		break;
211+	}
212+
213+	switch (*expr) {
214+	case '\0':
215+		break;
216+	case '*':				/* Backward compatible */
217+	case 'x':
218+		t = num;
219+		num *= strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen);
220+		if (*ebuf != '\0')
221+			return (0);
222+		if (t > num) {
223+ erange:	 	
224+			snprintf(ebuf, ebuflen,
225+			    "%s: %s", desc, strerror(ERANGE));
226+			return (0);
227+		}
228+		break;
229+	default:
230+ badnum:	snprintf(ebuf, ebuflen,
231+		    "%s `%s': illegal number", desc, val);
232+		return (0);
233+	}
234+	if (num < min) {
235+			/* LONGLONG */
236+		snprintf(ebuf, ebuflen, "%s %lld is less than %lld.",
237+		    desc, (long long)num, (long long)min);
238+		return (0);
239+	}
240+	if (num > max) {
241+			/* LONGLONG */
242+		snprintf(ebuf, ebuflen,
243+		    "%s %lld is greater than %lld.",
244+		    desc, (long long)num, (long long)max);
245+		return (0);
246+	}
247+	*ebuf = '\0';
248+	return (num);
249+}
250+
251+#endif /* !HAVE_STRSUFTOLL */
+69, -0
 1@@ -0,0 +1,69 @@
 2+/*	$NetBSD: strtou.c,v 1.3 2019/11/28 12:33:23 roy Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 2005 The DragonFly Project.  All rights reserved.
 6+ * Copyright (c) 2003 Citrus Project,
 7+ * All rights reserved.
 8+ *
 9+ * Redistribution and use in source and binary forms, with or without
10+ * modification, are permitted provided that the following conditions
11+ * are met:
12+ * 1. Redistributions of source code must retain the above copyright
13+ *    notice, this list of conditions and the following disclaimer.
14+ * 2. Redistributions in binary form must reproduce the above copyright
15+ *    notice, this list of conditions and the following disclaimer in the
16+ *    documentation and/or other materials provided with the distribution.
17+ *
18+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28+ * SUCH DAMAGE.
29+ *
30+ * Created by Kamil Rytarowski, based on ID:
31+ * NetBSD: src/common/lib/libc/stdlib/strtoul.c,v 1.3 2008/08/20 19:58:34 oster Exp
32+ */
33+
34+#if defined(HAVE_NBTOOL_CONFIG_H) && HAVE_NBTOOL_CONFIG_H
35+#include "nbtool_config.h"
36+#endif
37+
38+#include <sys/cdefs.h>
39+__RCSID("$NetBSD: strtou.c,v 1.3 2019/11/28 12:33:23 roy Exp $");
40+
41+#ifdef _LIBC
42+#include "namespace.h"
43+#endif
44+
45+#if defined(_KERNEL)
46+#include <sys/param.h>
47+#include <sys/types.h>
48+#include <lib/libkern/libkern.h>
49+#elif defined(_STANDALONE)
50+#include <sys/param.h>
51+#include <sys/types.h>
52+#include <lib/libkern/libkern.h>
53+#include <lib/libsa/stand.h>
54+#else
55+#include <stddef.h>
56+#include <assert.h>
57+#include <errno.h>
58+#include <inttypes.h>
59+#endif
60+
61+#define	_FUNCNAME	strtou
62+#define	__TYPE		uintmax_t
63+#define	__WRAPPED	strtoumax
64+
65+#include "_strtoi.h"
66+
67+#ifdef _LIBC
68+__weak_alias(strtou, _strtou)
69+__weak_alias(strtou_l, _strtou_l)
70+#endif
+61, -0
 1@@ -40,4 +40,65 @@
 2 #define __THROW
 3 #endif
 4 
 5+#ifndef __P
 6+#define __P(x) x
 7+#endif
 8+
 9+#ifndef __RENAME
10+#define __RENAME(x)
11+#endif
12+
13+#ifndef __packed
14+#if defined(__GNUC__) || defined(__clang__)
15+#define __packed __attribute__((__packed__))
16+#else
17+#define __packed
18+#endif
19+#endif
20+
21+#ifndef __constfunc
22+#if defined(__GNUC__) || defined(__clang__)
23+#define __constfunc __attribute__((__const__))
24+#else
25+#define __constfunc
26+#endif
27+#endif
28+
29+#ifndef __RCSID
30+#define __RCSID(x)
31+#endif
32+
33+#ifndef __COPYRIGHT
34+#define __COPYRIGHT(x)
35+#endif
36+
37+#ifndef __dead
38+#if defined(__GNUC__) || defined(__clang__)
39+#define __dead __attribute__((__noreturn__))
40+#else
41+#define __dead
42+#endif
43+#endif
44+
45+#ifndef __printflike
46+#if defined(__GNUC__) || defined(__clang__)
47+#define __printflike(fmtarg, firstvararg) \
48+	__attribute__((__format__(__printf__, fmtarg, firstvararg)))
49+#else
50+#define __printflike(fmtarg, firstvararg)
51+#endif
52+#endif
53+
54+#ifndef __predict_false
55+#define __predict_false(exp) (exp)
56+#endif
57+
58+#ifndef __predict_true
59+#define __predict_true(exp) (exp)
60+#endif
61+
62+#ifndef _DIAGASSERT
63+#define _DIAGASSERT(exp) ((void)0)
64+#endif
65+
66 #endif
+140, -0
  1@@ -0,0 +1,140 @@
  2+/*	$NetBSD: endian.h,v 1.39 2026/01/08 15:39:08 nia Exp $	*/
  3+
  4+#ifndef _SYS_ENDIAN_H_
  5+#define _SYS_ENDIAN_H_
  6+
  7+#include <stdint.h>
  8+#include <endian.h>
  9+#include <byteswap.h>
 10+#include <sys/cdefs.h>
 11+
 12+#define	_LITTLE_ENDIAN	1234
 13+#define	_BIG_ENDIAN	4321
 14+#define	_PDP_ENDIAN	3412
 15+
 16+#ifndef LITTLE_ENDIAN
 17+#define	LITTLE_ENDIAN	_LITTLE_ENDIAN
 18+#endif
 19+#ifndef BIG_ENDIAN
 20+#define	BIG_ENDIAN	_BIG_ENDIAN
 21+#endif
 22+#ifndef PDP_ENDIAN
 23+#define	PDP_ENDIAN	_PDP_ENDIAN
 24+#endif
 25+
 26+#ifndef BYTE_ORDER
 27+#if __BYTE_ORDER == __LITTLE_ENDIAN
 28+#define	BYTE_ORDER	LITTLE_ENDIAN
 29+#else
 30+#define	BYTE_ORDER	BIG_ENDIAN
 31+#endif
 32+#endif
 33+
 34+#ifndef bswap16
 35+#define bswap16 bswap_16
 36+#endif
 37+
 38+#ifndef bswap32
 39+#define bswap32 bswap_32
 40+#endif
 41+
 42+#ifndef bswap64
 43+#define bswap64 bswap_64
 44+#endif
 45+
 46+#if BYTE_ORDER == BIG_ENDIAN
 47+#ifndef htobe16
 48+#define htobe16(x)	__CAST(uint16_t, (x))
 49+#endif
 50+#ifndef htobe32
 51+#define htobe32(x)	__CAST(uint32_t, (x))
 52+#endif
 53+#ifndef htobe64
 54+#define htobe64(x)	__CAST(uint64_t, (x))
 55+#endif
 56+#ifndef htole16
 57+#define htole16(x)	bswap16(__CAST(uint16_t, (x)))
 58+#endif
 59+#ifndef htole32
 60+#define htole32(x)	bswap32(__CAST(uint32_t, (x)))
 61+#endif
 62+#ifndef htole64
 63+#define htole64(x)	bswap64(__CAST(uint64_t, (x)))
 64+#endif
 65+#else
 66+#ifndef htobe16
 67+#define htobe16(x)	bswap16(__CAST(uint16_t, (x)))
 68+#endif
 69+#ifndef htobe32
 70+#define htobe32(x)	bswap32(__CAST(uint32_t, (x)))
 71+#endif
 72+#ifndef htobe64
 73+#define htobe64(x)	bswap64(__CAST(uint64_t, (x)))
 74+#endif
 75+#ifndef htole16
 76+#define htole16(x)	__CAST(uint16_t, (x))
 77+#endif
 78+#ifndef htole32
 79+#define htole32(x)	__CAST(uint32_t, (x))
 80+#endif
 81+#ifndef htole64
 82+#define htole64(x)	__CAST(uint64_t, (x))
 83+#endif
 84+#endif
 85+
 86+#ifndef be16toh
 87+#define be16toh(x)	htobe16(x)
 88+#endif
 89+#ifndef be32toh
 90+#define be32toh(x)	htobe32(x)
 91+#endif
 92+#ifndef be64toh
 93+#define be64toh(x)	htobe64(x)
 94+#endif
 95+#ifndef le16toh
 96+#define le16toh(x)	htole16(x)
 97+#endif
 98+#ifndef le32toh
 99+#define le32toh(x)	htole32(x)
100+#endif
101+#ifndef le64toh
102+#define le64toh(x)	htole64(x)
103+#endif
104+
105+static __inline uint16_t NETCOMPAT_UNUSED
106+be16dec(const void *_buf)
107+{
108+	uint16_t u;
109+
110+	__builtin_memcpy(&u, _buf, sizeof(u));
111+	return be16toh(u);
112+}
113+
114+static __inline uint16_t NETCOMPAT_UNUSED
115+le16dec(const void *_buf)
116+{
117+	uint16_t u;
118+
119+	__builtin_memcpy(&u, _buf, sizeof(u));
120+	return le16toh(u);
121+}
122+
123+static __inline uint32_t NETCOMPAT_UNUSED
124+be32dec(const void *_buf)
125+{
126+	uint32_t u;
127+
128+	__builtin_memcpy(&u, _buf, sizeof(u));
129+	return be32toh(u);
130+}
131+
132+static __inline uint32_t NETCOMPAT_UNUSED
133+le32dec(const void *_buf)
134+{
135+	uint32_t u;
136+
137+	__builtin_memcpy(&u, _buf, sizeof(u));
138+	return le32toh(u);
139+}
140+
141+#endif
+612, -0
  1@@ -0,0 +1,612 @@
  2+/*	$NetBSD: queue.h,v 1.39 2004/04/18 14:25:34 lukem Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1991, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. Neither the name of the University nor the names of its contributors
 17+ *    may be used to endorse or promote products derived from this software
 18+ *    without specific prior written permission.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 30+ * SUCH DAMAGE.
 31+ *
 32+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
 33+ */
 34+
 35+#ifndef	_SYS_QUEUE_H_
 36+#define	_SYS_QUEUE_H_
 37+
 38+/*
 39+ * This file defines five types of data structures: singly-linked lists,
 40+ * lists, simple queues, tail queues, and circular queues.
 41+ *
 42+ * A singly-linked list is headed by a single forward pointer. The
 43+ * elements are singly linked for minimum space and pointer manipulation
 44+ * overhead at the expense of O(n) removal for arbitrary elements. New
 45+ * elements can be added to the list after an existing element or at the
 46+ * head of the list.  Elements being removed from the head of the list
 47+ * should use the explicit macro for this purpose for optimum
 48+ * efficiency. A singly-linked list may only be traversed in the forward
 49+ * direction.  Singly-linked lists are ideal for applications with large
 50+ * datasets and few or no removals or for implementing a LIFO queue.
 51+ *
 52+ * A list is headed by a single forward pointer (or an array of forward
 53+ * pointers for a hash table header). The elements are doubly linked
 54+ * so that an arbitrary element can be removed without a need to
 55+ * traverse the list. New elements can be added to the list before
 56+ * or after an existing element or at the head of the list. A list
 57+ * may only be traversed in the forward direction.
 58+ *
 59+ * A simple queue is headed by a pair of pointers, one the head of the
 60+ * list and the other to the tail of the list. The elements are singly
 61+ * linked to save space, so only elements can only be removed from the
 62+ * head of the list. New elements can be added to the list after
 63+ * an existing element, at the head of the list, or at the end of the
 64+ * list. A simple queue may only be traversed in the forward direction.
 65+ *
 66+ * A tail queue is headed by a pair of pointers, one to the head of the
 67+ * list and the other to the tail of the list. The elements are doubly
 68+ * linked so that an arbitrary element can be removed without a need to
 69+ * traverse the list. New elements can be added to the list before or
 70+ * after an existing element, at the head of the list, or at the end of
 71+ * the list. A tail queue may be traversed in either direction.
 72+ *
 73+ * A circle queue is headed by a pair of pointers, one to the head of the
 74+ * list and the other to the tail of the list. The elements are doubly
 75+ * linked so that an arbitrary element can be removed without a need to
 76+ * traverse the list. New elements can be added to the list before or after
 77+ * an existing element, at the head of the list, or at the end of the list.
 78+ * A circle queue may be traversed in either direction, but has a more
 79+ * complex end of list detection.
 80+ *
 81+ * For details on the use of these macros, see the queue(3) manual page.
 82+ */
 83+
 84+/*
 85+ * List definitions.
 86+ */
 87+#define	LIST_HEAD(name, type)						\
 88+struct name {								\
 89+	struct type *lh_first;	/* first element */			\
 90+}
 91+
 92+#define	LIST_HEAD_INITIALIZER(head)					\
 93+	{ NULL }
 94+
 95+#define	LIST_ENTRY(type)						\
 96+struct {								\
 97+	struct type *le_next;	/* next element */			\
 98+	struct type **le_prev;	/* address of previous next element */	\
 99+}
100+
101+/*
102+ * List functions.
103+ */
104+#if defined(_KERNEL) && defined(QUEUEDEBUG)
105+#define	QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)			\
106+	if ((head)->lh_first &&						\
107+	    (head)->lh_first->field.le_prev != &(head)->lh_first)	\
108+		panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
109+#define	QUEUEDEBUG_LIST_OP(elm, field)					\
110+	if ((elm)->field.le_next &&					\
111+	    (elm)->field.le_next->field.le_prev !=			\
112+	    &(elm)->field.le_next)					\
113+		panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
114+	if (*(elm)->field.le_prev != (elm))				\
115+		panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__);
116+#define	QUEUEDEBUG_LIST_POSTREMOVE(elm, field)				\
117+	(elm)->field.le_next = (void *)1L;				\
118+	(elm)->field.le_prev = (void *)1L;
119+#else
120+#define	QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
121+#define	QUEUEDEBUG_LIST_OP(elm, field)
122+#define	QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
123+#endif
124+
125+#define	LIST_INIT(head) do {						\
126+	(head)->lh_first = NULL;					\
127+} while (/*CONSTCOND*/0)
128+
129+#define	LIST_INSERT_AFTER(listelm, elm, field) do {			\
130+	QUEUEDEBUG_LIST_OP((listelm), field)				\
131+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
132+		(listelm)->field.le_next->field.le_prev =		\
133+		    &(elm)->field.le_next;				\
134+	(listelm)->field.le_next = (elm);				\
135+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
136+} while (/*CONSTCOND*/0)
137+
138+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
139+	QUEUEDEBUG_LIST_OP((listelm), field)				\
140+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
141+	(elm)->field.le_next = (listelm);				\
142+	*(listelm)->field.le_prev = (elm);				\
143+	(listelm)->field.le_prev = &(elm)->field.le_next;		\
144+} while (/*CONSTCOND*/0)
145+
146+#define	LIST_INSERT_HEAD(head, elm, field) do {				\
147+	QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field)		\
148+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
149+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
150+	(head)->lh_first = (elm);					\
151+	(elm)->field.le_prev = &(head)->lh_first;			\
152+} while (/*CONSTCOND*/0)
153+
154+#define	LIST_REMOVE(elm, field) do {					\
155+	QUEUEDEBUG_LIST_OP((elm), field)				\
156+	if ((elm)->field.le_next != NULL)				\
157+		(elm)->field.le_next->field.le_prev = 			\
158+		    (elm)->field.le_prev;				\
159+	*(elm)->field.le_prev = (elm)->field.le_next;			\
160+	QUEUEDEBUG_LIST_POSTREMOVE((elm), field)			\
161+} while (/*CONSTCOND*/0)
162+
163+#define	LIST_FOREACH(var, head, field)					\
164+	for ((var) = ((head)->lh_first);				\
165+		(var);							\
166+		(var) = ((var)->field.le_next))
167+
168+/*
169+ * List access methods.
170+ */
171+#define	LIST_EMPTY(head)		((head)->lh_first == NULL)
172+#define	LIST_FIRST(head)		((head)->lh_first)
173+#define	LIST_NEXT(elm, field)		((elm)->field.le_next)
174+
175+
176+/*
177+ * Singly-linked List definitions.
178+ */
179+#define	SLIST_HEAD(name, type)						\
180+struct name {								\
181+	struct type *slh_first;	/* first element */			\
182+}
183+
184+#define	SLIST_HEAD_INITIALIZER(head)					\
185+	{ NULL }
186+ 
187+#define	SLIST_ENTRY(type)						\
188+struct {								\
189+	struct type *sle_next;	/* next element */			\
190+}
191+ 
192+/*
193+ * Singly-linked List functions.
194+ */
195+#define	SLIST_INIT(head) do {						\
196+	(head)->slh_first = NULL;					\
197+} while (/*CONSTCOND*/0)
198+
199+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
200+	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
201+	(slistelm)->field.sle_next = (elm);				\
202+} while (/*CONSTCOND*/0)
203+
204+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
205+	(elm)->field.sle_next = (head)->slh_first;			\
206+	(head)->slh_first = (elm);					\
207+} while (/*CONSTCOND*/0)
208+
209+#define	SLIST_REMOVE_HEAD(head, field) do {				\
210+	(head)->slh_first = (head)->slh_first->field.sle_next;		\
211+} while (/*CONSTCOND*/0)
212+
213+#define	SLIST_REMOVE(head, elm, type, field) do {			\
214+	if ((head)->slh_first == (elm)) {				\
215+		SLIST_REMOVE_HEAD((head), field);			\
216+	}								\
217+	else {								\
218+		struct type *curelm = (head)->slh_first;		\
219+		while(curelm->field.sle_next != (elm))			\
220+			curelm = curelm->field.sle_next;		\
221+		curelm->field.sle_next =				\
222+		    curelm->field.sle_next->field.sle_next;		\
223+	}								\
224+} while (/*CONSTCOND*/0)
225+
226+#define	SLIST_FOREACH(var, head, field)					\
227+	for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
228+
229+/*
230+ * Singly-linked List access methods.
231+ */
232+#define	SLIST_EMPTY(head)	((head)->slh_first == NULL)
233+#define	SLIST_FIRST(head)	((head)->slh_first)
234+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
235+
236+
237+/*
238+ * Singly-linked Tail queue declarations.
239+ */
240+#define	STAILQ_HEAD(name, type)					\
241+struct name {								\
242+	struct type *stqh_first;	/* first element */			\
243+	struct type **stqh_last;	/* addr of last next element */		\
244+}
245+
246+#define	STAILQ_HEAD_INITIALIZER(head)					\
247+	{ NULL, &(head).stqh_first }
248+
249+#define	STAILQ_ENTRY(type)						\
250+struct {								\
251+	struct type *stqe_next;	/* next element */			\
252+}
253+
254+/*
255+ * Singly-linked Tail queue functions.
256+ */
257+#define	STAILQ_INIT(head) do {						\
258+	(head)->stqh_first = NULL;					\
259+	(head)->stqh_last = &(head)->stqh_first;				\
260+} while (/*CONSTCOND*/0)
261+
262+#define	STAILQ_INSERT_HEAD(head, elm, field) do {			\
263+	if (((elm)->field.stqe_next = (head)->stqh_first) == NULL)	\
264+		(head)->stqh_last = &(elm)->field.stqe_next;		\
265+	(head)->stqh_first = (elm);					\
266+} while (/*CONSTCOND*/0)
267+
268+#define	STAILQ_INSERT_TAIL(head, elm, field) do {			\
269+	(elm)->field.stqe_next = NULL;					\
270+	*(head)->stqh_last = (elm);					\
271+	(head)->stqh_last = &(elm)->field.stqe_next;			\
272+} while (/*CONSTCOND*/0)
273+
274+#define	STAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
275+	if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
276+		(head)->stqh_last = &(elm)->field.stqe_next;		\
277+	(listelm)->field.stqe_next = (elm);				\
278+} while (/*CONSTCOND*/0)
279+
280+#define	STAILQ_REMOVE_HEAD(head, field) do {				\
281+	if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
282+		(head)->stqh_last = &(head)->stqh_first;			\
283+} while (/*CONSTCOND*/0)
284+
285+#define	STAILQ_REMOVE(head, elm, type, field) do {			\
286+	if ((head)->stqh_first == (elm)) {				\
287+		STAILQ_REMOVE_HEAD((head), field);			\
288+	} else {							\
289+		struct type *curelm = (head)->stqh_first;		\
290+		while (curelm->field.stqe_next != (elm))			\
291+			curelm = curelm->field.stqe_next;		\
292+		if ((curelm->field.stqe_next =				\
293+			curelm->field.stqe_next->field.stqe_next) == NULL) \
294+			    (head)->stqh_last = &(curelm)->field.stqe_next; \
295+	}								\
296+} while (/*CONSTCOND*/0)
297+
298+#define	STAILQ_FOREACH(var, head, field)				\
299+	for ((var) = ((head)->stqh_first);				\
300+		(var);							\
301+		(var) = ((var)->field.stqe_next))
302+
303+/*
304+ * Singly-linked Tail queue access methods.
305+ */
306+#define	STAILQ_EMPTY(head)	((head)->stqh_first == NULL)
307+#define	STAILQ_FIRST(head)	((head)->stqh_first)
308+#define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
309+
310+
311+/*
312+ * Simple queue definitions.
313+ */
314+#define	SIMPLEQ_HEAD(name, type)					\
315+struct name {								\
316+	struct type *sqh_first;	/* first element */			\
317+	struct type **sqh_last;	/* addr of last next element */		\
318+}
319+
320+#define	SIMPLEQ_HEAD_INITIALIZER(head)					\
321+	{ NULL, &(head).sqh_first }
322+
323+#define	SIMPLEQ_ENTRY(type)						\
324+struct {								\
325+	struct type *sqe_next;	/* next element */			\
326+}
327+
328+/*
329+ * Simple queue functions.
330+ */
331+#define	SIMPLEQ_INIT(head) do {						\
332+	(head)->sqh_first = NULL;					\
333+	(head)->sqh_last = &(head)->sqh_first;				\
334+} while (/*CONSTCOND*/0)
335+
336+#define	SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
337+	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\
338+		(head)->sqh_last = &(elm)->field.sqe_next;		\
339+	(head)->sqh_first = (elm);					\
340+} while (/*CONSTCOND*/0)
341+
342+#define	SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
343+	(elm)->field.sqe_next = NULL;					\
344+	*(head)->sqh_last = (elm);					\
345+	(head)->sqh_last = &(elm)->field.sqe_next;			\
346+} while (/*CONSTCOND*/0)
347+
348+#define	SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
349+	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
350+		(head)->sqh_last = &(elm)->field.sqe_next;		\
351+	(listelm)->field.sqe_next = (elm);				\
352+} while (/*CONSTCOND*/0)
353+
354+#define	SIMPLEQ_REMOVE_HEAD(head, field) do {				\
355+	if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
356+		(head)->sqh_last = &(head)->sqh_first;			\
357+} while (/*CONSTCOND*/0)
358+
359+#define	SIMPLEQ_REMOVE(head, elm, type, field) do {			\
360+	if ((head)->sqh_first == (elm)) {				\
361+		SIMPLEQ_REMOVE_HEAD((head), field);			\
362+	} else {							\
363+		struct type *curelm = (head)->sqh_first;		\
364+		while (curelm->field.sqe_next != (elm))			\
365+			curelm = curelm->field.sqe_next;		\
366+		if ((curelm->field.sqe_next =				\
367+			curelm->field.sqe_next->field.sqe_next) == NULL) \
368+			    (head)->sqh_last = &(curelm)->field.sqe_next; \
369+	}								\
370+} while (/*CONSTCOND*/0)
371+
372+#define	SIMPLEQ_FOREACH(var, head, field)				\
373+	for ((var) = ((head)->sqh_first);				\
374+		(var);							\
375+		(var) = ((var)->field.sqe_next))
376+
377+/*
378+ * Simple queue access methods.
379+ */
380+#define	SIMPLEQ_EMPTY(head)		((head)->sqh_first == NULL)
381+#define	SIMPLEQ_FIRST(head)		((head)->sqh_first)
382+#define	SIMPLEQ_NEXT(elm, field)	((elm)->field.sqe_next)
383+
384+
385+/*
386+ * Tail queue definitions.
387+ */
388+#define	TAILQ_HEAD(name, type)						\
389+struct name {								\
390+	struct type *tqh_first;	/* first element */			\
391+	struct type **tqh_last;	/* addr of last next element */		\
392+}
393+
394+#define	TAILQ_HEAD_INITIALIZER(head)					\
395+	{ NULL, &(head).tqh_first }
396+
397+#define	TAILQ_ENTRY(type)						\
398+struct {								\
399+	struct type *tqe_next;	/* next element */			\
400+	struct type **tqe_prev;	/* address of previous next element */	\
401+}
402+
403+/*
404+ * Tail queue functions.
405+ */
406+#if defined(_KERNEL) && defined(QUEUEDEBUG)
407+#define	QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)			\
408+	if ((head)->tqh_first &&					\
409+	    (head)->tqh_first->field.tqe_prev != &(head)->tqh_first)	\
410+		panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
411+#define	QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)			\
412+	if (*(head)->tqh_last != NULL)					\
413+		panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__);
414+#define	QUEUEDEBUG_TAILQ_OP(elm, field)					\
415+	if ((elm)->field.tqe_next &&					\
416+	    (elm)->field.tqe_next->field.tqe_prev !=			\
417+	    &(elm)->field.tqe_next)					\
418+		panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
419+	if (*(elm)->field.tqe_prev != (elm))				\
420+		panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__);
421+#define	QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)			\
422+	if ((elm)->field.tqe_next == NULL &&				\
423+	    (head)->tqh_last != &(elm)->field.tqe_next)			\
424+		panic("TAILQ_PREREMOVE head %p elm %p %s:%d",		\
425+		      (head), (elm), __FILE__, __LINE__);
426+#define	QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)				\
427+	(elm)->field.tqe_next = (void *)1L;				\
428+	(elm)->field.tqe_prev = (void *)1L;
429+#else
430+#define	QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
431+#define	QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
432+#define	QUEUEDEBUG_TAILQ_OP(elm, field)
433+#define	QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)
434+#define	QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
435+#endif
436+
437+#define	TAILQ_INIT(head) do {						\
438+	(head)->tqh_first = NULL;					\
439+	(head)->tqh_last = &(head)->tqh_first;				\
440+} while (/*CONSTCOND*/0)
441+
442+#define	TAILQ_INSERT_HEAD(head, elm, field) do {			\
443+	QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field)		\
444+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
445+		(head)->tqh_first->field.tqe_prev =			\
446+		    &(elm)->field.tqe_next;				\
447+	else								\
448+		(head)->tqh_last = &(elm)->field.tqe_next;		\
449+	(head)->tqh_first = (elm);					\
450+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
451+} while (/*CONSTCOND*/0)
452+
453+#define	TAILQ_INSERT_TAIL(head, elm, field) do {			\
454+	QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field)		\
455+	(elm)->field.tqe_next = NULL;					\
456+	(elm)->field.tqe_prev = (head)->tqh_last;			\
457+	*(head)->tqh_last = (elm);					\
458+	(head)->tqh_last = &(elm)->field.tqe_next;			\
459+} while (/*CONSTCOND*/0)
460+
461+#define	TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
462+	QUEUEDEBUG_TAILQ_OP((listelm), field)				\
463+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
464+		(elm)->field.tqe_next->field.tqe_prev = 		\
465+		    &(elm)->field.tqe_next;				\
466+	else								\
467+		(head)->tqh_last = &(elm)->field.tqe_next;		\
468+	(listelm)->field.tqe_next = (elm);				\
469+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
470+} while (/*CONSTCOND*/0)
471+
472+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
473+	QUEUEDEBUG_TAILQ_OP((listelm), field)				\
474+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
475+	(elm)->field.tqe_next = (listelm);				\
476+	*(listelm)->field.tqe_prev = (elm);				\
477+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
478+} while (/*CONSTCOND*/0)
479+
480+#define	TAILQ_REMOVE(head, elm, field) do {				\
481+	QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field)		\
482+	QUEUEDEBUG_TAILQ_OP((elm), field)				\
483+	if (((elm)->field.tqe_next) != NULL)				\
484+		(elm)->field.tqe_next->field.tqe_prev = 		\
485+		    (elm)->field.tqe_prev;				\
486+	else								\
487+		(head)->tqh_last = (elm)->field.tqe_prev;		\
488+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
489+	QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field);			\
490+} while (/*CONSTCOND*/0)
491+
492+#define	TAILQ_FOREACH(var, head, field)					\
493+	for ((var) = ((head)->tqh_first);				\
494+		(var);							\
495+		(var) = ((var)->field.tqe_next))
496+
497+#define	TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
498+	for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));	\
499+		(var);							\
500+		(var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
501+
502+/*
503+ * Tail queue access methods.
504+ */
505+#define	TAILQ_EMPTY(head)		((head)->tqh_first == NULL)
506+#define	TAILQ_FIRST(head)		((head)->tqh_first)
507+#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
508+
509+#define	TAILQ_LAST(head, headname) \
510+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
511+#define	TAILQ_PREV(elm, headname, field) \
512+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
513+
514+
515+/*
516+ * Circular queue definitions.
517+ */
518+#define	CIRCLEQ_HEAD(name, type)					\
519+struct name {								\
520+	struct type *cqh_first;		/* first element */		\
521+	struct type *cqh_last;		/* last element */		\
522+}
523+
524+#define	CIRCLEQ_HEAD_INITIALIZER(head)					\
525+	{ (void *)&head, (void *)&head }
526+
527+#define	CIRCLEQ_ENTRY(type)						\
528+struct {								\
529+	struct type *cqe_next;		/* next element */		\
530+	struct type *cqe_prev;		/* previous element */		\
531+}
532+
533+/*
534+ * Circular queue functions.
535+ */
536+#define	CIRCLEQ_INIT(head) do {						\
537+	(head)->cqh_first = (void *)(head);				\
538+	(head)->cqh_last = (void *)(head);				\
539+} while (/*CONSTCOND*/0)
540+
541+#define	CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
542+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
543+	(elm)->field.cqe_prev = (listelm);				\
544+	if ((listelm)->field.cqe_next == (void *)(head))		\
545+		(head)->cqh_last = (elm);				\
546+	else								\
547+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
548+	(listelm)->field.cqe_next = (elm);				\
549+} while (/*CONSTCOND*/0)
550+
551+#define	CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\
552+	(elm)->field.cqe_next = (listelm);				\
553+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
554+	if ((listelm)->field.cqe_prev == (void *)(head))		\
555+		(head)->cqh_first = (elm);				\
556+	else								\
557+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
558+	(listelm)->field.cqe_prev = (elm);				\
559+} while (/*CONSTCOND*/0)
560+
561+#define	CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
562+	(elm)->field.cqe_next = (head)->cqh_first;			\
563+	(elm)->field.cqe_prev = (void *)(head);				\
564+	if ((head)->cqh_last == (void *)(head))				\
565+		(head)->cqh_last = (elm);				\
566+	else								\
567+		(head)->cqh_first->field.cqe_prev = (elm);		\
568+	(head)->cqh_first = (elm);					\
569+} while (/*CONSTCOND*/0)
570+
571+#define	CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
572+	(elm)->field.cqe_next = (void *)(head);				\
573+	(elm)->field.cqe_prev = (head)->cqh_last;			\
574+	if ((head)->cqh_first == (void *)(head))			\
575+		(head)->cqh_first = (elm);				\
576+	else								\
577+		(head)->cqh_last->field.cqe_next = (elm);		\
578+	(head)->cqh_last = (elm);					\
579+} while (/*CONSTCOND*/0)
580+
581+#define	CIRCLEQ_REMOVE(head, elm, field) do {				\
582+	if ((elm)->field.cqe_next == (void *)(head))			\
583+		(head)->cqh_last = (elm)->field.cqe_prev;		\
584+	else								\
585+		(elm)->field.cqe_next->field.cqe_prev =			\
586+		    (elm)->field.cqe_prev;				\
587+	if ((elm)->field.cqe_prev == (void *)(head))			\
588+		(head)->cqh_first = (elm)->field.cqe_next;		\
589+	else								\
590+		(elm)->field.cqe_prev->field.cqe_next =			\
591+		    (elm)->field.cqe_next;				\
592+} while (/*CONSTCOND*/0)
593+
594+#define	CIRCLEQ_FOREACH(var, head, field)				\
595+	for ((var) = ((head)->cqh_first);				\
596+		(var) != (void *)(head);				\
597+		(var) = ((var)->field.cqe_next))
598+
599+#define	CIRCLEQ_FOREACH_REVERSE(var, head, field)			\
600+	for ((var) = ((head)->cqh_last);				\
601+		(var) != (void *)(head);				\
602+		(var) = ((var)->field.cqe_prev))
603+
604+/*
605+ * Circular queue access methods.
606+ */
607+#define	CIRCLEQ_EMPTY(head)		((head)->cqh_first == (void *)(head))
608+#define	CIRCLEQ_FIRST(head)		((head)->cqh_first)
609+#define	CIRCLEQ_LAST(head)		((head)->cqh_last)
610+#define	CIRCLEQ_NEXT(elm, field)	((elm)->field.cqe_next)
611+#define	CIRCLEQ_PREV(elm, field)	((elm)->field.cqe_prev)
612+
613+#endif	/* !_SYS_QUEUE_H_ */
+559, -0
  1@@ -0,0 +1,559 @@
  2+/*	$NetBSD: unvis.c,v 1.45 2022/04/19 20:32:15 rillig Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1989, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. Neither the name of the University nor the names of its contributors
 17+ *    may be used to endorse or promote products derived from this software
 18+ *    without specific prior written permission.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 30+ * SUCH DAMAGE.
 31+ */
 32+
 33+#include <sys/cdefs.h>
 34+#if defined(LIBC_SCCS) && !defined(lint)
 35+#if 0
 36+static char sccsid[] = "@(#)unvis.c	8.1 (Berkeley) 6/4/93";
 37+#else
 38+__RCSID("$NetBSD: unvis.c,v 1.45 2022/04/19 20:32:15 rillig Exp $");
 39+#endif
 40+#endif /* LIBC_SCCS and not lint */
 41+
 42+#include "namespace.h"
 43+#include <sys/types.h>
 44+
 45+#include <assert.h>
 46+#include <ctype.h>
 47+#include <stdint.h>
 48+#include <stdio.h>
 49+#include <errno.h>
 50+#include <vis.h>
 51+
 52+#ifdef __weak_alias
 53+__weak_alias(strnunvisx,_strnunvisx)
 54+#endif
 55+
 56+#if !HAVE_VIS
 57+/*
 58+ * decode driven by state machine
 59+ */
 60+#define	S_GROUND	0	/* haven't seen escape char */
 61+#define	S_START		1	/* start decoding special sequence */
 62+#define	S_META		2	/* metachar started (M) */
 63+#define	S_META1		3	/* metachar more, regular char (-) */
 64+#define	S_CTRL		4	/* control char started (^) */
 65+#define	S_OCTAL2	5	/* octal digit 2 */
 66+#define	S_OCTAL3	6	/* octal digit 3 */
 67+#define	S_HEX		7	/* mandatory hex digit */
 68+#define	S_HEX1		8	/* http hex digit */
 69+#define	S_HEX2		9	/* http hex digit 2 */
 70+#define	S_MIME1		10	/* mime hex digit 1 */
 71+#define	S_MIME2		11	/* mime hex digit 2 */
 72+#define	S_EATCRNL	12	/* mime eating CRNL */
 73+#define	S_AMP		13	/* seen & */
 74+#define	S_NUMBER	14	/* collecting number */
 75+#define	S_STRING	15	/* collecting string */
 76+
 77+#define	isoctal(c)	(((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
 78+#define	xtod(c)		(isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
 79+#define	XTOD(c)		(isdigit(c) ? (c - '0') : ((c - 'A') + 10))
 80+
 81+/*
 82+ * RFC 1866
 83+ */
 84+static const struct nv {
 85+	char name[7];
 86+	uint8_t value;
 87+} nv[] = {
 88+	{ "AElig",	198 }, /* capital AE diphthong (ligature)  */
 89+	{ "Aacute",	193 }, /* capital A, acute accent  */
 90+	{ "Acirc",	194 }, /* capital A, circumflex accent  */
 91+	{ "Agrave",	192 }, /* capital A, grave accent  */
 92+	{ "Aring",	197 }, /* capital A, ring  */
 93+	{ "Atilde",	195 }, /* capital A, tilde  */
 94+	{ "Auml",	196 }, /* capital A, dieresis or umlaut mark  */
 95+	{ "Ccedil",	199 }, /* capital C, cedilla  */
 96+	{ "ETH",	208 }, /* capital Eth, Icelandic  */
 97+	{ "Eacute",	201 }, /* capital E, acute accent  */
 98+	{ "Ecirc",	202 }, /* capital E, circumflex accent  */
 99+	{ "Egrave",	200 }, /* capital E, grave accent  */
100+	{ "Euml",	203 }, /* capital E, dieresis or umlaut mark  */
101+	{ "Iacute",	205 }, /* capital I, acute accent  */
102+	{ "Icirc",	206 }, /* capital I, circumflex accent  */
103+	{ "Igrave",	204 }, /* capital I, grave accent  */
104+	{ "Iuml",	207 }, /* capital I, dieresis or umlaut mark  */
105+	{ "Ntilde",	209 }, /* capital N, tilde  */
106+	{ "Oacute",	211 }, /* capital O, acute accent  */
107+	{ "Ocirc",	212 }, /* capital O, circumflex accent  */
108+	{ "Ograve",	210 }, /* capital O, grave accent  */
109+	{ "Oslash",	216 }, /* capital O, slash  */
110+	{ "Otilde",	213 }, /* capital O, tilde  */
111+	{ "Ouml",	214 }, /* capital O, dieresis or umlaut mark  */
112+	{ "THORN",	222 }, /* capital THORN, Icelandic  */
113+	{ "Uacute",	218 }, /* capital U, acute accent  */
114+	{ "Ucirc",	219 }, /* capital U, circumflex accent  */
115+	{ "Ugrave",	217 }, /* capital U, grave accent  */
116+	{ "Uuml",	220 }, /* capital U, dieresis or umlaut mark  */
117+	{ "Yacute",	221 }, /* capital Y, acute accent  */
118+	{ "aacute",	225 }, /* small a, acute accent  */
119+	{ "acirc",	226 }, /* small a, circumflex accent  */
120+	{ "acute",	180 }, /* acute accent  */
121+	{ "aelig",	230 }, /* small ae diphthong (ligature)  */
122+	{ "agrave",	224 }, /* small a, grave accent  */
123+	{ "amp",	 38 }, /* ampersand  */
124+	{ "aring",	229 }, /* small a, ring  */
125+	{ "atilde",	227 }, /* small a, tilde  */
126+	{ "auml",	228 }, /* small a, dieresis or umlaut mark  */
127+	{ "brvbar",	166 }, /* broken (vertical) bar  */
128+	{ "ccedil",	231 }, /* small c, cedilla  */
129+	{ "cedil",	184 }, /* cedilla  */
130+	{ "cent",	162 }, /* cent sign  */
131+	{ "copy",	169 }, /* copyright sign  */
132+	{ "curren",	164 }, /* general currency sign  */
133+	{ "deg",	176 }, /* degree sign  */
134+	{ "divide",	247 }, /* divide sign  */
135+	{ "eacute",	233 }, /* small e, acute accent  */
136+	{ "ecirc",	234 }, /* small e, circumflex accent  */
137+	{ "egrave",	232 }, /* small e, grave accent  */
138+	{ "eth",	240 }, /* small eth, Icelandic  */
139+	{ "euml",	235 }, /* small e, dieresis or umlaut mark  */
140+	{ "frac12",	189 }, /* fraction one-half  */
141+	{ "frac14",	188 }, /* fraction one-quarter  */
142+	{ "frac34",	190 }, /* fraction three-quarters  */
143+	{ "gt",		 62 }, /* greater than  */
144+	{ "iacute",	237 }, /* small i, acute accent  */
145+	{ "icirc",	238 }, /* small i, circumflex accent  */
146+	{ "iexcl",	161 }, /* inverted exclamation mark  */
147+	{ "igrave",	236 }, /* small i, grave accent  */
148+	{ "iquest",	191 }, /* inverted question mark  */
149+	{ "iuml",	239 }, /* small i, dieresis or umlaut mark  */
150+	{ "laquo",	171 }, /* angle quotation mark, left  */
151+	{ "lt",		 60 }, /* less than  */
152+	{ "macr",	175 }, /* macron  */
153+	{ "micro",	181 }, /* micro sign  */
154+	{ "middot",	183 }, /* middle dot  */
155+	{ "nbsp",	160 }, /* no-break space  */
156+	{ "not",	172 }, /* not sign  */
157+	{ "ntilde",	241 }, /* small n, tilde  */
158+	{ "oacute",	243 }, /* small o, acute accent  */
159+	{ "ocirc",	244 }, /* small o, circumflex accent  */
160+	{ "ograve",	242 }, /* small o, grave accent  */
161+	{ "ordf",	170 }, /* ordinal indicator, feminine  */
162+	{ "ordm",	186 }, /* ordinal indicator, masculine  */
163+	{ "oslash",	248 }, /* small o, slash  */
164+	{ "otilde",	245 }, /* small o, tilde  */
165+	{ "ouml",	246 }, /* small o, dieresis or umlaut mark  */
166+	{ "para",	182 }, /* pilcrow (paragraph sign)  */
167+	{ "plusmn",	177 }, /* plus-or-minus sign  */
168+	{ "pound",	163 }, /* pound sterling sign  */
169+	{ "quot",	 34 }, /* double quote  */
170+	{ "raquo",	187 }, /* angle quotation mark, right  */
171+	{ "reg",	174 }, /* registered sign  */
172+	{ "sect",	167 }, /* section sign  */
173+	{ "shy",	173 }, /* soft hyphen  */
174+	{ "sup1",	185 }, /* superscript one  */
175+	{ "sup2",	178 }, /* superscript two  */
176+	{ "sup3",	179 }, /* superscript three  */
177+	{ "szlig",	223 }, /* small sharp s, German (sz ligature)  */
178+	{ "thorn",	254 }, /* small thorn, Icelandic  */
179+	{ "times",	215 }, /* multiply sign  */
180+	{ "uacute",	250 }, /* small u, acute accent  */
181+	{ "ucirc",	251 }, /* small u, circumflex accent  */
182+	{ "ugrave",	249 }, /* small u, grave accent  */
183+	{ "uml",	168 }, /* umlaut (dieresis)  */
184+	{ "uuml",	252 }, /* small u, dieresis or umlaut mark  */
185+	{ "yacute",	253 }, /* small y, acute accent  */
186+	{ "yen",	165 }, /* yen sign  */
187+	{ "yuml",	255 }, /* small y, dieresis or umlaut mark  */
188+};
189+
190+/*
191+ * unvis - decode characters previously encoded by vis
192+ */
193+int
194+unvis(char *cp, int c, int *astate, int flag)
195+{
196+	unsigned char uc = (unsigned char)c;
197+	unsigned char st, ia, is, lc;
198+
199+/*
200+ * Bottom 8 bits of astate hold the state machine state.
201+ * Top 8 bits hold the current character in the http 1866 nv string decoding
202+ */
203+#define GS(a)		((a) & 0xff)
204+#define SS(a, b)	(((uint32_t)(a) << 24) | (b))
205+#define GI(a)		((uint32_t)(a) >> 24)
206+
207+	_DIAGASSERT(cp != NULL);
208+	_DIAGASSERT(astate != NULL);
209+	st = GS(*astate);
210+
211+	if (flag & UNVIS_END) {
212+		switch (st) {
213+		case S_OCTAL2:
214+		case S_OCTAL3:
215+		case S_HEX2:
216+			*astate = SS(0, S_GROUND);
217+			return UNVIS_VALID;
218+		case S_GROUND:
219+			return UNVIS_NOCHAR;
220+		default:
221+			return UNVIS_SYNBAD;
222+		}
223+	}
224+
225+	switch (st) {
226+
227+	case S_GROUND:
228+		*cp = 0;
229+		if ((flag & VIS_NOESCAPE) == 0 && c == '\\') {
230+			*astate = SS(0, S_START);
231+			return UNVIS_NOCHAR;
232+		}
233+		if ((flag & VIS_HTTP1808) && c == '%') {
234+			*astate = SS(0, S_HEX1);
235+			return UNVIS_NOCHAR;
236+		}
237+		if ((flag & VIS_HTTP1866) && c == '&') {
238+			*astate = SS(0, S_AMP);
239+			return UNVIS_NOCHAR;
240+		}
241+		if ((flag & VIS_MIMESTYLE) && c == '=') {
242+			*astate = SS(0, S_MIME1);
243+			return UNVIS_NOCHAR;
244+		}
245+		*cp = c;
246+		return UNVIS_VALID;
247+
248+	case S_START:
249+		switch(c) {
250+		case '\\':
251+			*cp = c;
252+			*astate = SS(0, S_GROUND);
253+			return UNVIS_VALID;
254+		case '0': case '1': case '2': case '3':
255+		case '4': case '5': case '6': case '7':
256+			*cp = (c - '0');
257+			*astate = SS(0, S_OCTAL2);
258+			return UNVIS_NOCHAR;
259+		case 'M':
260+			*cp = (char)0200;
261+			*astate = SS(0, S_META);
262+			return UNVIS_NOCHAR;
263+		case '^':
264+			*astate = SS(0, S_CTRL);
265+			return UNVIS_NOCHAR;
266+		case 'n':
267+			*cp = '\n';
268+			*astate = SS(0, S_GROUND);
269+			return UNVIS_VALID;
270+		case 'r':
271+			*cp = '\r';
272+			*astate = SS(0, S_GROUND);
273+			return UNVIS_VALID;
274+		case 'b':
275+			*cp = '\b';
276+			*astate = SS(0, S_GROUND);
277+			return UNVIS_VALID;
278+		case 'a':
279+			*cp = '\007';
280+			*astate = SS(0, S_GROUND);
281+			return UNVIS_VALID;
282+		case 'v':
283+			*cp = '\v';
284+			*astate = SS(0, S_GROUND);
285+			return UNVIS_VALID;
286+		case 't':
287+			*cp = '\t';
288+			*astate = SS(0, S_GROUND);
289+			return UNVIS_VALID;
290+		case 'f':
291+			*cp = '\f';
292+			*astate = SS(0, S_GROUND);
293+			return UNVIS_VALID;
294+		case 's':
295+			*cp = ' ';
296+			*astate = SS(0, S_GROUND);
297+			return UNVIS_VALID;
298+		case 'E':
299+			*cp = '\033';
300+			*astate = SS(0, S_GROUND);
301+			return UNVIS_VALID;
302+		case 'x':
303+			*astate = SS(0, S_HEX);
304+			return UNVIS_NOCHAR;
305+		case '\n':
306+			/*
307+			 * hidden newline
308+			 */
309+			*astate = SS(0, S_GROUND);
310+			return UNVIS_NOCHAR;
311+		case '$':
312+			/*
313+			 * hidden marker
314+			 */
315+			*astate = SS(0, S_GROUND);
316+			return UNVIS_NOCHAR;
317+		default:
318+			if (isgraph(c)) {
319+				*cp = c;
320+				*astate = SS(0, S_GROUND);
321+				return UNVIS_VALID;
322+			}
323+		}
324+		goto bad;
325+
326+	case S_META:
327+		if (c == '-')
328+			*astate = SS(0, S_META1);
329+		else if (c == '^')
330+			*astate = SS(0, S_CTRL);
331+		else 
332+			goto bad;
333+		return UNVIS_NOCHAR;
334+
335+	case S_META1:
336+		*astate = SS(0, S_GROUND);
337+		*cp |= c;
338+		return UNVIS_VALID;
339+
340+	case S_CTRL:
341+		if (c == '?')
342+			*cp |= 0177;
343+		else
344+			*cp |= c & 037;
345+		*astate = SS(0, S_GROUND);
346+		return UNVIS_VALID;
347+
348+	case S_OCTAL2:	/* second possible octal digit */
349+		if (isoctal(uc)) {
350+			/*
351+			 * yes - and maybe a third
352+			 */
353+			*cp = (*cp << 3) + (c - '0');
354+			*astate = SS(0, S_OCTAL3);
355+			return UNVIS_NOCHAR;
356+		}
357+		/*
358+		 * no - done with current sequence, push back passed char
359+		 */
360+		*astate = SS(0, S_GROUND);
361+		return UNVIS_VALIDPUSH;
362+
363+	case S_OCTAL3:	/* third possible octal digit */
364+		*astate = SS(0, S_GROUND);
365+		if (isoctal(uc)) {
366+			*cp = (*cp << 3) + (c - '0');
367+			return UNVIS_VALID;
368+		}
369+		/*
370+		 * we were done, push back passed char
371+		 */
372+		return UNVIS_VALIDPUSH;
373+
374+	case S_HEX:
375+		if (!isxdigit(uc))
376+			goto bad;
377+		/*FALLTHROUGH*/
378+	case S_HEX1:
379+		if (isxdigit(uc)) {
380+			*cp = xtod(uc);
381+			*astate = SS(0, S_HEX2);
382+			return UNVIS_NOCHAR;
383+		}
384+		/*
385+		 * no - done with current sequence, push back passed char
386+		 */
387+		*astate = SS(0, S_GROUND);
388+		return UNVIS_VALIDPUSH;
389+
390+	case S_HEX2:
391+		*astate = S_GROUND;
392+		if (isxdigit(uc)) {
393+			*cp = xtod(uc) | (*cp << 4);
394+			return UNVIS_VALID;
395+		}
396+		return UNVIS_VALIDPUSH;
397+
398+	case S_MIME1:
399+		if (uc == '\n' || uc == '\r') {
400+			*astate = SS(0, S_EATCRNL);
401+			return UNVIS_NOCHAR;
402+		}
403+		if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) {
404+			*cp = XTOD(uc);
405+			*astate = SS(0, S_MIME2);
406+			return UNVIS_NOCHAR;
407+		}
408+		goto bad;
409+
410+	case S_MIME2:
411+		if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) {
412+			*astate = SS(0, S_GROUND);
413+			*cp = XTOD(uc) | (*cp << 4);
414+			return UNVIS_VALID;
415+		}
416+		goto bad;
417+
418+	case S_EATCRNL:
419+		switch (uc) {
420+		case '\r':
421+		case '\n':
422+			return UNVIS_NOCHAR;
423+		case '=':
424+			*astate = SS(0, S_MIME1);
425+			return UNVIS_NOCHAR;
426+		default:
427+			*cp = uc;
428+			*astate = SS(0, S_GROUND);
429+			return UNVIS_VALID;
430+		}
431+
432+	case S_AMP:
433+		*cp = 0;
434+		if (uc == '#') {
435+			*astate = SS(0, S_NUMBER);
436+			return UNVIS_NOCHAR;
437+		}
438+		*astate = SS(0, S_STRING);
439+		/*FALLTHROUGH*/
440+
441+	case S_STRING:
442+		ia = *cp;		/* index in the array */
443+		is = GI(*astate);	/* index in the string */
444+		lc = is == 0 ? 0 : nv[ia].name[is - 1];	/* last character */
445+
446+		if (uc == ';')
447+			uc = '\0';
448+
449+		for (; ia < __arraycount(nv); ia++) {
450+			if (is != 0 && nv[ia].name[is - 1] != lc)
451+				goto bad;
452+			if (nv[ia].name[is] == uc)
453+				break;
454+		}
455+
456+		if (ia == __arraycount(nv))
457+			goto bad;
458+
459+		if (uc != 0) {
460+			*cp = ia;
461+			*astate = SS(is + 1, S_STRING);
462+			return UNVIS_NOCHAR;
463+		}
464+
465+		*cp = nv[ia].value;
466+		*astate = SS(0, S_GROUND);
467+		return UNVIS_VALID;
468+
469+	case S_NUMBER:
470+		if (uc == ';')
471+			return UNVIS_VALID;
472+		if (!isdigit(uc))
473+			goto bad;
474+		*cp += (*cp * 10) + uc - '0';
475+		return UNVIS_NOCHAR;
476+
477+	default:
478+	bad:
479+		/*
480+		 * decoder in unknown state - (probably uninitialized)
481+		 */
482+		*astate = SS(0, S_GROUND);
483+		return UNVIS_SYNBAD;
484+	}
485+}
486+
487+/*
488+ * strnunvisx - decode src into dst
489+ *
490+ *	Number of chars decoded into dst is returned, -1 on error.
491+ *	Dst is null terminated.
492+ */
493+
494+int
495+strnunvisx(char *dst, size_t dlen, const char *src, int flag)
496+{
497+	char c;
498+	char t = '\0', *start = dst;
499+	int state = 0;
500+
501+	_DIAGASSERT(src != NULL);
502+	_DIAGASSERT(dst != NULL);
503+#define CHECKSPACE() \
504+	do { \
505+		if (dlen-- == 0) { \
506+			errno = ENOSPC; \
507+			return -1; \
508+		} \
509+	} while (0)
510+
511+	while ((c = *src++) != '\0') {
512+ again:
513+		switch (unvis(&t, c, &state, flag)) {
514+		case UNVIS_VALID:
515+			CHECKSPACE();
516+			*dst++ = t;
517+			break;
518+		case UNVIS_VALIDPUSH:
519+			CHECKSPACE();
520+			*dst++ = t;
521+			goto again;
522+		case 0:
523+		case UNVIS_NOCHAR:
524+			break;
525+		case UNVIS_SYNBAD:
526+			errno = EINVAL;
527+			return -1;
528+		default:
529+			_DIAGASSERT(/*CONSTCOND*/0);
530+			errno = EINVAL;
531+			return -1;
532+		}
533+	}
534+	if (unvis(&t, c, &state, UNVIS_END) == UNVIS_VALID) {
535+		CHECKSPACE();
536+		*dst++ = t;
537+	}
538+	CHECKSPACE();
539+	*dst = '\0';
540+	return (int)(dst - start);
541+}
542+
543+int
544+strunvisx(char *dst, const char *src, int flag)
545+{
546+	return strnunvisx(dst, (size_t)~0, src, flag);
547+}
548+
549+int
550+strunvis(char *dst, const char *src)
551+{
552+	return strnunvisx(dst, (size_t)~0, src, 0);
553+}
554+
555+int
556+strnunvis(char *dst, size_t dlen, const char *src)
557+{
558+	return strnunvisx(dst, dlen, src, 0);
559+}
560+#endif
+68, -0
 1@@ -0,0 +1,68 @@
 2+#ifndef _UTIL_H_
 3+#define _UTIL_H_
 4+
 5+#include "netcompat.h"
 6+#include <sys/cdefs.h>
 7+#include <sys/types.h>
 8+#include <inttypes.h>
 9+#include <stdio.h>
10+#include <stdarg.h>
11+#include <sys/stat.h>
12+#include <grp.h>
13+#include <pwd.h>
14+
15+#ifndef FPARSELN_UNESCESC
16+#define FPARSELN_UNESCESC 0x01
17+#define FPARSELN_UNESCCONT 0x02
18+#define FPARSELN_UNESCCOMM 0x04
19+#define FPARSELN_UNESCREST 0x08
20+#define FPARSELN_UNESCALL 0x0f
21+#endif
22+
23+__BEGIN_DECLS
24+
25+char *flags_to_string(unsigned long, const char *);
26+int string_to_flags(char **, unsigned long *, unsigned long *);
27+
28+void (*esetfunc(void (*)(int, const char *, ...)))(int, const char *, ...);
29+size_t estrlcpy(char *, const char *, size_t);
30+size_t estrlcat(char *, const char *, size_t);
31+char *estrdup(const char *);
32+char *estrndup(const char *, size_t);
33+intmax_t estrtoi(const char *, int, intmax_t, intmax_t);
34+uintmax_t estrtou(const char *, int, uintmax_t, uintmax_t);
35+void *ecalloc(size_t, size_t);
36+void *emalloc(size_t);
37+void *erealloc(void *, size_t);
38+void ereallocarr(void *, size_t, size_t);
39+FILE *efopen(const char *, const char *);
40+int easprintf(char ** __restrict, const char * __restrict, ...)
41+    __printflike(2, 3);
42+int evasprintf(char ** __restrict, const char * __restrict, va_list)
43+    __printflike(2, 0);
44+
45+char *fgetln(FILE *, size_t *);
46+char *fparseln(FILE *, size_t *, size_t *, const char[3], int);
47+mode_t getmode(const void *, mode_t);
48+void *setmode(const char *);
49+long long strsuftoll(const char *, const char *, long long, long long);
50+uintmax_t strtou(const char * __restrict, char ** __restrict, int,
51+    uintmax_t, uintmax_t, int *);
52+
53+const char *getprogname(void);
54+void setprogname(const char *);
55+
56+const char *user_from_uid(uid_t, int);
57+const char *group_from_gid(gid_t, int);
58+int uid_from_user(const char *, uid_t *);
59+int gid_from_group(const char *, gid_t *);
60+int pwcache_userdb(int (*)(int), void (*)(void),
61+    struct passwd *(*)(const char *), struct passwd *(*)(uid_t));
62+int pwcache_groupdb(int (*)(int), void (*)(void),
63+    struct group *(*)(const char *), struct group *(*)(gid_t));
64+int setpassent(int);
65+int setgroupent(int);
66+
67+__END_DECLS
68+
69+#endif
+819, -85
  1@@ -1,120 +1,854 @@
  2+/*	$NetBSD: vis.c,v 1.88 2024/03/17 21:48:02 andvar Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1989, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. Neither the name of the University nor the names of its contributors
 17+ *    may be used to endorse or promote products derived from this software
 18+ *    without specific prior written permission.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 30+ * SUCH DAMAGE.
 31+ */
 32+
 33+/*-
 34+ * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
 35+ * All rights reserved.
 36+ *
 37+ * Redistribution and use in source and binary forms, with or without
 38+ * modification, are permitted provided that the following conditions
 39+ * are met:
 40+ * 1. Redistributions of source code must retain the above copyright
 41+ *    notice, this list of conditions and the following disclaimer.
 42+ * 2. Redistributions in binary form must reproduce the above copyright
 43+ *    notice, this list of conditions and the following disclaimer in the
 44+ *    documentation and/or other materials provided with the distribution.
 45+ *
 46+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 47+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 48+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 49+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 50+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 51+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 52+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 53+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 54+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 55+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 56+ * POSSIBILITY OF SUCH DAMAGE.
 57+ */
 58+
 59 #include <sys/cdefs.h>
 60+#if defined(LIBC_SCCS) && !defined(lint)
 61+__RCSID("$NetBSD: vis.c,v 1.88 2024/03/17 21:48:02 andvar Exp $");
 62+#endif /* LIBC_SCCS and not lint */
 63+#ifdef __FBSDID
 64+__FBSDID("$FreeBSD$");
 65+#define	_DIAGASSERT(x)	assert(x)
 66+#endif
 67+
 68+#include "namespace.h"
 69+
 70+#include <sys/param.h>
 71+#include <sys/types.h>
 72+
 73+#include <assert.h>
 74+#include <errno.h>
 75+#include <stdint.h>
 76+#include <stdlib.h>
 77+#include <vis.h>
 78+#include <wchar.h>
 79+#include <wctype.h>
 80 
 81+#ifdef __weak_alias
 82+__weak_alias(strvisx,_strvisx)
 83+#endif
 84+
 85+#if !HAVE_VIS || !HAVE_SVIS
 86 #include <ctype.h>
 87-#include <stddef.h>
 88+#include <limits.h>
 89+#include <stdio.h>
 90+#include <string.h>
 91+
 92+/*
 93+ * The reason for going through the trouble to deal with character encodings
 94+ * in vis(3), is that we use this to safe encode output of commands. This
 95+ * safe encoding varies depending on the character set. For example if we
 96+ * display ps output in French, we don't want to display French characters
 97+ * as M-foo.
 98+ */
 99 
100-#include "vis.h"
101+static wchar_t *do_svis(wchar_t *, wint_t, int, wint_t, const wchar_t *);
102 
103+#undef BELL
104+#define BELL L'\a'
105+ 
106+#if defined(LC_C_LOCALE)
107+#define iscgraph(c)      isgraph_l(c, LC_C_LOCALE)
108+#else
109+/* Keep it simple for now, no locale stuff */
110+#define iscgraph(c)	isgraph(c)
111+#ifdef notyet
112+#include <locale.h>
113 static int
114-needs_vis(unsigned char c, int flags)
115+iscgraph(int c) {
116+	int rv;
117+	char *ol;
118+
119+	ol = setlocale(LC_CTYPE, "C");
120+	rv = isgraph(c);
121+	if (ol)
122+		setlocale(LC_CTYPE, ol);
123+	return rv;
124+}
125+#endif
126+#endif
127+
128+#define ISGRAPH(flags, c) \
129+    (((flags) & VIS_NOLOCALE) ? iscgraph(c) : iswgraph(c))
130+
131+#define iswoctal(c)	(((u_char)(c)) >= L'0' && ((u_char)(c)) <= L'7')
132+#define iswwhite(c)	(c == L' ' || c == L'\t' || c == L'\n')
133+#define iswsafe(c)	(c == L'\b' || c == BELL || c == L'\r')
134+#define xtoa(c)		L"0123456789abcdef"[c]
135+#define XTOA(c)		L"0123456789ABCDEF"[c]
136+
137+#define MAXEXTRAS	30
138+
139+static const wchar_t char_shell[] = L"'`\";&<>()|{}]\\$!^~";
140+static const wchar_t char_glob[] = L"*?[#";
141+
142+#if !HAVE_NBTOOL_CONFIG_H
143+#ifndef __NetBSD__
144+/*
145+ * On NetBSD MB_LEN_MAX is currently 32 which does not fit on any integer
146+ * integral type and it is probably wrong, since currently the maximum
147+ * number of bytes and character needs is 6. Until this is fixed, the
148+ * loops below are using sizeof(uint64_t) - 1 instead of MB_LEN_MAX, and
149+ * the assertion is commented out.
150+ */
151+#ifdef __FreeBSD__
152+/*
153+ * On FreeBSD including <sys/systm.h> for CTASSERT only works in kernel
154+ * mode.
155+ */
156+#ifndef CTASSERT
157+#define CTASSERT(x)             _CTASSERT(x, __LINE__)
158+#define _CTASSERT(x, y)         __CTASSERT(x, y)
159+#define __CTASSERT(x, y)        typedef char __assert ## y[(x) ? 1 : -1]
160+#endif
161+#endif /* __FreeBSD__ */
162+CTASSERT(MB_LEN_MAX <= sizeof(uint64_t));
163+#endif /* !__NetBSD__ */
164+#endif
165+
166+/*
167+ * This is do_hvis, for HTTP style (RFC 1808)
168+ */
169+static wchar_t *
170+do_hvis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
171 {
172-	if (c == '\\' && !(flags & VIS_NOSLASH))
173-		return 1;
174-	if ((flags & VIS_SP) && c == ' ')
175-		return 1;
176-	if ((flags & VIS_TAB) && c == '\t')
177-		return 1;
178-	if ((flags & VIS_NL) && c == '\n')
179-		return 1;
180-	if (!isprint(c))
181-		return 1;
182-	return 0;
183+	if (iswalnum(c)
184+	    /* safe */
185+	    || c == L'$' || c == L'-' || c == L'_' || c == L'.' || c == L'+'
186+	    /* extra */
187+	    || c == L'!' || c == L'*' || c == L'\'' || c == L'(' || c == L')'
188+	    || c == L',')
189+		dst = do_svis(dst, c, flags, nextc, extra);
190+	else {
191+		*dst++ = L'%';
192+		*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
193+		*dst++ = xtoa((unsigned int)c & 0xf);
194+	}
195+
196+	return dst;
197 }
198 
199-static int
200-putc_vis(char **dst, size_t *dlen, unsigned char c)
201+/*
202+ * This is do_mvis, for Quoted-Printable MIME (RFC 2045)
203+ * NB: No handling of long lines or CRLF.
204+ */
205+static wchar_t *
206+do_mvis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
207 {
208-	if (*dlen <= 1)
209-		return -1;
210-	*(*dst)++ = (char)c;
211-	(*dlen)--;
212-	return 0;
213+	if ((c != L'\n') &&
214+	    /* Space at the end of the line */
215+	    ((iswspace(c) && (nextc == L'\r' || nextc == L'\n')) ||
216+	    /* Out of range */
217+	    (!iswspace(c) && (c < 33 || (c > 60 && c < 62) || c > 126)) ||
218+	    /* Specific char to be escaped */
219+	    wcschr(L"#$@[\\]^`{|}~", c) != NULL)) {
220+		*dst++ = L'=';
221+		*dst++ = XTOA(((unsigned int)c >> 4) & 0xf);
222+		*dst++ = XTOA((unsigned int)c & 0xf);
223+	} else
224+		dst = do_svis(dst, c, flags, nextc, extra);
225+	return dst;
226 }
227 
228-static int
229-put_oct(char **dst, size_t *dlen, unsigned char c)
230+/*
231+ * Output single byte of multibyte character.
232+ */
233+static wchar_t *
234+do_mbyte(wchar_t *dst, wint_t c, int flags, wint_t nextc, int iswextra)
235 {
236-	if (*dlen <= 4)
237-		return -1;
238-	*(*dst)++ = '\\';
239-	*(*dst)++ = (char)(((c >> 6) & 0x07) + '0');
240-	*(*dst)++ = (char)(((c >> 3) & 0x07) + '0');
241-	*(*dst)++ = (char)((c & 0x07) + '0');
242-	*dlen -= 4;
243-	return 0;
244+	if (flags & VIS_CSTYLE) {
245+		switch (c) {
246+		case L'\n':
247+			*dst++ = L'\\'; *dst++ = L'n';
248+			return dst;
249+		case L'\r':
250+			*dst++ = L'\\'; *dst++ = L'r';
251+			return dst;
252+		case L'\b':
253+			*dst++ = L'\\'; *dst++ = L'b';
254+			return dst;
255+		case BELL:
256+			*dst++ = L'\\'; *dst++ = L'a';
257+			return dst;
258+		case L'\v':
259+			*dst++ = L'\\'; *dst++ = L'v';
260+			return dst;
261+		case L'\t':
262+			*dst++ = L'\\'; *dst++ = L't';
263+			return dst;
264+		case L'\f':
265+			*dst++ = L'\\'; *dst++ = L'f';
266+			return dst;
267+		case L' ':
268+			*dst++ = L'\\'; *dst++ = L's';
269+			return dst;
270+		case L'\0':
271+			*dst++ = L'\\'; *dst++ = L'0';
272+			if (iswoctal(nextc)) {
273+				*dst++ = L'0';
274+				*dst++ = L'0';
275+			}
276+			return dst;
277+		/* We cannot encode these characters in VIS_CSTYLE
278+		 * because they special meaning */
279+		case L'n':
280+		case L'r':
281+		case L'b':
282+		case L'a':
283+		case L'v':
284+		case L't':
285+		case L'f':
286+		case L's':
287+		case L'0':
288+		case L'M':
289+		case L'^':
290+		case L'$': /* vis(1) -l */
291+			break;
292+		default:
293+			if (ISGRAPH(flags, c) && !iswoctal(c)) {
294+				*dst++ = L'\\';
295+				*dst++ = c;
296+				return dst;
297+			}
298+		}
299+	}
300+	if (iswextra || ((c & 0177) == L' ') || (flags & VIS_OCTAL)) {
301+		*dst++ = L'\\';
302+		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + L'0';
303+		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + L'0';
304+		*dst++ =			     (c	      & 07) + L'0';
305+	} else {
306+		if ((flags & VIS_NOSLASH) == 0)
307+			*dst++ = L'\\';
308+
309+		if (c & 0200) {
310+			c &= 0177;
311+			*dst++ = L'M';
312+		}
313+
314+		if (iswcntrl(c)) {
315+			*dst++ = L'^';
316+			if (c == 0177)
317+				*dst++ = L'?';
318+			else
319+				*dst++ = c + L'@';
320+		} else {
321+			*dst++ = L'-';
322+			*dst++ = c;
323+		}
324+	}
325+
326+	return dst;
327 }
328 
329-static int
330-put_cstyle(char **dst, size_t *dlen, unsigned char c)
331-{
332-	char esc;
333-
334-	switch (c) {
335-	case '\n': esc = 'n'; break;
336-	case '\r': esc = 'r'; break;
337-	case '\b': esc = 'b'; break;
338-	case '\a': esc = 'a'; break;
339-	case '\v': esc = 'v'; break;
340-	case '\t': esc = 't'; break;
341-	case '\f': esc = 'f'; break;
342-	case ' ':  esc = 's'; break;
343-	case '\\': esc = '\\'; break;
344-	default:
345-		return put_oct(dst, dlen, c);
346+/*
347+ * This is do_vis, the central code of vis.
348+ * dst:	      Pointer to the destination buffer
349+ * c:	      Character to encode
350+ * flags:     Flags word
351+ * nextc:     The character following 'c'
352+ * extra:     Pointer to the list of extra characters to be
353+ *	      backslash-protected.
354+ */
355+static wchar_t *
356+do_svis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
357+{
358+	int iswextra, i, shft;
359+	uint64_t bmsk, wmsk;
360+
361+	iswextra = wcschr(extra, c) != NULL;
362+	if (!iswextra && (ISGRAPH(flags, c) || iswwhite(c) ||
363+	    ((flags & VIS_SAFE) && iswsafe(c)))) {
364+		*dst++ = c;
365+		return dst;
366 	}
367 
368-	if (*dlen <= 2)
369-		return -1;
370-	*(*dst)++ = '\\';
371-	*(*dst)++ = esc;
372-	*dlen -= 2;
373-	return 0;
374+	/* See comment in istrsenvisx() output loop, below. */
375+	wmsk = 0;
376+	for (i = sizeof(wmsk) - 1; i >= 0; i--) {
377+		shft = i * NBBY;
378+		bmsk = (uint64_t)0xffLL << shft;
379+		wmsk |= bmsk;
380+		if ((c & wmsk) || i == 0)
381+			dst = do_mbyte(dst, (wint_t)(
382+			    (uint64_t)(c & bmsk) >> shft),
383+			    flags, nextc, iswextra);
384+	}
385+
386+	return dst;
387 }
388 
389-int
390-strnvisx(char *dst, size_t dlen, const char *src, size_t slen, int flags)
391+typedef wchar_t *(*visfun_t)(wchar_t *, wint_t, int, wint_t, const wchar_t *);
392+
393+/*
394+ * Return the appropriate encoding function depending on the flags given.
395+ */
396+static visfun_t
397+getvisfun(int flags)
398 {
399-	char *start;
400-	unsigned char c;
401+	if (flags & VIS_HTTPSTYLE)
402+		return do_hvis;
403+	if (flags & VIS_MIMESTYLE)
404+		return do_mvis;
405+	return do_svis;
406+}
407 
408-	if (dlen == 0)
409-		return -1;
410+/*
411+ * Expand list of extra characters to not visually encode.
412+ */
413+static wchar_t *
414+makeextralist(int flags, const char *src)
415+{
416+	wchar_t *dst, *d;
417+	size_t len;
418+	const wchar_t *s;
419+	mbstate_t mbstate;
420+
421+	len = strlen(src);
422+	if ((dst = calloc(len + MAXEXTRAS, sizeof(*dst))) == NULL)
423+		return NULL;
424+
425+	memset(&mbstate, 0, sizeof(mbstate));
426+	if ((flags & VIS_NOLOCALE)
427+	    || mbsrtowcs(dst, &src, len, &mbstate) == (size_t)-1) {
428+		size_t i;
429+		for (i = 0; i < len; i++)
430+			dst[i] = (wchar_t)(u_char)src[i];
431+		d = dst + len;
432+	} else
433+		d = dst + wcslen(dst);
434 
435-	start = dst;
436-	while (slen-- > 0) {
437-		c = (unsigned char)*src++;
438-		if (!needs_vis(c, flags)) {
439-			if (putc_vis(&dst, &dlen, c) == -1)
440-				goto trunc;
441+	if (flags & VIS_GLOB)
442+		for (s = char_glob; *s; *d++ = *s++)
443 			continue;
444+
445+	if (flags & VIS_SHELL)
446+		for (s = char_shell; *s; *d++ = *s++)
447+			continue;
448+
449+	if (flags & VIS_SP) *d++ = L' ';
450+	if (flags & VIS_TAB) *d++ = L'\t';
451+	if (flags & VIS_NL) *d++ = L'\n';
452+	if (flags & VIS_DQ) *d++ = L'"';
453+	if ((flags & VIS_NOSLASH) == 0) *d++ = L'\\';
454+	*d = L'\0';
455+
456+	return dst;
457+}
458+
459+/*
460+ * istrsenvisx()
461+ * 	The main internal function.
462+ *	All user-visible functions call this one.
463+ */
464+static int
465+istrsenvisx(char **mbdstp, size_t *dlen, const char *mbsrc, size_t mblength,
466+    int flags, const char *mbextra, int *cerr_ptr)
467+{
468+	char mbbuf[MB_LEN_MAX];
469+	wchar_t *dst, *src, *pdst, *psrc, *start, *extra;
470+	size_t len, olen;
471+	uint64_t bmsk, wmsk;
472+	wint_t c;
473+	visfun_t f;
474+	int cerr, error = -1, i, shft;
475+	ssize_t clen = 0;
476+	char *mbdst, *mbwrite, *mdst;
477+	size_t mbslength;
478+	size_t maxolen;
479+	mbstate_t mbstate;
480+
481+	_DIAGASSERT(mbdstp != NULL);
482+	_DIAGASSERT(mbsrc != NULL || mblength == 0);
483+	_DIAGASSERT(mbextra != NULL);
484+
485+	mbslength = mblength;
486+	/*
487+	 * When inputing a single character, must also read in the
488+	 * next character for nextc, the look-ahead character.
489+	 */
490+	if (mbslength == 1)
491+		mbslength++;
492+
493+	/*
494+	 * Input (mbsrc) is a char string considered to be multibyte
495+	 * characters.  The input loop will read this string pulling
496+	 * one character, possibly multiple bytes, from mbsrc and
497+	 * converting each to wchar_t in src.
498+	 *
499+	 * The vis conversion will be done using the wide char
500+	 * wchar_t string.
501+	 *
502+	 * This will then be converted back to a multibyte string to
503+	 * return to the caller.
504+	 */
505+
506+	/*
507+	 * Guarantee the arithmetic on input to calloc won't overflow.
508+	 */
509+	if (mbslength > (SIZE_MAX - 1)/16) {
510+		errno = ENOMEM;
511+		return -1;
512+	}
513+
514+	/* Allocate space for the wide char strings */
515+	psrc = pdst = extra = NULL;
516+	mdst = NULL;
517+	if ((psrc = calloc(mbslength + 1, sizeof(*psrc))) == NULL)
518+		return -1;
519+	if ((pdst = calloc((16 * mbslength) + 1, sizeof(*pdst))) == NULL)
520+		goto out;
521+	if (*mbdstp == NULL) {
522+		if ((mdst = calloc((16 * mbslength) + 1, sizeof(*mdst))) == NULL)
523+			goto out;
524+		*mbdstp = mdst;
525+	}
526+
527+	mbdst = *mbdstp;
528+	dst = pdst;
529+	src = psrc;
530+
531+	if (flags & VIS_NOLOCALE) {
532+		/* Do one byte at a time conversion */
533+		cerr = 1;
534+	} else {
535+		/* Use caller's multibyte conversion error flag. */
536+		cerr = cerr_ptr ? *cerr_ptr : 0;
537+	}
538+
539+	/*
540+	 * Input loop.
541+	 * Handle up to mblength characters (not bytes).  We do not
542+	 * stop at NULs because we may be processing a block of data
543+	 * that includes NULs.
544+	 */
545+	memset(&mbstate, 0, sizeof(mbstate));
546+	while (mbslength > 0) {
547+		/* Convert one multibyte character to wchar_t. */
548+		if (!cerr) {
549+			clen = (ssize_t)mbrtowc(src, mbsrc,
550+			    (mbslength < MB_LEN_MAX
551+				? mbslength
552+				: MB_LEN_MAX),
553+			    &mbstate);
554+			assert(clen < 0 || (size_t)clen <= mbslength);
555+			assert(clen <= MB_LEN_MAX);
556 		}
557-		if ((flags & VIS_CSTYLE) != 0) {
558-			if (put_cstyle(&dst, &dlen, c) == -1)
559-				goto trunc;
560-		} else {
561-			if (put_oct(&dst, &dlen, c) == -1)
562-				goto trunc;
563+		if (cerr || clen < 0) {
564+			/* Conversion error, process as a byte instead. */
565+			*src = (wint_t)(u_char)*mbsrc;
566+			clen = 1;
567+			cerr = 1;
568+		}
569+		if (clen == 0) {
570+			/*
571+			 * NUL in input gives 0 return value. process
572+			 * as single NUL byte and keep going.
573+			 */
574+			clen = 1;
575+		}
576+		/*
577+		 * Let n := MIN(mbslength, MB_LEN_MAX).  We have:
578+		 *
579+		 *	mbslength >= 1
580+		 *	mbrtowc(..., n, &mbstate) <= n,
581+		 *		by the contract of mbrtowc
582+		 *
583+		 *  clen is either
584+		 *  (a) mbrtowc(..., n, &mbstate), in which case
585+		 *      clen <= n <= mbslength; or
586+		 *  (b) 1, in which case clen = 1 <= mbslength.
587+		 */
588+		assert(clen > 0);
589+		assert((size_t)clen <= mbslength);
590+		/* Advance buffer character pointer. */
591+		src++;
592+		/* Advance input pointer by number of bytes read. */
593+		mbsrc += clen;
594+		/* Decrement input byte count. */
595+		mbslength -= clen;
596+	}
597+	len = src - psrc;
598+	src = psrc;
599+
600+	/*
601+	 * In the single character input case, we will have actually
602+	 * processed two characters, c and nextc.  Reset len back to
603+	 * just a single character.
604+	 */
605+	if (mblength < len)
606+		len = mblength;
607+
608+	/* Convert extra argument to list of characters for this mode. */
609+	extra = makeextralist(flags, mbextra);
610+	if (!extra) {
611+		if (dlen && *dlen == 0) {
612+			errno = ENOSPC;
613+			goto out;
614 		}
615+		*mbdst = '\0';	/* can't create extra, return "" */
616+		error = 0;
617+		goto out;
618 	}
619 
620-	*dst = '\0';
621-	return (int)(dst - start);
622+	/* Look up which processing function to call. */
623+	f = getvisfun(flags);
624+
625+	/*
626+	 * Main processing loop.
627+	 * Call do_Xvis processing function one character at a time
628+	 * with next character available for look-ahead.
629+	 */
630+	for (start = dst; len > 0; len--) {
631+		c = *src++;
632+		dst = (*f)(dst, c, flags, len >= 1 ? *src : L'\0', extra);
633+		if (dst == NULL) {
634+			errno = ENOSPC;
635+			goto out;
636+		}
637+	}
638+
639+	/* Terminate the string in the buffer. */
640+	*dst = L'\0';
641+
642+	/*
643+	 * Output loop.
644+	 * Convert wchar_t string back to multibyte output string.
645+	 * If we have hit a multi-byte conversion error on input,
646+	 * output byte-by-byte here.  Else use wctomb().
647+	 */
648+	len = wcslen(start);
649+	if (dlen) {
650+		maxolen = *dlen;
651+		if (maxolen == 0) {
652+			errno = ENOSPC;
653+			goto out;
654+		}
655+	} else {
656+		if (len > (SIZE_MAX - 1)/MB_LEN_MAX) {
657+			errno = ENOSPC;
658+			goto out;
659+		}
660+		maxolen = len*MB_LEN_MAX + 1;
661+	}
662+	olen = 0;
663+	memset(&mbstate, 0, sizeof(mbstate));
664+	for (dst = start; len > 0; len--) {
665+		if (!cerr) {
666+			/*
667+			 * If we have at least MB_CUR_MAX bytes in the buffer,
668+			 * we'll just do the conversion in-place into mbdst.  We
669+			 * need to be a little more conservative when we get to
670+			 * the end of the buffer, as we may not have MB_CUR_MAX
671+			 * bytes but we may not need it.
672+			 */
673+			if (maxolen - olen > MB_CUR_MAX)
674+				mbwrite = mbdst;
675+			else
676+				mbwrite = mbbuf;
677+			clen = (ssize_t)wcrtomb(mbwrite, *dst, &mbstate);
678+			if (clen > 0 && mbwrite != mbdst) {
679+				/*
680+				 * Don't break past our output limit, noting
681+				 * that maxolen includes the nul terminator so
682+				 * we can't write past maxolen - 1 here.
683+				 */
684+				if (olen + clen >= maxolen) {
685+					errno = ENOSPC;
686+					goto out;
687+				}
688+
689+				memcpy(mbdst, mbwrite, clen);
690+			}
691+		}
692+		if (cerr || clen < 0) {
693+			/*
694+			 * Conversion error, process as a byte(s) instead.
695+			 * Examine each byte and higher-order bytes for
696+			 * data.  E.g.,
697+			 *	0x000000000000a264 -> a2 64
698+			 *	0x000000001f00a264 -> 1f 00 a2 64
699+			 */
700+			clen = 0;
701+			wmsk = 0;
702+			for (i = sizeof(wmsk) - 1; i >= 0; i--) {
703+				shft = i * NBBY;
704+				bmsk = (uint64_t)0xffLL << shft;
705+				wmsk |= bmsk;
706+				if ((*dst & wmsk) || i == 0) {
707+					if (olen + clen + 1 >= maxolen) {
708+						errno = ENOSPC;
709+						goto out;
710+					}
711 
712-trunc:
713-	*dst = '\0';
714-	return -1;
715+					mbdst[clen++] = (char)(
716+					    (uint64_t)(*dst & bmsk) >>
717+					    shft);
718+				}
719+			}
720+			cerr = 1;
721+		}
722+
723+		/*
724+		 * We'll be dereferencing mbdst[clen] after this to write the
725+		 * nul terminator; the above paths should have checked for a
726+		 * possible overflow already.
727+		 */
728+		assert(olen + clen < maxolen);
729+
730+		/* Advance output pointer by number of bytes written. */
731+		mbdst += clen;
732+		/* Advance buffer character pointer. */
733+		dst++;
734+		/* Increment output character count. */
735+		olen += clen;
736+	}
737+
738+	/* Terminate the output string. */
739+	assert(olen < maxolen);
740+	*mbdst = '\0';
741+
742+	if (flags & VIS_NOLOCALE) {
743+		/* Pass conversion error flag out. */
744+		if (cerr_ptr)
745+			*cerr_ptr = cerr;
746+	}
747+
748+	free(extra);
749+	free(pdst);
750+	free(psrc);
751+
752+	return (int)olen;
753+out:
754+	free(extra);
755+	free(pdst);
756+	free(psrc);
757+	free(mdst);
758+	return error;
759+}
760+
761+static int
762+istrsenvisxl(char **mbdstp, size_t *dlen, const char *mbsrc,
763+    int flags, const char *mbextra, int *cerr_ptr)
764+{
765+	return istrsenvisx(mbdstp, dlen, mbsrc,
766+	    mbsrc != NULL ? strlen(mbsrc) : 0, flags, mbextra, cerr_ptr);
767 }
768 
769+#endif
770+
771+#if !HAVE_SVIS
772+/*
773+ *	The "svis" variants all take an "extra" arg that is a pointer
774+ *	to a NUL-terminated list of characters to be encoded, too.
775+ *	These functions are useful e. g. to encode strings in such a
776+ *	way so that they are not interpreted by a shell.
777+ */
778+
779 char *
780-vis(char *dst, int c, int flags, int nextc)
781+svis(char *mbdst, int c, int flags, int nextc, const char *mbextra)
782 {
783-	char src[1];
784+	char cc[2];
785+	int ret;
786+
787+	cc[0] = c;
788+	cc[1] = nextc;
789 
790-	(void)nextc;
791-	src[0] = (char)c;
792-	if (strnvisx(dst, 5, src, 1, flags) == -1)
793+	ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, mbextra, NULL);
794+	if (ret < 0)
795 		return NULL;
796-	while (*dst != '\0')
797-		dst++;
798-	return dst;
799+	return mbdst + ret;
800+}
801+
802+char *
803+snvis(char *mbdst, size_t dlen, int c, int flags, int nextc, const char *mbextra)
804+{
805+	char cc[2];
806+	int ret;
807+
808+	cc[0] = c;
809+	cc[1] = nextc;
810+
811+	ret = istrsenvisx(&mbdst, &dlen, cc, 1, flags, mbextra, NULL);
812+	if (ret < 0)
813+		return NULL;
814+	return mbdst + ret;
815+}
816+
817+int
818+strsvis(char *mbdst, const char *mbsrc, int flags, const char *mbextra)
819+{
820+	return istrsenvisxl(&mbdst, NULL, mbsrc, flags, mbextra, NULL);
821+}
822+
823+int
824+strsnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags, const char *mbextra)
825+{
826+	return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, mbextra, NULL);
827+}
828+
829+int
830+strsvisx(char *mbdst, const char *mbsrc, size_t len, int flags, const char *mbextra)
831+{
832+	return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, mbextra, NULL);
833+}
834+
835+int
836+strsnvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
837+    const char *mbextra)
838+{
839+	return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, mbextra, NULL);
840+}
841+
842+int
843+strsenvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
844+    const char *mbextra, int *cerr_ptr)
845+{
846+	return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, mbextra, cerr_ptr);
847+}
848+#endif
849+
850+#if !HAVE_VIS
851+/*
852+ * vis - visually encode characters
853+ */
854+char *
855+vis(char *mbdst, int c, int flags, int nextc)
856+{
857+	char cc[2];
858+	int ret;
859+
860+	cc[0] = c;
861+	cc[1] = nextc;
862+
863+	ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, "", NULL);
864+	if (ret < 0)
865+		return NULL;
866+	return mbdst + ret;
867+}
868+
869+char *
870+nvis(char *mbdst, size_t dlen, int c, int flags, int nextc)
871+{
872+	char cc[2];
873+	int ret;
874+
875+	cc[0] = c;
876+	cc[1] = nextc;
877+
878+	ret = istrsenvisx(&mbdst, &dlen, cc, 1, flags, "", NULL);
879+	if (ret < 0)
880+		return NULL;
881+	return mbdst + ret;
882+}
883+
884+/*
885+ * strvis - visually encode characters from src into dst
886+ *
887+ *	Dst must be 4 times the size of src to account for possible
888+ *	expansion.  The length of dst, not including the trailing NULL,
889+ *	is returned.
890+ */
891+
892+int
893+strvis(char *mbdst, const char *mbsrc, int flags)
894+{
895+	return istrsenvisxl(&mbdst, NULL, mbsrc, flags, "", NULL);
896+}
897+
898+int
899+strnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags)
900+{
901+	return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, "", NULL);
902+}
903+
904+int
905+stravis(char **mbdstp, const char *mbsrc, int flags)
906+{
907+	*mbdstp = NULL;
908+	return istrsenvisxl(mbdstp, NULL, mbsrc, flags, "", NULL);
909+}
910+
911+/*
912+ * strvisx - visually encode characters from src into dst
913+ *
914+ *	Dst must be 4 times the size of src to account for possible
915+ *	expansion.  The length of dst, not including the trailing NULL,
916+ *	is returned.
917+ *
918+ *	Strvisx encodes exactly len characters from src into dst.
919+ *	This is useful for encoding a block of data.
920+ */
921+
922+int
923+strvisx(char *mbdst, const char *mbsrc, size_t len, int flags)
924+{
925+	return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, "", NULL);
926+}
927+
928+int
929+strnvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags)
930+{
931+	return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", NULL);
932+}
933+
934+int
935+strenvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
936+    int *cerr_ptr)
937+{
938+	return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", cerr_ptr);
939 }
940+#endif
+110, -11
  1@@ -1,22 +1,121 @@
  2+/*	$NetBSD: vis.h,v 1.26 2022/05/20 21:31:24 andvar Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1990, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. Neither the name of the University nor the names of its contributors
 17+ *    may be used to endorse or promote products derived from this software
 18+ *    without specific prior written permission.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 30+ * SUCH DAMAGE.
 31+ *
 32+ *	@(#)vis.h	8.1 (Berkeley) 6/2/93
 33+ */
 34+
 35 #ifndef _VIS_H_
 36-#define _VIS_H_
 37+#define	_VIS_H_
 38 
 39 #include <sys/types.h>
 40-#include <sys/cdefs.h>
 41 
 42-#define	VIS_OCTAL	0x0001
 43-#define	VIS_CSTYLE	0x0002
 44-#define	VIS_SP		0x0004
 45-#define	VIS_TAB		0x0008
 46-#define	VIS_NL		0x0010
 47+/*
 48+ * to select alternate encoding format
 49+ */
 50+#define	VIS_OCTAL	0x0001	/* use octal \ddd format */
 51+#define	VIS_CSTYLE	0x0002	/* use \[nrft0..] where appropriate */
 52+
 53+/*
 54+ * to alter set of characters encoded (default is to encode all
 55+ * non-graphic except space, tab, and newline).
 56+ */
 57+#define	VIS_SP		0x0004	/* also encode space */
 58+#define	VIS_TAB		0x0008	/* also encode tab */
 59+#define	VIS_NL		0x0010	/* also encode newline */
 60 #define	VIS_WHITE	(VIS_SP | VIS_TAB | VIS_NL)
 61-#define	VIS_SAFE	0x0020
 62-#define	VIS_DQ		0x8000
 63-#define	VIS_NOSLASH	0x0040
 64+#define	VIS_SAFE	0x0020	/* only encode "unsafe" characters */
 65+#define	VIS_DQ		0x8000	/* also encode double quotes */
 66+
 67+/*
 68+ * other
 69+ */
 70+#define	VIS_NOSLASH	0x0040	/* inhibit printing '\' */
 71+#define	VIS_HTTP1808	0x0080	/* http-style escape % hex hex */
 72+#define	VIS_HTTPSTYLE	0x0080	/* http-style escape % hex hex */
 73+#define	VIS_MIMESTYLE	0x0100	/* mime-style escape = HEX HEX */
 74+#define	VIS_HTTP1866	0x0200	/* http-style &#num; or &string; */
 75+#define	VIS_NOESCAPE	0x0400	/* don't decode `\' */
 76+#define	_VIS_END	0x0800	/* for unvis */
 77+#define	VIS_GLOB	0x1000	/* encode glob(3) magic characters */
 78+#define	VIS_SHELL	0x2000	/* encode shell special characters [not glob] */
 79+#define	VIS_META	(VIS_WHITE | VIS_GLOB | VIS_SHELL)
 80+#define	VIS_NOLOCALE	0x4000	/* encode using the C locale */
 81+
 82+/*
 83+ * unvis return codes
 84+ */
 85+#define	UNVIS_VALID	 1	/* character valid */
 86+#define	UNVIS_VALIDPUSH	 2	/* character valid, push back passed char */
 87+#define	UNVIS_NOCHAR	 3	/* valid sequence, no character produced */
 88+#define	UNVIS_SYNBAD	-1	/* unrecognized escape sequence */
 89+#define	UNVIS_ERROR	-2	/* decoder in unknown state (unrecoverable) */
 90+
 91+/*
 92+ * unvis flags
 93+ */
 94+#define	UNVIS_END	_VIS_END	/* no more characters */
 95+
 96+#include <sys/cdefs.h>
 97 
 98 __BEGIN_DECLS
 99 char	*vis(char *, int, int, int);
100+char	*nvis(char *, size_t, int, int, int);
101+
102+char	*svis(char *, int, int, int, const char *);
103+char	*snvis(char *, size_t, int, int, int, const char *);
104+
105+int	strvis(char *, const char *, int);
106+int	stravis(char **, const char *, int);
107+int	strnvis(char *, size_t, const char *, int);
108+
109+int	strsvis(char *, const char *, int, const char *);
110+int	strsnvis(char *, size_t, const char *, int, const char *);
111+
112+int	strvisx(char *, const char *, size_t, int);
113 int	strnvisx(char *, size_t, const char *, size_t, int);
114-__END_DECLS
115+int 	strenvisx(char *, size_t, const char *, size_t, int, int *);
116+
117+int	strsvisx(char *, const char *, size_t, int, const char *);
118+int	strsnvisx(char *, size_t, const char *, size_t, int, const char *);
119+int	strsenvisx(char *, size_t, const char *, size_t , int, const char *,
120+    int *);
121 
122+int	strunvis(char *, const char *);
123+int	strnunvis(char *, size_t, const char *);
124+
125+int	strunvisx(char *, const char *, int);
126+int	strnunvisx(char *, size_t, const char *, int);
127+
128+#ifndef __LIBC12_SOURCE__
129+int	unvis(char *, int, int *, int) __RENAME(__unvis50);
130 #endif
131+__END_DECLS
132+
133+#endif /* !_VIS_H_ */
+7, -4
 1@@ -1,8 +1,8 @@
 2 .POSIX:
 3 
 4 PROG = ctags
 5-SRCS = C.c ctags.c fortran.c lisp.c print.c tree.c yacc.c \
 6-	../compat/strlcpy.c ../compat/shquote.c
 7+SRCS = C.c ctags.c fortran.c lisp.c print.c tree.c yacc.c
 8+COMPATLIB = ../compat/libnetcompat.a
 9 MAN = ctags.1
10 
11 CC = cc
12@@ -14,8 +14,11 @@ MANDIR = /usr/local/share/man
13 
14 all: $(PROG)
15 
16-$(PROG): $(SRCS)
17-	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
18+$(PROG): $(SRCS) $(COMPATLIB)
19+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
20+
21+$(COMPATLIB):
22+	(cd ../compat && $(MAKE) all)
23 
24 install: $(PROG)
25 	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
+7, -5
 1@@ -1,9 +1,8 @@
 2 .POSIX:
 3 
 4 PROG = finger
 5-SRCS = finger.c lprint.c net.c sprint.c util.c utmpentry.c \
 6-	../compat/strlcpy.c ../compat/reallocarr.c ../compat/db.c \
 7-	../compat/vis.c
 8+SRCS = finger.c lprint.c net.c sprint.c util.c utmpentry.c
 9+COMPATLIB = ../compat/libnetcompat.a
10 MAN = finger.1
11 
12 CC = cc
13@@ -16,8 +15,11 @@ MANDIR = /usr/local/share/man
14 
15 all: $(PROG)
16 
17-$(PROG): $(SRCS)
18-	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
19+$(PROG): $(SRCS) $(COMPATLIB)
20+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
21+
22+$(COMPATLIB):
23+	(cd ../compat && $(MAKE) all)
24 
25 install: $(PROG)
26 	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
+7, -3
 1@@ -1,7 +1,8 @@
 2 .POSIX:
 3 
 4 PROG = jot
 5-SRCS = jot.c ../compat/getprogname.c ../compat/strlcpy.c
 6+SRCS = jot.c
 7+COMPATLIB = ../compat/libnetcompat.a
 8 MAN = jot.1
 9 
10 CC = cc
11@@ -14,8 +15,11 @@ MANDIR = /usr/local/share/man
12 
13 all: $(PROG)
14 
15-$(PROG): $(SRCS)
16-	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
17+$(PROG): $(SRCS) $(COMPATLIB)
18+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
19+
20+$(COMPATLIB):
21+	(cd ../compat && $(MAKE) all)
22 
23 install: $(PROG)
24 	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
+6, -2
 1@@ -2,6 +2,7 @@
 2 
 3 PROG = look
 4 SRCS = look.c
 5+COMPATLIB = ../compat/libnetcompat.a
 6 MAN = look.1
 7 DICT = words
 8 
 9@@ -15,8 +16,11 @@ DATADIR = /usr/local/share
10 
11 all: $(PROG)
12 
13-$(PROG): $(SRCS)
14-	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
15+$(PROG): $(SRCS) $(COMPATLIB)
16+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
17+
18+$(COMPATLIB):
19+	(cd ../compat && $(MAKE) all)
20 
21 install: $(PROG)
22 	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1 \
+40, -0
 1@@ -0,0 +1,40 @@
 2+.POSIX:
 3+
 4+PROG = makefs
 5+COMPATLIB = ../compat/libnetcompat.a
 6+MAN = makefs.8
 7+
 8+CC = cc
 9+CFLAGS = -O2
10+CPPFLAGS = -DHAVE_NBTOOL_CONFIG_H=1 -I. -I./cd9660 -I./mtree -I./fs/cd9660 -I../compat -include ../compat/netcompat.h
11+LDLIBS = -lm -lz
12+
13+SRCS = \
14+	makefs.c walk.c \
15+	cd9660.c \
16+	cd9660/cd9660_strings.c cd9660/cd9660_debug.c cd9660/cd9660_eltorito.c \
17+	cd9660/cd9660_write.c cd9660/cd9660_conversion.c cd9660/iso9660_rrip.c \
18+	cd9660/cd9660_archimedes.c \
19+	mtree/getid.c mtree/misc.c mtree/spec.c mtree/pack_dev.c mtree/stat_flags.c
20+
21+DESTDIR =
22+BINDIR = /usr/local/bin
23+MANDIR = /usr/local/share/man
24+
25+all: $(PROG)
26+
27+$(PROG): $(SRCS) $(COMPATLIB)
28+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
29+
30+$(COMPATLIB):
31+	(cd ../compat && $(MAKE) all)
32+
33+install: $(PROG)
34+	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
35+	cp $(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
36+	chmod 755 $(DESTDIR)$(BINDIR)/$(PROG)
37+	cp $(MAN) $(DESTDIR)$(MANDIR)/man8/$(MAN)
38+	chmod 644 $(DESTDIR)$(MANDIR)/man8/$(MAN)
39+
40+clean:
41+	rm -f $(PROG) *.o */*.o */*/*.o
+2153, -0
   1@@ -0,0 +1,2153 @@
   2+/*	$NetBSD: cd9660.c,v 1.61 2026/01/07 16:04:51 nia Exp $	*/
   3+
   4+/*
   5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
   6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
   7+ *
   8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
   9+ * Alan Perez-Rathke and Ram Vedam.
  10+ *
  11+ * Redistribution and use in source and binary forms, with or
  12+ * without modification, are permitted provided that the following
  13+ * conditions are met:
  14+ * 1. Redistributions of source code must retain the above copyright
  15+ *    notice, this list of conditions and the following disclaimer.
  16+ * 2. Redistributions in binary form must reproduce the above
  17+ *    copyright notice, this list of conditions and the following
  18+ *    disclaimer in the documentation and/or other materials provided
  19+ *    with the distribution.
  20+ *
  21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
  22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
  23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
  26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
  27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  33+ * OF SUCH DAMAGE.
  34+ */
  35+/*
  36+ * Copyright (c) 2001 Wasabi Systems, Inc.
  37+ * All rights reserved.
  38+ *
  39+ * Written by Luke Mewburn for Wasabi Systems, Inc.
  40+ *
  41+ * Redistribution and use in source and binary forms, with or without
  42+ * modification, are permitted provided that the following conditions
  43+ * are met:
  44+ * 1. Redistributions of source code must retain the above copyright
  45+ *    notice, this list of conditions and the following disclaimer.
  46+ * 2. Redistributions in binary form must reproduce the above copyright
  47+ *    notice, this list of conditions and the following disclaimer in the
  48+ *    documentation and/or other materials provided with the distribution.
  49+ * 3. All advertising materials mentioning features or use of this software
  50+ *    must display the following acknowledgement:
  51+ *      This product includes software developed for the NetBSD Project by
  52+ *      Wasabi Systems, Inc.
  53+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
  54+ *    or promote products derived from this software without specific prior
  55+ *    written permission.
  56+ *
  57+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
  58+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  59+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  60+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
  61+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  62+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  63+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  64+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  65+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  66+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  67+ * POSSIBILITY OF SUCH DAMAGE.
  68+ */
  69+/*
  70+ * Copyright (c) 1982, 1986, 1989, 1993
  71+ *	The Regents of the University of California.  All rights reserved.
  72+ *
  73+ * Redistribution and use in source and binary forms, with or without
  74+ * modification, are permitted provided that the following conditions
  75+ * are met:
  76+ * 1. Redistributions of source code must retain the above copyright
  77+ *    notice, this list of conditions and the following disclaimer.
  78+ * 2. Redistributions in binary form must reproduce the above copyright
  79+ *    notice, this list of conditions and the following disclaimer in the
  80+ *    documentation and/or other materials provided with the distribution.
  81+ * 3. Neither the name of the University nor the names of its contributors
  82+ *    may be used to endorse or promote products derived from this software
  83+ *    without specific prior written permission.
  84+ *
  85+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  86+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  87+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  88+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  89+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  90+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  91+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  92+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  93+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  94+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  95+ * SUCH DAMAGE.
  96+ *
  97+  */
  98+
  99+#if HAVE_NBTOOL_CONFIG_H
 100+#include "nbtool_config.h"
 101+#else
 102+#include <sys/mount.h>
 103+#endif
 104+
 105+#include <sys/cdefs.h>
 106+#if defined(__RCSID) && !defined(__lint)
 107+__RCSID("$NetBSD: cd9660.c,v 1.61 2026/01/07 16:04:51 nia Exp $");
 108+#endif  /* !__lint */
 109+
 110+#include <string.h>
 111+#include <ctype.h>
 112+#include <sys/param.h>
 113+#include <sys/queue.h>
 114+#include <util.h>
 115+
 116+#include "makefs.h"
 117+#include "cd9660.h"
 118+#include "cd9660/iso9660_rrip.h"
 119+#include "cd9660/cd9660_archimedes.h"
 120+
 121+/*
 122+ * Global variables
 123+ */
 124+
 125+static void cd9660_finalize_PVD(iso9660_disk *);
 126+static cd9660node *cd9660_allocate_cd9660node(void);
 127+static void cd9660_set_defaults(iso9660_disk *);
 128+static int cd9660_arguments_set_string(const char *, const char *, size_t,
 129+    char, char *);
 130+static void cd9660_populate_iso_dir_record(
 131+    struct _iso_directory_record_cd9660 *, u_char, u_char, u_char,
 132+    const char *);
 133+static void cd9660_setup_root_node(iso9660_disk *);
 134+static int cd9660_setup_volume_descriptors(iso9660_disk *);
 135+#if 0
 136+static int cd9660_fill_extended_attribute_record(cd9660node *);
 137+#endif
 138+static void cd9660_sort_nodes(cd9660node *);
 139+static int cd9660_translate_node_common(iso9660_disk *, cd9660node *);
 140+static int cd9660_translate_node(iso9660_disk *, fsnode *, cd9660node *);
 141+static int cd9660_compare_filename(const char *, const char *);
 142+static void cd9660_sorted_child_insert(cd9660node *, cd9660node *);
 143+static int cd9660_handle_collisions(iso9660_disk *, cd9660node *, int);
 144+static cd9660node *cd9660_rename_filename(iso9660_disk *, cd9660node *, int,
 145+    int);
 146+static void cd9660_copy_filenames(iso9660_disk *, cd9660node *);
 147+static void cd9660_sorting_nodes(cd9660node *);
 148+static int cd9660_count_collisions(cd9660node *);
 149+static cd9660node *cd9660_rrip_move_directory(iso9660_disk *, cd9660node *);
 150+static int cd9660_add_dot_records(iso9660_disk *, cd9660node *);
 151+
 152+static void cd9660_convert_structure(iso9660_disk *, fsnode *, cd9660node *, int,
 153+    int *, int *);
 154+static void cd9660_free_structure(cd9660node *);
 155+static int cd9660_generate_path_table(iso9660_disk *);
 156+static int cd9660_level1_convert_filename(iso9660_disk *, const char *, char *,
 157+    int);
 158+static int cd9660_level2_convert_filename(iso9660_disk *, const char *, char *,
 159+    int);
 160+#if 0
 161+static int cd9660_joliet_convert_filename(iso9660_disk *, const char *, char *,
 162+    int);
 163+#endif
 164+static int cd9660_convert_filename(iso9660_disk *, const char *, char *, int);
 165+static void cd9660_populate_dot_records(iso9660_disk *, cd9660node *);
 166+static int64_t cd9660_compute_offsets(iso9660_disk *, cd9660node *, int64_t);
 167+#if 0
 168+static int cd9660_copy_stat_info(cd9660node *, cd9660node *, int);
 169+#endif
 170+static cd9660node *cd9660_create_virtual_entry(iso9660_disk *, const char *,
 171+    cd9660node *, int, int);
 172+static cd9660node *cd9660_create_file(iso9660_disk *, const char *,
 173+    cd9660node *, cd9660node *);
 174+static cd9660node *cd9660_create_directory(iso9660_disk *, const char *,
 175+    cd9660node *, cd9660node *);
 176+static cd9660node *cd9660_create_special_directory(iso9660_disk *, u_char,
 177+    cd9660node *);
 178+static int  cd9660_add_generic_bootimage(iso9660_disk *, const char *);
 179+
 180+
 181+/*
 182+ * Allocate and initialize a cd9660node
 183+ * @returns struct cd9660node * Pointer to new node, or NULL on error
 184+ */
 185+static cd9660node *
 186+cd9660_allocate_cd9660node(void)
 187+{
 188+	cd9660node *temp = ecalloc(1, sizeof(*temp));
 189+	TAILQ_INIT(&temp->cn_children);
 190+	temp->parent = temp->dot_record = temp->dot_dot_record = NULL;
 191+	temp->ptnext = temp->ptprev = temp->ptlast = NULL;
 192+	temp->node = NULL;
 193+	temp->isoDirRecord = NULL;
 194+	temp->isoExtAttributes = NULL;
 195+	temp->rr_real_parent = temp->rr_relocated = NULL;
 196+	temp->su_tail_data = NULL;
 197+	return temp;
 198+}
 199+
 200+int cd9660_defaults_set = 0;
 201+
 202+/**
 203+* Set default values for cd9660 extension to makefs
 204+*/
 205+static void
 206+cd9660_set_defaults(iso9660_disk *diskStructure)
 207+{
 208+	/*Fix the sector size for now, though the spec allows for other sizes*/
 209+	diskStructure->sectorSize = 2048;
 210+
 211+	/* Set up defaults in our own structure */
 212+	diskStructure->verbose_level = 0;
 213+	diskStructure->keep_bad_images = 0;
 214+	diskStructure->follow_sym_links = 0;
 215+	diskStructure->isoLevel = 2;
 216+
 217+	diskStructure->rock_ridge_enabled = 0;
 218+	diskStructure->rock_ridge_renamed_dir_name = 0;
 219+	diskStructure->rock_ridge_move_count = 0;
 220+	diskStructure->rr_moved_dir = 0;
 221+
 222+	diskStructure->archimedes_enabled = 0;
 223+	diskStructure->chrp_boot = 0;
 224+
 225+	diskStructure->include_padding_areas = 1;
 226+
 227+	/* Spec breaking functionality */
 228+	diskStructure->allow_deep_trees =
 229+	    diskStructure->allow_start_dot =
 230+	    diskStructure->allow_max_name =
 231+	    diskStructure->allow_illegal_chars =
 232+	    diskStructure->allow_lowercase =
 233+	    diskStructure->allow_multidot =
 234+	    diskStructure->omit_trailing_period = 0;
 235+
 236+	/* Make sure the PVD is clear */
 237+	memset(&diskStructure->primaryDescriptor, 0, 2048);
 238+
 239+	memset(diskStructure->primaryDescriptor.publisher_id,	0x20,128);
 240+	memset(diskStructure->primaryDescriptor.preparer_id,	0x20,128);
 241+	memset(diskStructure->primaryDescriptor.application_id,	0x20,128);
 242+	memset(diskStructure->primaryDescriptor.copyright_file_id, 0x20,37);
 243+	memset(diskStructure->primaryDescriptor.abstract_file_id, 0x20,37);
 244+	memset(diskStructure->primaryDescriptor.bibliographic_file_id, 0x20,37);
 245+
 246+	strcpy(diskStructure->primaryDescriptor.system_id,"NetBSD");
 247+
 248+	cd9660_defaults_set = 1;
 249+
 250+	/* Boot support: Initially disabled */
 251+	diskStructure->has_generic_bootimage = 0;
 252+	diskStructure->generic_bootimage = NULL;
 253+
 254+	diskStructure->boot_image_directory = 0;
 255+	/*memset(diskStructure->boot_descriptor, 0, 2048);*/
 256+
 257+	diskStructure->is_bootable = 0;
 258+	TAILQ_INIT(&diskStructure->boot_images);
 259+	LIST_INIT(&diskStructure->boot_entries);
 260+}
 261+
 262+void
 263+cd9660_prep_opts(fsinfo_t *fsopts)
 264+{
 265+	iso9660_disk *diskStructure = ecalloc(1, sizeof(*diskStructure));
 266+
 267+#define OPT_STR(letter, name, desc)  \
 268+	{ letter, name, NULL, OPT_STRBUF, 0, 0, desc }
 269+
 270+#define OPT_NUM(letter, name, field, min, max, desc) \
 271+	{ letter, name, &diskStructure->field, \
 272+	  sizeof(diskStructure->field) == 8 ? OPT_INT64 : \
 273+	  (sizeof(diskStructure->field) == 4 ? OPT_INT32 : \
 274+	  (sizeof(diskStructure->field) == 2 ? OPT_INT16 : OPT_INT8)), \
 275+	  min, max, desc }
 276+
 277+#define OPT_BOOL(letter, name, field, desc) \
 278+	OPT_NUM(letter, name, field, 0, 1, desc)
 279+
 280+	const option_t cd9660_options[] = {
 281+		OPT_NUM('l', "isolevel", isoLevel,
 282+		    1, 3, "ISO Level"),
 283+		OPT_NUM('v', "verbose",  verbose_level,
 284+		    0, 2, "Turns on verbose output"),
 285+
 286+		OPT_BOOL('h', "help", displayHelp,
 287+		    "Show help message"),
 288+		OPT_BOOL('S', "follow-symlinks", follow_sym_links,
 289+		    "Resolve symlinks in pathnames"),
 290+	        OPT_BOOL('R', "rockridge", rock_ridge_enabled,
 291+		    "Enable Rock-Ridge extensions"),
 292+	        OPT_BOOL('C', "chrp-boot", chrp_boot,
 293+		    "Enable CHRP boot"),
 294+	        OPT_BOOL('K', "keep-bad-images", keep_bad_images,
 295+		    "Keep bad images"),
 296+	        OPT_BOOL('D', "allow-deep-trees", allow_deep_trees,
 297+		    "Allow trees more than 8 levels"),
 298+	        OPT_BOOL('a', "allow-max-name", allow_max_name,
 299+		    "Allow 37 char filenames (unimplemented)"),
 300+	        OPT_BOOL('i', "allow-illegal-chars", allow_illegal_chars,
 301+		    "Allow illegal characters in filenames"),
 302+	        OPT_BOOL('m', "allow-multidot", allow_multidot,
 303+		    "Allow multiple periods in filenames"),
 304+	        OPT_BOOL('o', "omit-trailing-period", omit_trailing_period,
 305+		    "Omit trailing periods in filenames"),
 306+	        OPT_BOOL('\0', "allow-lowercase", allow_lowercase,
 307+		    "Allow lowercase characters in filenames"),
 308+	        OPT_BOOL('\0', "archimedes", archimedes_enabled,
 309+		    "Enable Archimedes structure"),
 310+		OPT_BOOL('\0', "no-trailing-padding", include_padding_areas,
 311+		    "Include padding areas"),
 312+
 313+		OPT_STR('A', "applicationid", "Application Identifier"),
 314+		OPT_STR('P', "publisher", "Publisher Identifier"),
 315+		OPT_STR('p', "preparer", "Preparer Identifier"),
 316+		OPT_STR('L', "label", "Disk Label"),
 317+		OPT_STR('V', "volumeid", "Volume Set Identifier"),
 318+		OPT_STR('B', "bootimage", "Boot image parameter"),
 319+		OPT_STR('G', "generic-bootimage", "Generic boot image param"),
 320+		OPT_STR('\0', "bootimagedir", "Boot image directory"),
 321+		OPT_STR('\0', "no-emul-boot", "No boot emulation"),
 322+		OPT_STR('\0', "no-boot", "No boot support"),
 323+		OPT_STR('\0', "hard-disk-boot", "Boot from hard disk"),
 324+		OPT_STR('\0', "boot-load-segment", "Boot load segment"),
 325+		OPT_STR('\0', "platformid", "Section Header Platform ID"),
 326+
 327+		{ .name = NULL }
 328+	};
 329+
 330+	fsopts->fs_specific = diskStructure;
 331+	fsopts->fs_options = copy_opts(cd9660_options);
 332+
 333+	cd9660_set_defaults(diskStructure);
 334+}
 335+
 336+void
 337+cd9660_cleanup_opts(fsinfo_t *fsopts)
 338+{
 339+	free(fsopts->fs_specific);
 340+	free(fsopts->fs_options);
 341+}
 342+
 343+static int
 344+cd9660_arguments_set_string(const char *val, const char *fieldtitle,
 345+    size_t length, char testmode, char * dest)
 346+{
 347+	size_t len;
 348+	int test;
 349+
 350+	if (val == NULL)
 351+		warnx("error: The %s requires a string argument", fieldtitle);
 352+	else if ((len = strlen(val)) <= length) {
 353+		if (testmode == 'd')
 354+			test = cd9660_valid_d_chars(val);
 355+		else
 356+			test = cd9660_valid_a_chars(val);
 357+		if (test) {
 358+			memcpy(dest, val, len);
 359+			if (test == 2)
 360+				cd9660_uppercase_characters(dest, len);
 361+			return 1;
 362+		} else
 363+			warnx("error: The %s must be composed of "
 364+			      "%c-characters", fieldtitle, testmode);
 365+	} else
 366+		warnx("error: The %s must be at most 32 characters long",
 367+		    fieldtitle);
 368+	return 0;
 369+}
 370+
 371+/*
 372+ * Command-line parsing function
 373+ */
 374+
 375+int
 376+cd9660_parse_opts(const char *option, fsinfo_t *fsopts)
 377+{
 378+	int	rv, i;
 379+	iso9660_disk *diskStructure = fsopts->fs_specific;
 380+	option_t *cd9660_options = fsopts->fs_options;
 381+	char buf[1024];
 382+	const char *name, *desc;
 383+
 384+	assert(option != NULL);
 385+
 386+	if (debug & DEBUG_FS_PARSE_OPTS)
 387+		printf("%s: got `%s'\n", __func__, option);
 388+
 389+	i = set_option(cd9660_options, option, buf, sizeof(buf));
 390+	if (i == -1)
 391+		return 0;
 392+
 393+	if (cd9660_options[i].name == NULL)
 394+		abort();
 395+
 396+
 397+	name = cd9660_options[i].name;
 398+	desc = cd9660_options[i].desc;
 399+	switch (cd9660_options[i].letter) {
 400+	case 'h':
 401+	case 'S':
 402+		rv = 0;	/* this is not handled yet */
 403+		break;
 404+	case 'L':
 405+		rv = cd9660_arguments_set_string(buf, desc, 32, 'd',
 406+		    diskStructure->primaryDescriptor.volume_id);
 407+		break;
 408+	case 'A':
 409+		rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
 410+		    diskStructure->primaryDescriptor.application_id);
 411+		break;
 412+	case 'P':
 413+		rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
 414+		    diskStructure->primaryDescriptor.publisher_id);
 415+		break;
 416+	case 'p':
 417+		rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
 418+		    diskStructure->primaryDescriptor.preparer_id);
 419+		break;
 420+	case 'V':
 421+		rv = cd9660_arguments_set_string(buf, desc, 128, 'a',
 422+		    diskStructure->primaryDescriptor.volume_set_id);
 423+		break;
 424+	/* Boot options */
 425+	case 'B':
 426+		if (buf[0] == '\0') {
 427+			warnx("The Boot Image parameter requires a valid boot"
 428+			" information string");
 429+			rv = 0;
 430+		} else
 431+			rv = cd9660_add_boot_disk(diskStructure, buf);
 432+		break;
 433+	case 'G':
 434+		if (buf[0] == '\0') {
 435+			warnx("The Generic Boot Image parameter requires a"
 436+			    " valid boot information string");
 437+			rv = 0;
 438+		} else
 439+			rv = cd9660_add_generic_bootimage(diskStructure, buf);
 440+		break;
 441+	default:
 442+		if (strcmp(name, "bootimagedir") == 0) {
 443+			/*
 444+			 * XXXfvdl this is unused.
 445+			 */
 446+			if (buf[0] == '\0') {
 447+				warnx("The Boot Image Directory parameter"
 448+				 " requires a directory name");
 449+				rv = 0;
 450+			} else {
 451+				diskStructure->boot_image_directory =
 452+				     emalloc(strlen(buf) + 1);
 453+				/* BIG TODO: Add the max length function here */
 454+				rv = cd9660_arguments_set_string(buf, desc, 12,
 455+				    'd', diskStructure->boot_image_directory);
 456+			}
 457+		} else if (strcmp(name, "no-emul-boot") == 0 ||
 458+		    strcmp(name, "no-boot") == 0 ||
 459+		    strcmp(name, "hard-disk-boot") == 0) {
 460+			/* RRIP */
 461+			cd9660_eltorito_add_boot_option(diskStructure, name, 0);
 462+			rv = 1;
 463+		} else if (strcmp(name, "boot-load-segment") == 0 ||
 464+		    strcmp(name, "platformid") == 0) {
 465+			if (buf[0] == '\0') {
 466+				warnx("Option `%s' doesn't contain a value",
 467+				    name);
 468+				rv = 0;
 469+			} else {
 470+				cd9660_eltorito_add_boot_option(diskStructure,
 471+				    name, buf);
 472+				rv = 1;
 473+			}
 474+		} else
 475+			rv = 1;
 476+	}
 477+	return rv;
 478+}
 479+
 480+/*
 481+ * Main function for cd9660_makefs
 482+ * Builds the ISO image file
 483+ * @param const char *image The image filename to create
 484+ * @param const char *dir The directory that is being read
 485+ * @param struct fsnode *root The root node of the filesystem tree
 486+ * @param struct fsinfo_t *fsopts Any options
 487+ */
 488+void
 489+cd9660_makefs(const char *image, const char *dir, fsnode *root,
 490+    fsinfo_t *fsopts)
 491+{
 492+	int64_t startoffset;
 493+	int numDirectories;
 494+	uint64_t pathTableSectors;
 495+	int64_t firstAvailableSector;
 496+	int64_t totalSpace;
 497+	int error;
 498+	cd9660node *real_root;
 499+	iso9660_disk *diskStructure = fsopts->fs_specific;
 500+
 501+	if (diskStructure->verbose_level > 0)
 502+		printf("%s: ISO level is %i\n", __func__,
 503+		    diskStructure->isoLevel);
 504+	if (diskStructure->isoLevel < 2 &&
 505+	    diskStructure->allow_multidot)
 506+		errx(EXIT_FAILURE, "allow-multidot requires iso level of 2");
 507+
 508+	assert(image != NULL);
 509+	assert(dir != NULL);
 510+	assert(root != NULL);
 511+
 512+	if (diskStructure->displayHelp) {
 513+		/*
 514+		 * Display help here - probably want to put it in
 515+		 * a separate function
 516+		 */
 517+		return;
 518+	}
 519+
 520+	if (diskStructure->verbose_level > 0)
 521+		printf("%s: image %s directory %s root %p\n", __func__,
 522+		    image, dir, root);
 523+
 524+	/* Set up some constants. Later, these will be defined with options */
 525+
 526+	/* Counter needed for path tables */
 527+	numDirectories = 0;
 528+
 529+	/* Convert tree to our own format */
 530+	/* Actually, we now need to add the REAL root node, at level 0 */
 531+
 532+	real_root = cd9660_allocate_cd9660node();
 533+	real_root->isoDirRecord = emalloc(sizeof(*real_root->isoDirRecord));
 534+	/* Leave filename blank for root */
 535+	memset(real_root->isoDirRecord->name, 0,
 536+	    ISO_FILENAME_MAXLENGTH_WITH_PADDING);
 537+
 538+	real_root->level = 0;
 539+	diskStructure->rootNode = real_root;
 540+	real_root->type = CD9660_TYPE_DIR;
 541+	error = 0;
 542+	real_root->node = root;
 543+	cd9660_convert_structure(diskStructure, root, real_root, 1,
 544+	    &numDirectories, &error);
 545+
 546+	if (TAILQ_EMPTY(&real_root->cn_children)) {
 547+		errx(EXIT_FAILURE, "%s: converted directory is empty. "
 548+		    "Tree conversion failed", __func__);
 549+	} else if (error != 0) {
 550+		errx(EXIT_FAILURE, "%s: tree conversion failed", __func__);
 551+	} else {
 552+		if (diskStructure->verbose_level > 0)
 553+			printf("%s: tree converted\n", __func__);
 554+	}
 555+
 556+	/* Add the dot and dot dot records */
 557+	cd9660_add_dot_records(diskStructure, real_root);
 558+
 559+	cd9660_setup_root_node(diskStructure);
 560+
 561+	if (diskStructure->verbose_level > 0)
 562+		printf("%s: done converting tree\n", __func__);
 563+
 564+	/* non-SUSP extensions */
 565+	if (diskStructure->archimedes_enabled)
 566+		archimedes_convert_tree(diskStructure->rootNode);
 567+
 568+	/* Rock ridge / SUSP init pass */
 569+	if (diskStructure->rock_ridge_enabled) {
 570+		cd9660_susp_initialize(diskStructure, diskStructure->rootNode,
 571+		    diskStructure->rootNode, NULL);
 572+	}
 573+
 574+	/* Build path table structure */
 575+	diskStructure->pathTableLength = cd9660_generate_path_table(
 576+	    diskStructure);
 577+
 578+	pathTableSectors = CD9660_BLOCKS(diskStructure->sectorSize,
 579+		diskStructure->pathTableLength);
 580+
 581+	firstAvailableSector = cd9660_setup_volume_descriptors(diskStructure);
 582+	if (diskStructure->is_bootable) {
 583+		firstAvailableSector = cd9660_setup_boot(diskStructure,
 584+		    firstAvailableSector);
 585+		if (firstAvailableSector < 0)
 586+			errx(EXIT_FAILURE, "setup_boot failed");
 587+	}
 588+	/* LE first, then BE */
 589+	diskStructure->primaryLittleEndianTableSector = firstAvailableSector;
 590+	diskStructure->primaryBigEndianTableSector =
 591+		diskStructure->primaryLittleEndianTableSector + pathTableSectors;
 592+
 593+	/* Set the secondary ones to -1, not going to use them for now */
 594+	diskStructure->secondaryBigEndianTableSector = -1;
 595+	diskStructure->secondaryLittleEndianTableSector = -1;
 596+
 597+	diskStructure->dataFirstSector =
 598+	    diskStructure->primaryBigEndianTableSector + pathTableSectors;
 599+	if (diskStructure->verbose_level > 0)
 600+		printf("%s: Path table conversion complete. "
 601+		    "Each table is %i bytes, or %" PRIu64 " sectors.\n",
 602+		    __func__,
 603+		    diskStructure->pathTableLength, pathTableSectors);
 604+
 605+	startoffset = diskStructure->sectorSize*diskStructure->dataFirstSector;
 606+
 607+	totalSpace = cd9660_compute_offsets(diskStructure, real_root, startoffset);
 608+
 609+	if ((fsopts->maxsize > 0) && (totalSpace > fsopts->maxsize))
 610+		errx(EXIT_FAILURE, "won't fit due to set maximum disk size");
 611+
 612+	diskStructure->totalSectors = diskStructure->dataFirstSector +
 613+		CD9660_BLOCKS(diskStructure->sectorSize, totalSpace);
 614+
 615+	/* Disabled until pass 1 is done */
 616+	if (diskStructure->rock_ridge_enabled) {
 617+		diskStructure->susp_continuation_area_start_sector =
 618+		    diskStructure->totalSectors;
 619+		diskStructure->totalSectors +=
 620+		    CD9660_BLOCKS(diskStructure->sectorSize,
 621+			diskStructure->susp_continuation_area_size);
 622+		cd9660_susp_finalize(diskStructure, diskStructure->rootNode);
 623+	}
 624+
 625+
 626+	cd9660_finalize_PVD(diskStructure);
 627+
 628+	/* Add padding sectors, just for testing purposes right now */
 629+	/* diskStructure->totalSectors+=150; */
 630+
 631+	/* Debugging output */
 632+	if (diskStructure->verbose_level > 0) {
 633+		printf("%s: Sectors 0-15 reserved\n", __func__);
 634+		printf("%s: Primary path tables starts in sector %"
 635+		    PRId64 "\n", __func__,
 636+		    diskStructure->primaryLittleEndianTableSector);
 637+		printf("%s: File data starts in sector %"
 638+		    PRId64 "\n", __func__, diskStructure->dataFirstSector);
 639+		printf("%s: Total sectors: %"
 640+		    PRId64 "\n", __func__, diskStructure->totalSectors);
 641+	}
 642+
 643+	/*
 644+	 * Add padding sectors at the end
 645+	 * TODO: Clean this up and separate padding
 646+	 */
 647+	if (diskStructure->include_padding_areas)
 648+		diskStructure->totalSectors += 150;
 649+
 650+	cd9660_write_image(diskStructure, image);
 651+
 652+	if (diskStructure->verbose_level > 1) {
 653+		debug_print_volume_descriptor_information(diskStructure);
 654+		debug_print_tree(diskStructure, real_root, 0);
 655+		debug_print_path_tree(real_root);
 656+	}
 657+
 658+	/* Clean up data structures */
 659+	cd9660_free_structure(real_root);
 660+
 661+	if (diskStructure->verbose_level > 0)
 662+		printf("%s: done\n", __func__);
 663+}
 664+
 665+/* Generic function pointer - implement later */
 666+typedef int (*cd9660node_func)(cd9660node *);
 667+
 668+static void
 669+cd9660_finalize_PVD(iso9660_disk *diskStructure)
 670+{
 671+	time_t tstamp = stampst.st_ino ? stampst.st_mtime : time(NULL);
 672+
 673+	/* root should be a fixed size of 34 bytes since it has no name */
 674+	memcpy(diskStructure->primaryDescriptor.root_directory_record,
 675+		diskStructure->rootNode->dot_record->isoDirRecord, 34);
 676+
 677+	/* In RRIP, this might be longer than 34 */
 678+	diskStructure->primaryDescriptor.root_directory_record[0] = 34;
 679+
 680+	/* Set up all the important numbers in the PVD */
 681+	cd9660_bothendian_dword(diskStructure->totalSectors,
 682+	    (unsigned char *)diskStructure->primaryDescriptor.volume_space_size);
 683+	cd9660_bothendian_word(1,
 684+	    (unsigned char *)diskStructure->primaryDescriptor.volume_set_size);
 685+	cd9660_bothendian_word(1,
 686+	    (unsigned char *)
 687+		diskStructure->primaryDescriptor.volume_sequence_number);
 688+	cd9660_bothendian_word(diskStructure->sectorSize,
 689+	    (unsigned char *)
 690+		diskStructure->primaryDescriptor.logical_block_size);
 691+	cd9660_bothendian_dword(diskStructure->pathTableLength,
 692+	    (unsigned char *)diskStructure->primaryDescriptor.path_table_size);
 693+
 694+	cd9660_731(diskStructure->primaryLittleEndianTableSector,
 695+		(u_char *)diskStructure->primaryDescriptor.type_l_path_table);
 696+	cd9660_732(diskStructure->primaryBigEndianTableSector,
 697+		(u_char *)diskStructure->primaryDescriptor.type_m_path_table);
 698+
 699+	diskStructure->primaryDescriptor.file_structure_version[0] = 1;
 700+
 701+	/* Pad all strings with spaces instead of nulls */
 702+	cd9660_pad_string_spaces(diskStructure->primaryDescriptor.volume_id, 32);
 703+	cd9660_pad_string_spaces(diskStructure->primaryDescriptor.system_id, 32);
 704+	cd9660_pad_string_spaces(diskStructure->primaryDescriptor.volume_set_id,
 705+	    128);
 706+	cd9660_pad_string_spaces(diskStructure->primaryDescriptor.publisher_id,
 707+	    128);
 708+	cd9660_pad_string_spaces(diskStructure->primaryDescriptor.preparer_id,
 709+	    128);
 710+	cd9660_pad_string_spaces(diskStructure->primaryDescriptor.application_id,
 711+	    128);
 712+	cd9660_pad_string_spaces(
 713+	    diskStructure->primaryDescriptor.copyright_file_id, 37);
 714+	cd9660_pad_string_spaces(
 715+		diskStructure->primaryDescriptor.abstract_file_id, 37);
 716+	cd9660_pad_string_spaces(
 717+		diskStructure->primaryDescriptor.bibliographic_file_id, 37);
 718+
 719+	/* Setup dates */
 720+	cd9660_time_8426(
 721+	    (unsigned char *)diskStructure->primaryDescriptor.creation_date,
 722+	    tstamp);
 723+	cd9660_time_8426(
 724+	    (unsigned char *)diskStructure->primaryDescriptor.modification_date,
 725+	    tstamp);
 726+
 727+#if 0
 728+	cd9660_set_date(diskStructure->primaryDescriptor.expiration_date,
 729+	    tstamp);
 730+#endif
 731+	memset(diskStructure->primaryDescriptor.expiration_date, '0' ,16);
 732+	diskStructure->primaryDescriptor.expiration_date[16] = 0;
 733+
 734+	cd9660_time_8426(
 735+	    (unsigned char *)diskStructure->primaryDescriptor.effective_date,
 736+	    tstamp);
 737+}
 738+
 739+static void
 740+cd9660_populate_iso_dir_record(struct _iso_directory_record_cd9660 *record,
 741+			       u_char ext_attr_length, u_char flags,
 742+			       u_char name_len, const char * name)
 743+{
 744+	time_t tstamp = stampst.st_ino ? stampst.st_mtime : time(NULL);
 745+
 746+	record->ext_attr_length[0] = ext_attr_length;
 747+	cd9660_time_915(record->date, tstamp);
 748+	record->flags[0] = ISO_FLAG_CLEAR | flags;
 749+	record->file_unit_size[0] = 0;
 750+	record->interleave[0] = 0;
 751+	cd9660_bothendian_word(1, record->volume_sequence_number);
 752+	record->name_len[0] = name_len;
 753+	memset(record->name, '\0', sizeof (record->name));
 754+	memcpy(record->name, name, name_len);
 755+	record->length[0] = 33 + name_len;
 756+
 757+	/* Todo : better rounding */
 758+	record->length[0] += (record->length[0] & 1) ? 1 : 0;
 759+}
 760+
 761+static void
 762+cd9660_setup_root_node(iso9660_disk *diskStructure)
 763+{
 764+	cd9660_populate_iso_dir_record(diskStructure->rootNode->isoDirRecord,
 765+	    0, ISO_FLAG_DIRECTORY, 1, "\0");
 766+
 767+}
 768+
 769+/*********** SUPPORT FUNCTIONS ***********/
 770+static int
 771+cd9660_setup_volume_descriptors(iso9660_disk *diskStructure)
 772+{
 773+	/* Boot volume descriptor should come second */
 774+	int sector = 16;
 775+	/* For now, a fixed 2 : PVD and terminator */
 776+	volume_descriptor *temp, *t;
 777+
 778+	/* Set up the PVD */
 779+	temp = emalloc(sizeof(*temp));
 780+	temp->volumeDescriptorData =
 781+	   (unsigned char *)&diskStructure->primaryDescriptor;
 782+	temp->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_PVD;
 783+	temp->volumeDescriptorData[6] = 1;
 784+	temp->sector = sector;
 785+	memcpy(temp->volumeDescriptorData + 1,
 786+	    ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
 787+	diskStructure->firstVolumeDescriptor = temp;
 788+
 789+	sector++;
 790+	/* Set up boot support if enabled. BVD must reside in sector 17 */
 791+	if (diskStructure->is_bootable) {
 792+		t = emalloc(sizeof(*t));
 793+		t->volumeDescriptorData = ecalloc(1, 2048);
 794+		temp->next = t;
 795+		temp = t;
 796+		t->sector = 17;
 797+		if (diskStructure->verbose_level > 0)
 798+			printf("Setting up boot volume descriptor\n");
 799+		cd9660_setup_boot_volume_descriptor(diskStructure, t);
 800+		sector++;
 801+	}
 802+
 803+	/* Set up the terminator */
 804+	t = emalloc(sizeof(*t));
 805+	t->volumeDescriptorData = ecalloc(1, 2048);
 806+	temp->next = t;
 807+	t->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_TERMINATOR;
 808+	t->next = 0;
 809+	t->volumeDescriptorData[6] = 1;
 810+	t->sector = sector;
 811+	memcpy(t->volumeDescriptorData + 1,
 812+	    ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
 813+
 814+	sector++;
 815+	return sector;
 816+}
 817+
 818+#if 0
 819+/*
 820+ * Populate EAR at some point. Not required, but is used by NetBSD's
 821+ * cd9660 support
 822+ */
 823+static int
 824+cd9660_fill_extended_attribute_record(cd9660node *node)
 825+{
 826+	node->isoExtAttributes = emalloc(sizeof(*node->isoExtAttributes));
 827+	return 1;
 828+}
 829+#endif
 830+
 831+static int
 832+cd9660_translate_node_common(iso9660_disk *diskStructure, cd9660node *newnode)
 833+{
 834+	u_char flag;
 835+	char temp[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
 836+
 837+	/* Now populate the isoDirRecord structure */
 838+	memset(temp, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING);
 839+
 840+	(void)cd9660_convert_filename(diskStructure, newnode->node->name,
 841+	    temp, !(S_ISDIR(newnode->node->type)));
 842+
 843+	flag = ISO_FLAG_CLEAR;
 844+	if (S_ISDIR(newnode->node->type))
 845+		flag |= ISO_FLAG_DIRECTORY;
 846+
 847+	cd9660_populate_iso_dir_record(newnode->isoDirRecord, 0,
 848+	    flag, strlen(temp), temp);
 849+
 850+	cd9660_bothendian_dword(newnode->fileDataLength,
 851+	    newnode->isoDirRecord->size);
 852+	/* If the file is a link, we want to set the size to 0 */
 853+	if (S_ISLNK(newnode->node->type))
 854+		newnode->fileDataLength = 0;
 855+
 856+	return 1;
 857+}
 858+
 859+/*
 860+ * Translate fsnode to cd9660node
 861+ * Translate filenames and other metadata, including dates, sizes,
 862+ * permissions, etc
 863+ * @param struct fsnode * The node generated by makefs
 864+ * @param struct cd9660node * The intermediate node to be written to
 865+ * @returns int 0 on failure, 1 on success
 866+ */
 867+static int
 868+cd9660_translate_node(iso9660_disk *diskStructure, fsnode *node,
 869+    cd9660node *newnode)
 870+{
 871+	if (node == NULL) {
 872+		if (diskStructure->verbose_level > 0)
 873+			printf("%s: NULL node passed, returning\n", __func__);
 874+		return 0;
 875+	}
 876+	newnode->isoDirRecord = emalloc(sizeof(*newnode->isoDirRecord));
 877+	/* Set the node pointer */
 878+	newnode->node = node;
 879+
 880+	/* Set the size */
 881+	if (!(S_ISDIR(node->type)))
 882+		newnode->fileDataLength = node->inode->st.st_size;
 883+
 884+	if (cd9660_translate_node_common(diskStructure, newnode) == 0)
 885+		return 0;
 886+
 887+	/* Finally, overwrite some of the values that are set by default */
 888+	cd9660_time_915(newnode->isoDirRecord->date,
 889+	    stampst.st_ino ? stampst.st_mtime : node->inode->st.st_mtime);
 890+
 891+	return 1;
 892+}
 893+
 894+/*
 895+ * Compares two ISO filenames
 896+ * @param const char * The first file name
 897+ * @param const char * The second file name
 898+ * @returns : -1 if first is less than second, 0 if they are the same, 1 if
 899+ * 	the second is greater than the first
 900+ */
 901+static int
 902+cd9660_compare_filename(const char *first, const char *second)
 903+{
 904+	/*
 905+	 * This can be made more optimal once it has been tested
 906+	 * (the extra character, for example, is for testing)
 907+	 */
 908+
 909+	int p1 = 0;
 910+	int p2 = 0;
 911+	char c1, c2;
 912+	/* First, on the filename */
 913+
 914+	while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1
 915+		&& p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1) {
 916+		c1 = first[p1];
 917+		c2 = second[p2];
 918+		if (c1 == '.' && c2 =='.')
 919+			break;
 920+		else if (c1 == '.') {
 921+			p2++;
 922+			c1 = ' ';
 923+		} else if (c2 == '.') {
 924+			p1++;
 925+			c2 = ' ';
 926+		} else {
 927+			p1++;
 928+			p2++;
 929+		}
 930+
 931+		if (c1 < c2)
 932+			return -1;
 933+		else if (c1 > c2) {
 934+			return 1;
 935+		}
 936+	}
 937+
 938+	if (first[p1] == '.' && second[p2] == '.') {
 939+		p1++;
 940+		p2++;
 941+		while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1
 942+			&& p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1) {
 943+			c1 = first[p1];
 944+			c2 = second[p2];
 945+			if (c1 == ';' && c2 == ';')
 946+				break;
 947+			else if (c1 == ';') {
 948+				p2++;
 949+				c1 = ' ';
 950+			} else if (c2 == ';') {
 951+				p1++;
 952+				c2 = ' ';
 953+			} else {
 954+				p1++;
 955+				p2++;
 956+			}
 957+
 958+			if (c1 < c2)
 959+				return -1;
 960+			else if (c1 > c2)
 961+				return 1;
 962+		}
 963+	}
 964+	return 0;
 965+}
 966+
 967+/*
 968+ * Insert a node into list with ISO sorting rules
 969+ * @param cd9660node * The head node of the list
 970+ * @param cd9660node * The node to be inserted
 971+ */
 972+static void
 973+cd9660_sorted_child_insert(cd9660node *parent, cd9660node *cn_new)
 974+{
 975+	int compare;
 976+	cd9660node *cn;
 977+	struct cd9660_children_head *head = &parent->cn_children;
 978+
 979+	/* TODO: Optimize? */
 980+	cn_new->parent = parent;
 981+
 982+	/*
 983+	 * first will either be 0, the . or the ..
 984+	 * if . or .., this means no other entry may be written before first
 985+	 * if 0, the new node may be inserted at the head
 986+	 */
 987+
 988+	TAILQ_FOREACH(cn, head, cn_next_child) {
 989+		/*
 990+		 * Dont insert a node twice -
 991+		 * that would cause an infinite loop
 992+		 */
 993+		if (cn_new == cn)
 994+			return;
 995+
 996+		compare = cd9660_compare_filename(cn_new->isoDirRecord->name,
 997+			cn->isoDirRecord->name);
 998+
 999+		if (compare == 0)
1000+			compare = cd9660_compare_filename(cn_new->node->name,
1001+				cn->node->name);
1002+
1003+		if (compare < 0)
1004+			break;
1005+	}
1006+	if (cn == NULL)
1007+		TAILQ_INSERT_TAIL(head, cn_new, cn_next_child);
1008+	else
1009+		TAILQ_INSERT_BEFORE(cn, cn_new, cn_next_child);
1010+}
1011+
1012+/*
1013+ * Called After cd9660_sorted_child_insert
1014+ * handles file collisions by suffixing each filename with ~n
1015+ * where n represents the files respective place in the ordering
1016+ */
1017+static int
1018+cd9660_handle_collisions(iso9660_disk *diskStructure, cd9660node *colliding,
1019+    int past)
1020+{
1021+	cd9660node *iter, *next, *prev;
1022+	int skip;
1023+	int delete_chars = 0;
1024+	int temp_past = past;
1025+	int temp_skip;
1026+	int flag = 0;
1027+	cd9660node *end_of_range;
1028+
1029+	for (iter = TAILQ_FIRST(&colliding->cn_children);
1030+	     iter != NULL && (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;) {
1031+		if (strcmp(iter->isoDirRecord->name,
1032+		           next->isoDirRecord->name) != 0) {
1033+			iter = TAILQ_NEXT(iter, cn_next_child);
1034+			continue;
1035+		}
1036+		flag = 1;
1037+		temp_skip = skip = cd9660_count_collisions(iter);
1038+		end_of_range = iter;
1039+		while (temp_skip > 0) {
1040+			temp_skip--;
1041+			end_of_range = TAILQ_NEXT(end_of_range, cn_next_child);
1042+		}
1043+		temp_past = past;
1044+		while (temp_past > 0) {
1045+			if ((next = TAILQ_NEXT(end_of_range, cn_next_child)) != NULL)
1046+				end_of_range = next;
1047+			else if ((prev = TAILQ_PREV(iter, cd9660_children_head, cn_next_child)) != NULL)
1048+				iter = prev;
1049+			else
1050+				delete_chars++;
1051+			temp_past--;
1052+		}
1053+		skip += past;
1054+		iter = cd9660_rename_filename(diskStructure, iter, skip,
1055+		    delete_chars);
1056+	}
1057+	return flag;
1058+}
1059+
1060+
1061+static cd9660node *
1062+cd9660_rename_filename(iso9660_disk *diskStructure, cd9660node *iter, int num,
1063+    int delete_chars)
1064+{
1065+	int i = 0;
1066+	int numbts, dot, semi, digit, digits, temp, powers, multiplier, count;
1067+	char *naming;
1068+	int maxlength;
1069+        char *tmp;
1070+
1071+	if (diskStructure->verbose_level > 0)
1072+		printf("Rename_filename called\n");
1073+
1074+	/* TODO : A LOT of chanes regarding 8.3 filenames */
1075+	if (diskStructure->isoLevel == 1)
1076+		maxlength = 8;
1077+	else if (diskStructure->isoLevel == 2)
1078+		maxlength = 31;
1079+	else
1080+		maxlength = ISO_FILENAME_MAXLENGTH_BEFORE_VERSION;
1081+
1082+	tmp = emalloc(ISO_FILENAME_MAXLENGTH_WITH_PADDING);
1083+
1084+	while (i < num && iter) {
1085+		powers = 1;
1086+		count = 0;
1087+		digits = 1;
1088+		multiplier = 1;
1089+		while (((int)(i / powers) ) >= 10) {
1090+			digits++;
1091+			powers = powers * 10;
1092+		}
1093+
1094+		naming = iter->o_name;
1095+
1096+		/*
1097+		while ((*naming != '.') && (*naming != ';')) {
1098+			naming++;
1099+			count++;
1100+		}
1101+		*/
1102+
1103+		dot = -1;
1104+		semi = -1;
1105+		while (count < maxlength) {
1106+			if (*naming == '.')
1107+				dot = count;
1108+			else if (*naming == ';') {
1109+				semi = count;
1110+				break;
1111+			}
1112+			naming++;
1113+			count++;
1114+		}
1115+
1116+		if ((count + digits) < maxlength)
1117+			numbts = count;
1118+		else
1119+			numbts = maxlength - (digits);
1120+		numbts -= delete_chars;
1121+
1122+		/* 8.3 rules - keep the extension, add before the dot */
1123+
1124+		/*
1125+		 * This code makes a bunch of assumptions.
1126+		 * See if you can spot them all :)
1127+		 */
1128+
1129+#if 0
1130+		if (diskStructure->isoLevel == 1) {
1131+			numbts = 8 - digits - delete_chars;
1132+			if (dot < 0) {
1133+
1134+			} else {
1135+				if (dot < 8) {
1136+					memmove(&tmp[numbts],&tmp[dot],4);
1137+				}
1138+			}
1139+		}
1140+#else
1141+		__USE(dot);
1142+		__USE(semi);
1143+		__USE(multiplier);
1144+#endif
1145+
1146+		/* (copying just the filename before the '.' */
1147+		memcpy(tmp, (iter->o_name), numbts);
1148+
1149+		/* adding the appropriate number following the name */
1150+		temp = i;
1151+		while (digits > 0) {
1152+			digit = (int)(temp / powers);
1153+			temp = temp - digit * powers;
1154+			sprintf(&tmp[numbts] , "%d", digit);
1155+			digits--;
1156+			numbts++;
1157+			powers = powers / 10;
1158+		}
1159+
1160+		while ((*naming != ';')  && (numbts < maxlength)) {
1161+			tmp[numbts] = (*naming);
1162+			naming++;
1163+			numbts++;
1164+		}
1165+
1166+		tmp[numbts] = ';';
1167+		tmp[numbts+1] = '1';
1168+		tmp[numbts+2] = '\0';
1169+
1170+		/*
1171+		 * now tmp has exactly the identifier
1172+		 * we want so we'll copy it back to record
1173+		 */
1174+		memcpy((iter->isoDirRecord->name), tmp, numbts + 3);
1175+
1176+		iter = TAILQ_NEXT(iter, cn_next_child);
1177+		i++;
1178+	}
1179+
1180+	free(tmp);
1181+	return iter;
1182+}
1183+
1184+/* Todo: Figure out why these functions are nec. */
1185+static void
1186+cd9660_copy_filenames(iso9660_disk *diskStructure, cd9660node *node)
1187+{
1188+	cd9660node *cn;
1189+
1190+	if (TAILQ_EMPTY(&node->cn_children))
1191+		return;
1192+
1193+	if (TAILQ_FIRST(&node->cn_children)->isoDirRecord == NULL) {
1194+		debug_print_tree(diskStructure, diskStructure->rootNode, 0);
1195+		exit(EXIT_FAILURE);
1196+	}
1197+
1198+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
1199+		cd9660_copy_filenames(diskStructure, cn);
1200+		memcpy(cn->o_name, cn->isoDirRecord->name,
1201+		    ISO_FILENAME_MAXLENGTH_WITH_PADDING);
1202+	}
1203+}
1204+
1205+static void
1206+cd9660_sorting_nodes(cd9660node *node)
1207+{
1208+	cd9660node *cn;
1209+
1210+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
1211+		cd9660_sorting_nodes(cn);
1212+	cd9660_sort_nodes(node);
1213+}
1214+
1215+/* XXX Bubble sort. */
1216+static void
1217+cd9660_sort_nodes(cd9660node *node)
1218+{
1219+	cd9660node *cn, *next;
1220+
1221+	do {
1222+		TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
1223+			if ((next = TAILQ_NEXT(cn, cn_next_child)) == NULL)
1224+				return;
1225+			else if (strcmp(next->isoDirRecord->name,
1226+				        cn->isoDirRecord->name) >= 0)
1227+				continue;
1228+			TAILQ_REMOVE(&node->cn_children, next, cn_next_child);
1229+			TAILQ_INSERT_BEFORE(cn, next, cn_next_child);
1230+			break;
1231+		}
1232+	} while (cn != NULL);
1233+}
1234+
1235+static int
1236+cd9660_count_collisions(cd9660node *copy)
1237+{
1238+	int count = 0;
1239+	cd9660node *iter, *next;
1240+
1241+	for (iter = copy;
1242+	     (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;
1243+	     iter = next) {
1244+		if (cd9660_compare_filename(iter->isoDirRecord->name,
1245+			next->isoDirRecord->name) == 0)
1246+			count++;
1247+		else
1248+			return count;
1249+	}
1250+#if 0
1251+	if ((next = TAILQ_NEXT(iter, cn_next_child)) != NULL) {
1252+		printf("%s: count is %i \n", __func__, count);
1253+		compare = cd9660_compare_filename(iter->isoDirRecord->name,
1254+			next->isoDirRecord->name);
1255+		if (compare == 0) {
1256+			count++;
1257+			return cd9660_recurse_on_collision(next, count);
1258+		} else
1259+			return count;
1260+	}
1261+#endif
1262+	return count;
1263+}
1264+
1265+static cd9660node *
1266+cd9660_rrip_move_directory(iso9660_disk *diskStructure, cd9660node *dir)
1267+{
1268+	char newname[9];
1269+	cd9660node *tfile;
1270+
1271+	/*
1272+	 * This function needs to:
1273+	 * 1) Create an empty virtual file in place of the old directory
1274+	 * 2) Point the virtual file to the new directory
1275+	 * 3) Point the relocated directory to its old parent
1276+	 * 4) Move the directory specified by dir into rr_moved_dir,
1277+	 * and rename it to "diskStructure->rock_ridge_move_count" (as a string)
1278+	 */
1279+
1280+	/* First see if the moved directory even exists */
1281+	if (diskStructure->rr_moved_dir == NULL) {
1282+		diskStructure->rr_moved_dir = cd9660_create_directory(
1283+		    diskStructure, ISO_RRIP_DEFAULT_MOVE_DIR_NAME,
1284+		    diskStructure->rootNode, dir);
1285+		if (diskStructure->rr_moved_dir == NULL)
1286+			return 0;
1287+		cd9660_time_915(diskStructure->rr_moved_dir->isoDirRecord->date,
1288+		    stampst.st_ino ? stampst.st_mtime : start_time.tv_sec);
1289+	}
1290+
1291+	/* Create a file with the same ORIGINAL name */
1292+	tfile = cd9660_create_file(diskStructure, dir->node->name, dir->parent,
1293+	    dir);
1294+	if (tfile == NULL)
1295+		return NULL;
1296+
1297+	diskStructure->rock_ridge_move_count++;
1298+	snprintf(newname, sizeof(newname), "%08u",
1299+	    diskStructure->rock_ridge_move_count);
1300+
1301+	/* Point to old parent */
1302+	dir->rr_real_parent = dir->parent;
1303+
1304+	/* Place the placeholder file */
1305+	if (TAILQ_EMPTY(&dir->rr_real_parent->cn_children)) {
1306+		TAILQ_INSERT_HEAD(&dir->rr_real_parent->cn_children, tfile,
1307+		    cn_next_child);
1308+	} else {
1309+		cd9660_sorted_child_insert(dir->rr_real_parent, tfile);
1310+	}
1311+
1312+	/* Point to new parent */
1313+	dir->parent = diskStructure->rr_moved_dir;
1314+
1315+	/* Point the file to the moved directory */
1316+	tfile->rr_relocated = dir;
1317+
1318+	/* Actually move the directory */
1319+	cd9660_sorted_child_insert(diskStructure->rr_moved_dir, dir);
1320+
1321+	/* TODO: Inherit permissions / ownership (basically the entire inode) */
1322+
1323+	/* Set the new name */
1324+	memset(dir->isoDirRecord->name, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING);
1325+	strncpy(dir->isoDirRecord->name, newname, 8);
1326+	dir->isoDirRecord->length[0] = 34 + 8;
1327+	dir->isoDirRecord->name_len[0] = 8;
1328+
1329+	return dir;
1330+}
1331+
1332+static int
1333+cd9660_add_dot_records(iso9660_disk *diskStructure, cd9660node *root)
1334+{
1335+	struct cd9660_children_head *head = &root->cn_children;
1336+	cd9660node *cn;
1337+
1338+	TAILQ_FOREACH(cn, head, cn_next_child) {
1339+		if ((cn->type & CD9660_TYPE_DIR) == 0)
1340+			continue;
1341+		/* Recursion first */
1342+		cd9660_add_dot_records(diskStructure, cn);
1343+	}
1344+	cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOT, root);
1345+	cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOTDOT,
1346+	    root);
1347+	return 1;
1348+}
1349+
1350+/*
1351+ * Convert node to cd9660 structure
1352+ * This function is designed to be called recursively on the root node of
1353+ * the filesystem
1354+ * Lots of recursion going on here, want to make sure it is efficient
1355+ * @param struct fsnode * The root node to be converted
1356+ * @param struct cd9660* The parent node (should not be NULL)
1357+ * @param int Current directory depth
1358+ * @param int* Running count of the number of directories that are being created
1359+ */
1360+static void
1361+cd9660_convert_structure(iso9660_disk *diskStructure, fsnode *root,
1362+    cd9660node *parent_node, int level, int *numDirectories, int *error)
1363+{
1364+	fsnode *iterator = root;
1365+	cd9660node *this_node;
1366+	int working_level;
1367+	int add;
1368+	int flag = 0;
1369+	int counter = 0;
1370+
1371+	/*
1372+	 * Newer, more efficient method, reduces recursion depth
1373+	 */
1374+	if (root == NULL) {
1375+		warnx("%s: root is null", __func__);
1376+		return;
1377+	}
1378+
1379+	/* Test for an empty directory - makefs still gives us the . record */
1380+	if ((S_ISDIR(root->type)) && (root->name[0] == '.')
1381+		&& (root->name[1] == '\0')) {
1382+		root = root->next;
1383+		if (root == NULL)
1384+			return;
1385+	}
1386+	if ((this_node = cd9660_allocate_cd9660node()) == NULL) {
1387+		CD9660_MEM_ALLOC_ERROR(__func__);
1388+	}
1389+
1390+	/*
1391+	 * To reduce the number of recursive calls, we will iterate over
1392+	 * the next pointers to the right.
1393+	 */
1394+	while (iterator != NULL) {
1395+		add = 1;
1396+		/*
1397+		 * Increment the directory count if this is a directory
1398+		 * Ignore "." entries. We will generate them later
1399+		 */
1400+		if (!S_ISDIR(iterator->type) ||
1401+		    strcmp(iterator->name, ".") != 0) {
1402+
1403+			/* Translate the node, including its filename */
1404+			this_node->parent = parent_node;
1405+			cd9660_translate_node(diskStructure, iterator,
1406+			    this_node);
1407+			this_node->level = level;
1408+
1409+			if (S_ISDIR(iterator->type)) {
1410+				(*numDirectories)++;
1411+				this_node->type = CD9660_TYPE_DIR;
1412+				working_level = level + 1;
1413+
1414+				/*
1415+				 * If at level 8, directory would be at 8
1416+				 * and have children at 9 which is not
1417+				 * allowed as per ISO spec
1418+				 */
1419+				if (level == 8) {
1420+					if ((!diskStructure->allow_deep_trees) &&
1421+					  (!diskStructure->rock_ridge_enabled)) {
1422+						warnx("error: found entry "
1423+						     "with depth greater "
1424+						     "than 8.");
1425+						(*error) = 1;
1426+						return;
1427+					} else if (diskStructure->
1428+						   rock_ridge_enabled) {
1429+						working_level = 3;
1430+						/*
1431+						 * Moved directory is actually
1432+						 * at level 2.
1433+						 */
1434+						this_node->level =
1435+						    working_level - 1;
1436+						if (cd9660_rrip_move_directory(
1437+							diskStructure,
1438+							this_node) == 0) {
1439+							warnx("Failure in "
1440+							      "cd9660_rrip_"
1441+							      "move_directory"
1442+							);
1443+							(*error) = 1;
1444+							return;
1445+						}
1446+						add = 0;
1447+					}
1448+				}
1449+
1450+				/* Do the recursive call on the children */
1451+				if (iterator->child != 0) {
1452+					cd9660_convert_structure(diskStructure,
1453+						iterator->child, this_node,
1454+						working_level,
1455+						numDirectories, error);
1456+
1457+					if ((*error) == 1) {
1458+						warnx("%s: Error on recursive "
1459+						    "call", __func__);
1460+						return;
1461+					}
1462+				}
1463+
1464+			} else {
1465+				/* Only directories should have children */
1466+				assert(iterator->child == NULL);
1467+
1468+				this_node->type = CD9660_TYPE_FILE;
1469+			}
1470+
1471+			/*
1472+			 * Finally, do a sorted insert
1473+			 */
1474+			if (add) {
1475+				cd9660_sorted_child_insert(
1476+				    parent_node, this_node);
1477+			}
1478+
1479+			/*Allocate new temp_node */
1480+			if (iterator->next != 0) {
1481+				this_node = cd9660_allocate_cd9660node();
1482+				if (this_node == NULL)
1483+					CD9660_MEM_ALLOC_ERROR(__func__);
1484+			}
1485+		}
1486+		iterator = iterator->next;
1487+	}
1488+
1489+	/* cd9660_handle_collisions(first_node); */
1490+
1491+	/* TODO: need cleanup */
1492+	cd9660_copy_filenames(diskStructure, parent_node);
1493+
1494+	do {
1495+		flag = cd9660_handle_collisions(diskStructure, parent_node,
1496+		    counter);
1497+		counter++;
1498+		cd9660_sorting_nodes(parent_node);
1499+	} while ((flag == 1) && (counter < 100));
1500+}
1501+
1502+/*
1503+ * Clean up the cd9660node tree
1504+ * This is designed to be called recursively on the root node
1505+ * @param struct cd9660node *root The node to free
1506+ * @returns void
1507+ */
1508+static void
1509+cd9660_free_structure(cd9660node *root)
1510+{
1511+	cd9660node *cn;
1512+
1513+	while ((cn = TAILQ_FIRST(&root->cn_children)) != NULL) {
1514+		TAILQ_REMOVE(&root->cn_children, cn, cn_next_child);
1515+		cd9660_free_structure(cn);
1516+	}
1517+	free(root);
1518+}
1519+
1520+/*
1521+ * Be a little more memory conservative:
1522+ * instead of having the TAILQ_ENTRY as part of the cd9660node,
1523+ * just create a temporary structure
1524+ */
1525+struct ptq_entry
1526+{
1527+	TAILQ_ENTRY(ptq_entry) ptq;
1528+	cd9660node *node;
1529+} *n;
1530+
1531+#define PTQUEUE_NEW(n,s,r,t){\
1532+	n = emalloc(sizeof(struct s));	\
1533+	if (n == NULL)	\
1534+		return r; \
1535+	n->node = t;\
1536+}
1537+
1538+/*
1539+ * Generate the path tables
1540+ * The specific implementation of this function is left as an exercise to the
1541+ * programmer. It could be done recursively. Make sure you read how the path
1542+ * table has to be laid out, it has levels.
1543+ * @param struct iso9660_disk *disk The disk image
1544+ * @returns int The number of built path tables (between 1 and 4), 0 on failure
1545+ */
1546+static int
1547+cd9660_generate_path_table(iso9660_disk *diskStructure)
1548+{
1549+	cd9660node *cn, *dirNode = diskStructure->rootNode;
1550+	cd9660node *last = dirNode;
1551+	int pathTableSize = 0;	/* computed as we go */
1552+	int counter = 1;	/* root gets a count of 0 */
1553+
1554+	TAILQ_HEAD(cd9660_pt_head, ptq_entry) pt_head;
1555+	TAILQ_INIT(&pt_head);
1556+
1557+	PTQUEUE_NEW(n, ptq_entry, -1, diskStructure->rootNode);
1558+
1559+	/* Push the root node */
1560+	TAILQ_INSERT_HEAD(&pt_head, n, ptq);
1561+
1562+	/* Breadth-first traversal of file structure */
1563+	while (pt_head.tqh_first != 0) {
1564+		n = pt_head.tqh_first;
1565+		dirNode = n->node;
1566+		TAILQ_REMOVE(&pt_head, pt_head.tqh_first, ptq);
1567+		free(n);
1568+
1569+		/* Update the size */
1570+		pathTableSize += ISO_PATHTABLE_ENTRY_BASESIZE
1571+		    + dirNode->isoDirRecord->name_len[0]+
1572+			(dirNode->isoDirRecord->name_len[0] % 2 == 0 ? 0 : 1);
1573+			/* includes the padding bit */
1574+
1575+		dirNode->ptnumber=counter;
1576+		if (dirNode != last) {
1577+			last->ptnext = dirNode;
1578+			dirNode->ptprev = last;
1579+		}
1580+		last = dirNode;
1581+
1582+		/* Push children onto queue */
1583+		TAILQ_FOREACH(cn, &dirNode->cn_children, cn_next_child) {
1584+			/*
1585+			 * Dont add the DOT and DOTDOT types to the path
1586+			 * table.
1587+			 */
1588+			if ((cn->type != CD9660_TYPE_DOT)
1589+				&& (cn->type != CD9660_TYPE_DOTDOT)) {
1590+
1591+				if (S_ISDIR(cn->node->type)) {
1592+					PTQUEUE_NEW(n, ptq_entry, -1, cn);
1593+					TAILQ_INSERT_TAIL(&pt_head, n, ptq);
1594+				}
1595+			}
1596+		}
1597+		counter++;
1598+	}
1599+	return pathTableSize;
1600+}
1601+
1602+void
1603+cd9660_compute_full_filename(cd9660node *node, char *buf)
1604+{
1605+	int len;
1606+
1607+	len = CD9660MAXPATH + 1;
1608+	len = snprintf(buf, len, "%s/%s/%s", node->node->root,
1609+	    node->node->path, node->node->name);
1610+	if (len > CD9660MAXPATH)
1611+		errx(EXIT_FAILURE, "Pathname too long.");
1612+}
1613+
1614+/* NEW filename conversion method */
1615+typedef int(*cd9660_filename_conversion_functor)(iso9660_disk *, const char *,
1616+    char *, int);
1617+
1618+
1619+/*
1620+ * TODO: These two functions are almost identical.
1621+ * Some code cleanup is possible here
1622+ *
1623+ * XXX bounds checking!
1624+ */
1625+static int
1626+cd9660_level1_convert_filename(iso9660_disk *diskStructure, const char *oldname,
1627+    char *newname, int is_file)
1628+{
1629+	/*
1630+	 * ISO 9660 : 10.1
1631+	 * File Name shall not contain more than 8 d or d1 characters
1632+	 * File Name Extension shall not contain more than 3 d or d1 characters
1633+	 * Directory Identifier shall not contain more than 8 d or d1 characters
1634+	 */
1635+	int namelen = 0;
1636+	int extlen = 0;
1637+	int found_ext = 0;
1638+
1639+	while (*oldname != '\0' && extlen < 3) {
1640+		/* Handle period first, as it is special */
1641+		if (*oldname == '.') {
1642+			if (found_ext) {
1643+				*newname++ = '_';
1644+				extlen ++;
1645+			}
1646+			else {
1647+				*newname++ = '.';
1648+				found_ext = 1;
1649+			}
1650+		} else {
1651+			/* cut RISC OS file type off ISO name */
1652+			if (diskStructure->archimedes_enabled &&
1653+			    *oldname == ',' && strlen(oldname) == 4)
1654+				break;
1655+
1656+			/* Enforce 12.3 / 8 */
1657+			if (namelen == 8 && !found_ext)
1658+				break;
1659+
1660+			if (islower((unsigned char)*oldname))
1661+				*newname++ = toupper((unsigned char)*oldname);
1662+			else if (isupper((unsigned char)*oldname)
1663+			    || isdigit((unsigned char)*oldname))
1664+				*newname++ = *oldname;
1665+			else
1666+				*newname++ = '_';
1667+
1668+			if (found_ext)
1669+				extlen++;
1670+			else
1671+				namelen++;
1672+		}
1673+		oldname++;
1674+	}
1675+	if (is_file) {
1676+		if (!found_ext && !diskStructure->omit_trailing_period)
1677+			*newname++ = '.';
1678+		/* Add version */
1679+		sprintf(newname, ";%i", 1);
1680+	}
1681+	return namelen + extlen + found_ext;
1682+}
1683+
1684+/* XXX bounds checking! */
1685+static int
1686+cd9660_level2_convert_filename(iso9660_disk *diskStructure, const char *oldname,
1687+    char *newname, int is_file)
1688+{
1689+	/*
1690+	 * ISO 9660 : 7.5.1
1691+	 * File name : 0+ d or d1 characters
1692+	 * separator 1 (.)
1693+	 * File name extension : 0+ d or d1 characters
1694+	 * separator 2 (;)
1695+	 * File version number (5 characters, 1-32767)
1696+	 * 1 <= Sum of File name and File name extension <= 30
1697+	 */
1698+	int namelen = 0;
1699+	int extlen = 0;
1700+	int found_ext = 0;
1701+
1702+	while (*oldname != '\0' && namelen + extlen < 30) {
1703+		/* Handle period first, as it is special */
1704+		if (*oldname == '.') {
1705+			if (found_ext) {
1706+				if (diskStructure->allow_multidot) {
1707+					*newname++ = '.';
1708+				} else {
1709+					*newname++ = '_';
1710+				}
1711+				extlen ++;
1712+			}
1713+			else {
1714+				*newname++ = '.';
1715+				found_ext = 1;
1716+			}
1717+		} else {
1718+			/* cut RISC OS file type off ISO name */
1719+			if (diskStructure->archimedes_enabled &&
1720+			    *oldname == ',' && strlen(oldname) == 4)
1721+				break;
1722+
1723+			 if (islower((unsigned char)*oldname))
1724+				*newname++ = toupper((unsigned char)*oldname);
1725+			else if (isupper((unsigned char)*oldname) ||
1726+			    isdigit((unsigned char)*oldname))
1727+				*newname++ = *oldname;
1728+			else if (diskStructure->allow_multidot &&
1729+			    *oldname == '.') {
1730+			    	*newname++ = '.';
1731+			} else {
1732+				*newname++ = '_';
1733+			}
1734+
1735+			if (found_ext)
1736+				extlen++;
1737+			else
1738+				namelen++;
1739+		}
1740+		oldname ++;
1741+	}
1742+	if (is_file) {
1743+		if (!found_ext && !diskStructure->omit_trailing_period)
1744+			*newname++ = '.';
1745+		/* Add version */
1746+		sprintf(newname, ";%i", 1);
1747+	}
1748+	return namelen + extlen + found_ext;
1749+}
1750+
1751+#if 0
1752+static int
1753+cd9660_joliet_convert_filename(iso9660_disk *diskStructure, const char *oldname,
1754+    char *newname, int is_file)
1755+{
1756+	/* TODO: implement later, move to cd9660_joliet.c ?? */
1757+}
1758+#endif
1759+
1760+
1761+/*
1762+ * Convert a file name to ISO compliant file name
1763+ * @param char * oldname The original filename
1764+ * @param char ** newname The new file name, in the appropriate character
1765+ *                        set and of appropriate length
1766+ * @param int 1 if file, 0 if directory
1767+ * @returns int The length of the new string
1768+ */
1769+static int
1770+cd9660_convert_filename(iso9660_disk *diskStructure, const char *oldname,
1771+    char *newname, int is_file)
1772+{
1773+	/* NEW */
1774+	cd9660_filename_conversion_functor conversion_function = 0;
1775+	if (diskStructure->isoLevel == 1)
1776+		conversion_function = &cd9660_level1_convert_filename;
1777+	else if (diskStructure->isoLevel == 2)
1778+		conversion_function = &cd9660_level2_convert_filename;
1779+	return (*conversion_function)(diskStructure, oldname, newname, is_file);
1780+}
1781+
1782+int
1783+cd9660_compute_record_size(iso9660_disk *diskStructure, cd9660node *node)
1784+{
1785+	int size = node->isoDirRecord->length[0];
1786+
1787+	if (diskStructure->rock_ridge_enabled)
1788+		size += node->susp_entry_size;
1789+	size += node->su_tail_size;
1790+	size += size & 1; /* Ensure length of record is even. */
1791+	assert(size <= 254);
1792+	return size;
1793+}
1794+
1795+static void
1796+cd9660_populate_dot_records(iso9660_disk *diskStructure, cd9660node *node)
1797+{
1798+	node->dot_record->fileDataSector = node->fileDataSector;
1799+	memcpy(node->dot_record->isoDirRecord,node->isoDirRecord, 34);
1800+	node->dot_record->isoDirRecord->name_len[0] = 1;
1801+	node->dot_record->isoDirRecord->name[0] = 0;
1802+	node->dot_record->isoDirRecord->name[1] = 0;
1803+	node->dot_record->isoDirRecord->length[0] = 34;
1804+	node->dot_record->fileRecordSize =
1805+	    cd9660_compute_record_size(diskStructure, node->dot_record);
1806+
1807+	if (node == diskStructure->rootNode) {
1808+		node->dot_dot_record->fileDataSector = node->fileDataSector;
1809+		memcpy(node->dot_dot_record->isoDirRecord,node->isoDirRecord,
1810+		    34);
1811+	} else {
1812+		node->dot_dot_record->fileDataSector =
1813+		    node->parent->fileDataSector;
1814+		memcpy(node->dot_dot_record->isoDirRecord,
1815+		    node->parent->isoDirRecord,34);
1816+	}
1817+	node->dot_dot_record->isoDirRecord->name_len[0] = 1;
1818+	node->dot_dot_record->isoDirRecord->name[0] = 1;
1819+	node->dot_dot_record->isoDirRecord->name[1] = 0;
1820+	node->dot_dot_record->isoDirRecord->length[0] = 34;
1821+	node->dot_dot_record->fileRecordSize =
1822+	    cd9660_compute_record_size(diskStructure, node->dot_dot_record);
1823+}
1824+
1825+/*
1826+ * @param struct cd9660node *node The node
1827+ * @param int The offset (in bytes) - SHOULD align to the beginning of a sector
1828+ * @returns int The total size of files and directory entries (should be
1829+ *              a multiple of sector size)
1830+*/
1831+static int64_t
1832+cd9660_compute_offsets(iso9660_disk *diskStructure, cd9660node *node,
1833+    int64_t startOffset)
1834+{
1835+	/*
1836+	 * This function needs to compute the size of directory records and
1837+	 * runs, file lengths, and set the appropriate variables both in
1838+	 * cd9660node and isoDirEntry
1839+	 */
1840+	int64_t used_bytes = 0;
1841+	int64_t current_sector_usage = 0;
1842+	cd9660node *child;
1843+	fsinode *inode;
1844+	int64_t r;
1845+
1846+	assert(node != NULL);
1847+
1848+
1849+	/*
1850+	 * NOTE : There needs to be some special case detection for
1851+	 * the "real root" node, since for it, node->node is undefined
1852+	 */
1853+
1854+	node->fileDataSector = -1;
1855+
1856+	if (node->type & CD9660_TYPE_DIR) {
1857+		node->fileRecordSize = cd9660_compute_record_size(
1858+		    diskStructure, node);
1859+		/*Set what sector this directory starts in*/
1860+		node->fileDataSector =
1861+		    CD9660_BLOCKS(diskStructure->sectorSize,startOffset);
1862+
1863+		cd9660_bothendian_dword(node->fileDataSector,
1864+		    node->isoDirRecord->extent);
1865+
1866+		/*
1867+		 * First loop over children, need to know the size of
1868+		 * their directory records
1869+		 */
1870+		node->fileSectorsUsed = 1;
1871+		TAILQ_FOREACH(child, &node->cn_children, cn_next_child) {
1872+			node->fileDataLength +=
1873+			    cd9660_compute_record_size(diskStructure, child);
1874+			if ((cd9660_compute_record_size(diskStructure, child) +
1875+			    current_sector_usage) >=
1876+		 	    diskStructure->sectorSize) {
1877+				current_sector_usage = 0;
1878+				node->fileSectorsUsed++;
1879+			}
1880+
1881+			current_sector_usage +=
1882+			    cd9660_compute_record_size(diskStructure, child);
1883+		}
1884+
1885+		cd9660_bothendian_dword(node->fileSectorsUsed *
1886+			diskStructure->sectorSize,node->isoDirRecord->size);
1887+
1888+		/*
1889+		 * This should point to the sector after the directory
1890+		 * record (or, the first byte in that sector)
1891+		 */
1892+		used_bytes += node->fileSectorsUsed * diskStructure->sectorSize;
1893+
1894+		for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child);
1895+		     child != NULL; child = TAILQ_NEXT(child, cn_next_child)) {
1896+			/* Directories need recursive call */
1897+			if (S_ISDIR(child->node->type)) {
1898+				r = cd9660_compute_offsets(diskStructure, child,
1899+				    used_bytes + startOffset);
1900+
1901+				if (r != -1)
1902+					used_bytes += r;
1903+				else
1904+					return -1;
1905+			}
1906+		}
1907+
1908+		/* Explicitly set the . and .. records */
1909+		cd9660_populate_dot_records(diskStructure, node);
1910+
1911+		/* Finally, do another iteration to write the file data*/
1912+		for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child);
1913+		     child != NULL;
1914+		     child = TAILQ_NEXT(child, cn_next_child)) {
1915+			/* Files need extent set */
1916+			if (S_ISDIR(child->node->type))
1917+				continue;
1918+			child->fileRecordSize =
1919+			    cd9660_compute_record_size(diskStructure, child);
1920+
1921+			child->fileSectorsUsed =
1922+			    CD9660_BLOCKS(diskStructure->sectorSize,
1923+				child->fileDataLength);
1924+
1925+			inode = child->node->inode;
1926+			if ((inode->flags & FI_ALLOCATED) == 0) {
1927+				inode->ino =
1928+				    CD9660_BLOCKS(diskStructure->sectorSize,
1929+				        used_bytes + startOffset);
1930+				inode->flags |= FI_ALLOCATED;
1931+				used_bytes += child->fileSectorsUsed *
1932+				    diskStructure->sectorSize;
1933+			} else {
1934+				INODE_WARNX(("%s: already allocated inode %d "
1935+				      "data sectors at %" PRIu32, __func__,
1936+				      (int)inode->st.st_ino, inode->ino));
1937+			}
1938+			child->fileDataSector = inode->ino;
1939+			cd9660_bothendian_dword(child->fileDataSector,
1940+				child->isoDirRecord->extent);
1941+		}
1942+	}
1943+
1944+	return used_bytes;
1945+}
1946+
1947+#if 0
1948+/* Might get rid of this func */
1949+static int
1950+cd9660_copy_stat_info(cd9660node *from, cd9660node *to, int file)
1951+{
1952+	to->node->inode->st.st_dev = 0;
1953+	to->node->inode->st.st_ino = 0;
1954+	to->node->inode->st.st_size = 0;
1955+	to->node->inode->st.st_blksize = from->node->inode->st.st_blksize;
1956+	to->node->inode->st.st_atime = from->node->inode->st.st_atime;
1957+	to->node->inode->st.st_mtime = from->node->inode->st.st_mtime;
1958+	to->node->inode->st.st_ctime = from->node->inode->st.st_ctime;
1959+	to->node->inode->st.st_uid = from->node->inode->st.st_uid;
1960+	to->node->inode->st.st_gid = from->node->inode->st.st_gid;
1961+	to->node->inode->st.st_mode = from->node->inode->st.st_mode;
1962+	/* Clear out type */
1963+	to->node->inode->st.st_mode = to->node->inode->st.st_mode & ~(S_IFMT);
1964+	if (file)
1965+		to->node->inode->st.st_mode |= S_IFREG;
1966+	else
1967+		to->node->inode->st.st_mode |= S_IFDIR;
1968+	return 1;
1969+}
1970+#endif
1971+
1972+static cd9660node *
1973+cd9660_create_virtual_entry(iso9660_disk *diskStructure, const char *name,
1974+    cd9660node *parent, int file, int insert)
1975+{
1976+	cd9660node *temp;
1977+	fsnode * tfsnode;
1978+
1979+	assert(parent != NULL);
1980+
1981+	temp = cd9660_allocate_cd9660node();
1982+	if (temp == NULL)
1983+		return NULL;
1984+
1985+	tfsnode = emalloc(sizeof(*tfsnode));
1986+	tfsnode->name = estrdup(name);
1987+	temp->isoDirRecord = emalloc(sizeof(*temp->isoDirRecord));
1988+
1989+	cd9660_convert_filename(diskStructure, tfsnode->name,
1990+	    temp->isoDirRecord->name, file);
1991+
1992+	temp->node = tfsnode;
1993+	temp->parent = parent;
1994+
1995+	if (insert) {
1996+		if (temp->parent != NULL) {
1997+			temp->level = temp->parent->level + 1;
1998+			if (!TAILQ_EMPTY(&temp->parent->cn_children))
1999+				cd9660_sorted_child_insert(temp->parent, temp);
2000+			else
2001+				TAILQ_INSERT_HEAD(&temp->parent->cn_children,
2002+				    temp, cn_next_child);
2003+		}
2004+	}
2005+
2006+	if (parent->node != NULL) {
2007+		tfsnode->type = parent->node->type;
2008+	}
2009+
2010+	/* Clear out file type bits */
2011+	tfsnode->type &= ~(S_IFMT);
2012+	if (file)
2013+		tfsnode->type |= S_IFREG;
2014+	else
2015+		tfsnode->type |= S_IFDIR;
2016+
2017+	/* Indicate that there is no spec entry (inode) */
2018+	tfsnode->flags &= ~(FSNODE_F_HASSPEC);
2019+#if 0
2020+	cd9660_copy_stat_info(parent, temp, file);
2021+#endif
2022+	return temp;
2023+}
2024+
2025+static cd9660node *
2026+cd9660_create_file(iso9660_disk *diskStructure, const char *name,
2027+    cd9660node *parent, cd9660node *me)
2028+{
2029+	cd9660node *temp;
2030+
2031+	temp = cd9660_create_virtual_entry(diskStructure, name, parent, 1, 1);
2032+	if (temp == NULL)
2033+		return NULL;
2034+
2035+	temp->fileDataLength = 0;
2036+
2037+	temp->type = CD9660_TYPE_FILE | CD9660_TYPE_VIRTUAL;
2038+
2039+	temp->node->inode = ecalloc(1, sizeof(*temp->node->inode));
2040+	*temp->node->inode = *me->node->inode;
2041+
2042+	if (cd9660_translate_node_common(diskStructure, temp) == 0)
2043+		return NULL;
2044+	return temp;
2045+}
2046+
2047+/*
2048+ * Create a new directory which does not exist on disk
2049+ * @param const char * name The name to assign to the directory
2050+ * @param const char * parent Pointer to the parent directory
2051+ * @returns cd9660node * Pointer to the new directory
2052+ */
2053+static cd9660node *
2054+cd9660_create_directory(iso9660_disk *diskStructure, const char *name,
2055+    cd9660node *parent, cd9660node *me)
2056+{
2057+	cd9660node *temp;
2058+
2059+	temp = cd9660_create_virtual_entry(diskStructure, name, parent, 0, 1);
2060+	if (temp == NULL)
2061+		return NULL;
2062+	temp->node->type |= S_IFDIR;
2063+
2064+	temp->type = CD9660_TYPE_DIR | CD9660_TYPE_VIRTUAL;
2065+
2066+	temp->node->inode = ecalloc(1, sizeof(*temp->node->inode));
2067+	*temp->node->inode = *me->node->inode;
2068+
2069+	if (cd9660_translate_node_common(diskStructure, temp) == 0)
2070+		return NULL;
2071+	return temp;
2072+}
2073+
2074+static cd9660node *
2075+cd9660_create_special_directory(iso9660_disk *diskStructure, u_char type,
2076+    cd9660node *parent)
2077+{
2078+	cd9660node *temp, *first;
2079+	char na[2];
2080+
2081+	assert(parent != NULL);
2082+
2083+	if (type == CD9660_TYPE_DOT)
2084+		na[0] = 0;
2085+	else if (type == CD9660_TYPE_DOTDOT)
2086+		na[0] = 1;
2087+	else
2088+		return 0;
2089+
2090+	na[1] = 0;
2091+	if ((temp = cd9660_create_virtual_entry(diskStructure, na, parent,
2092+	    0, 0)) == NULL)
2093+		return NULL;
2094+
2095+	temp->parent = parent;
2096+	temp->type = type;
2097+	temp->isoDirRecord->length[0] = 34;
2098+	/* Dot record is always first */
2099+	if (type == CD9660_TYPE_DOT) {
2100+		parent->dot_record = temp;
2101+		TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child);
2102+	/* DotDot should be second */
2103+	} else if (type == CD9660_TYPE_DOTDOT) {
2104+		parent->dot_dot_record = temp;
2105+		/*
2106+                 * If the first child is the dot record, insert
2107+                 * this second.  Otherwise, insert it at the head.
2108+		 */
2109+		if ((first = TAILQ_FIRST(&parent->cn_children)) == NULL ||
2110+		    (first->type & CD9660_TYPE_DOT) == 0) {
2111+			TAILQ_INSERT_HEAD(&parent->cn_children, temp,
2112+			    cn_next_child);
2113+		} else {
2114+			TAILQ_INSERT_AFTER(&parent->cn_children, first, temp,
2115+			    cn_next_child);
2116+		}
2117+	}
2118+
2119+	return temp;
2120+}
2121+
2122+static int
2123+cd9660_add_generic_bootimage(iso9660_disk *diskStructure, const char *bootimage)
2124+{
2125+	struct stat stbuf;
2126+
2127+	assert(bootimage != NULL);
2128+
2129+	if (*bootimage == '\0') {
2130+		warnx("Error: Boot image must be a filename");
2131+		return 0;
2132+	}
2133+
2134+	diskStructure->generic_bootimage = estrdup(bootimage);
2135+
2136+	/* Get information about the file */
2137+	if (lstat(diskStructure->generic_bootimage, &stbuf) == -1)
2138+		err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
2139+		    diskStructure->generic_bootimage);
2140+
2141+	if (stbuf.st_size > 32768) {
2142+		warnx("Error: Boot image must be no greater than 32768 bytes");
2143+		return 0;
2144+	}
2145+
2146+	if (diskStructure->verbose_level > 0) {
2147+		printf("Generic boot image image has size %lld\n",
2148+		    (long long)stbuf.st_size);
2149+	}
2150+
2151+	diskStructure->has_generic_bootimage = 1;
2152+
2153+	return 1;
2154+}
+357, -0
  1@@ -0,0 +1,357 @@
  2+/*	$NetBSD: cd9660.h,v 1.21 2015/12/24 15:52:37 christos Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
  6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
  7+ *
  8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
  9+ * Alan Perez-Rathke and Ram Vedam.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or
 12+ * without modification, are permitted provided that the following
 13+ * conditions are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above
 17+ *    copyright notice, this list of conditions and the following
 18+ *    disclaimer in the documentation and/or other materials provided
 19+ *    with the distribution.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
 22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
 23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
 26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
 27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 33+ * OF SUCH DAMAGE.
 34+ */
 35+
 36+#ifndef _MAKEFS_CD9660_H
 37+#define _MAKEFS_CD9660_H
 38+
 39+#if HAVE_NBTOOL_CONFIG_H
 40+#include "nbtool_config.h"
 41+#endif
 42+
 43+#include <assert.h>
 44+#include <errno.h>
 45+#include <fcntl.h>
 46+#include <stdarg.h>
 47+#include <stdio.h>
 48+#include <stdlib.h>
 49+#include <string.h>
 50+#include <unistd.h>
 51+#include <time.h>
 52+#include <limits.h>
 53+#include <sys/queue.h>
 54+#include <sys/param.h>
 55+#include <sys/endian.h>
 56+
 57+#include "makefs.h"
 58+#include "iso.h"
 59+#include "iso_rrip.h"
 60+#include "cd9660/cd9660_eltorito.h"
 61+
 62+#ifdef DEBUG
 63+#define	INODE_WARNX(__x)	warnx __x
 64+#else /* DEBUG */
 65+#define	INODE_WARNX(__x)
 66+#endif /* DEBUG */
 67+
 68+#define CD9660MAXPATH 4096
 69+
 70+#define ISO_STRING_FILTER_NONE = 0x00
 71+#define ISO_STRING_FILTER_DCHARS = 0x01
 72+#define ISO_STRING_FILTER_ACHARS = 0x02
 73+
 74+/*
 75+Extended preferences type, in the spirit of what makefs gives us (only ints)
 76+*/
 77+typedef struct {
 78+	const char  *shortName;		/* Short option */
 79+	const char	*name;		/* option name */
 80+	char		*value;		/* where to stuff the value */
 81+	int		minLength;	/* minimum for value */
 82+	int		maxLength;	/* maximum for value */
 83+	const char	*desc;		/* option description */
 84+	int		filterFlags;
 85+} string_option_t;
 86+
 87+/******** STRUCTURES **********/
 88+
 89+/*Defaults*/
 90+#define ISO_DEFAULT_VOLUMEID "MAKEFS_CD9660_IMAGE"
 91+#define ISO_DEFAULT_APPID "MAKEFS"
 92+#define ISO_DEFAULT_PUBLISHER "MAKEFS"
 93+#define ISO_DEFAULT_PREPARER "MAKEFS"
 94+
 95+#define ISO_VOLUME_DESCRIPTOR_STANDARD_ID "CD001"
 96+#define ISO_VOLUME_DESCRIPTOR_BOOT 0
 97+#define ISO_VOLUME_DESCRIPTOR_PVD 1
 98+#define ISO_VOLUME_DESCRIPTOR_TERMINATOR 255
 99+
100+/*30 for name and extension, as well as version number and padding bit*/
101+#define ISO_FILENAME_MAXLENGTH_BEFORE_VERSION 30
102+#define ISO_FILENAME_MAXLENGTH	36
103+#define ISO_FILENAME_MAXLENGTH_WITH_PADDING 37
104+
105+#define ISO_FLAG_CLEAR 0x00
106+#define ISO_FLAG_HIDDEN 0x01
107+#define ISO_FLAG_DIRECTORY 0x02
108+#define ISO_FLAG_ASSOCIATED 0x04
109+#define ISO_FLAG_PERMISSIONS 0x08
110+#define ISO_FLAG_RESERVED5 0x10
111+#define ISO_FLAG_RESERVED6 0x20
112+#define ISO_FLAG_FINAL_RECORD 0x40
113+
114+#define ISO_PATHTABLE_ENTRY_BASESIZE 8
115+
116+#define ISO_RRIP_DEFAULT_MOVE_DIR_NAME "RR_MOVED"
117+#define RRIP_DEFAULT_MOVE_DIR_NAME ".rr_moved"
118+
119+#define	CD9660_BLOCKS(__sector_size, __bytes)	\
120+	howmany((__bytes), (__sector_size))
121+
122+#define CD9660_MEM_ALLOC_ERROR(_F)	\
123+    err(EXIT_FAILURE, "%s, %s l. %d", _F, __FILE__, __LINE__)
124+
125+#define CD9660_TYPE_FILE	0x01
126+#define CD9660_TYPE_DIR		0x02
127+#define CD9660_TYPE_DOT		0x04
128+#define CD9660_TYPE_DOTDOT	0x08
129+#define CD9660_TYPE_VIRTUAL	0x80
130+
131+#define CD9660_INODE_HASH_SIZE	1024
132+#define CD9660_SECTOR_SIZE	2048
133+
134+#define CD9660_END_PADDING	150
135+
136+/* Slight modification of the ISO structure in iso.h */
137+typedef struct _iso_directory_record_cd9660 {
138+	u_char length			[ISODCL (1, 1)];	/* 711 */
139+	u_char ext_attr_length		[ISODCL (2, 2)];	/* 711 */
140+	u_char extent			[ISODCL (3, 10)];	/* 733 */
141+	u_char size			[ISODCL (11, 18)];	/* 733 */
142+	u_char date			[ISODCL (19, 25)];	/* 7 by 711 */
143+	u_char flags			[ISODCL (26, 26)];
144+	u_char file_unit_size		[ISODCL (27, 27)];	/* 711 */
145+	u_char interleave		[ISODCL (28, 28)];	/* 711 */
146+	u_char volume_sequence_number	[ISODCL (29, 32)];	/* 723 */
147+	u_char name_len			[ISODCL (33, 33)];	/* 711 */
148+	char name			[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
149+} iso_directory_record_cd9660;
150+
151+/* TODO: Lots of optimization of this structure */
152+typedef struct _cd9660node {
153+	u_char	type;/* Used internally */
154+	/* Tree structure */
155+	struct _cd9660node	*parent;	/* parent (NULL if root) */
156+	TAILQ_HEAD(cd9660_children_head, _cd9660node)	cn_children;
157+	TAILQ_ENTRY(_cd9660node)		cn_next_child;
158+
159+	struct _cd9660node *dot_record; /* For directories, used mainly in RRIP */
160+	struct _cd9660node *dot_dot_record;
161+
162+	fsnode		*node;		/* pointer to fsnode */
163+	struct _iso_directory_record_cd9660	*isoDirRecord;
164+	struct iso_extended_attributes	*isoExtAttributes;
165+
166+	/***** SIZE CALCULATION *****/
167+	/*already stored in isoDirRecord, but this is an int version, and will be
168+		copied to isoDirRecord on writing*/
169+	uint32_t fileDataSector;
170+
171+	/*
172+	 * same thing, though some notes:
173+	 * If a file, this is the file size
174+	 * If a directory, this is the size of all its children's
175+	 *	directory records
176+	 * plus necessary padding
177+	 */
178+	int64_t fileDataLength;
179+
180+	int64_t fileSectorsUsed;
181+	int fileRecordSize;/*copy of a variable, int for quicker calculations*/
182+
183+	/* Old name, used for renaming - needs to be optimized but low priority */
184+	char o_name [ISO_FILENAME_MAXLENGTH_WITH_PADDING];
185+
186+	/***** SPACE RESERVED FOR EXTENSIONS *****/
187+	/* For memory efficiency's sake - we should move this to a separate struct
188+		and point to null if not needed */
189+	/* For Rock Ridge */
190+	struct _cd9660node *rr_real_parent, *rr_relocated;
191+
192+	int64_t susp_entry_size;
193+	int64_t susp_dot_entry_size;
194+	int64_t susp_dot_dot_entry_size;
195+
196+	/* Continuation area stuff */
197+	int64_t susp_entry_ce_start;
198+	int64_t susp_dot_ce_start;
199+	int64_t susp_dot_dot_ce_start;
200+
201+	int64_t susp_entry_ce_length;
202+	int64_t susp_dot_ce_length;
203+	int64_t susp_dot_dot_ce_length;
204+
205+	/* Data to put at the end of the System Use field */
206+	int64_t su_tail_size;
207+	char *su_tail_data;
208+
209+	/*** PATH TABLE STUFF ***/
210+	int level;			/*depth*/
211+	int ptnumber;
212+	struct _cd9660node *ptnext, *ptprev, *ptlast;
213+
214+	/* SUSP entries */
215+	TAILQ_HEAD(susp_linked_list, ISO_SUSP_ATTRIBUTES) head;
216+} cd9660node;
217+
218+typedef struct _path_table_entry
219+{
220+	u_char length[ISODCL (1, 1)];
221+	u_char extended_attribute_length[ISODCL (2, 2)];
222+	u_char first_sector[ISODCL (3, 6)];
223+	u_char parent_number[ISODCL (7, 8)];
224+	u_char name[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
225+} path_table_entry;
226+
227+typedef struct _volume_descriptor
228+{
229+	u_char *volumeDescriptorData; /*ALWAYS 2048 bytes long*/
230+	int64_t sector;
231+	struct _volume_descriptor *next;
232+} volume_descriptor;
233+
234+typedef struct _iso9660_disk {
235+	int sectorSize;
236+	struct iso_primary_descriptor		primaryDescriptor;
237+	struct iso_supplementary_descriptor	supplementaryDescriptor;
238+
239+	volume_descriptor *firstVolumeDescriptor;
240+
241+	cd9660node *rootNode;
242+
243+	/* Important sector numbers here */
244+	/* primaryDescriptor.type_l_path_table*/
245+	int64_t primaryBigEndianTableSector;
246+
247+	/* primaryDescriptor.type_m_path_table*/
248+	int64_t primaryLittleEndianTableSector;
249+
250+	/* primaryDescriptor.opt_type_l_path_table*/
251+	int64_t secondaryBigEndianTableSector;
252+
253+	/* primaryDescriptor.opt_type_m_path_table*/
254+	int64_t secondaryLittleEndianTableSector;
255+
256+	/* primaryDescriptor.path_table_size*/
257+	int pathTableLength;
258+	int64_t dataFirstSector;
259+
260+	int64_t totalSectors;
261+	/* OPTIONS GO HERE */
262+	int	isoLevel;
263+
264+	int include_padding_areas;
265+
266+	int follow_sym_links;
267+	int verbose_level;
268+	int displayHelp;
269+	int keep_bad_images;
270+
271+	/* SUSP options and variables */
272+	int64_t susp_continuation_area_start_sector;
273+	int64_t susp_continuation_area_size;
274+	int64_t susp_continuation_area_current_free;
275+
276+	int rock_ridge_enabled;
277+	/* Other Rock Ridge Variables */
278+	char *rock_ridge_renamed_dir_name;
279+	int rock_ridge_move_count;
280+	cd9660node *rr_moved_dir;
281+
282+	int archimedes_enabled;
283+	int chrp_boot;
284+
285+	/* Spec breaking options */
286+	u_char allow_deep_trees;
287+	u_char allow_start_dot;
288+	u_char allow_max_name; /* Allow 37 char filenames*/
289+	u_char allow_illegal_chars; /* ~, !, # */
290+	u_char allow_lowercase;
291+	u_char allow_multidot;
292+	u_char omit_trailing_period;
293+
294+	/* BOOT INFORMATION HERE */
295+	int has_generic_bootimage; /* Default to 0 */
296+	char *generic_bootimage;
297+
298+	int is_bootable;/* Default to 0 */
299+	int64_t boot_catalog_sector;
300+	boot_volume_descriptor *boot_descriptor;
301+	char * boot_image_directory;
302+
303+	TAILQ_HEAD(boot_image_list,cd9660_boot_image) boot_images;
304+	int image_serialno;
305+	LIST_HEAD(boot_catalog_entries,boot_catalog_entry) boot_entries;
306+
307+} iso9660_disk;
308+
309+/************ FUNCTIONS **************/
310+int			cd9660_valid_a_chars(const char *);
311+int			cd9660_valid_d_chars(const char *);
312+void			cd9660_uppercase_characters(char *, size_t);
313+
314+/* ISO Data Types */
315+void			cd9660_721(uint16_t, unsigned char *);
316+void			cd9660_731(uint32_t, unsigned char *);
317+void			cd9660_722(uint16_t, unsigned char *);
318+void			cd9660_732(uint32_t, unsigned char *);
319+void 			cd9660_bothendian_dword(uint32_t dw, unsigned char *);
320+void 			cd9660_bothendian_word(uint16_t dw, unsigned char *);
321+void			cd9660_set_date(char *, time_t);
322+void			cd9660_time_8426(unsigned char *, time_t);
323+void			cd9660_time_915(unsigned char *, time_t);
324+
325+/*** Boot Functions ***/
326+int	cd9660_write_generic_bootimage(FILE *);
327+int	cd9660_write_boot(iso9660_disk *, FILE *);
328+int	cd9660_add_boot_disk(iso9660_disk *, const char *);
329+int	cd9660_eltorito_add_boot_option(iso9660_disk *, const char *,
330+    const char *);
331+int	cd9660_setup_boot(iso9660_disk *, int);
332+int	cd9660_setup_boot_volume_descriptor(iso9660_disk *,
333+    volume_descriptor *);
334+
335+
336+/*** Write Functions ***/
337+int	cd9660_write_image(iso9660_disk *, const char *image);
338+int	cd9660_copy_file(iso9660_disk *, FILE *, off_t, const char *);
339+
340+void	cd9660_compute_full_filename(cd9660node *, char *);
341+int	cd9660_compute_record_size(iso9660_disk *, cd9660node *);
342+
343+/* Debugging functions */
344+void	debug_print_tree(iso9660_disk *, cd9660node *,int);
345+void	debug_print_path_tree(cd9660node *);
346+void	debug_print_volume_descriptor_information(iso9660_disk *);
347+void	debug_dump_to_xml_ptentry(path_table_entry *,int, int);
348+void	debug_dump_to_xml_path_table(FILE *, off_t, int, int);
349+void	debug_dump_to_xml(FILE *);
350+int	debug_get_encoded_number(unsigned char *, int);
351+void	debug_dump_integer(const char *, char *,int);
352+void	debug_dump_string(const char *,unsigned char *,int);
353+void	debug_dump_directory_record_9_1(unsigned char *);
354+void	debug_dump_to_xml_volume_descriptor(unsigned char *,int);
355+
356+void	cd9660_pad_string_spaces(char *, int);
357+
358+#endif
+15, -0
 1@@ -0,0 +1,15 @@
 2+#	$NetBSD: Makefile.inc,v 1.3 2012/08/10 12:10:29 joerg Exp $
 3+#
 4+
 5+.PATH:	${.CURDIR}/cd9660 ${NETBSDSRCDIR}/sys/fs/cd9660
 6+
 7+CPPFLAGS+=-I${NETBSDSRCDIR}/sys/fs/cd9660
 8+
 9+SRCS+=	cd9660_strings.c cd9660_debug.c cd9660_eltorito.c
10+SRCS+=	cd9660_write.c cd9660_conversion.c iso9660_rrip.c cd9660_archimedes.c
11+
12+.if !defined(HOSTPROGNAME)
13+.for f in cd9660_debug cd9660_write
14+COPTS.${f}.c+=  -Wno-pointer-sign
15+.endfor
16+.endif
+130, -0
  1@@ -0,0 +1,130 @@
  2+/* $NetBSD: cd9660_archimedes.c,v 1.3 2022/04/09 10:05:35 riastradh Exp $ */
  3+
  4+/*-
  5+ * Copyright (c) 1998, 2009 Ben Harris
  6+ * All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. The name of the author may not be used to endorse or promote products
 17+ *    derived from this software without specific prior written permission.
 18+ *
 19+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 20+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 21+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 22+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 23+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 24+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 25+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 26+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 27+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 28+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 29+ */
 30+/*
 31+ * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension
 32+ *
 33+ * RISC OS CDFS looks for a special block at the end of the System Use
 34+ * Field for each file.  If present, this contains the RISC OS load
 35+ * and exec address (used to hold the file timestamp and type), the
 36+ * file attributes, and a flag indicating whether the first character
 37+ * of the filename should be replaced with '!' (since many special
 38+ * RISC OS filenames do).
 39+ */
 40+
 41+#if HAVE_NBTOOL_CONFIG_H
 42+#include "nbtool_config.h"
 43+#endif
 44+
 45+#include <sys/cdefs.h>
 46+#if defined(__RCSID) && !defined(__lint)
 47+__RCSID("$NetBSD: cd9660_archimedes.c,v 1.3 2022/04/09 10:05:35 riastradh Exp $");
 48+#endif  /* !__lint */
 49+
 50+#include <assert.h>
 51+#include <stdint.h>
 52+#include <stdio.h>
 53+#include <string.h>
 54+#include <util.h>
 55+
 56+#include "makefs.h"
 57+#include "cd9660.h"
 58+#include "cd9660_archimedes.h"
 59+
 60+/*
 61+ * Convert a Unix time_t (non-leap seconds since 1970-01-01) to a RISC
 62+ * OS time (non-leap(?) centiseconds since 1900-01-01(?)).
 63+ */
 64+
 65+static u_int64_t
 66+riscos_date(time_t unixtime)
 67+{
 68+	u_int64_t base;
 69+
 70+	base = 31536000ULL * 70 + 86400 * 17;
 71+	return (((u_int64_t)unixtime) + base)*100;
 72+}
 73+
 74+/*
 75+ * Add "ARCHIMEDES" metadata to a node if that seems appropriate.
 76+ *
 77+ * We touch regular files with names matching /,[0-9a-f]{3}$/ and
 78+ * directories matching /^!/.
 79+ */
 80+static void
 81+archimedes_convert_node(cd9660node *node)
 82+{
 83+	struct ISO_ARCHIMEDES *arc;
 84+	size_t len;
 85+	int type = -1;
 86+	uint64_t stamp;
 87+
 88+	if (node->su_tail_data != NULL)
 89+		/* Something else already has the tail. */
 90+		return;
 91+
 92+	len = strlen(node->node->name);
 93+	if (len < 1) return;
 94+
 95+	if (len >= 4 && node->node->name[len-4] == ',')
 96+		/* XXX should support ,xxx and ,lxa */
 97+		type = strtoul(node->node->name + len - 3, NULL, 16);
 98+	if (type == -1 && node->node->name[0] != '!')
 99+		return;
100+	if (type == -1) type = 0;
101+
102+	assert(sizeof(*arc) == 32);
103+	arc = ecalloc(1, sizeof(*arc));
104+
105+	stamp = riscos_date(node->node->inode->st.st_mtime);
106+
107+	memcpy(arc->magic, "ARCHIMEDES", 10);
108+	cd9660_731(0xfff00000 | (type << 8) | (stamp >> 32), arc->loadaddr);
109+	cd9660_731(stamp & 0x00ffffffffULL, arc->execaddr);
110+	arc->ro_attr = RO_ACCESS_UR | RO_ACCESS_OR;
111+	arc->cdfs_attr = node->node->name[0] == '!' ? CDFS_PLING : 0;
112+	node->su_tail_data = (void *)arc;
113+	node->su_tail_size = sizeof(*arc);
114+}
115+
116+/*
117+ * Add "ARCHIMEDES" metadata to an entire tree recursively.
118+ */
119+void
120+archimedes_convert_tree(cd9660node *node)
121+{
122+	cd9660node *cn;
123+
124+	assert(node != NULL);
125+
126+	archimedes_convert_node(node);
127+
128+		/* Recurse on children. */
129+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
130+		archimedes_convert_tree(cn);
131+}
+48, -0
 1@@ -0,0 +1,48 @@
 2+/* $NetBSD: cd9660_archimedes.h,v 1.2 2022/04/09 10:05:35 riastradh Exp $ */
 3+
 4+/*-
 5+ * Copyright (c) 1998, 2009 Ben Harris
 6+ * All rights reserved.
 7+ *
 8+ * Redistribution and use in source and binary forms, with or without
 9+ * modification, are permitted provided that the following conditions
10+ * are met:
11+ * 1. Redistributions of source code must retain the above copyright
12+ *    notice, this list of conditions and the following disclaimer.
13+ * 2. Redistributions in binary form must reproduce the above copyright
14+ *    notice, this list of conditions and the following disclaimer in the
15+ *    documentation and/or other materials provided with the distribution.
16+ * 3. The name of the author may not be used to endorse or promote products
17+ *    derived from this software without specific prior written permission.
18+ *
19+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+ */
30+/*
31+ * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension
32+ */
33+
34+struct ISO_ARCHIMEDES {
35+	char		magic[10];	/* "ARCHIMEDES" */
36+	unsigned char	loadaddr[4];	/* Load address, little-endian */
37+	unsigned char	execaddr[4];	/* Exec address, little-endian */
38+	unsigned char	ro_attr;	/* RISC OS attributes */
39+#define RO_ACCESS_UR	0x01 /* Owner read */
40+#define RO_ACCESS_UW	0x02 /* Owner write */
41+#define RO_ACCESS_L	0x04 /* Locked */
42+#define RO_ACCESS_OR	0x10 /* Public read */
43+#define RO_ACCESS_OW	0x20 /* Public write */
44+	unsigned char	cdfs_attr;	/* Extra attributes for CDFS */
45+#define CDFS_PLING	0x01	/* Filename begins with '!' */
46+	char		reserved[12];
47+};
48+
49+extern void archimedes_convert_tree(cd9660node *);
+212, -0
  1@@ -0,0 +1,212 @@
  2+/*	$NetBSD: cd9660_conversion.c,v 1.6 2024/06/17 13:31:17 reinoud Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
  6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
  7+ *
  8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
  9+ * Alan Perez-Rathke and Ram Vedam.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or
 12+ * without modification, are permitted provided that the following
 13+ * conditions are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above
 17+ *    copyright notice, this list of conditions and the following
 18+ *    disclaimer in the documentation and/or other materials provided
 19+ *    with the distribution.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
 22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
 23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
 26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
 27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 33+ * OF SUCH DAMAGE.
 34+ */
 35+#include "cd9660.h"
 36+
 37+#include <sys/cdefs.h>
 38+#if defined(__RCSID) && !defined(__lint)
 39+__RCSID("$NetBSD: cd9660_conversion.c,v 1.6 2024/06/17 13:31:17 reinoud Exp $");
 40+#endif  /* !__lint */
 41+
 42+
 43+static char cd9660_compute_gm_offset(time_t);
 44+
 45+#if 0
 46+static inline int
 47+cd9660_pad_even(length)
 48+int length;
 49+{
 50+	return length + (length & 0x01);
 51+}
 52+#endif
 53+
 54+/*
 55+* These can probably be implemented using a macro
 56+*/
 57+
 58+/* Little endian */
 59+void
 60+cd9660_721(uint16_t w, unsigned char *twochar)
 61+{
 62+#if BYTE_ORDER == BIG_ENDIAN
 63+	w = bswap16(w);
 64+#endif
 65+	memcpy(twochar,&w,2);
 66+}
 67+
 68+void
 69+cd9660_731(uint32_t w, unsigned char *fourchar)
 70+{
 71+#if BYTE_ORDER == BIG_ENDIAN
 72+	w = bswap32(w);
 73+#endif
 74+	memcpy(fourchar,&w,4);
 75+}
 76+
 77+/* Big endian */
 78+void
 79+cd9660_722(uint16_t w, unsigned char *twochar)
 80+{
 81+#if BYTE_ORDER == LITTLE_ENDIAN
 82+	w = bswap16(w);
 83+#endif
 84+	memcpy(twochar,&w,2);
 85+}
 86+
 87+void
 88+cd9660_732(uint32_t w, unsigned char *fourchar)
 89+{
 90+#if BYTE_ORDER == LITTLE_ENDIAN
 91+	w = bswap32(w);
 92+#endif
 93+	memcpy(fourchar,&w,4);
 94+}
 95+
 96+/**
 97+* Convert a dword into a double endian string of eight characters
 98+* @param int The double word to convert
 99+* @param char* The string to write the both endian double word to - It is assumed this is allocated and at least
100+*		eight characters long
101+*/
102+void
103+cd9660_bothendian_dword(uint32_t dw, unsigned char *eightchar)
104+{
105+	uint32_t le, be;
106+#if BYTE_ORDER == LITTLE_ENDIAN
107+	le = dw;
108+	be = bswap32(dw);
109+#endif
110+#if BYTE_ORDER == BIG_ENDIAN
111+	be = dw;
112+	le = bswap32(dw);
113+#endif
114+	memcpy(eightchar, &le, 4);
115+	memcpy((eightchar+4), &be, 4);
116+}
117+
118+/**
119+* Convert a word into a double endian string of four characters
120+* @param int The word to convert
121+* @param char* The string to write the both endian word to - It is assumed this is allocated and at least
122+*		four characters long
123+*/
124+void
125+cd9660_bothendian_word(uint16_t dw, unsigned char *fourchar)
126+{
127+	uint16_t le, be;
128+#if BYTE_ORDER == LITTLE_ENDIAN
129+	le = dw;
130+	be = bswap16(dw);
131+#endif
132+#if BYTE_ORDER == BIG_ENDIAN
133+	be = dw;
134+	le = bswap16(dw);
135+#endif
136+	memcpy(fourchar, &le, 2);
137+	memcpy((fourchar+2), &be, 2);
138+}
139+
140+void
141+cd9660_pad_string_spaces(char *str, int len)
142+{
143+	int i;
144+
145+	for (i = 0; i < len; i ++) {
146+		if (str[i] == '\0')
147+			str[i] = 0x20;
148+	}
149+}
150+
151+static char
152+cd9660_compute_gm_offset(time_t tim)
153+{
154+	if (stampst.st_ino)
155+		return 0;
156+
157+	struct tm t, gm;
158+
159+	(void)localtime_r(&tim, &t);
160+	(void)gmtime_r(&tim, &gm);
161+	gm.tm_year -= t.tm_year;
162+	gm.tm_yday -= t.tm_yday;
163+	gm.tm_hour -= t.tm_hour;
164+	gm.tm_min  -= t.tm_min;
165+	if (gm.tm_year < 0)
166+		gm.tm_yday = -1;
167+	else if (gm.tm_year > 0)
168+		gm.tm_yday = 1;
169+
170+	return (char)(-(gm.tm_min + 60* (24 * gm.tm_yday + gm.tm_hour)) / 15);
171+}
172+
173+/* Long dates: 17 characters */
174+void
175+cd9660_time_8426(unsigned char *buf, time_t tim)
176+{
177+	struct tm t;
178+	char temp[70];	/* we know its only 18 but gcc can't figure this out */
179+
180+	if (stampst.st_ino)
181+		(void)gmtime_r(&tim, &t);
182+	else
183+		(void)localtime_r(&tim, &t);
184+	(void)snprintf(temp, sizeof(temp), "%04i%02i%02i%02i%02i%02i%02i",
185+		1900+(int)t.tm_year,
186+		(int)t.tm_mon+1,
187+		(int)t.tm_mday,
188+		(int)t.tm_hour,
189+		(int)t.tm_min,
190+		(int)t.tm_sec,
191+		0);
192+	(void)memcpy(buf, temp, 16);
193+	buf[16] = cd9660_compute_gm_offset(tim);
194+}
195+
196+/* Short dates: 7 characters */
197+void
198+cd9660_time_915(unsigned char *buf, time_t tim)
199+{
200+	struct tm t;
201+
202+	if (stampst.st_ino)
203+		(void)gmtime_r(&tim, &t);
204+	else
205+		(void)localtime_r(&tim, &t);
206+	buf[0] = t.tm_year;
207+	buf[1] = t.tm_mon+1;
208+	buf[2] = t.tm_mday;
209+	buf[3] = t.tm_hour;
210+	buf[4] = t.tm_min;
211+	buf[5] = t.tm_sec;
212+	buf[6] = cd9660_compute_gm_offset(tim);
213+}
+498, -0
  1@@ -0,0 +1,498 @@
  2+/*	$NetBSD: cd9660_debug.c,v 1.14 2023/12/28 12:13:55 tsutsui Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
  6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
  7+ *
  8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
  9+ * Alan Perez-Rathke and Ram Vedam.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or
 12+ * without modification, are permitted provided that the following
 13+ * conditions are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above
 17+ *    copyright notice, this list of conditions and the following
 18+ *    disclaimer in the documentation and/or other materials provided
 19+ *    with the distribution.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
 22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
 23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
 26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
 27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 33+ * OF SUCH DAMAGE.
 34+ */
 35+
 36+#if HAVE_NBTOOL_CONFIG_H
 37+#include "nbtool_config.h"
 38+#endif
 39+
 40+#include <sys/cdefs.h>
 41+#include <sys/param.h>
 42+
 43+#if defined(__RCSID) && !defined(__lint)
 44+__RCSID("$NetBSD: cd9660_debug.c,v 1.14 2023/12/28 12:13:55 tsutsui Exp $");
 45+#endif  /* !__lint */
 46+
 47+#if !HAVE_NBTOOL_CONFIG_H
 48+#include <sys/mount.h>
 49+#endif
 50+
 51+#include "makefs.h"
 52+#include "cd9660.h"
 53+#include "iso9660_rrip.h"
 54+
 55+static void debug_print_susp_attrs(cd9660node *, int);
 56+static void debug_dump_to_xml_padded_hex_output(const char *, unsigned char *,
 57+						int);
 58+
 59+static inline void
 60+print_n_tabs(int n)
 61+{
 62+	int i;
 63+
 64+	for (i = 1; i <= n; i ++)
 65+		printf("\t");
 66+}
 67+
 68+#if 0
 69+void
 70+debug_print_rrip_info(n)
 71+cd9660node *n;
 72+{
 73+	struct ISO_SUSP_ATTRIBUTES *t;
 74+	TAILQ_FOREACH(t, &node->head, rr_ll) {
 75+
 76+	}
 77+}
 78+#endif
 79+
 80+static void
 81+debug_print_susp_attrs(cd9660node *n, int indent)
 82+{
 83+	struct ISO_SUSP_ATTRIBUTES *t;
 84+
 85+	TAILQ_FOREACH(t, &n->head, rr_ll) {
 86+		print_n_tabs(indent);
 87+		printf("-");
 88+		printf("%c%c: L:%i",t->attr.su_entry.SP.h.type[0],
 89+		    t->attr.su_entry.SP.h.type[1],
 90+		    (int)t->attr.su_entry.SP.h.length[0]);
 91+		printf("\n");
 92+	}
 93+}
 94+
 95+void
 96+debug_print_tree(iso9660_disk *diskStructure, cd9660node *node, int level)
 97+{
 98+#if !HAVE_NBTOOL_CONFIG_H
 99+	cd9660node *cn;
100+
101+	print_n_tabs(level);
102+	if (node->type & CD9660_TYPE_DOT) {
103+		printf(". (%i)\n",
104+		    isonum_733(node->isoDirRecord->extent));
105+	} else if (node->type & CD9660_TYPE_DOTDOT) {
106+		printf("..(%i)\n",
107+		    isonum_733(node->isoDirRecord->extent));
108+	} else if (node->isoDirRecord->name[0]=='\0') {
109+		printf("(ROOT) (%" PRIu32 " to %" PRId64 ")\n",
110+		    node->fileDataSector,
111+		    node->fileDataSector +
112+			node->fileSectorsUsed - 1);
113+	} else {
114+		printf("%s (%s) (%" PRIu32 " to %" PRId64 ")\n",
115+		    node->isoDirRecord->name,
116+		    (node->isoDirRecord->flags[0]
117+			& ISO_FLAG_DIRECTORY) ?  "DIR" : "FILE",
118+		    node->fileDataSector,
119+		    (node->fileSectorsUsed == 0) ?
120+			node->fileDataSector :
121+			node->fileDataSector
122+			    + node->fileSectorsUsed - 1);
123+	}
124+	if (diskStructure->rock_ridge_enabled)
125+		debug_print_susp_attrs(node, level + 1);
126+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
127+		debug_print_tree(diskStructure, cn, level + 1);
128+#else
129+	printf("Sorry, debugging is not supported in host-tools mode.\n");
130+#endif
131+}
132+
133+void
134+debug_print_path_tree(cd9660node *n)
135+{
136+	cd9660node *iterator = n;
137+
138+	/* Only display this message when called with the root node */
139+	if (n->parent == NULL)
140+		printf("debug_print_path_table: Dumping path table contents\n");
141+
142+	while (iterator != NULL) {
143+		if (iterator->isoDirRecord->name[0] == '\0')
144+			printf("0) (ROOT)\n");
145+		else
146+			printf("%i) %s\n", iterator->level,
147+			    iterator->isoDirRecord->name);
148+
149+		iterator = iterator->ptnext;
150+	}
151+}
152+
153+void
154+debug_print_volume_descriptor_information(iso9660_disk *diskStructure)
155+{
156+	volume_descriptor *tmp = diskStructure->firstVolumeDescriptor;
157+	char temp[CD9660_SECTOR_SIZE];
158+
159+	printf("==Listing Volume Descriptors==\n");
160+
161+	while (tmp != NULL) {
162+		memset(temp, 0, CD9660_SECTOR_SIZE);
163+		memcpy(temp, tmp->volumeDescriptorData + 1, 5);
164+		printf("Volume descriptor in sector %" PRId64
165+		    ": type %i, ID %s\n",
166+		    tmp->sector, tmp->volumeDescriptorData[0], temp);
167+		switch(tmp->volumeDescriptorData[0]) {
168+		case 0:/*boot record*/
169+			break;
170+
171+		case 1:		/* PVD */
172+			break;
173+
174+		case 2:		/* SVD */
175+			break;
176+
177+		case 3:		/* Volume Partition Descriptor */
178+			break;
179+
180+		case 255:	/* terminator */
181+			break;
182+		}
183+		tmp = tmp->next;
184+	}
185+
186+	printf("==Done Listing Volume Descriptors==\n");
187+}
188+
189+void
190+debug_dump_to_xml_ptentry(path_table_entry *pttemp, int num, int mode)
191+{
192+	printf("<ptentry num=\"%i\">\n" ,num);
193+	printf("<length>%i</length>\n", pttemp->length[0]);
194+	printf("<extended_attribute_length>%i</extended_attribute_length>\n",
195+	    pttemp->extended_attribute_length[0]);
196+	printf("<parent_number>%i</parent_number>\n",
197+	    debug_get_encoded_number(pttemp->parent_number,mode));
198+	debug_dump_to_xml_padded_hex_output("name",
199+	    pttemp->name, pttemp->length[0]);
200+	printf("</ptentry>\n");
201+}
202+
203+void
204+debug_dump_to_xml_path_table(FILE *fd, off_t sector, int size, int mode)
205+{
206+	path_table_entry pttemp;
207+	int t = 0;
208+	int n = 0;
209+
210+	if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
211+		err(EXIT_FAILURE, "fseeko");
212+
213+	while (t < size) {
214+		/* Read fixed data first */
215+		fread(&pttemp, 1, 8, fd);
216+		t += 8;
217+		/* Read variable */
218+		fread(((unsigned char*)&pttemp) + 8, 1, pttemp.length[0], fd);
219+		t += pttemp.length[0];
220+		debug_dump_to_xml_ptentry(&pttemp, n, mode);
221+		n++;
222+	}
223+
224+}
225+
226+/*
227+ * XML Debug output functions
228+ * Dump hierarchy of CD, as well as volume info, to XML
229+ * Can be used later to diff against a standard,
230+ * or just provide easy to read detailed debug output
231+ */
232+void
233+debug_dump_to_xml(FILE *fd)
234+{
235+	unsigned char buf[CD9660_SECTOR_SIZE];
236+	off_t sector;
237+	int t, t2;
238+	struct iso_primary_descriptor primaryVD;
239+	struct _boot_volume_descriptor bootVD;
240+
241+	memset(&primaryVD, 0, sizeof(primaryVD));
242+	printf("<cd9660dump>\n");
243+
244+	/* Display Volume Descriptors */
245+	sector = 16;
246+	do {
247+		if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
248+			err(EXIT_FAILURE, "fseeko");
249+		fread(buf, 1, CD9660_SECTOR_SIZE, fd);
250+		t = (int)((unsigned char)buf[0]);
251+		switch (t) {
252+		case 0:
253+			memcpy(&bootVD, buf, CD9660_SECTOR_SIZE);
254+			break;
255+		case 1:
256+			memcpy(&primaryVD, buf, CD9660_SECTOR_SIZE);
257+			break;
258+		}
259+		debug_dump_to_xml_volume_descriptor(buf, sector);
260+		sector++;
261+	} while (t != 255);
262+
263+	t = debug_get_encoded_number((u_char *)primaryVD.type_l_path_table,
264+	    731);
265+	t2 = debug_get_encoded_number((u_char *)primaryVD.path_table_size, 733);
266+	printf("Path table 1 located at sector %i and is %i bytes long\n",
267+	    t,t2);
268+	debug_dump_to_xml_path_table(fd, t, t2, 721);
269+
270+	t = debug_get_encoded_number((u_char *)primaryVD.type_m_path_table,
271+	    731);
272+	debug_dump_to_xml_path_table(fd, t, t2, 722);
273+
274+	printf("</cd9660dump>\n");
275+}
276+
277+static void
278+debug_dump_to_xml_padded_hex_output(const char *element, unsigned char *buf,
279+				    int len)
280+{
281+	int i;
282+	int t;
283+
284+	printf("<%s>",element);
285+	for (i = 0; i < len; i++) {
286+		t = (unsigned char)buf[i];
287+		if (t >= 32 && t < 127)
288+			printf("%c",t);
289+	}
290+	printf("</%s>\n",element);
291+
292+	printf("<%s:hex>",element);
293+	for (i = 0; i < len; i++) {
294+		t = (unsigned char)buf[i];
295+		printf(" %x",t);
296+	}
297+	printf("</%s:hex>\n",element);
298+}
299+
300+int
301+debug_get_encoded_number(unsigned char* buf, int mode)
302+{
303+#if !HAVE_NBTOOL_CONFIG_H
304+	switch (mode) {
305+	/* 711: Single bite */
306+	case 711:
307+		return isonum_711(buf);
308+
309+	/* 712: Single signed byte */
310+	case 712:
311+		return isonum_712((signed char *)buf);
312+
313+	/* 721: 16 bit LE */
314+	case 721:
315+		return isonum_721(buf);
316+
317+	/* 731: 32 bit LE */
318+	case 731:
319+		return isonum_731(buf);
320+
321+	/* 722: 16 bit BE */
322+	case 722:
323+		return isonum_722(buf);
324+
325+	/* 732: 32 bit BE */
326+	case 732:
327+		return isonum_732(buf);
328+
329+	/* 723: 16 bit bothE */
330+	case 723:
331+		return isonum_723(buf);
332+
333+	/* 733: 32 bit bothE */
334+	case 733:
335+		return isonum_733(buf);
336+	}
337+#endif
338+	return 0;
339+}
340+
341+void
342+debug_dump_integer(const char *element, char* buf, int mode)
343+{
344+	printf("<%s>%i</%s>\n", element,
345+	    debug_get_encoded_number((unsigned char *)buf, mode), element);
346+}
347+
348+void
349+debug_dump_string(const char *element NETCOMPAT_UNUSED,
350+    unsigned char *buf NETCOMPAT_UNUSED, int len NETCOMPAT_UNUSED)
351+{
352+
353+}
354+
355+void
356+debug_dump_directory_record_9_1(unsigned char* buf)
357+{
358+	printf("<directoryrecord>\n");
359+	debug_dump_integer("length",
360+	    ((struct iso_directory_record*) buf)->length, 711);
361+	debug_dump_integer("ext_attr_length",
362+	    ((struct iso_directory_record*) buf)->ext_attr_length,711);
363+	debug_dump_integer("extent",
364+	    (char *)((struct iso_directory_record*) buf)->extent, 733);
365+	debug_dump_integer("size",
366+	    (char *)((struct iso_directory_record*) buf)->size, 733);
367+	debug_dump_integer("flags",
368+	    ((struct iso_directory_record*) buf)->flags, 711);
369+	debug_dump_integer("file_unit_size",
370+	    ((struct iso_directory_record*) buf)->file_unit_size,711);
371+	debug_dump_integer("interleave",
372+	    ((struct iso_directory_record*) buf)->interleave, 711);
373+	debug_dump_integer("volume_sequence_number",
374+	    ((struct iso_directory_record*) buf)->volume_sequence_number,
375+	    723);
376+	debug_dump_integer("name_len",
377+	    ((struct iso_directory_record*) buf)->name_len, 711);
378+	debug_dump_to_xml_padded_hex_output("name",
379+	    (u_char *)((struct iso_directory_record*) buf)->name,
380+		debug_get_encoded_number((u_char *)
381+		    ((struct iso_directory_record*) buf)->length, 711));
382+	printf("</directoryrecord>\n");
383+}
384+
385+
386+void
387+debug_dump_to_xml_volume_descriptor(unsigned char* buf, int sector)
388+{
389+	printf("<volumedescriptor sector=\"%i\">\n", sector);
390+	printf("<vdtype>");
391+	switch(buf[0]) {
392+	case 0:
393+		printf("boot");
394+		break;
395+
396+	case 1:
397+		printf("primary");
398+		break;
399+
400+	case 2:
401+		printf("supplementary");
402+		break;
403+
404+	case 3:
405+		printf("volume partition descriptor");
406+		break;
407+
408+	case 255:
409+		printf("terminator");
410+		break;
411+	}
412+
413+	printf("</vdtype>\n");
414+	switch(buf[0]) {
415+	case 1:
416+		debug_dump_integer("type",
417+		    ((struct iso_primary_descriptor*)buf)->type, 711);
418+		debug_dump_to_xml_padded_hex_output("id",
419+		    (u_char *)((struct iso_primary_descriptor*) buf)->id,
420+		    ISODCL (  2,   6));
421+		debug_dump_integer("version",
422+		    ((struct iso_primary_descriptor*)buf)->version,
423+		     711);
424+		debug_dump_to_xml_padded_hex_output("system_id",
425+		    (u_char *)((struct iso_primary_descriptor*)buf)->system_id,
426+		    ISODCL(9,40));
427+		debug_dump_to_xml_padded_hex_output("volume_id",
428+		    (u_char *)((struct iso_primary_descriptor*)buf)->volume_id,
429+		    ISODCL(41,72));
430+		debug_dump_integer("volume_space_size",
431+		    ((struct iso_primary_descriptor*)buf)->volume_space_size,
432+		    733);
433+		debug_dump_integer("volume_set_size",
434+		    ((struct iso_primary_descriptor*)buf)->volume_set_size,
435+			    733);
436+		debug_dump_integer("volume_sequence_number",
437+		    ((struct iso_primary_descriptor*)buf)->volume_sequence_number,
438+		    723);
439+		debug_dump_integer("logical_block_size",
440+		    ((struct iso_primary_descriptor*)buf)->logical_block_size,
441+			    723);
442+		debug_dump_integer("path_table_size",
443+		    ((struct iso_primary_descriptor*)buf)->path_table_size,
444+			    733);
445+		debug_dump_integer("type_l_path_table",
446+		    ((struct iso_primary_descriptor*)buf)->type_l_path_table,
447+		    731);
448+		debug_dump_integer("opt_type_l_path_table",
449+		    ((struct iso_primary_descriptor*)buf)->opt_type_l_path_table,
450+		    731);
451+		debug_dump_integer("type_m_path_table",
452+		    ((struct iso_primary_descriptor*)buf)->type_m_path_table,
453+		    732);
454+		debug_dump_integer("opt_type_m_path_table",
455+			((struct iso_primary_descriptor*)buf)->opt_type_m_path_table,732);
456+		debug_dump_directory_record_9_1(
457+		    (u_char *)((struct iso_primary_descriptor*)buf)->root_directory_record);
458+		debug_dump_to_xml_padded_hex_output("volume_set_id",
459+		    (u_char *)((struct iso_primary_descriptor*) buf)->volume_set_id,
460+		    ISODCL (191, 318));
461+		debug_dump_to_xml_padded_hex_output("publisher_id",
462+		    (u_char *)((struct iso_primary_descriptor*) buf)->publisher_id,
463+		    ISODCL (319, 446));
464+		debug_dump_to_xml_padded_hex_output("preparer_id",
465+		    (u_char *)((struct iso_primary_descriptor*) buf)->preparer_id,
466+		    ISODCL (447, 574));
467+		debug_dump_to_xml_padded_hex_output("application_id",
468+		    (u_char *)((struct iso_primary_descriptor*) buf)->application_id,
469+		    ISODCL (575, 702));
470+		debug_dump_to_xml_padded_hex_output("copyright_file_id",
471+		    (u_char *)((struct iso_primary_descriptor*) buf)->copyright_file_id,
472+		    ISODCL (703, 739));
473+		debug_dump_to_xml_padded_hex_output("abstract_file_id",
474+		    (u_char *)((struct iso_primary_descriptor*) buf)->abstract_file_id,
475+		    ISODCL (740, 776));
476+		debug_dump_to_xml_padded_hex_output("bibliographic_file_id",
477+		    (u_char *)((struct iso_primary_descriptor*) buf)->bibliographic_file_id,
478+		    ISODCL (777, 813));
479+
480+		debug_dump_to_xml_padded_hex_output("creation_date",
481+		    (u_char *)((struct iso_primary_descriptor*) buf)->creation_date,
482+		    ISODCL (814, 830));
483+		debug_dump_to_xml_padded_hex_output("modification_date",
484+		    (u_char *)((struct iso_primary_descriptor*) buf)->modification_date,
485+		    ISODCL (831, 847));
486+		debug_dump_to_xml_padded_hex_output("expiration_date",
487+		    (u_char *)((struct iso_primary_descriptor*) buf)->expiration_date,
488+		    ISODCL (848, 864));
489+		debug_dump_to_xml_padded_hex_output("effective_date",
490+		    (u_char *)((struct iso_primary_descriptor*) buf)->effective_date,
491+		    ISODCL (865, 881));
492+
493+		debug_dump_to_xml_padded_hex_output("file_structure_version",
494+		    (u_char *)((struct iso_primary_descriptor*) buf)->file_structure_version,
495+		    ISODCL(882,882));
496+		break;
497+	}
498+	printf("</volumedescriptor>\n");
499+}
+752, -0
  1@@ -0,0 +1,752 @@
  2+/*	$NetBSD: cd9660_eltorito.c,v 1.27 2023/12/28 12:13:56 tsutsui Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
  6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
  7+ *
  8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
  9+ * Alan Perez-Rathke and Ram Vedam.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or
 12+ * without modification, are permitted provided that the following
 13+ * conditions are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above
 17+ *    copyright notice, this list of conditions and the following
 18+ *    disclaimer in the documentation and/or other materials provided
 19+ *    with the distribution.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
 22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
 23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
 26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
 27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 33+ * OF SUCH DAMAGE.
 34+ */
 35+
 36+
 37+#include "cd9660.h"
 38+#include "cd9660_eltorito.h"
 39+#include <sys/bootblock.h>
 40+#include <util.h>
 41+
 42+#include <sys/cdefs.h>
 43+#if defined(__RCSID) && !defined(__lint)
 44+__RCSID("$NetBSD: cd9660_eltorito.c,v 1.27 2023/12/28 12:13:56 tsutsui Exp $");
 45+#endif  /* !__lint */
 46+
 47+#ifdef DEBUG
 48+#define	ELTORITO_DPRINTF(__x)	printf __x
 49+#else
 50+#define	ELTORITO_DPRINTF(__x)
 51+#endif
 52+
 53+#include <util.h>
 54+
 55+static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void);
 56+static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
 57+static struct boot_catalog_entry *cd9660_boot_setup_default_entry(
 58+    struct cd9660_boot_image *);
 59+static struct boot_catalog_entry *cd9660_boot_setup_section_head(char);
 60+#if 0
 61+static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *);
 62+#endif
 63+
 64+static struct cd9660_boot_image *default_boot_image;
 65+
 66+int
 67+cd9660_add_boot_disk(iso9660_disk *diskStructure, const char *boot_info)
 68+{
 69+	struct stat stbuf;
 70+	const char *mode_msg;
 71+	char *temp;
 72+	char *sysname;
 73+	char *filename;
 74+	struct cd9660_boot_image *new_image, *tmp_image;
 75+
 76+	assert(boot_info != NULL);
 77+
 78+	if (*boot_info == '\0') {
 79+		warnx("Error: Boot disk information must be in the "
 80+		      "format 'system;filename'");
 81+		return 0;
 82+	}
 83+
 84+	/* First decode the boot information */
 85+	temp = estrdup(boot_info);
 86+
 87+	sysname = temp;
 88+	filename = strchr(sysname, ';');
 89+	if (filename == NULL) {
 90+		warnx("supply boot disk information in the format "
 91+		    "'system;filename'");
 92+		free(temp);
 93+		return 0;
 94+	}
 95+
 96+	*filename++ = '\0';
 97+
 98+	if (diskStructure->verbose_level > 0) {
 99+		printf("Found bootdisk with system %s, and filename %s\n",
100+		    sysname, filename);
101+	}
102+	new_image = ecalloc(1, sizeof(*new_image));
103+	new_image->loadSegment = 0;	/* default for now */
104+
105+	/* Decode System */
106+	if (strcmp(sysname, "i386") == 0)
107+		new_image->system = ET_SYS_X86;
108+	else if (strcmp(sysname, "powerpc") == 0)
109+		new_image->system = ET_SYS_PPC;
110+	else if (strcmp(sysname, "macppc") == 0 ||
111+	         strcmp(sysname, "mac68k") == 0)
112+		new_image->system = ET_SYS_MAC;
113+	else if (strcmp(sysname, "efi") == 0)
114+		new_image->system = ET_SYS_EFI;
115+	else {
116+		warnx("boot disk system must be "
117+		      "i386, powerpc, macppc, mac68k, or efi");
118+		free(temp);
119+		free(new_image);
120+		return 0;
121+	}
122+
123+
124+	new_image->filename = estrdup(filename);
125+
126+	free(temp);
127+
128+	/* Get information about the file */
129+	if (lstat(new_image->filename, &stbuf) == -1)
130+		err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
131+		    new_image->filename);
132+
133+	switch (stbuf.st_size) {
134+	case 1440 * 1024:
135+		new_image->targetMode = ET_MEDIA_144FDD;
136+		mode_msg = "Assigned boot image to 1.44 emulation mode";
137+		break;
138+	case 1200 * 1024:
139+		new_image->targetMode = ET_MEDIA_12FDD;
140+		mode_msg = "Assigned boot image to 1.2 emulation mode";
141+		break;
142+	case 2880 * 1024:
143+		new_image->targetMode = ET_MEDIA_288FDD;
144+		mode_msg = "Assigned boot image to 2.88 emulation mode";
145+		break;
146+	default:
147+		new_image->targetMode = ET_MEDIA_NOEM;
148+		mode_msg = "Assigned boot image to no emulation mode";
149+		break;
150+	}
151+
152+	if (diskStructure->verbose_level > 0)
153+		printf("%s\n", mode_msg);
154+
155+	new_image->size = stbuf.st_size;
156+	new_image->num_sectors =
157+	    howmany(new_image->size, diskStructure->sectorSize) *
158+	    howmany(diskStructure->sectorSize, 512);
159+	if (diskStructure->verbose_level > 0) {
160+		printf("New image has size %d, uses %d 512-byte sectors\n",
161+		    new_image->size, new_image->num_sectors);
162+	}
163+	new_image->sector = -1;
164+	/* Bootable by default */
165+	new_image->bootable = ET_BOOTABLE;
166+	/* Add boot disk */
167+
168+	/* Group images for the same platform together. */
169+	TAILQ_FOREACH(tmp_image, &diskStructure->boot_images, image_list) {
170+		if (tmp_image->system != new_image->system)
171+			break;
172+	}
173+
174+	if (tmp_image == NULL) {
175+		TAILQ_INSERT_HEAD(&diskStructure->boot_images, new_image,
176+		    image_list);
177+	} else
178+		TAILQ_INSERT_BEFORE(tmp_image, new_image, image_list);
179+
180+	new_image->serialno = diskStructure->image_serialno++;
181+
182+	new_image->platform_id = new_image->system;
183+
184+	/* TODO : Need to do anything about the boot image in the tree? */
185+	diskStructure->is_bootable = 1;
186+
187+	/* First boot image is initial/default entry. */
188+	if (default_boot_image == NULL)
189+		default_boot_image = new_image;
190+
191+	return 1;
192+}
193+
194+int
195+cd9660_eltorito_add_boot_option(iso9660_disk *diskStructure,
196+    const char *option_string, const char *value)
197+{
198+	char *eptr;
199+	struct cd9660_boot_image *image;
200+
201+	assert(option_string != NULL);
202+
203+	/* Find the last image added */
204+	TAILQ_FOREACH(image, &diskStructure->boot_images, image_list) {
205+		if (image->serialno + 1 == diskStructure->image_serialno)
206+			break;
207+	}
208+	if (image == NULL)
209+		errx(EXIT_FAILURE, "Attempted to add boot option, "
210+		    "but no boot images have been specified");
211+
212+	if (strcmp(option_string, "no-emul-boot") == 0) {
213+		image->targetMode = ET_MEDIA_NOEM;
214+	} else if (strcmp(option_string, "no-boot") == 0) {
215+		image->bootable = ET_NOT_BOOTABLE;
216+	} else if (strcmp(option_string, "hard-disk-boot") == 0) {
217+		image->targetMode = ET_MEDIA_HDD;
218+	} else if (strcmp(option_string, "boot-load-segment") == 0) {
219+		image->loadSegment = strtoul(value, &eptr, 16);
220+		if (eptr == value || *eptr != '\0' || errno != ERANGE) {
221+			warn("%s: strtoul", __func__);
222+			return 0;
223+		}
224+	} else if (strcmp(option_string, "platformid") == 0) {
225+		if (strcmp(value, "efi") == 0)
226+			image->platform_id = ET_SYS_EFI;
227+		else {
228+			warn("%s: unknown platform: %s", __func__, value);
229+			return 0;
230+		}
231+	} else {
232+		return 0;
233+	}
234+	return 1;
235+}
236+
237+static struct boot_catalog_entry *
238+cd9660_init_boot_catalog_entry(void)
239+{
240+	return ecalloc(1, sizeof(struct boot_catalog_entry));
241+}
242+
243+static struct boot_catalog_entry *
244+cd9660_boot_setup_validation_entry(char sys)
245+{
246+	struct boot_catalog_entry *entry;
247+	boot_catalog_validation_entry *ve;
248+	int16_t checksum;
249+	unsigned char *csptr;
250+	size_t i;
251+	entry = cd9660_init_boot_catalog_entry();
252+
253+	entry->entry_type = ET_ENTRY_VE;
254+	ve = &entry->entry_data.VE;
255+
256+	ve->header_id[0] = 1;
257+	ve->platform_id[0] = sys;
258+	ve->key[0] = 0x55;
259+	ve->key[1] = 0xAA;
260+
261+	/* Calculate checksum */
262+	checksum = 0;
263+	cd9660_721(0, ve->checksum);
264+	csptr = (unsigned char*)ve;
265+	for (i = 0; i < sizeof(*ve); i += 2) {
266+		checksum += (int16_t)csptr[i];
267+		checksum += 256 * (int16_t)csptr[i + 1];
268+	}
269+	checksum = -checksum;
270+	cd9660_721(checksum, ve->checksum);
271+
272+        ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, "
273+	    "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0],
274+	    ve->key[0], ve->key[1], checksum));
275+	return entry;
276+}
277+
278+static struct boot_catalog_entry *
279+cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk)
280+{
281+	struct boot_catalog_entry *default_entry;
282+	boot_catalog_initial_entry *ie;
283+
284+	default_entry = cd9660_init_boot_catalog_entry();
285+	if (default_entry == NULL)
286+		return NULL;
287+
288+	default_entry->entry_type = ET_ENTRY_IE;
289+	ie = &default_entry->entry_data.IE;
290+
291+	ie->boot_indicator[0] = disk->bootable;
292+	ie->media_type[0] = disk->targetMode;
293+	cd9660_721(disk->loadSegment, ie->load_segment);
294+	ie->system_type[0] = disk->system;
295+	cd9660_721(disk->num_sectors, ie->sector_count);
296+	cd9660_731(disk->sector, ie->load_rba);
297+
298+	ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, "
299+	    "load segment %04x, system type %d, sector count %d, "
300+	    "load rba %d\n", __func__, ie->boot_indicator[0],
301+	    ie->media_type[0], disk->loadSegment, ie->system_type[0],
302+	    disk->num_sectors, disk->sector));
303+	return default_entry;
304+}
305+
306+static struct boot_catalog_entry *
307+cd9660_boot_setup_section_head(char platform)
308+{
309+	struct boot_catalog_entry *entry;
310+	boot_catalog_section_header *sh;
311+
312+	entry = cd9660_init_boot_catalog_entry();
313+	if (entry == NULL)
314+		return NULL;
315+
316+	entry->entry_type = ET_ENTRY_SH;
317+	sh = &entry->entry_data.SH;
318+	/* More by default. The last one will manually be set to 0x91 */
319+	sh->header_indicator[0] = ET_SECTION_HEADER_MORE;
320+	sh->platform_id[0] = platform;
321+	sh->num_section_entries[0] = 0;
322+	return entry;
323+}
324+
325+static struct boot_catalog_entry *
326+cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk)
327+{
328+	struct boot_catalog_entry *entry;
329+	boot_catalog_section_entry *se;
330+	if ((entry = cd9660_init_boot_catalog_entry()) == NULL)
331+		return NULL;
332+
333+	entry->entry_type = ET_ENTRY_SE;
334+	se = &entry->entry_data.SE;
335+
336+	se->boot_indicator[0] = ET_BOOTABLE;
337+	se->media_type[0] = disk->targetMode;
338+	cd9660_721(disk->loadSegment, se->load_segment);
339+	cd9660_721(disk->num_sectors, se->sector_count);
340+	cd9660_731(disk->sector, se->load_rba);
341+	return entry;
342+}
343+
344+#if 0
345+static u_char
346+cd9660_boot_get_system_type(struct cd9660_boot_image *disk)
347+{
348+	/*
349+		For hard drive booting, we need to examine the MBR to figure
350+		out what the partition type is
351+	*/
352+	return 0;
353+}
354+#endif
355+
356+/*
357+ * Set up the BVD, Boot catalog, and the boot entries, but do no writing
358+ */
359+int
360+cd9660_setup_boot(iso9660_disk *diskStructure, int first_sector)
361+{
362+	int sector;
363+	int used_sectors;
364+	int num_entries = 0;
365+	int catalog_sectors;
366+	struct boot_catalog_entry *x86_head, *mac_head, *ppc_head, *efi_head,
367+		*valid_entry, *default_entry, *temp, *head, **headp, *next;
368+	struct cd9660_boot_image *tmp_disk;
369+	u_char system;
370+
371+	headp = NULL;
372+	x86_head = mac_head = ppc_head = efi_head = NULL;
373+
374+	/* If there are no boot disks, don't bother building boot information */
375+	if (TAILQ_EMPTY(&diskStructure->boot_images))
376+		return 0;
377+
378+	/* Point to catalog: For now assume it consumes one sector */
379+	ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector));
380+	diskStructure->boot_catalog_sector = first_sector;
381+	cd9660_731(first_sector,
382+	    diskStructure->boot_descriptor->boot_catalog_pointer);
383+
384+	/*
385+	 * Use system type of default image for validation entry. Fallback to
386+	 * X86 system type if not found.
387+	 */
388+	system = default_boot_image != NULL ? default_boot_image->system :
389+					      ET_SYS_X86;
390+
391+	/* Step 1: Generate boot catalog */
392+	/* Step 1a: Validation entry */
393+	valid_entry = cd9660_boot_setup_validation_entry(system);
394+	if (valid_entry == NULL)
395+		return -1;
396+
397+	/*
398+	 * Count how many boot images there are,
399+	 * and how many sectors they consume.
400+	 */
401+	num_entries = 1;
402+	used_sectors = 0;
403+
404+	TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
405+		used_sectors += tmp_disk->num_sectors;
406+
407+		/* One default entry per image */
408+		num_entries++;
409+	}
410+	catalog_sectors = howmany(num_entries * 0x20, diskStructure->sectorSize);
411+	used_sectors += catalog_sectors;
412+
413+	if (diskStructure->verbose_level > 0) {
414+		printf("%s: there will be %i entries consuming %i sectors. "
415+		       "Catalog is %i sectors\n", __func__, num_entries,
416+		       used_sectors, catalog_sectors);
417+	}
418+
419+	/* Populate sector numbers */
420+	sector = first_sector + catalog_sectors;
421+	TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) {
422+		tmp_disk->sector = sector;
423+		sector += tmp_disk->num_sectors /
424+		    (diskStructure->sectorSize / 512);
425+	}
426+
427+	LIST_INSERT_HEAD(&diskStructure->boot_entries, valid_entry, ll_struct);
428+
429+	/* Step 1b: Initial/default entry */
430+	/* TODO : PARAM */
431+	if (default_boot_image != NULL) {
432+		struct cd9660_boot_image *tcbi;
433+		TAILQ_FOREACH(tcbi, &diskStructure->boot_images, image_list) {
434+			if (tcbi == default_boot_image) {
435+				tmp_disk = tcbi;
436+				break;
437+			}
438+		}
439+	}
440+	if (tmp_disk == NULL)
441+		tmp_disk = TAILQ_FIRST(&diskStructure->boot_images);
442+	default_entry = cd9660_boot_setup_default_entry(tmp_disk);
443+	if (default_entry == NULL) {
444+		warnx("Error: memory allocation failed in cd9660_setup_boot");
445+		return -1;
446+	}
447+
448+	LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct);
449+
450+	/* Todo: multiple default entries? */
451+
452+	tmp_disk = TAILQ_FIRST(&diskStructure->boot_images);
453+
454+	head = NULL;
455+	temp = default_entry;
456+
457+	/* If multiple boot images are given : */
458+	for (; tmp_disk != NULL; tmp_disk = TAILQ_NEXT(tmp_disk, image_list)) {
459+		if (tmp_disk == default_boot_image)
460+			continue;
461+
462+		/* Step 2: Section header */
463+		switch (tmp_disk->platform_id) {
464+		case ET_SYS_X86:
465+			headp = &x86_head;
466+			break;
467+		case ET_SYS_PPC:
468+			headp = &ppc_head;
469+			break;
470+		case ET_SYS_MAC:
471+			headp = &mac_head;
472+			break;
473+		case ET_SYS_EFI:
474+			headp = &efi_head;
475+			break;
476+		default:
477+			warnx("%s: internal error: unknown system type",
478+			    __func__);
479+			return -1;
480+		}
481+
482+		if (*headp == NULL) {
483+			head =
484+			  cd9660_boot_setup_section_head(tmp_disk->platform_id);
485+			if (head == NULL) {
486+				warnx("Error: memory allocation failed in "
487+				      "cd9660_setup_boot");
488+				return -1;
489+			}
490+			LIST_INSERT_AFTER(default_entry, head, ll_struct);
491+			*headp = head;
492+		} else
493+			head = *headp;
494+
495+		head->entry_data.SH.num_section_entries[0]++;
496+
497+		/* Step 2a: Section entry and extensions */
498+		temp = cd9660_boot_setup_section_entry(tmp_disk);
499+		if (temp == NULL) {
500+			warn("%s: cd9660_boot_setup_section_entry", __func__);
501+			return -1;
502+		}
503+
504+		while ((next = LIST_NEXT(head, ll_struct)) != NULL &&
505+		       next->entry_type == ET_ENTRY_SE)
506+			head = next;
507+
508+		LIST_INSERT_AFTER(head, temp, ll_struct);
509+	}
510+
511+	/* Find the last Section Header entry and mark it as the last. */
512+	head = NULL;
513+	LIST_FOREACH(next, &diskStructure->boot_entries, ll_struct) {
514+		if (next->entry_type == ET_ENTRY_SH)
515+			head = next;
516+	}
517+	if (head != NULL)
518+		head->entry_data.SH.header_indicator[0] = ET_SECTION_HEADER_LAST;
519+
520+	/* TODO: Remaining boot disks when implemented */
521+
522+	return first_sector + used_sectors;
523+}
524+
525+int
526+cd9660_setup_boot_volume_descriptor(iso9660_disk *diskStructure,
527+    volume_descriptor *bvd)
528+{
529+	boot_volume_descriptor *bvdData =
530+	    (boot_volume_descriptor*)bvd->volumeDescriptorData;
531+
532+	bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT;
533+	memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
534+	bvdData->version[0] = 1;
535+	memcpy(bvdData->boot_system_identifier, ET_ID, 23);
536+	memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
537+	diskStructure->boot_descriptor =
538+	    (boot_volume_descriptor*) bvd->volumeDescriptorData;
539+	return 1;
540+}
541+
542+static int
543+cd9660_write_mbr_partition_entry(FILE *fd, int idx, off_t sector_start,
544+    off_t nsectors, int type)
545+{
546+	uint8_t val;
547+	uint32_t lba;
548+
549+	if (fseeko(fd, (off_t)(idx) * 16 + 0x1be, SEEK_SET) == -1)
550+		err(EXIT_FAILURE, "fseeko");
551+
552+	val = 0x80; /* Bootable */
553+	fwrite(&val, sizeof(val), 1, fd);
554+
555+	val = 0xff; /* CHS begin */
556+	fwrite(&val, sizeof(val), 1, fd);
557+	fwrite(&val, sizeof(val), 1, fd);
558+	fwrite(&val, sizeof(val), 1, fd);
559+
560+	val = type; /* Part type */
561+	fwrite(&val, sizeof(val), 1, fd);
562+
563+	val = 0xff; /* CHS end */
564+	fwrite(&val, sizeof(val), 1, fd);
565+	fwrite(&val, sizeof(val), 1, fd);
566+	fwrite(&val, sizeof(val), 1, fd);
567+
568+	/* LBA extent */
569+	lba = htole32(sector_start);
570+	fwrite(&lba, sizeof(lba), 1, fd);
571+	lba = htole32(nsectors);
572+	fwrite(&lba, sizeof(lba), 1, fd);
573+
574+	return 0;
575+}
576+
577+static int
578+cd9660_write_apm_partition_entry(FILE *fd, int idx, int total_partitions,
579+    off_t sector_start, off_t nsectors, off_t sector_size,
580+    const char *part_name, const char *part_type)
581+{
582+	uint32_t apm32, part_status;
583+	uint16_t apm16;
584+
585+	/* See Apple Tech Note 1189 for the details about the pmPartStatus
586+	 * flags.
587+	 * Below the flags which are default:
588+	 * - IsValid     0x01
589+	 * - IsAllocated 0x02
590+	 * - IsReadable  0x10
591+	 * - IsWritable  0x20
592+	 */
593+	part_status = APPLE_PS_VALID | APPLE_PS_ALLOCATED | APPLE_PS_READABLE |
594+	    APPLE_PS_WRITABLE;
595+
596+	if (fseeko(fd, (off_t)(idx + 1) * sector_size, SEEK_SET) == -1)
597+		err(EXIT_FAILURE, "fseeko");
598+
599+	/* Signature */
600+	apm16 = htobe16(0x504d);
601+	fwrite(&apm16, sizeof(apm16), 1, fd);
602+	apm16 = 0;
603+	fwrite(&apm16, sizeof(apm16), 1, fd);
604+
605+	/* Total number of partitions */
606+	apm32 = htobe32(total_partitions);
607+	fwrite(&apm32, sizeof(apm32), 1, fd);
608+	/* Bounds */
609+	apm32 = htobe32(sector_start);
610+	fwrite(&apm32, sizeof(apm32), 1, fd);
611+	apm32 = htobe32(nsectors);
612+	fwrite(&apm32, sizeof(apm32), 1, fd);
613+
614+	fwrite(part_name, strlen(part_name) + 1, 1, fd);
615+	fseek(fd, 32 - strlen(part_name) - 1, SEEK_CUR);
616+	fwrite(part_type, strlen(part_type) + 1, 1, fd);
617+	fseek(fd, 32 - strlen(part_type) - 1, SEEK_CUR);
618+
619+	apm32 = 0;
620+	/* pmLgDataStart */
621+	fwrite(&apm32, sizeof(apm32), 1, fd);
622+	/* pmDataCnt */
623+	apm32 = htobe32(nsectors);
624+	fwrite(&apm32, sizeof(apm32), 1, fd);
625+	/* pmPartStatus */
626+	apm32 = htobe32(part_status);
627+	fwrite(&apm32, sizeof(apm32), 1, fd);
628+
629+	return 0;
630+}
631+
632+int
633+cd9660_write_boot(iso9660_disk *diskStructure, FILE *fd)
634+{
635+	struct boot_catalog_entry *e;
636+	struct cd9660_boot_image *t;
637+	int apm_partitions = 0;
638+	int mbr_partitions = 0;
639+
640+	/* write boot catalog */
641+	if (fseeko(fd, (off_t)diskStructure->boot_catalog_sector *
642+	    diskStructure->sectorSize, SEEK_SET) == -1)
643+		err(EXIT_FAILURE, "fseeko");
644+
645+	if (diskStructure->verbose_level > 0) {
646+		printf("Writing boot catalog to sector %" PRId64 "\n",
647+		    diskStructure->boot_catalog_sector);
648+	}
649+	LIST_FOREACH(e, &diskStructure->boot_entries, ll_struct) {
650+		if (diskStructure->verbose_level > 0) {
651+			printf("Writing catalog entry of type %d\n",
652+			    e->entry_type);
653+		}
654+		/*
655+		 * It doesnt matter which one gets written
656+		 * since they are the same size
657+		 */
658+		fwrite(&(e->entry_data.VE), 1, 32, fd);
659+	}
660+	if (diskStructure->verbose_level > 0)
661+		printf("Finished writing boot catalog\n");
662+
663+	/* copy boot images */
664+	TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
665+		if (diskStructure->verbose_level > 0) {
666+			printf("Writing boot image from %s to sectors %d\n",
667+			    t->filename, t->sector);
668+		}
669+		cd9660_copy_file(diskStructure, fd, t->sector, t->filename);
670+
671+		if (t->system == ET_SYS_MAC)
672+			apm_partitions++;
673+		if (t->system == ET_SYS_PPC)
674+			mbr_partitions++;
675+	}
676+
677+	/* some systems need partition tables as well */
678+	if (mbr_partitions > 0 || diskStructure->chrp_boot) {
679+		uint16_t sig;
680+
681+		fseek(fd, 0x1fe, SEEK_SET);
682+		sig = htole16(0xaa55);
683+		fwrite(&sig, sizeof(sig), 1, fd);
684+
685+		mbr_partitions = 0;
686+
687+		/* Write ISO9660 descriptor, enclosing the whole disk */
688+		if (diskStructure->chrp_boot)
689+			cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
690+			    0, diskStructure->totalSectors *
691+			    (diskStructure->sectorSize / 512), 0x96);
692+
693+		/* Write all partition entries */
694+		TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
695+			if (t->system != ET_SYS_PPC)
696+				continue;
697+			cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
698+			    t->sector * (diskStructure->sectorSize / 512),
699+			    t->num_sectors * (diskStructure->sectorSize / 512),
700+			    0x41 /* PReP Boot */);
701+		}
702+	}
703+
704+	if (apm_partitions > 0) {
705+		/* Write DDR and global APM info */
706+		uint32_t apm32;
707+		uint16_t apm16;
708+		int total_parts;
709+
710+		fseek(fd, 0, SEEK_SET);
711+		apm16 = htobe16(0x4552);
712+		fwrite(&apm16, sizeof(apm16), 1, fd);
713+		/* Device block size */
714+		apm16 = htobe16(512);
715+		fwrite(&apm16, sizeof(apm16), 1, fd);
716+		/* Device block count */
717+		apm32 = htobe32(diskStructure->totalSectors *
718+		    (diskStructure->sectorSize / 512));
719+		fwrite(&apm32, sizeof(apm32), 1, fd);
720+		/* Device type/id */
721+		apm16 = htobe16(1);
722+		fwrite(&apm16, sizeof(apm16), 1, fd);
723+		fwrite(&apm16, sizeof(apm16), 1, fd);
724+
725+		/* Count total needed entries */
726+		total_parts = 2 + apm_partitions; /* Self + ISO9660 */
727+
728+		/* Write self-descriptor */
729+		cd9660_write_apm_partition_entry(fd, 0, total_parts, 1,
730+		    total_parts, 512, "Apple", "Apple_partition_map");
731+
732+		/* Write all partition entries */
733+		apm_partitions = 0;
734+		TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) {
735+			if (t->system != ET_SYS_MAC)
736+				continue;
737+
738+			cd9660_write_apm_partition_entry(fd,
739+			    1 + apm_partitions++, total_parts,
740+			    t->sector * (diskStructure->sectorSize / 512),
741+			    t->num_sectors * (diskStructure->sectorSize / 512),
742+			    512, "CD Boot", "Apple_Bootstrap");
743+		}
744+
745+		/* Write ISO9660 descriptor, enclosing the whole disk */
746+		cd9660_write_apm_partition_entry(fd, 2 + apm_partitions,
747+		    total_parts, 0, diskStructure->totalSectors *
748+		    (diskStructure->sectorSize / 512), 512, "ISO9660",
749+		    "CD_ROM_Mode_1");
750+	}
751+
752+	return 0;
753+}
+164, -0
  1@@ -0,0 +1,164 @@
  2+/*	$NetBSD: cd9660_eltorito.h,v 1.6 2017/01/24 11:22:43 nonaka Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
  6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
  7+ *
  8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
  9+ * Alan Perez-Rathke and Ram Vedam.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or
 12+ * without modification, are permitted provided that the following
 13+ * conditions are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above
 17+ *    copyright notice, this list of conditions and the following
 18+ *    disclaimer in the documentation and/or other materials provided
 19+ *    with the distribution.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
 22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
 23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
 26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
 27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 33+ * OF SUCH DAMAGE.
 34+ */
 35+
 36+#ifndef _CD9660_ELTORITO_H_
 37+#define _CD9660_ELTORITO_H_
 38+
 39+/* Boot defines */
 40+#define	ET_ID		"EL TORITO SPECIFICATION"
 41+#define	ET_SYS_X86	0
 42+#define	ET_SYS_PPC	1
 43+#define	ET_SYS_MAC	2
 44+#define	ET_SYS_EFI	0xef	/* Platform ID at section header entry */
 45+
 46+#define ET_BOOT_ENTRY_SIZE 0x20
 47+
 48+#define	ET_BOOTABLE		0x88
 49+#define	ET_NOT_BOOTABLE	0
 50+
 51+#define	ET_MEDIA_NOEM	0
 52+#define	ET_MEDIA_12FDD			1
 53+#define	ET_MEDIA_144FDD			2
 54+#define	ET_MEDIA_288FDD			3
 55+#define	ET_MEDIA_HDD			4
 56+
 57+#define ET_INDICATOR_HEADERMORE	0x90
 58+#define ET_INDICATOR_HEADERLAST	0x91
 59+#define ET_INDICATOR_EXTENSION	0x44
 60+
 61+/*** Boot Structures ***/
 62+
 63+typedef struct _boot_volume_descriptor {
 64+	u_char boot_record_indicator	[ISODCL(0x00,0x00)];
 65+	u_char identifier		[ISODCL(0x01,0x05)];
 66+	u_char version			[ISODCL(0x06,0x06)];
 67+	u_char boot_system_identifier	[ISODCL(0x07,0x26)];
 68+	u_char unused1			[ISODCL(0x27,0x46)];
 69+	u_char boot_catalog_pointer	[ISODCL(0x47,0x4A)];
 70+	u_char unused2			[ISODCL(0x4B,0x7FF)];
 71+} boot_volume_descriptor;
 72+
 73+typedef struct _boot_catalog_validation_entry {
 74+	u_char header_id		[ISODCL(0x00,0x00)];
 75+	u_char platform_id		[ISODCL(0x01,0x01)];
 76+	u_char reserved1		[ISODCL(0x02,0x03)];
 77+	u_char manufacturer		[ISODCL(0x04,0x1B)];
 78+	u_char checksum			[ISODCL(0x1C,0x1D)];
 79+	u_char key			[ISODCL(0x1E,0x1F)];
 80+} boot_catalog_validation_entry;
 81+
 82+typedef struct _boot_catalog_initial_entry {
 83+	u_char boot_indicator		[ISODCL(0x00,0x00)];
 84+	u_char media_type		[ISODCL(0x01,0x01)];
 85+	u_char load_segment		[ISODCL(0x02,0x03)];
 86+	u_char system_type		[ISODCL(0x04,0x04)];
 87+	u_char unused_1			[ISODCL(0x05,0x05)];
 88+	u_char sector_count		[ISODCL(0x06,0x07)];
 89+	u_char load_rba			[ISODCL(0x08,0x0B)];
 90+	u_char unused_2			[ISODCL(0x0C,0x1F)];
 91+} boot_catalog_initial_entry;
 92+
 93+#define ET_SECTION_HEADER_MORE		0x90
 94+#define ET_SECTION_HEADER_LAST		0x91
 95+
 96+typedef struct _boot_catalog_section_header {
 97+	u_char header_indicator		[ISODCL(0x00,0x00)];
 98+	u_char platform_id		[ISODCL(0x01,0x01)];
 99+	u_char num_section_entries	[ISODCL(0x02,0x03)];
100+	u_char id_string		[ISODCL(0x04,0x1F)];
101+} boot_catalog_section_header;
102+
103+typedef struct _boot_catalog_section_entry {
104+	u_char boot_indicator		[ISODCL(0x00,0x00)];
105+	u_char media_type		[ISODCL(0x01,0x01)];
106+	u_char load_segment		[ISODCL(0x02,0x03)];
107+	u_char system_type		[ISODCL(0x04,0x04)];
108+	u_char unused_1			[ISODCL(0x05,0x05)];
109+	u_char sector_count		[ISODCL(0x06,0x07)];
110+	u_char load_rba			[ISODCL(0x08,0x0B)];
111+	u_char selection_criteria	[ISODCL(0x0C,0x0C)];
112+	u_char vendor_criteria		[ISODCL(0x0D,0x1F)];
113+} boot_catalog_section_entry;
114+
115+typedef struct _boot_catalog_section_entry_extension {
116+	u_char extension_indicator	[ISODCL(0x00,0x00)];
117+	u_char flags			[ISODCL(0x01,0x01)];
118+	u_char vendor_criteria		[ISODCL(0x02,0x1F)];
119+} boot_catalog_section_entry_extension;
120+
121+#define ET_ENTRY_VE 1
122+#define ET_ENTRY_IE 2
123+#define ET_ENTRY_SH 3
124+#define ET_ENTRY_SE 4
125+#define ET_ENTRY_EX 5
126+
127+struct boot_catalog_entry {
128+	char entry_type;
129+	union {
130+		boot_catalog_validation_entry		VE;
131+		boot_catalog_initial_entry 		IE;
132+		boot_catalog_section_header		SH;
133+		boot_catalog_section_entry		SE;
134+		boot_catalog_section_entry_extension	EX;
135+	} entry_data;
136+
137+	LIST_ENTRY(boot_catalog_entry) ll_struct;
138+};
139+
140+/* Temporary structure */
141+struct cd9660_boot_image {
142+	char *filename;
143+	int size;
144+	int sector; 			/* copied to LoadRBA */
145+	int num_sectors;
146+	unsigned int loadSegment;
147+	u_char targetMode;
148+	u_char system;
149+	u_char bootable;
150+	u_char platform_id;		/* for section header entry */
151+	/*
152+	 * If the boot image exists in the filesystem
153+	 * already, this is a pointer to that node. For the sake
154+	 * of simplicity in future versions, this pointer is only
155+	 * to the node in the primary volume. This SHOULD be done
156+	 * via a hashtable lookup.
157+	 */
158+	struct _cd9660node *boot_image_node;
159+	TAILQ_ENTRY(cd9660_boot_image) image_list;
160+	int serialno;
161+};
162+
163+
164+#endif /* _CD9660_ELTORITO_H_ */
165+
+127, -0
  1@@ -0,0 +1,127 @@
  2+/*	$NetBSD: cd9660_strings.c,v 1.6 2015/12/24 15:52:37 christos Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
  6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
  7+ *
  8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
  9+ * Alan Perez-Rathke and Ram Vedam.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or
 12+ * without modification, are permitted provided that the following
 13+ * conditions are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above
 17+ *    copyright notice, this list of conditions and the following
 18+ *    disclaimer in the documentation and/or other materials provided
 19+ *    with the distribution.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
 22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
 23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
 26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
 27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 33+ * OF SUCH DAMAGE.
 34+ */
 35+
 36+#if HAVE_NBTOOL_CONFIG_H
 37+#include "nbtool_config.h"
 38+#else
 39+#include <sys/mount.h>
 40+#endif
 41+
 42+#include <sys/cdefs.h>
 43+#include <sys/param.h>
 44+#include <ctype.h>
 45+
 46+#include "makefs.h"
 47+#include "cd9660.h"
 48+
 49+#if defined(__RCSID) && !defined(__lint)
 50+__RCSID("$NetBSD: cd9660_strings.c,v 1.6 2015/12/24 15:52:37 christos Exp $");
 51+#endif  /* !__lint */
 52+
 53+
 54+void
 55+cd9660_uppercase_characters(char *str, size_t len)
 56+{
 57+	size_t p;
 58+
 59+	for (p = 0; p < len; p++) {
 60+		if (islower((unsigned char)str[p]) )
 61+			str[p] -= 32;
 62+	}
 63+}
 64+
 65+static inline int
 66+cd9660_is_d_char(char c)
 67+{
 68+	return (isupper((unsigned char)c)
 69+		|| c == '_'
 70+		|| (c >= '0' && c <= '9'));
 71+}
 72+
 73+static inline int
 74+cd9660_is_a_char(char c)
 75+{
 76+	return (isupper((unsigned char)c)
 77+			|| c == '_'
 78+			|| (c >= '%' && c <= '?')
 79+			|| (c >= ' ' && c <= '\"'));
 80+}
 81+
 82+/*
 83+ * Test a string to see if it is composed of valid a characters
 84+ * @param const char* The string to test
 85+ * @returns int 1 if valid, 2 if valid if characters are converted to
 86+ *              upper case, 0 otherwise
 87+ */
 88+int
 89+cd9660_valid_a_chars(const char *str)
 90+{
 91+	const char *c = str;
 92+	int upperFound = 0;
 93+
 94+	while ((*c) != '\0') {
 95+		if (!(cd9660_is_a_char(*c))) {
 96+			if (islower((unsigned char)*c) )
 97+				upperFound = 1;
 98+			else
 99+				return 0;
100+		}
101+		c++;
102+	}
103+	return upperFound + 1;
104+}
105+
106+/*
107+ * Test a string to see if it is composed of valid d characters
108+ * @param const char* The string to test
109+ * @returns int 1 if valid, 2 if valid if characters are converted to
110+ *                upper case, 0 otherwise
111+ */
112+int
113+cd9660_valid_d_chars(const char *str)
114+{
115+	const char *c=str;
116+	int upperFound = 0;
117+
118+	while ((*c) != '\0') {
119+		if (!(cd9660_is_d_char(*c))) {
120+			if (islower((unsigned char)*c) )
121+				upperFound = 1;
122+			else
123+				return 0;
124+		}
125+		c++;
126+	}
127+	return upperFound + 1;
128+}
+509, -0
  1@@ -0,0 +1,509 @@
  2+/*	$NetBSD: cd9660_write.c,v 1.18 2023/12/28 12:13:56 tsutsui Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
  6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
  7+ *
  8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
  9+ * Alan Perez-Rathke and Ram Vedam.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or
 12+ * without modification, are permitted provided that the following
 13+ * conditions are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above
 17+ *    copyright notice, this list of conditions and the following
 18+ *    disclaimer in the documentation and/or other materials provided
 19+ *    with the distribution.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
 22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
 23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
 26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
 27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 33+ * OF SUCH DAMAGE.
 34+ */
 35+
 36+#include "cd9660.h"
 37+#include "iso9660_rrip.h"
 38+
 39+#include <sys/cdefs.h>
 40+#if defined(__RCSID) && !defined(__lint)
 41+__RCSID("$NetBSD: cd9660_write.c,v 1.18 2023/12/28 12:13:56 tsutsui Exp $");
 42+#endif  /* !__lint */
 43+
 44+#include <util.h>
 45+
 46+static int cd9660_write_volume_descriptors(iso9660_disk *, FILE *);
 47+static int cd9660_write_path_table(iso9660_disk *, FILE *, off_t, int);
 48+static int cd9660_write_path_tables(iso9660_disk *, FILE *);
 49+static int cd9660_write_file(iso9660_disk *, FILE *, cd9660node *);
 50+static int cd9660_write_filedata(iso9660_disk *, FILE *, off_t,
 51+    const unsigned char *, int);
 52+#if 0
 53+static int cd9660_write_buffered(FILE *, off_t, int, const unsigned char *);
 54+#endif
 55+static void cd9660_write_rr(iso9660_disk *, FILE *, cd9660node *, off_t, off_t);
 56+
 57+/*
 58+ * Write the image
 59+ * Writes the entire image
 60+ * @param const char* The filename for the image
 61+ * @returns int 1 on success, 0 on failure
 62+ */
 63+int
 64+cd9660_write_image(iso9660_disk *diskStructure, const char* image)
 65+{
 66+	FILE *fd;
 67+	int status;
 68+	char buf[CD9660_SECTOR_SIZE];
 69+
 70+	if ((fd = fopen(image, "w+")) == NULL) {
 71+		err(EXIT_FAILURE, "%s: Can't open `%s' for writing", __func__,
 72+		    image);
 73+	}
 74+
 75+	if (diskStructure->verbose_level > 0)
 76+		printf("Writing image\n");
 77+
 78+	if (diskStructure->has_generic_bootimage) {
 79+		status = cd9660_copy_file(diskStructure, fd, 0,
 80+		    diskStructure->generic_bootimage);
 81+		if (status == 0) {
 82+			warnx("%s: Error writing generic boot image",
 83+			    __func__);
 84+			goto cleanup_bad_image;
 85+		}
 86+	}
 87+
 88+	/* Write the volume descriptors */
 89+	status = cd9660_write_volume_descriptors(diskStructure, fd);
 90+	if (status == 0) {
 91+		warnx("%s: Error writing volume descriptors to image",
 92+		    __func__);
 93+		goto cleanup_bad_image;
 94+	}
 95+
 96+	if (diskStructure->verbose_level > 0)
 97+		printf("Volume descriptors written\n");
 98+
 99+	/*
100+	 * Write the path tables: there are actually four, but right
101+	 * now we are only concearned with two.
102+	 */
103+	status = cd9660_write_path_tables(diskStructure, fd);
104+	if (status == 0) {
105+		warnx("%s: Error writing path tables to image", __func__);
106+		goto cleanup_bad_image;
107+	}
108+
109+	if (diskStructure->verbose_level > 0)
110+		printf("Path tables written\n");
111+
112+	/* Write the directories and files */
113+	status = cd9660_write_file(diskStructure, fd, diskStructure->rootNode);
114+	if (status == 0) {
115+		warnx("%s: Error writing files to image", __func__);
116+		goto cleanup_bad_image;
117+	}
118+
119+	if (diskStructure->is_bootable) {
120+		cd9660_write_boot(diskStructure, fd);
121+	}
122+
123+	/* Write padding bits. This is temporary */
124+	memset(buf, 0, CD9660_SECTOR_SIZE);
125+	cd9660_write_filedata(diskStructure, fd,
126+	    diskStructure->totalSectors - 1, buf, 1);
127+
128+	if (diskStructure->verbose_level > 0)
129+		printf("Files written\n");
130+	fclose(fd);
131+
132+	if (diskStructure->verbose_level > 0)
133+		printf("Image closed\n");
134+	return 1;
135+
136+cleanup_bad_image:
137+	fclose(fd);
138+	if (!diskStructure->keep_bad_images)
139+		unlink(image);
140+	if (diskStructure->verbose_level > 0)
141+		printf("Bad image cleaned up\n");
142+	return 0;
143+}
144+
145+static int
146+cd9660_write_volume_descriptors(iso9660_disk *diskStructure, FILE *fd)
147+{
148+	volume_descriptor *vd_temp = diskStructure->firstVolumeDescriptor;
149+	while (vd_temp != NULL) {
150+		cd9660_write_filedata(diskStructure, fd, vd_temp->sector,
151+		    vd_temp->volumeDescriptorData, 1);
152+		vd_temp = vd_temp->next;
153+	}
154+	return 1;
155+}
156+
157+/*
158+ * Write out an individual path table
159+ * Used just to keep redundant code to a minimum
160+ * @param FILE *fd Valid file pointer
161+ * @param int Sector to start writing path table to
162+ * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN
163+ * @returns int 1 on success, 0 on failure
164+ */
165+static int
166+cd9660_write_path_table(iso9660_disk *diskStructure, FILE *fd, off_t sector,
167+    int mode)
168+{
169+	int path_table_sectors = CD9660_BLOCKS(diskStructure->sectorSize,
170+	    diskStructure->pathTableLength);
171+	unsigned char *buffer;
172+	unsigned char *buffer_head;
173+	int len;
174+	path_table_entry temp_entry;
175+	cd9660node *ptcur;
176+
177+	buffer = ecalloc(path_table_sectors, diskStructure->sectorSize);
178+	buffer_head = buffer;
179+
180+	ptcur = diskStructure->rootNode;
181+
182+	while (ptcur != NULL) {
183+		memset(&temp_entry, 0, sizeof(path_table_entry));
184+		temp_entry.length[0] = ptcur->isoDirRecord->name_len[0];
185+		temp_entry.extended_attribute_length[0] =
186+		    ptcur->isoDirRecord->ext_attr_length[0];
187+		memcpy(temp_entry.name, ptcur->isoDirRecord->name,
188+		    temp_entry.length[0] + 1);
189+
190+		/* round up */
191+		len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01);
192+
193+                /* todo: function pointers instead */
194+		if (mode == LITTLE_ENDIAN) {
195+			cd9660_731(ptcur->fileDataSector,
196+			    temp_entry.first_sector);
197+			cd9660_721((ptcur->parent == NULL ?
198+				1 : ptcur->parent->ptnumber),
199+			    temp_entry.parent_number);
200+		} else {
201+			cd9660_732(ptcur->fileDataSector,
202+			    temp_entry.first_sector);
203+			cd9660_722((ptcur->parent == NULL ?
204+				1 : ptcur->parent->ptnumber),
205+			    temp_entry.parent_number);
206+		}
207+
208+
209+		memcpy(buffer, &temp_entry, len);
210+		buffer += len;
211+
212+		ptcur = ptcur->ptnext;
213+	}
214+
215+	return cd9660_write_filedata(diskStructure, fd, sector, buffer_head,
216+	    path_table_sectors);
217+}
218+
219+
220+/*
221+ * Write out the path tables to disk
222+ * Each file descriptor should be pointed to by the PVD, so we know which
223+ * sector to copy them to. One thing to watch out for: the only path tables
224+ * stored are in the endian mode that the application is compiled for. So,
225+ * the first thing to do is write out that path table, then to write the one
226+ * in the other endian mode requires to convert the endianness of each entry
227+ * in the table. The best way to do this would be to create a temporary
228+ * path_table_entry structure, then for each path table entry, copy it to
229+ * the temporary entry, translate, then copy that to disk.
230+ *
231+ * @param FILE* Valid file descriptor
232+ * @returns int 0 on failure, 1 on success
233+ */
234+static int
235+cd9660_write_path_tables(iso9660_disk *diskStructure, FILE *fd)
236+{
237+	if (cd9660_write_path_table(diskStructure, fd,
238+	    diskStructure->primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0)
239+		return 0;
240+
241+	if (cd9660_write_path_table(diskStructure, fd,
242+	    diskStructure->primaryBigEndianTableSector, BIG_ENDIAN) == 0)
243+		return 0;
244+
245+	/* @TODO: handle remaining two path tables */
246+	return 1;
247+}
248+
249+/*
250+ * Write a file to disk
251+ * Writes a file, its directory record, and its data to disk
252+ * This file is designed to be called RECURSIVELY, so initially call it
253+ * with the root node. All of the records should store what sector the
254+ * file goes in, so no computation should be  necessary.
255+ *
256+ * @param int fd Valid file descriptor
257+ * @param struct cd9660node* writenode Pointer to the file to be written
258+ * @returns int 0 on failure, 1 on success
259+ */
260+static int
261+cd9660_write_file(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode)
262+{
263+	char *buf;
264+	char *temp_file_name;
265+	int ret;
266+	off_t working_sector;
267+	int cur_sector_offset;
268+	iso_directory_record_cd9660 temp_record;
269+	cd9660node *temp;
270+	int rv = 0;
271+
272+	/* Todo : clean up variables */
273+
274+	temp_file_name = ecalloc(CD9660MAXPATH + 1, 1);
275+	buf = emalloc(diskStructure->sectorSize);
276+	if ((writenode->level != 0) &&
277+	    !(writenode->node->type & S_IFDIR)) {
278+		fsinode *inode = writenode->node->inode;
279+		/* Only attempt to write unwritten files that have length. */
280+		if ((inode->flags & FI_WRITTEN) != 0) {
281+			INODE_WARNX(("%s: skipping written inode %d", __func__,
282+			    (int)inode->st.st_ino));
283+		} else if (writenode->fileDataLength > 0) {
284+			INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32,
285+			    __func__, (int)inode->st.st_ino, inode->ino));
286+			inode->flags |= FI_WRITTEN;
287+			cd9660_compute_full_filename(writenode,
288+			    temp_file_name);
289+			ret = cd9660_copy_file(diskStructure, fd,
290+			    writenode->fileDataSector, temp_file_name);
291+			if (ret == 0)
292+				goto out;
293+		}
294+	} else {
295+		/*
296+		 * Here is a new revelation that ECMA didnt explain
297+		 * (at least not well).
298+		 * ALL . and .. records store the name "\0" and "\1"
299+		 * resepctively. So, for each directory, we have to
300+		 * make a new node.
301+		 *
302+		 * This is where it gets kinda messy, since we have to
303+		 * be careful of sector boundaries
304+		 */
305+		cur_sector_offset = 0;
306+		working_sector = writenode->fileDataSector;
307+		if (fseeko(fd, working_sector * diskStructure->sectorSize,
308+		    SEEK_SET) == -1)
309+			err(EXIT_FAILURE, "fseeko");
310+
311+		/*
312+		 * Now loop over children, writing out their directory
313+		 * records - beware of sector boundaries
314+	 	 */
315+		TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
316+			/*
317+			 * Copy the temporary record and adjust its size
318+			 * if necessary
319+			 */
320+			memcpy(&temp_record, temp->isoDirRecord,
321+			    sizeof(iso_directory_record_cd9660));
322+
323+			temp_record.length[0] =
324+			    cd9660_compute_record_size(diskStructure, temp);
325+
326+			if (temp_record.length[0] + cur_sector_offset >=
327+			    diskStructure->sectorSize) {
328+				cur_sector_offset = 0;
329+				working_sector++;
330+
331+				/* Seek to the next sector. */
332+				if (fseeko(fd, working_sector *
333+				    diskStructure->sectorSize, SEEK_SET) == -1)
334+					err(EXIT_FAILURE, "fseeko");
335+			}
336+			/* Write out the basic ISO directory record */
337+			(void)fwrite(&temp_record, 1,
338+			    temp->isoDirRecord->length[0], fd);
339+			if (diskStructure->rock_ridge_enabled) {
340+				cd9660_write_rr(diskStructure, fd, temp,
341+				    cur_sector_offset, working_sector);
342+			}
343+			if (fseeko(fd, working_sector *
344+			    diskStructure->sectorSize + cur_sector_offset +
345+			    temp_record.length[0] - temp->su_tail_size,
346+			    SEEK_SET) == -1)
347+				err(EXIT_FAILURE, "fseeko");
348+			if (temp->su_tail_size > 0)
349+				fwrite(temp->su_tail_data, 1,
350+				    temp->su_tail_size, fd);
351+			if (ferror(fd)) {
352+				warnx("%s: write error", __func__);
353+				goto out;
354+			}
355+			cur_sector_offset += temp_record.length[0];
356+
357+		}
358+
359+		/*
360+		 * Recurse on children.
361+		 */
362+		TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
363+			if ((ret = cd9660_write_file(diskStructure, fd, temp))
364+			    == 0)
365+				goto out;
366+		}
367+	}
368+	rv = 1;
369+out:
370+	free(temp_file_name);
371+	free(buf);
372+	return rv;
373+}
374+
375+/*
376+ * Wrapper function to write a buffer (one sector) to disk.
377+ * Seeks and writes the buffer.
378+ * NOTE: You dont NEED to use this function, but it might make your
379+ * life easier if you have to write things that align to a sector
380+ * (such as volume descriptors).
381+ *
382+ * @param int fd Valid file descriptor
383+ * @param int sector Sector number to write to
384+ * @param const unsigned char* Buffer to write. This should be the
385+ *                             size of a sector, and if only a portion
386+ *                             is written, the rest should be set to 0.
387+ */
388+static int
389+cd9660_write_filedata(iso9660_disk *diskStructure, FILE *fd, off_t sector,
390+    const unsigned char *buf, int numsecs)
391+{
392+	off_t curpos;
393+	size_t success;
394+
395+	curpos = ftello(fd);
396+
397+	if (fseeko(fd, sector * diskStructure->sectorSize, SEEK_SET) == -1)
398+		err(EXIT_FAILURE, "fseeko");
399+
400+	success = fwrite(buf, diskStructure->sectorSize * numsecs, 1, fd);
401+
402+	if (fseeko(fd, curpos, SEEK_SET) == -1)
403+		err(EXIT_FAILURE, "fseeko");
404+
405+	if (success == 1)
406+		success = diskStructure->sectorSize * numsecs;
407+	return success;
408+}
409+
410+#if 0
411+static int
412+cd9660_write_buffered(FILE *fd, off_t offset, int buff_len,
413+		      const unsigned char* buffer)
414+{
415+	static int working_sector = -1;
416+	static char buf[CD9660_SECTOR_SIZE];
417+
418+	return 0;
419+}
420+#endif
421+
422+int
423+cd9660_copy_file(iso9660_disk *diskStructure, FILE *fd, off_t start_sector,
424+    const char *filename)
425+{
426+	FILE *rf;
427+	int bytes_read;
428+	off_t sector = start_sector;
429+	int buf_size = diskStructure->sectorSize;
430+	char *buf;
431+
432+	buf = emalloc(buf_size);
433+	if ((rf = fopen(filename, "rb")) == NULL) {
434+		warn("%s: cannot open %s", __func__, filename);
435+		free(buf);
436+		return 0;
437+	}
438+
439+	if (diskStructure->verbose_level > 1)
440+		printf("Writing file: %s\n",filename);
441+
442+	if (fseeko(fd, start_sector * diskStructure->sectorSize, SEEK_SET) == -1)
443+		err(EXIT_FAILURE, "fseeko");
444+
445+	while (!feof(rf)) {
446+		bytes_read = fread(buf,1,buf_size,rf);
447+		if (ferror(rf)) {
448+			warn("%s: fread", __func__);
449+			free(buf);
450+			(void)fclose(rf);
451+			return 0;
452+		}
453+
454+		fwrite(buf,1,bytes_read,fd);
455+		if (ferror(fd)) {
456+			warn("%s: fwrite", __func__);
457+			free(buf);
458+			(void)fclose(rf);
459+			return 0;
460+		}
461+		sector++;
462+	}
463+
464+	fclose(rf);
465+	free(buf);
466+	return 1;
467+}
468+
469+static void
470+cd9660_write_rr(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode,
471+    off_t offset, off_t sector)
472+{
473+	int in_ca = 0;
474+	struct ISO_SUSP_ATTRIBUTES *myattr;
475+
476+	offset += writenode->isoDirRecord->length[0];
477+	if (fseeko(fd, sector * diskStructure->sectorSize + offset, SEEK_SET) ==
478+	    -1)
479+		err(EXIT_FAILURE, "fseeko");
480+	/* Offset now points at the end of the record */
481+	TAILQ_FOREACH(myattr, &writenode->head, rr_ll) {
482+		fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd);
483+
484+		if (!in_ca) {
485+			offset += CD9660_SUSP_ENTRY_SIZE(myattr);
486+			if (myattr->last_in_suf) {
487+				/*
488+				 * Point the offset to the start of this
489+				 * record's CE area
490+				 */
491+				if (fseeko(fd, ((off_t)diskStructure->
492+				    susp_continuation_area_start_sector *
493+				    diskStructure->sectorSize)
494+				    + writenode->susp_entry_ce_start,
495+				    SEEK_SET) == -1)
496+					err(EXIT_FAILURE, "fseeko");
497+				in_ca = 1;
498+			}
499+		}
500+	}
501+
502+	/*
503+	 * If we had to go to the continuation area, head back to
504+	 * where we should be.
505+	 */
506+	if (in_ca)
507+		if (fseeko(fd, sector * diskStructure->sectorSize + offset,
508+		    SEEK_SET) == -1)
509+			err(EXIT_FAILURE, "fseeko");
510+}
+827, -0
  1@@ -0,0 +1,827 @@
  2+/*	$NetBSD: iso9660_rrip.c,v 1.17 2025/02/17 22:56:46 andvar Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
  6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
  7+ *
  8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
  9+ * Alan Perez-Rathke and Ram Vedam.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or
 12+ * without modification, are permitted provided that the following
 13+ * conditions are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above
 17+ *    copyright notice, this list of conditions and the following
 18+ *    disclaimer in the documentation and/or other materials provided
 19+ *    with the distribution.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
 22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
 23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
 26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
 27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 33+ * OF SUCH DAMAGE.
 34+ */
 35+/* This will hold all the function definitions
 36+ * defined in iso9660_rrip.h
 37+ */
 38+
 39+#include "makefs.h"
 40+#include "cd9660.h"
 41+#include "iso9660_rrip.h"
 42+#include <sys/queue.h>
 43+#include <stdio.h>
 44+#include <util.h>
 45+
 46+#include <sys/cdefs.h>
 47+#if defined(__RCSID) && !defined(__lint)
 48+__RCSID("$NetBSD: iso9660_rrip.c,v 1.17 2025/02/17 22:56:46 andvar Exp $");
 49+#endif  /* !__lint */
 50+
 51+static void cd9660_rrip_initialize_inode(cd9660node *);
 52+static int cd9660_susp_handle_continuation(iso9660_disk *, cd9660node *);
 53+static int cd9660_susp_handle_continuation_common(iso9660_disk *, cd9660node *,
 54+    int);
 55+
 56+int
 57+cd9660_susp_initialize(iso9660_disk *diskStructure, cd9660node *node,
 58+    cd9660node *parent, cd9660node *grandparent)
 59+{
 60+	cd9660node *cn;
 61+	int r;
 62+
 63+	/* Make sure the node is not NULL. If it is, there are major problems */
 64+	assert(node != NULL);
 65+
 66+	if (!(node->type & CD9660_TYPE_DOT) &&
 67+	    !(node->type & CD9660_TYPE_DOTDOT))
 68+		TAILQ_INIT(&(node->head));
 69+	if (node->dot_record != 0)
 70+		TAILQ_INIT(&(node->dot_record->head));
 71+	if (node->dot_dot_record != 0)
 72+		TAILQ_INIT(&(node->dot_dot_record->head));
 73+
 74+	 /* SUSP specific entries here */
 75+	if ((r = cd9660_susp_initialize_node(diskStructure, node)) < 0)
 76+		return r;
 77+
 78+	/* currently called cd9660node_rrip_init_links */
 79+	r = cd9660_rrip_initialize_node(diskStructure, node, parent, grandparent);
 80+	if (r < 0)
 81+		return r;
 82+
 83+	/*
 84+	 * See if we need a CE record, and set all of the
 85+	 * associated counters.
 86+	 *
 87+	 * This should be called after all extensions. After
 88+	 * this is called, no new records should be added.
 89+	 */
 90+	if ((r = cd9660_susp_handle_continuation(diskStructure, node)) < 0)
 91+		return r;
 92+
 93+	/* Recurse on children. */
 94+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
 95+		if ((r = cd9660_susp_initialize(diskStructure, cn, node, parent)) < 0)
 96+			return 0;
 97+	}
 98+	return 1;
 99+}
100+
101+int
102+cd9660_susp_finalize(iso9660_disk *diskStructure, cd9660node *node)
103+{
104+	cd9660node *temp;
105+	int r;
106+
107+	assert(node != NULL);
108+
109+	if (node == diskStructure->rootNode)
110+		diskStructure->susp_continuation_area_current_free = 0;
111+
112+	if ((r = cd9660_susp_finalize_node(diskStructure, node)) < 0)
113+		return r;
114+	if ((r = cd9660_rrip_finalize_node(diskStructure, node)) < 0)
115+		return r;
116+
117+	TAILQ_FOREACH(temp, &node->cn_children, cn_next_child) {
118+		if ((r = cd9660_susp_finalize(diskStructure, temp)) < 0)
119+			return r;
120+	}
121+	return 1;
122+}
123+
124+/*
125+ * If we really wanted to speed things up, we could have some sort of
126+ * lookup table on the SUSP entry type that calls a functor. Or, we could
127+ * combine the functions. These functions are kept separate to allow
128+ * easier addition of other extensions.
129+
130+ * For the sake of simplicity and clarity, we won't be doing that for now.
131+ */
132+
133+/*
134+ * SUSP needs to update the following types:
135+ * CE (continuation area)
136+ */
137+int
138+cd9660_susp_finalize_node(iso9660_disk *diskStructure, cd9660node *node)
139+{
140+	struct ISO_SUSP_ATTRIBUTES *t;
141+
142+	/* Handle CE counters */
143+	if (node->susp_entry_ce_length > 0) {
144+		node->susp_entry_ce_start =
145+		    diskStructure->susp_continuation_area_current_free;
146+		diskStructure->susp_continuation_area_current_free +=
147+		    node->susp_entry_ce_length;
148+	}
149+
150+	TAILQ_FOREACH(t, &node->head, rr_ll) {
151+		if (t->susp_type != SUSP_TYPE_SUSP ||
152+		    t->entry_type != SUSP_ENTRY_SUSP_CE)
153+			continue;
154+		cd9660_bothendian_dword(
155+			diskStructure->
156+			  susp_continuation_area_start_sector,
157+			t->attr.su_entry.CE.ca_sector);
158+
159+		cd9660_bothendian_dword(
160+			diskStructure->
161+			  susp_continuation_area_start_sector,
162+			t->attr.su_entry.CE.ca_sector);
163+		cd9660_bothendian_dword(node->susp_entry_ce_start,
164+			t->attr.su_entry.CE.offset);
165+		cd9660_bothendian_dword(node->susp_entry_ce_length,
166+			t->attr.su_entry.CE.length);
167+	}
168+	return 0;
169+}
170+
171+int
172+cd9660_rrip_finalize_node(iso9660_disk *diskStructure NETCOMPAT_UNUSED,
173+    cd9660node *node)
174+{
175+	struct ISO_SUSP_ATTRIBUTES *t;
176+
177+	TAILQ_FOREACH(t, &node->head, rr_ll) {
178+		if (t->susp_type != SUSP_TYPE_RRIP)
179+			continue;
180+		switch (t->entry_type) {
181+		case SUSP_ENTRY_RRIP_CL:
182+			/* Look at rr_relocated*/
183+			if (node->rr_relocated == NULL)
184+				return -1;
185+			cd9660_bothendian_dword(
186+				node->rr_relocated->fileDataSector,
187+				(unsigned char *)
188+				    t->attr.rr_entry.CL.dir_loc);
189+			break;
190+		case SUSP_ENTRY_RRIP_PL:
191+			/* Look at rr_real_parent */
192+			if (node->parent == NULL ||
193+			    node->parent->rr_real_parent == NULL)
194+				return -1;
195+			cd9660_bothendian_dword(
196+				node->parent->rr_real_parent->fileDataSector,
197+				(unsigned char *)
198+				    t->attr.rr_entry.PL.dir_loc);
199+			break;
200+		}
201+	}
202+	return 0;
203+}
204+
205+static int
206+cd9660_susp_handle_continuation_common(iso9660_disk *diskStructure,
207+    cd9660node *node, int space)
208+{
209+	int ca_used, susp_used, susp_used_pre_ce, working;
210+	struct ISO_SUSP_ATTRIBUTES *temp, *pre_ce, *last, *CE, *ST;
211+
212+	pre_ce = last = NULL;
213+	working = 254 - space;
214+	if (node->su_tail_size > 0)
215+		/* Allow 4 bytes for "ST" record. */
216+		working -= node->su_tail_size + 4;
217+	/* printf("There are %i bytes to work with\n",working); */
218+
219+	susp_used_pre_ce = susp_used = 0;
220+	ca_used = 0;
221+	TAILQ_FOREACH(temp, &node->head, rr_ll) {
222+		if (working < 0)
223+			break;
224+		/*
225+		 * printf("SUSP Entry found, length is %i\n",
226+		 * CD9660_SUSP_ENTRY_SIZE(temp));
227+		 */
228+		working -= CD9660_SUSP_ENTRY_SIZE(temp);
229+		if (working >= 0) {
230+			last = temp;
231+			susp_used += CD9660_SUSP_ENTRY_SIZE(temp);
232+		}
233+		if (working >= 28) {
234+			/*
235+			 * Remember the last entry after which we
236+			 * could insert a "CE" entry.
237+			 */
238+			pre_ce = last;
239+			susp_used_pre_ce = susp_used;
240+		}
241+	}
242+
243+	/* A CE entry is needed */
244+	if (working <= 0) {
245+		CE = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
246+			SUSP_ENTRY_SUSP_CE, "CE", SUSP_LOC_ENTRY);
247+		cd9660_susp_ce(CE, node);
248+		/* This will automatically insert at the appropriate location */
249+		if (pre_ce != NULL)
250+			TAILQ_INSERT_AFTER(&node->head, pre_ce, CE, rr_ll);
251+		else
252+			TAILQ_INSERT_HEAD(&node->head, CE, rr_ll);
253+		last = CE;
254+		susp_used = susp_used_pre_ce + 28;
255+		/* Count how much CA data is necessary */
256+		for (temp = TAILQ_NEXT(last, rr_ll); temp != NULL;
257+		     temp = TAILQ_NEXT(temp, rr_ll)) {
258+			ca_used += CD9660_SUSP_ENTRY_SIZE(temp);
259+		}
260+	}
261+
262+	/* An ST entry is needed */
263+	if (node->su_tail_size > 0) {
264+		ST = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
265+		    SUSP_ENTRY_SUSP_ST, "ST", SUSP_LOC_ENTRY);
266+		cd9660_susp_st(ST, node);
267+		if (last != NULL)
268+			TAILQ_INSERT_AFTER(&node->head, last, ST, rr_ll);
269+		else
270+			TAILQ_INSERT_HEAD(&node->head, ST, rr_ll);
271+		last = ST;
272+		susp_used += 4;
273+	}
274+	if (last != NULL)
275+		last->last_in_suf = 1;
276+
277+	node->susp_entry_size = susp_used;
278+	node->susp_entry_ce_length = ca_used;
279+
280+	diskStructure->susp_continuation_area_size += ca_used;
281+	return 1;
282+}
283+
284+/* See if a continuation entry is needed for each of the different types */
285+static int
286+cd9660_susp_handle_continuation(iso9660_disk *diskStructure, cd9660node *node)
287+{
288+	assert (node != NULL);
289+
290+	/* Entry */
291+	if (cd9660_susp_handle_continuation_common(diskStructure,
292+		node,(int)(node->isoDirRecord->length[0])) < 0)
293+		return 0;
294+
295+	return 1;
296+}
297+
298+int
299+cd9660_susp_initialize_node(iso9660_disk *diskStructure, cd9660node *node)
300+{
301+	struct ISO_SUSP_ATTRIBUTES *temp;
302+
303+	/*
304+	 * Requirements/notes:
305+	 * CE: is added for us where needed
306+	 * ST: not sure if it is even required, but if so, should be
307+	 *     handled by the CE code
308+	 * PD: isnt needed (though might be added for testing)
309+	 * SP: is stored ONLY on the . record of the root directory
310+	 * ES: not sure
311+	 */
312+
313+	/* Check for root directory, add SP and ER if needed. */
314+	if (node->type & CD9660_TYPE_DOT) {
315+		if (node->parent == diskStructure->rootNode) {
316+			temp = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
317+				SUSP_ENTRY_SUSP_SP, "SP", SUSP_LOC_DOT);
318+			cd9660_susp_sp(temp, node);
319+
320+			/* Should be first entry. */
321+			TAILQ_INSERT_HEAD(&node->head, temp, rr_ll);
322+		}
323+	}
324+	return 1;
325+}
326+
327+static void
328+cd9660_rrip_initialize_inode(cd9660node *node)
329+{
330+	struct ISO_SUSP_ATTRIBUTES *attr;
331+
332+	/*
333+	 * Inode dependent values - this may change,
334+	 * but for now virtual files and directories do
335+	 * not have an inode structure
336+	 */
337+
338+	if ((node->node != NULL) && (node->node->inode != NULL)) {
339+		/* PX - POSIX attributes */
340+		attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
341+			SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
342+		cd9660node_rrip_px(attr, node->node);
343+
344+		TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
345+
346+		/* TF - timestamp */
347+		attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
348+			SUSP_ENTRY_RRIP_TF, "TF", SUSP_LOC_ENTRY);
349+		cd9660node_rrip_tf(attr, node->node);
350+		TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
351+
352+		/* SL - Symbolic link */
353+		/* ?????????? Dan - why is this here? */
354+		if (TAILQ_EMPTY(&node->cn_children) &&
355+		    node->node->inode != NULL &&
356+		    S_ISLNK(node->node->inode->st.st_mode))
357+			cd9660_createSL(node);
358+
359+		/* PN - device number */
360+		if (node->node->inode != NULL &&
361+		    ((S_ISCHR(node->node->inode->st.st_mode) ||
362+		     S_ISBLK(node->node->inode->st.st_mode)))) {
363+			attr =
364+			    cd9660node_susp_create_node(SUSP_TYPE_RRIP,
365+				SUSP_ENTRY_RRIP_PN, "PN",
366+				SUSP_LOC_ENTRY);
367+			cd9660node_rrip_pn(attr, node->node);
368+			TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
369+		}
370+	}
371+}
372+
373+int
374+cd9660_rrip_initialize_node(iso9660_disk *diskStructure, cd9660node *node,
375+    cd9660node *parent, cd9660node *grandparent)
376+{
377+	struct ISO_SUSP_ATTRIBUTES *current = NULL;
378+
379+	assert(node != NULL);
380+
381+	if (node->type & CD9660_TYPE_DOT) {
382+		/*
383+		 * Handle ER - should be the only entry to appear on
384+		 * a "." record
385+		 */
386+		if (node->parent == diskStructure->rootNode) {
387+			cd9660_susp_ER(node, 1, SUSP_RRIP_ER_EXT_ID,
388+				SUSP_RRIP_ER_EXT_DES, SUSP_RRIP_ER_EXT_SRC);
389+		}
390+		if (parent != NULL && parent->node != NULL &&
391+		    parent->node->inode != NULL) {
392+			/* PX - POSIX attributes */
393+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
394+				SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
395+			cd9660node_rrip_px(current, parent->node);
396+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
397+		}
398+	} else if (node->type & CD9660_TYPE_DOTDOT) {
399+		if (grandparent != NULL && grandparent->node != NULL &&
400+		    grandparent->node->inode != NULL) {
401+			/* PX - POSIX attributes */
402+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
403+				SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
404+			cd9660node_rrip_px(current, grandparent->node);
405+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
406+		}
407+		/* Handle PL */
408+		if (parent != NULL && parent->rr_real_parent != NULL) {
409+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
410+			    SUSP_ENTRY_RRIP_PL, "PL", SUSP_LOC_DOTDOT);
411+			cd9660_rrip_PL(current,node);
412+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
413+		}
414+	} else {
415+		cd9660_rrip_initialize_inode(node);
416+
417+		if (node == diskStructure->rr_moved_dir) {
418+			cd9660_rrip_add_NM(node, RRIP_DEFAULT_MOVE_DIR_NAME);
419+		} else if (node->node != NULL) {
420+			cd9660_rrip_NM(node);
421+		}
422+
423+		/* Rock ridge directory relocation code here. */
424+
425+		/* First handle the CL for the placeholder file. */
426+		if (node->rr_relocated != NULL) {
427+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
428+				SUSP_ENTRY_RRIP_CL, "CL", SUSP_LOC_ENTRY);
429+			cd9660_rrip_CL(current, node);
430+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
431+		}
432+
433+		/* Handle RE*/
434+		if (node->rr_real_parent != NULL) {
435+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
436+				SUSP_ENTRY_RRIP_RE, "RE", SUSP_LOC_ENTRY);
437+			cd9660_rrip_RE(current,node);
438+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
439+		}
440+	}
441+	return 1;
442+}
443+
444+struct ISO_SUSP_ATTRIBUTES*
445+cd9660node_susp_create_node(int susp_type, int entry_type, const char *type_id,
446+			    int write_loc)
447+{
448+	struct ISO_SUSP_ATTRIBUTES* temp;
449+
450+	temp = emalloc(sizeof(*temp));
451+	temp->susp_type = susp_type;
452+	temp->entry_type = entry_type;
453+	temp->last_in_suf = 0;
454+	/* Phase this out */
455+	temp->type_of[0] = type_id[0];
456+	temp->type_of[1] = type_id[1];
457+	temp->write_location = write_loc;
458+
459+	/*
460+	 * Since the first four bytes is common, lets go ahead and
461+	 * set the type identifier, since we are passing that to this
462+	 * function anyhow.
463+	 */
464+	temp->attr.su_entry.SP.h.type[0] = type_id[0];
465+	temp->attr.su_entry.SP.h.type[1] = type_id[1];
466+	return temp;
467+}
468+
469+int
470+cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES* p, cd9660node *node NETCOMPAT_UNUSED)
471+{
472+	p->attr.rr_entry.PL.h.length[0] = 12;
473+	p->attr.rr_entry.PL.h.version[0] = 1;
474+	return 1;
475+}
476+
477+int
478+cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node NETCOMPAT_UNUSED)
479+{
480+	p->attr.rr_entry.CL.h.length[0] = 12;
481+	p->attr.rr_entry.CL.h.version[0] = 1;
482+	return 1;
483+}
484+
485+int
486+cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node NETCOMPAT_UNUSED)
487+{
488+	p->attr.rr_entry.RE.h.length[0] = 4;
489+	p->attr.rr_entry.RE.h.version[0] = 1;
490+	return 1;
491+}
492+
493+void
494+cd9660_createSL(cd9660node *node)
495+{
496+	struct ISO_SUSP_ATTRIBUTES* current;
497+	int path_count, dir_count, done, i, j, dir_copied;
498+	char temp_cr[255];
499+	char temp_sl[255]; /* used in copying continuation entry*/
500+	char* sl_ptr;
501+
502+	sl_ptr = node->node->symlink;
503+
504+	done = 0;
505+	path_count = 0;
506+	dir_count = 0;
507+	dir_copied = 0;
508+	current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
509+	    SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY);
510+
511+	current->attr.rr_entry.SL.h.version[0] = 1;
512+	current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE;
513+
514+	if (*sl_ptr == '/') {
515+		temp_cr[0] = SL_FLAGS_ROOT;
516+		temp_cr[1] = 0;
517+		memcpy(current->attr.rr_entry.SL.component + path_count,
518+		    temp_cr, 2);
519+		path_count += 2;
520+		sl_ptr++;
521+	}
522+
523+	for (i = 0; i < (dir_count + 2); i++)
524+		temp_cr[i] = '\0';
525+
526+	while (!done) {
527+		while ((*sl_ptr != '/') && (*sl_ptr != '\0')) {
528+			dir_copied = 1;
529+			if (*sl_ptr == '.') {
530+				if ((*(sl_ptr + 1) == '/') || (*(sl_ptr + 1)
531+				     == '\0')) {
532+					temp_cr[0] = SL_FLAGS_CURRENT;
533+					sl_ptr++;
534+				} else if(*(sl_ptr + 1) == '.') {
535+					if ((*(sl_ptr + 2) == '/') ||
536+					    (*(sl_ptr + 2) == '\0')) {
537+						temp_cr[0] = SL_FLAGS_PARENT;
538+						sl_ptr += 2;
539+					}
540+				} else {
541+					temp_cr[dir_count+2] = *sl_ptr;
542+					sl_ptr++;
543+					dir_count++;
544+				}
545+			} else {
546+				temp_cr[dir_count + 2] = *sl_ptr;
547+				sl_ptr++;
548+				dir_count++;
549+			}
550+		}
551+
552+		if ((path_count + dir_count) >= 249) {
553+			current->attr.rr_entry.SL.flags[0] |= SL_FLAGS_CONTINUE;
554+
555+			j = 0;
556+
557+			if (path_count <= 249) {
558+				while(j != (249 - path_count)) {
559+					temp_sl[j] = temp_cr[j];
560+					j++;
561+				}
562+				temp_sl[0] = SL_FLAGS_CONTINUE;
563+				temp_sl[1] = j - 2;
564+				memcpy(
565+				    current->attr.rr_entry.SL.component +
566+					path_count,
567+				    temp_sl, j);
568+			}
569+
570+			path_count += j;
571+			current->attr.rr_entry.SL.h.length[0] = path_count + 5;
572+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
573+			current= cd9660node_susp_create_node(SUSP_TYPE_RRIP,
574+			       SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY);
575+			current->attr.rr_entry.SL.h.version[0] = 1;
576+			current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE;
577+
578+			path_count = 0;
579+
580+			if (dir_count > 2) {
581+				while (j != dir_count + 2) {
582+					current->attr.rr_entry.SL.component[
583+					    path_count + 2] = temp_cr[j];
584+					j++;
585+					path_count++;
586+				}
587+				current->attr.rr_entry.SL.component[1]
588+				    = path_count;
589+				path_count+= 2;
590+			} else {
591+				while(j != dir_count) {
592+					current->attr.rr_entry.SL.component[
593+					    path_count+2] = temp_cr[j];
594+					j++;
595+					path_count++;
596+				}
597+			}
598+		} else {
599+			if (dir_copied == 1) {
600+				temp_cr[1] = dir_count;
601+				memcpy(current->attr.rr_entry.SL.component +
602+					path_count,
603+				    temp_cr, dir_count + 2);
604+				path_count += dir_count + 2;
605+			}
606+		}
607+
608+		if (*sl_ptr == '\0') {
609+			done = 1;
610+			current->attr.rr_entry.SL.h.length[0] = path_count + 5;
611+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
612+		} else {
613+			sl_ptr++;
614+			dir_count = 0;
615+			dir_copied = 0;
616+			for(i = 0; i < 255; i++) {
617+				temp_cr[i] = '\0';
618+			}
619+		}
620+	}
621+}
622+
623+int
624+cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *v, fsnode *pxinfo)
625+{
626+	v->attr.rr_entry.PX.h.length[0] = 36;
627+	v->attr.rr_entry.PX.h.version[0] = 1;
628+	cd9660_bothendian_dword(pxinfo->inode->st.st_mode,
629+	    v->attr.rr_entry.PX.mode);
630+	cd9660_bothendian_dword(pxinfo->inode->st.st_nlink,
631+	    v->attr.rr_entry.PX.links);
632+	cd9660_bothendian_dword(pxinfo->inode->st.st_uid,
633+	    v->attr.rr_entry.PX.uid);
634+	cd9660_bothendian_dword(pxinfo->inode->st.st_gid,
635+	    v->attr.rr_entry.PX.gid);
636+
637+	/* Ignoring the serial number for now */
638+	return 1;
639+}
640+
641+int
642+cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *pn_field, fsnode *fnode)
643+{
644+	pn_field->attr.rr_entry.PN.h.length[0] = 20;
645+	pn_field->attr.rr_entry.PN.h.version[0] = 1;
646+
647+	if (sizeof (fnode->inode->st.st_rdev) > 4)
648+		cd9660_bothendian_dword(
649+		    (uint64_t)fnode->inode->st.st_rdev >> 32,
650+		    pn_field->attr.rr_entry.PN.high);
651+	else
652+		cd9660_bothendian_dword(0, pn_field->attr.rr_entry.PN.high);
653+
654+	cd9660_bothendian_dword(fnode->inode->st.st_rdev & 0xffffffff,
655+		pn_field->attr.rr_entry.PN.low);
656+	return 1;
657+}
658+
659+#if 0
660+int
661+cd9660node_rrip_nm(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *file_node)
662+{
663+	int nm_length = strlen(file_node->isoDirRecord->name) + 5;
664+        p->attr.rr_entry.NM.h.type[0] = 'N';
665+	p->attr.rr_entry.NM.h.type[1] = 'M';
666+	sprintf(p->attr.rr_entry.NM.altname, "%s", file_node->isoDirRecord->name);
667+	p->attr.rr_entry.NM.h.length[0] = (unsigned char)nm_length;
668+	p->attr.rr_entry.NM.h.version[0] = (unsigned char)1;
669+	p->attr.rr_entry.NM.flags[0] = (unsigned char) NM_PARENT;
670+	return 1;
671+}
672+#endif
673+
674+int
675+cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *p, fsnode *_node)
676+{
677+	p->attr.rr_entry.TF.flags[0] = TF_MODIFY | TF_ACCESS | TF_ATTRIBUTES;
678+	p->attr.rr_entry.TF.h.length[0] = 5;
679+	p->attr.rr_entry.TF.h.version[0] = 1;
680+
681+	/*
682+	 * Need to add creation time, backup time,
683+	 * expiration time, and effective time.
684+	 */
685+
686+	cd9660_time_915(p->attr.rr_entry.TF.timestamp,
687+		_node->inode->st.st_mtime);
688+	p->attr.rr_entry.TF.h.length[0] += 7;
689+
690+	cd9660_time_915(p->attr.rr_entry.TF.timestamp + 7,
691+		_node->inode->st.st_atime);
692+	p->attr.rr_entry.TF.h.length[0] += 7;
693+
694+	cd9660_time_915(p->attr.rr_entry.TF.timestamp + 14,
695+		_node->inode->st.st_ctime);
696+	p->attr.rr_entry.TF.h.length[0] += 7;
697+	return 1;
698+}
699+
700+int
701+cd9660_susp_sp(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo NETCOMPAT_UNUSED)
702+{
703+	p->attr.su_entry.SP.h.length[0] = 7;
704+	p->attr.su_entry.SP.h.version[0] = 1;
705+	p->attr.su_entry.SP.check[0] = 0xBE;
706+	p->attr.su_entry.SP.check[1] = 0xEF;
707+	p->attr.su_entry.SP.len_skp[0] = 0;
708+	return 1;
709+}
710+
711+int
712+cd9660_susp_st(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *stinfo NETCOMPAT_UNUSED)
713+{
714+	p->attr.su_entry.ST.h.type[0] = 'S';
715+	p->attr.su_entry.ST.h.type[1] = 'T';
716+	p->attr.su_entry.ST.h.length[0] = 4;
717+	p->attr.su_entry.ST.h.version[0] = 1;
718+	return 1;
719+}
720+
721+int
722+cd9660_susp_ce(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo NETCOMPAT_UNUSED)
723+{
724+	p->attr.su_entry.CE.h.length[0] = 28;
725+	p->attr.su_entry.CE.h.version[0] = 1;
726+	/* Other attributes dont matter right now, will be updated later */
727+	return 1;
728+}
729+
730+int
731+cd9660_susp_pd(struct ISO_SUSP_ATTRIBUTES *p NETCOMPAT_UNUSED,
732+    int length NETCOMPAT_UNUSED)
733+{
734+	return 1;
735+}
736+
737+void
738+cd9660_rrip_add_NM(cd9660node *node, const char *name)
739+{
740+	int working,len;
741+	const char *p;
742+	struct ISO_SUSP_ATTRIBUTES *r;
743+
744+	/*
745+	 * Each NM record has 254 bytes to work with. This means that
746+	 * the name data itself only has 249 bytes to work with. So, a
747+	 * name with 251 characters would require two nm records.
748+	 */
749+	p = name;
750+	working = 1;
751+	while (working) {
752+		r = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
753+		    SUSP_ENTRY_RRIP_NM, "NM", SUSP_LOC_ENTRY);
754+		r->attr.rr_entry.NM.h.version[0] = 1;
755+		r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_NONE;
756+		len = strlen(p);
757+
758+		if (len > 249) {
759+			len = 249;
760+			r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_CONTINUE;
761+		} else {
762+			working = 0;
763+		}
764+		memcpy(r->attr.rr_entry.NM.altname, p, len);
765+		r->attr.rr_entry.NM.h.length[0] = 5 + len;
766+
767+		TAILQ_INSERT_TAIL(&node->head, r, rr_ll);
768+
769+		p += len;
770+	}
771+}
772+
773+void
774+cd9660_rrip_NM(cd9660node *node)
775+{
776+	cd9660_rrip_add_NM(node, node->node->name);
777+}
778+
779+struct ISO_SUSP_ATTRIBUTES*
780+cd9660_susp_ER(cd9660node *node,
781+	       u_char ext_version, const char* ext_id, const char* ext_des,
782+	       const char* ext_src)
783+{
784+	int l;
785+	struct ISO_SUSP_ATTRIBUTES *r;
786+
787+	r = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
788+			SUSP_ENTRY_SUSP_ER, "ER", SUSP_LOC_DOT);
789+
790+	/* Fixed data is 8 bytes */
791+	r->attr.su_entry.ER.h.length[0] = 8;
792+	r->attr.su_entry.ER.h.version[0] = 1;
793+
794+	r->attr.su_entry.ER.len_id[0] = (u_char)strlen(ext_id);
795+	r->attr.su_entry.ER.len_des[0] = (u_char)strlen(ext_des);
796+	r->attr.su_entry.ER.len_src[0] = (u_char)strlen(ext_src);
797+
798+	l = r->attr.su_entry.ER.len_id[0] +
799+		r->attr.su_entry.ER.len_src[0] +
800+		r->attr.su_entry.ER.len_des[0];
801+
802+	/* Everything must fit. */
803+	assert(l + r->attr.su_entry.ER.h.length[0] <= 254);
804+
805+	r->attr.su_entry.ER.h.length[0] += (u_char)l;
806+
807+
808+	r->attr.su_entry.ER.ext_ver[0] = ext_version;
809+	memcpy(r->attr.su_entry.ER.ext_data, ext_id,
810+		(int)r->attr.su_entry.ER.len_id[0]);
811+	l = (int) r->attr.su_entry.ER.len_id[0];
812+	memcpy(r->attr.su_entry.ER.ext_data + l,ext_des,
813+		(int)r->attr.su_entry.ER.len_des[0]);
814+
815+	l += (int)r->attr.su_entry.ER.len_des[0];
816+	memcpy(r->attr.su_entry.ER.ext_data + l,ext_src,
817+		(int)r->attr.su_entry.ER.len_src[0]);
818+
819+	TAILQ_INSERT_TAIL(&node->head, r, rr_ll);
820+	return r;
821+}
822+
823+struct ISO_SUSP_ATTRIBUTES*
824+cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES *last NETCOMPAT_UNUSED,
825+    cd9660node *node NETCOMPAT_UNUSED)
826+{
827+	return NULL;
828+}
+292, -0
  1@@ -0,0 +1,292 @@
  2+/*	$NetBSD: iso9660_rrip.h,v 1.8 2023/04/18 22:58:14 christos Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
  6+ * Perez-Rathke and Ram Vedam.  All rights reserved.
  7+ *
  8+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
  9+ * Alan Perez-Rathke and Ram Vedam.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or
 12+ * without modification, are permitted provided that the following
 13+ * conditions are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above
 17+ *    copyright notice, this list of conditions and the following
 18+ *    disclaimer in the documentation and/or other materials provided
 19+ *    with the distribution.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN
 22+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR
 23+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 24+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25+ * DISCLAIMED.  IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN
 26+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT,
 27+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 28+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 29+ * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 30+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 31+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 33+ * OF SUCH DAMAGE.
 34+ */
 35+#ifndef __ISO9660_RRIP_H__
 36+#define __ISO9660_RRIP_H__
 37+
 38+/*
 39+ * This will hold all the functions needed to
 40+ * write an ISO 9660 image with Rock Ridge Extensions
 41+ */
 42+
 43+/* For writing must use ISO_RRIP_EXTREF structure */
 44+
 45+#include "makefs.h"
 46+#include <cd9660_rrip.h>
 47+#include "cd9660.h"
 48+#include <sys/queue.h>
 49+
 50+#define	 PX_LENGTH	   0x2C
 51+#define	 PN_LENGTH	   0x14
 52+
 53+#define	 TF_CREATION	   0x01
 54+#define	 TF_MODIFY	   0x02
 55+#define	 TF_ACCESS	   0x04
 56+#define	 TF_ATTRIBUTES	   0x08
 57+#define	 TF_BACKUP	   0x10
 58+#define	 TF_EXPIRATION	   0x20
 59+#define	 TF_EFFECTIVE	   0x40
 60+#define	 TF_LONGFORM	   0x80
 61+
 62+#define	 NM_CONTINUE	   0x01
 63+#define	 NM_CURRENT	   0x02
 64+#define	 NM_PARENT	   0x04
 65+
 66+
 67+#define	 SUSP_LOC_ENTRY	   0x01
 68+#define	 SUSP_LOC_DOT	   0x02
 69+#define	 SUSP_LOC_DOTDOT   0x04
 70+
 71+#define SUSP_TYPE_SUSP		1
 72+#define SUSP_TYPE_RRIP		2
 73+
 74+#define SUSP_ENTRY_SUSP_CE	1
 75+#define SUSP_ENTRY_SUSP_PD	2
 76+#define SUSP_ENTRY_SUSP_SP	3
 77+#define SUSP_ENTRY_SUSP_ST	4
 78+#define SUSP_ENTRY_SUSP_ER	5
 79+#define SUSP_ENTRY_SUSP_ES	6
 80+
 81+#define SUSP_ENTRY_RRIP_PX	1
 82+#define SUSP_ENTRY_RRIP_PN	2
 83+#define SUSP_ENTRY_RRIP_SL	3
 84+#define SUSP_ENTRY_RRIP_NM	4
 85+#define SUSP_ENTRY_RRIP_CL	5
 86+#define SUSP_ENTRY_RRIP_PL	6
 87+#define SUSP_ENTRY_RRIP_RE	7
 88+#define SUSP_ENTRY_RRIP_TF	8
 89+#define SUSP_ENTRY_RRIP_SF	9
 90+
 91+#define SUSP_RRIP_ER_EXT_ID "IEEE_P1282"
 92+#define SUSP_RRIP_ER_EXT_DES "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS."
 93+#define SUSP_RRIP_ER_EXT_SRC "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION."
 94+
 95+#define SL_FLAGS_NONE	        0
 96+#define SL_FLAGS_CONTINUE       1
 97+#define SL_FLAGS_CURRENT        2
 98+#define SL_FLAGS_PARENT	        4
 99+#define SL_FLAGS_ROOT	        8
100+
101+typedef struct {
102+	ISO_SUSP_HEADER		 h;
103+	u_char mode		[ISODCL(5,12)];
104+	u_char links		[ISODCL(13,20)];
105+	u_char uid		[ISODCL(21,28)];
106+	u_char gid		[ISODCL(29,36)];
107+	u_char serial		[ISODCL(37,44)];/* Not used */
108+} ISO_RRIP_PX;
109+
110+typedef struct {
111+	ISO_SUSP_HEADER		 h;
112+	u_char high		[ISODCL(5,12)];
113+	u_char low		[ISODCL(13,20)];
114+} ISO_RRIP_PN;
115+
116+typedef struct {
117+	ISO_SUSP_HEADER		 h;
118+	u_char flags		 [ISODCL ( 4, 4)];
119+	u_char component	 [ISODCL ( 4, 256)];
120+	u_int  nBytes;
121+} ISO_RRIP_SL;
122+
123+typedef struct {
124+	ISO_SUSP_HEADER		 h;
125+	u_char flags		 [ISODCL ( 4, 4)];
126+	u_char timestamp	 [ISODCL ( 5, 256)];
127+} ISO_RRIP_TF;
128+
129+#define RRIP_NM_FLAGS_NONE 0x00
130+#define RRIP_NM_FLAGS_CONTINUE 0x01
131+#define RRIP_NM_FLAGS_CURRENT 0x02
132+#define RRIP_NM_FLAGS_PARENT 0x04
133+
134+typedef struct {
135+	ISO_SUSP_HEADER		 h;
136+	u_char flags		 [ISODCL ( 4, 4)];
137+	u_char altname		 [ISODCL ( 4, 256)];
138+} ISO_RRIP_NM;
139+
140+/* Note that this is the same structure as cd9660_rrip.h : ISO_RRIP_CONT */
141+typedef struct {
142+	ISO_SUSP_HEADER		 h;
143+	u_char ca_sector	 [ISODCL ( 5, 12)];
144+	u_char offset		 [ISODCL ( 13, 20)];
145+	u_char length		 [ISODCL ( 21, 28)];
146+} ISO_SUSP_CE;
147+
148+typedef struct {
149+	ISO_SUSP_HEADER		 h;
150+	u_char padding_area	 [ISODCL ( 4, 256)];
151+} ISO_SUSP_PD;
152+
153+typedef struct {
154+	ISO_SUSP_HEADER		 h;
155+	u_char check		 [ISODCL ( 4, 5)];
156+	u_char len_skp		 [ISODCL ( 6, 6)];
157+} ISO_SUSP_SP;
158+
159+typedef struct {
160+	ISO_SUSP_HEADER		 h;
161+} ISO_SUSP_ST;
162+
163+typedef struct {
164+	ISO_SUSP_HEADER		 h;
165+	u_char len_id		 [ISODCL ( 4, 4)];
166+	u_char len_des		 [ISODCL ( 5, 5)];
167+	u_char len_src		 [ISODCL ( 6, 6)];
168+	u_char ext_ver		 [ISODCL ( 7, 7)];
169+	u_char ext_data		 [ISODCL (8,256)];
170+/*	u_char ext_id		 [ISODCL ( 8, 256)];
171+	u_char ext_des		 [ISODCL ( 257, 513)];
172+	u_char ext_src		 [ISODCL ( 514, 770)];*/
173+} ISO_SUSP_ER;
174+
175+typedef struct {
176+	ISO_SUSP_HEADER		 h;
177+	u_char ext_seq		 [ISODCL ( 4, 4)];
178+} ISO_SUSP_ES;
179+
180+typedef union {
181+	ISO_RRIP_PX			PX;
182+	ISO_RRIP_PN			PN;
183+	ISO_RRIP_SL			SL;
184+	ISO_RRIP_NM			NM;
185+	ISO_RRIP_CLINK			CL;
186+	ISO_RRIP_PLINK			PL;
187+	ISO_RRIP_RELDIR			RE;
188+	ISO_RRIP_TF			TF;
189+} rrip_entry;
190+
191+typedef union {
192+	ISO_SUSP_CE			CE;
193+	ISO_SUSP_PD			PD;
194+	ISO_SUSP_SP			SP;
195+	ISO_SUSP_ST			ST;
196+	ISO_SUSP_ER			ER;
197+	ISO_SUSP_ES			ES;
198+} susp_entry;
199+
200+typedef union {
201+	susp_entry		  su_entry;
202+	rrip_entry		  rr_entry;
203+} SUSP_ENTRIES;
204+
205+struct ISO_SUSP_ATTRIBUTES {
206+	SUSP_ENTRIES attr;
207+	int type;
208+	char type_of[2];
209+	char last_in_suf;	/* last entry in the System Use Field? */
210+	/* Dan's addons - will merge later. This allows use of a switch */
211+	char susp_type; 	/* SUSP or RRIP */
212+	char entry_type;	/* Record type */
213+	char write_location;
214+	TAILQ_ENTRY(ISO_SUSP_ATTRIBUTES) rr_ll;
215+};
216+
217+#define CD9660_SUSP_ENTRY_SIZE(entry)\
218+	((int) ((entry)->attr.su_entry.SP.h.length[0]))
219+
220+/* Recursive function - move later to func pointer code*/
221+int cd9660_susp_finalize(iso9660_disk *, cd9660node *);
222+
223+/* These two operate on single nodes */
224+int cd9660_susp_finalize_node(iso9660_disk *, cd9660node *);
225+int cd9660_rrip_finalize_node(iso9660_disk *, cd9660node *);
226+
227+/* POSIX File attribute */
228+int cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
229+
230+/* Device number */
231+int cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
232+
233+/* Symbolic link */
234+int cd9660node_rrip_SL(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
235+
236+/* Alternate Name function */
237+void cd9660_rrip_NM(cd9660node *);
238+void cd9660_rrip_add_NM(cd9660node *,const char *);
239+
240+/* Parent and child link function */
241+int cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
242+int cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
243+int cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
244+
245+int cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
246+
247+
248+
249+/*
250+ * Relocation directory function. I'm not quite sure what
251+ * sort of parameters are needed, but personally I don't think
252+ * any parameters are needed except for the memory address where
253+ * the information needs to be put in
254+ */
255+int cd9660node_rrip_re(void *, fsnode *);
256+
257+/*
258+ * Don't know if this function is needed because it apparently is an
259+ * optional feature that does not really need to be implemented but I
260+ * thought I should add it anyway.
261+ */
262+int cd9660_susp_ce (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
263+int cd9660_susp_pd (struct ISO_SUSP_ATTRIBUTES *, int);
264+int cd9660_susp_sp (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
265+int cd9660_susp_st (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
266+
267+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ER(cd9660node *, u_char, const char *,
268+    const char *, const char *);
269+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES*,
270+    cd9660node *);
271+
272+
273+/* Helper functions */
274+
275+/* Common SUSP/RRIP functions */
276+int cd9660_susp_initialize(iso9660_disk *, cd9660node *, cd9660node *,
277+    cd9660node *);
278+int cd9660_susp_initialize_node(iso9660_disk *, cd9660node *);
279+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_create_node(int, int, const char *,
280+    int);
281+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_add_entry(cd9660node *,
282+    struct ISO_SUSP_ATTRIBUTES *, struct ISO_SUSP_ATTRIBUTES *, int);
283+
284+/* RRIP specific functions */
285+int cd9660_rrip_initialize_node(iso9660_disk *, cd9660node *, cd9660node *,
286+    cd9660node *);
287+void cd9660_createSL(cd9660node *);
288+
289+/* Functions that probably can be removed */
290+/* int cd9660node_initialize_node(int, char *); */
291+
292+
293+#endif
+143, -0
  1@@ -0,0 +1,143 @@
  2+/*	$NetBSD: cd9660_rrip.h,v 1.3 2005/12/03 17:34:43 christos Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1993, 1994
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to Berkeley
  9+ * by Pace Willisson (pace@blitz.com).  The Rock Ridge Extension
 10+ * Support code is derived from software contributed to Berkeley
 11+ * by Atsushi Murai (amurai@spec.co.jp).
 12+ *
 13+ * Redistribution and use in source and binary forms, with or without
 14+ * modification, are permitted provided that the following conditions
 15+ * are met:
 16+ * 1. Redistributions of source code must retain the above copyright
 17+ *    notice, this list of conditions and the following disclaimer.
 18+ * 2. Redistributions in binary form must reproduce the above copyright
 19+ *    notice, this list of conditions and the following disclaimer in the
 20+ *    documentation and/or other materials provided with the distribution.
 21+ * 3. Neither the name of the University nor the names of its contributors
 22+ *    may be used to endorse or promote products derived from this software
 23+ *    without specific prior written permission.
 24+ *
 25+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 26+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 27+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 28+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 29+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 30+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 31+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 32+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 33+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 34+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 35+ * SUCH DAMAGE.
 36+ *
 37+ *	@(#)cd9660_rrip.h	8.2 (Berkeley) 12/5/94
 38+ */
 39+
 40+#ifndef _ISOFS_CD9660_CD9660_RRIP_H_
 41+#define _ISOFS_CD9660_CD9660_RRIP_H_
 42+
 43+typedef struct {
 44+	char   type			[ISODCL (  0,    1)];
 45+	u_char length			[ISODCL (  2,    2)]; /* 711 */
 46+	u_char version			[ISODCL (  3,    3)];
 47+} ISO_SUSP_HEADER;
 48+
 49+typedef struct {
 50+	ISO_SUSP_HEADER			h;
 51+	char mode			[ISODCL (  4,   11)]; /* 733 */
 52+	char links			[ISODCL ( 12,   19)]; /* 733 */
 53+	char uid			[ISODCL ( 20,   27)]; /* 733 */
 54+	char gid			[ISODCL ( 28,   35)]; /* 733 */
 55+} ISO_RRIP_ATTR;
 56+
 57+typedef struct {
 58+	ISO_SUSP_HEADER			h;
 59+	char dev_t_high			[ISODCL (  4,   11)]; /* 733 */
 60+	char dev_t_low			[ISODCL ( 12,   19)]; /* 733 */
 61+} ISO_RRIP_DEVICE;
 62+
 63+#define	ISO_SUSP_CFLAG_CONTINUE	0x01
 64+#define	ISO_SUSP_CFLAG_CURRENT	0x02
 65+#define	ISO_SUSP_CFLAG_PARENT	0x04
 66+#define	ISO_SUSP_CFLAG_ROOT	0x08
 67+#define	ISO_SUSP_CFLAG_VOLROOT	0x10
 68+#define	ISO_SUSP_CFLAG_HOST	0x20
 69+
 70+typedef struct {
 71+	u_char cflag			[ISODCL (  1,    1)];
 72+	u_char clen			[ISODCL (  2,    2)];
 73+	u_char name			[1];			/* XXX */
 74+} ISO_RRIP_SLINK_COMPONENT;
 75+#define	ISO_RRIP_SLSIZ	2
 76+
 77+typedef struct {
 78+	ISO_SUSP_HEADER			h;
 79+	u_char flags			[ISODCL (  4,    4)];
 80+	u_char component		[ISODCL (  5,    5)];
 81+} ISO_RRIP_SLINK;
 82+
 83+typedef struct {
 84+	ISO_SUSP_HEADER			h;
 85+	char flags			[ISODCL (  4,    4)];
 86+} ISO_RRIP_ALTNAME;
 87+
 88+typedef struct {
 89+	ISO_SUSP_HEADER			h;
 90+	char dir_loc			[ISODCL (  4,    11)]; /* 733 */
 91+} ISO_RRIP_CLINK;
 92+
 93+typedef struct {
 94+	ISO_SUSP_HEADER			h;
 95+	char dir_loc			[ISODCL (  4,    11)]; /* 733 */
 96+} ISO_RRIP_PLINK;
 97+
 98+typedef struct {
 99+	ISO_SUSP_HEADER			h;
100+} ISO_RRIP_RELDIR;
101+
102+#define	ISO_SUSP_TSTAMP_FORM17	0x80
103+#define	ISO_SUSP_TSTAMP_FORM7	0x00
104+#define	ISO_SUSP_TSTAMP_CREAT	0x01
105+#define	ISO_SUSP_TSTAMP_MODIFY	0x02
106+#define	ISO_SUSP_TSTAMP_ACCESS	0x04
107+#define	ISO_SUSP_TSTAMP_ATTR	0x08
108+#define	ISO_SUSP_TSTAMP_BACKUP	0x10
109+#define	ISO_SUSP_TSTAMP_EXPIRE	0x20
110+#define	ISO_SUSP_TSTAMP_EFFECT	0x40
111+
112+typedef struct {
113+	ISO_SUSP_HEADER			h;
114+	u_char flags			[ISODCL (  4,    4)];
115+	u_char time			[ISODCL (  5,    5)];
116+} ISO_RRIP_TSTAMP;
117+
118+typedef struct {
119+	ISO_SUSP_HEADER			h;
120+	u_char flags			[ISODCL (  4,    4)];
121+} ISO_RRIP_IDFLAG;
122+
123+typedef struct {
124+	ISO_SUSP_HEADER			h;
125+	char len_id			[ISODCL (  4,    4)];
126+	char len_des			[ISODCL (  5,	 5)];
127+	char len_src			[ISODCL (  6,	 6)];
128+	char version			[ISODCL (  7,	 7)];
129+} ISO_RRIP_EXTREF;
130+
131+typedef struct {
132+	ISO_SUSP_HEADER			h;
133+	char check			[ISODCL (  4,	 5)];
134+	char skip			[ISODCL (  6,	 6)];
135+} ISO_RRIP_OFFSET;
136+
137+typedef struct {
138+	ISO_SUSP_HEADER			h;
139+	char location			[ISODCL (  4,	11)];
140+	char offset			[ISODCL ( 12,	19)];
141+	char length			[ISODCL ( 20,	27)];
142+} ISO_RRIP_CONT;
143+
144+#endif /* _ISOFS_CD9660_CD9660_RRIP_H_ */
+247, -0
  1@@ -0,0 +1,247 @@
  2+/*	$NetBSD: iso.h,v 1.11 2026/01/08 15:39:07 nia Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1994
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to Berkeley
  9+ * by Pace Willisson (pace@blitz.com).  The Rock Ridge Extension
 10+ * Support code is derived from software contributed to Berkeley
 11+ * by Atsushi Murai (amurai@spec.co.jp).
 12+ *
 13+ * Redistribution and use in source and binary forms, with or without
 14+ * modification, are permitted provided that the following conditions
 15+ * are met:
 16+ * 1. Redistributions of source code must retain the above copyright
 17+ *    notice, this list of conditions and the following disclaimer.
 18+ * 2. Redistributions in binary form must reproduce the above copyright
 19+ *    notice, this list of conditions and the following disclaimer in the
 20+ *    documentation and/or other materials provided with the distribution.
 21+ * 3. Neither the name of the University nor the names of its contributors
 22+ *    may be used to endorse or promote products derived from this software
 23+ *    without specific prior written permission.
 24+ *
 25+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 26+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 27+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 28+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 29+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 30+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 31+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 32+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 33+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 34+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 35+ * SUCH DAMAGE.
 36+ *
 37+ *	@(#)iso.h	8.6 (Berkeley) 5/10/95
 38+ */
 39+
 40+/*
 41+ * Definitions describing ISO9660 file system structure, as well as
 42+ * the functions necessary to access fields of ISO9660 file system
 43+ * structures.
 44+ */
 45+
 46+#ifndef _ISOFS_CD9660_ISO_H_
 47+#define _ISOFS_CD9660_ISO_H_
 48+
 49+#include <sys/endian.h>
 50+
 51+#define ISODCL(from, to) (to - from + 1)
 52+
 53+struct iso_volume_descriptor {
 54+	char type[ISODCL(1,1)]; /* 711 */
 55+	char id[ISODCL(2,6)];
 56+	char version[ISODCL(7,7)];
 57+	char data[ISODCL(8,2048)];
 58+};
 59+
 60+/* volume descriptor types */
 61+#define ISO_VD_PRIMARY 1
 62+#define ISO_VD_SUPPLEMENTARY 2
 63+#define ISO_VD_END 255
 64+
 65+#define ISO_STANDARD_ID "CD001"
 66+#define ISO_ECMA_ID     "CDW01"
 67+
 68+#define	ISO_MAXNAMLEN	255
 69+
 70+struct iso_primary_descriptor {
 71+	char type			[ISODCL (  1,   1)]; /* 711 */
 72+	char id				[ISODCL (  2,   6)];
 73+	char version			[ISODCL (  7,   7)]; /* 711 */
 74+	char unused1			[ISODCL (  8,   8)];
 75+	char system_id			[ISODCL (  9,  40)]; /* achars */
 76+	char volume_id			[ISODCL ( 41,  72)]; /* dchars */
 77+	char unused2			[ISODCL ( 73,  80)];
 78+	char volume_space_size		[ISODCL ( 81,  88)]; /* 733 */
 79+	char unused3			[ISODCL ( 89, 120)];
 80+	char volume_set_size		[ISODCL (121, 124)]; /* 723 */
 81+	char volume_sequence_number	[ISODCL (125, 128)]; /* 723 */
 82+	char logical_block_size		[ISODCL (129, 132)]; /* 723 */
 83+	char path_table_size		[ISODCL (133, 140)]; /* 733 */
 84+	char type_l_path_table		[ISODCL (141, 144)]; /* 731 */
 85+	char opt_type_l_path_table	[ISODCL (145, 148)]; /* 731 */
 86+	char type_m_path_table		[ISODCL (149, 152)]; /* 732 */
 87+	char opt_type_m_path_table	[ISODCL (153, 156)]; /* 732 */
 88+	char root_directory_record	[ISODCL (157, 190)]; /* 9.1 */
 89+	char volume_set_id		[ISODCL (191, 318)]; /* dchars */
 90+	char publisher_id		[ISODCL (319, 446)]; /* achars */
 91+	char preparer_id		[ISODCL (447, 574)]; /* achars */
 92+	char application_id		[ISODCL (575, 702)]; /* achars */
 93+	char copyright_file_id		[ISODCL (703, 739)]; /* 7.5 dchars */
 94+	char abstract_file_id		[ISODCL (740, 776)]; /* 7.5 dchars */
 95+	char bibliographic_file_id	[ISODCL (777, 813)]; /* 7.5 dchars */
 96+	char creation_date		[ISODCL (814, 830)]; /* 8.4.26.1 */
 97+	char modification_date		[ISODCL (831, 847)]; /* 8.4.26.1 */
 98+	char expiration_date		[ISODCL (848, 864)]; /* 8.4.26.1 */
 99+	char effective_date		[ISODCL (865, 881)]; /* 8.4.26.1 */
100+	char file_structure_version	[ISODCL (882, 882)]; /* 711 */
101+	char unused4			[ISODCL (883, 883)];
102+	char application_data		[ISODCL (884, 1395)];
103+	char unused5			[ISODCL (1396, 2048)];
104+};
105+#define ISO_DEFAULT_BLOCK_SIZE		2048
106+
107+struct iso_supplementary_descriptor {
108+	char type			[ISODCL (  1,	1)]; /* 711 */
109+	char id				[ISODCL (  2,	6)];
110+	char version			[ISODCL (  7,	7)]; /* 711 */
111+	char flags			[ISODCL (  8,	8)]; /* 711? */
112+	char system_id			[ISODCL (  9,  40)]; /* achars */
113+	char volume_id			[ISODCL ( 41,  72)]; /* dchars */
114+	char unused2			[ISODCL ( 73,  80)];
115+	char volume_space_size		[ISODCL ( 81,  88)]; /* 733 */
116+	char escape			[ISODCL ( 89, 120)];
117+	char volume_set_size		[ISODCL (121, 124)]; /* 723 */
118+	char volume_sequence_number	[ISODCL (125, 128)]; /* 723 */
119+	char logical_block_size		[ISODCL (129, 132)]; /* 723 */
120+	char path_table_size		[ISODCL (133, 140)]; /* 733 */
121+	char type_l_path_table		[ISODCL (141, 144)]; /* 731 */
122+	char opt_type_l_path_table	[ISODCL (145, 148)]; /* 731 */
123+	char type_m_path_table		[ISODCL (149, 152)]; /* 732 */
124+	char opt_type_m_path_table	[ISODCL (153, 156)]; /* 732 */
125+	char root_directory_record	[ISODCL (157, 190)]; /* 9.1 */
126+	char volume_set_id		[ISODCL (191, 318)]; /* dchars */
127+	char publisher_id		[ISODCL (319, 446)]; /* achars */
128+	char preparer_id		[ISODCL (447, 574)]; /* achars */
129+	char application_id		[ISODCL (575, 702)]; /* achars */
130+	char copyright_file_id		[ISODCL (703, 739)]; /* 7.5 dchars */
131+	char abstract_file_id		[ISODCL (740, 776)]; /* 7.5 dchars */
132+	char bibliographic_file_id	[ISODCL (777, 813)]; /* 7.5 dchars */
133+	char creation_date		[ISODCL (814, 830)]; /* 8.4.26.1 */
134+	char modification_date		[ISODCL (831, 847)]; /* 8.4.26.1 */
135+	char expiration_date		[ISODCL (848, 864)]; /* 8.4.26.1 */
136+	char effective_date		[ISODCL (865, 881)]; /* 8.4.26.1 */
137+	char file_structure_version	[ISODCL (882, 882)]; /* 711 */
138+	char unused4			[ISODCL (883, 883)];
139+	char application_data		[ISODCL (884, 1395)];
140+	char unused5			[ISODCL (1396, 2048)];
141+};
142+
143+struct iso_directory_record {
144+	char length			[ISODCL (1, 1)]; /* 711 */
145+	char ext_attr_length		[ISODCL (2, 2)]; /* 711 */
146+	u_char extent			[ISODCL (3, 10)]; /* 733 */
147+	u_char size			[ISODCL (11, 18)]; /* 733 */
148+	char date			[ISODCL (19, 25)]; /* 7 by 711 */
149+	char flags			[ISODCL (26, 26)];
150+	char file_unit_size		[ISODCL (27, 27)]; /* 711 */
151+	char interleave			[ISODCL (28, 28)]; /* 711 */
152+	char volume_sequence_number	[ISODCL (29, 32)]; /* 723 */
153+	char name_len			[ISODCL (33, 33)]; /* 711 */
154+	char name			[1];			/* XXX */
155+};
156+/* can't take sizeof(iso_directory_record), because of possible alignment
157+   of the last entry (34 instead of 33) */
158+#define ISO_DIRECTORY_RECORD_SIZE	33
159+
160+struct iso_extended_attributes {
161+	u_char owner			[ISODCL (1, 4)]; /* 723 */
162+	u_char group			[ISODCL (5, 8)]; /* 723 */
163+	u_char perm			[ISODCL (9, 10)]; /* 9.5.3 */
164+	char ctime			[ISODCL (11, 27)]; /* 8.4.26.1 */
165+	char mtime			[ISODCL (28, 44)]; /* 8.4.26.1 */
166+	char xtime			[ISODCL (45, 61)]; /* 8.4.26.1 */
167+	char ftime			[ISODCL (62, 78)]; /* 8.4.26.1 */
168+	char recfmt			[ISODCL (79, 79)]; /* 711 */
169+	char recattr			[ISODCL (80, 80)]; /* 711 */
170+	u_char reclen			[ISODCL (81, 84)]; /* 723 */
171+	char system_id			[ISODCL (85, 116)]; /* achars */
172+	char system_use			[ISODCL (117, 180)];
173+	char version			[ISODCL (181, 181)]; /* 711 */
174+	char len_esc			[ISODCL (182, 182)]; /* 711 */
175+	char reserved			[ISODCL (183, 246)];
176+	u_char len_au			[ISODCL (247, 250)]; /* 723 */
177+};
178+
179+/* 7.1.1: unsigned char */
180+static __inline NETCOMPAT_UNUSED int
181+isonum_711(const u_char *p)
182+{
183+	return *p;
184+}
185+
186+/* 7.1.2: signed char */
187+static __inline NETCOMPAT_UNUSED int
188+isonum_712(const u_char *p)
189+{
190+	return (signed char) *p;
191+}
192+
193+/* 7.2.1: unsigned little-endian 16-bit value.  NOT USED IN KERNEL. */
194+static __inline NETCOMPAT_UNUSED uint16_t
195+isonum_721(const u_char *p)
196+{
197+	return le16dec(p);
198+}
199+
200+/* 7.2.2: unsigned big-endian 16-bit value.  NOT USED IN KERNEL. */
201+static __inline NETCOMPAT_UNUSED uint16_t
202+isonum_722(const u_char *p)
203+{
204+	return be16dec(p);
205+}
206+
207+/* 7.2.3: unsigned both-endian (little, then big) 16-bit value */
208+static __inline NETCOMPAT_UNUSED uint16_t
209+isonum_723(const u_char *p)
210+{
211+#if BYTE_ORDER == BIG_ENDIAN
212+	return be16dec(p + 2);
213+#else
214+	return le16dec(p);
215+#endif
216+}
217+
218+/* 7.3.1: unsigned little-endian 32-bit value.  NOT USED IN KERNEL. */
219+static __inline NETCOMPAT_UNUSED uint32_t
220+isonum_731(const u_char *p)
221+{
222+	return le32dec(p);
223+}
224+
225+/* 7.3.2: unsigned big-endian 32-bit value.  NOT USED IN KERNEL. */
226+static __inline NETCOMPAT_UNUSED uint32_t
227+isonum_732(const u_char *p)
228+{
229+	return be32dec(p);
230+}
231+
232+/* 7.3.3: unsigned both-endian (little, then big) 32-bit value */
233+static __inline NETCOMPAT_UNUSED uint32_t
234+isonum_733(const u_char *p)
235+{
236+#if BYTE_ORDER == BIG_ENDIAN
237+	return be32dec(p + 4);
238+#else
239+	return le32dec(p);
240+#endif
241+}
242+
243+/*
244+ * Associated files have a leading '='.
245+ */
246+#define	ASSOCCHAR	'='
247+
248+#endif /* _ISOFS_CD9660_ISO_H_ */
+85, -0
 1@@ -0,0 +1,85 @@
 2+/*	$NetBSD: iso_rrip.h,v 1.4 2005/12/03 17:34:43 christos Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 1993, 1994
 6+ *	The Regents of the University of California.  All rights reserved.
 7+ *
 8+ * This code is derived from software contributed to Berkeley
 9+ * by Pace Willisson (pace@blitz.com).  The Rock Ridge Extension
10+ * Support code is derived from software contributed to Berkeley
11+ * by Atsushi Murai (amurai@spec.co.jp).
12+ *
13+ * Redistribution and use in source and binary forms, with or without
14+ * modification, are permitted provided that the following conditions
15+ * are met:
16+ * 1. Redistributions of source code must retain the above copyright
17+ *    notice, this list of conditions and the following disclaimer.
18+ * 2. Redistributions in binary form must reproduce the above copyright
19+ *    notice, this list of conditions and the following disclaimer in the
20+ *    documentation and/or other materials provided with the distribution.
21+ * 3. Neither the name of the University nor the names of its contributors
22+ *    may be used to endorse or promote products derived from this software
23+ *    without specific prior written permission.
24+ *
25+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35+ * SUCH DAMAGE.
36+ *
37+ *	@(#)iso_rrip.h	8.2 (Berkeley) 1/23/94
38+ */
39+
40+#ifndef _ISOFS_CD9660_ISO_RRIP_H_
41+#define _ISOFS_CD9660_ISO_RRIP_H_
42+
43+/*
44+ *	Analyze function flag (similar to RR field bits)
45+ */
46+#define	ISO_SUSP_ATTR		0x0001
47+#define	ISO_SUSP_DEVICE		0x0002
48+#define	ISO_SUSP_SLINK		0x0004
49+#define	ISO_SUSP_ALTNAME	0x0008
50+#define	ISO_SUSP_CLINK		0x0010
51+#define	ISO_SUSP_PLINK		0x0020
52+#define	ISO_SUSP_RELDIR		0x0040
53+#define	ISO_SUSP_TSTAMP		0x0080
54+#define	ISO_SUSP_IDFLAG		0x0100
55+#define	ISO_SUSP_EXTREF		0x0200
56+#define	ISO_SUSP_CONT		0x0400
57+#define	ISO_SUSP_OFFSET		0x0800
58+#define	ISO_SUSP_STOP		0x1000
59+#define	ISO_SUSP_UNKNOWN	0x8000
60+
61+typedef struct {
62+	struct iso_node	*inop;
63+	int		fields;		/* interesting fields in this analysis */
64+	daddr_t		iso_ce_blk;	/* block of continuation area */
65+	off_t		iso_ce_off;	/* offset of continuation area */
66+	int		iso_ce_len;	/* length of continuation area */
67+	struct iso_mnt	*imp;		/* mount structure */
68+	ino_t		*inump;		/* inode number pointer */
69+	char		*outbuf;	/* name/symbolic link output area */
70+	u_short		*outlen;	/* length of above */
71+	u_short		maxlen;		/* maximum length of above */
72+	int		cont;		/* continuation of above */
73+} ISO_RRIP_ANALYZE;
74+
75+int cd9660_rrip_analyze(struct iso_directory_record *isodir,
76+			    struct iso_node *inop, struct iso_mnt *imp);
77+int cd9660_rrip_getname(struct iso_directory_record *isodir,
78+			    char *outbuf, u_short *outlen,
79+			    ino_t *inump, struct iso_mnt *imp);
80+int cd9660_rrip_getsymname(struct iso_directory_record *isodir,
81+			       char *outbuf, u_short *outlen,
82+			       struct iso_mnt *imp);
83+int cd9660_rrip_offset(struct iso_directory_record *isodir,
84+			   struct iso_mnt *imp);
85+
86+#endif /* _ISOFS_CD9660_ISO_RRIP_H_ */
+648, -0
  1@@ -0,0 +1,648 @@
  2+.\"	$NetBSD: makefs.8,v 1.74 2026/01/10 08:58:47 tsutsui Exp $
  3+.\"
  4+.\" Copyright (c) 2001-2003 Wasabi Systems, Inc.
  5+.\" All rights reserved.
  6+.\"
  7+.\" Written by Luke Mewburn for Wasabi Systems, Inc.
  8+.\"
  9+.\" Redistribution and use in source and binary forms, with or without
 10+.\" modification, are permitted provided that the following conditions
 11+.\" are met:
 12+.\" 1. Redistributions of source code must retain the above copyright
 13+.\"    notice, this list of conditions and the following disclaimer.
 14+.\" 2. Redistributions in binary form must reproduce the above copyright
 15+.\"    notice, this list of conditions and the following disclaimer in the
 16+.\"    documentation and/or other materials provided with the distribution.
 17+.\" 3. All advertising materials mentioning features or use of this software
 18+.\"    must display the following acknowledgement:
 19+.\"      This product includes software developed for the NetBSD Project by
 20+.\"      Wasabi Systems, Inc.
 21+.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse
 22+.\"    or promote products derived from this software without specific prior
 23+.\"    written permission.
 24+.\"
 25+.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
 26+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 27+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 28+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
 29+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 30+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 31+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 32+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 33+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 34+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 35+.\" POSSIBILITY OF SUCH DAMAGE.
 36+.\"
 37+.Dd January 10, 2026
 38+.Dt MAKEFS 8
 39+.Os
 40+.Sh NAME
 41+.Nm makefs
 42+.Nd create a file system image from a directory tree
 43+.Sh SYNOPSIS
 44+.Nm
 45+.Op Fl LrxZ
 46+.Op Fl B Ar endian
 47+.Op Fl b Ar free-blocks
 48+.Op Fl d Ar debug-mask
 49+.Op Fl F Ar mtree-specfile
 50+.Op Fl f Ar free-files
 51+.Op Fl M Ar minimum-size
 52+.Op Fl m Ar maximum-size
 53+.Op Fl N Ar userdb-dir
 54+.Op Fl O Ar offset
 55+.Op Fl o Ar fs-options
 56+.Op Fl S Ar sector-size
 57+.Op Fl s Ar image-size
 58+.Op Fl T Ar timestamp
 59+.Op Fl t Ar fs-type
 60+.Ar image-file
 61+.Ar directory
 62+.Op Ar extra-directory ...
 63+.Sh DESCRIPTION
 64+The utility
 65+.Nm
 66+creates a file system image into
 67+.Ar image-file
 68+from the directory tree
 69+.Ar directory .
 70+If any optional directory trees are passed in the
 71+.Ar extra-directory
 72+arguments, then the directory tree of each argument will be merged
 73+into the
 74+.Ar directory
 75+first before creating
 76+.Ar image-file .
 77+No special devices or privileges are required to perform this task.
 78+.Pp
 79+The options are as follows:
 80+.Bl -tag -width flag
 81+.It Fl B Ar endian
 82+Set the byte order of the image to
 83+.Ar endian .
 84+Valid byte orders are
 85+.Ql 4321 ,
 86+.Ql big ,
 87+or
 88+.Ql be
 89+for big endian, and
 90+.Ql 1234 ,
 91+.Ql little ,
 92+or
 93+.Ql le
 94+for little endian.
 95+Some file systems may have a fixed byte order; in those cases this
 96+argument will be ignored.
 97+.It Fl b Ar free-blocks
 98+Ensure that a minimum of
 99+.Ar free-blocks
100+free blocks exist in the image.
101+An optional
102+.Ql %
103+suffix may be provided to indicate that
104+.Ar free-blocks
105+indicates a percentage of the calculated image size.
106+.It Fl d Ar debug-mask | comma-separated-debug-option
107+Enable various levels of debugging, depending upon which bits are
108+set in
109+.Ar debug-mask .
110+The mask can also be set by specifying a comma separated list of debugging
111+options.
112+These are:
113+.Bl -tag -width X -offset indent -compact
114+.It Ar debug_time
115+Print the time it takes to perform each step.
116+.It Ar debug_walk_dir
117+Print each directory as it gets processed.
118+.It Ar debug_walk_dir_node
119+Print each file as it gets processed.
120+.It Ar debug_walk_dir_linkcheck
121+Print file information for files that have a link count > 1.
122+.It Ar debug_dump_fsnodes
123+Dump information about the filesystem nodes.
124+.It Ar debug_dump_fsnodes_verbose
125+Enable more detail if
126+.Dv debug_dump_fsnodes
127+is enabled.
128+.It Ar debug_fs_parse_opts
129+Print debugging information about specific filesystem option parsing.
130+.It Ar debug_fs_makefs
131+Print nodes as they are created and enable buffer consistency checks.
132+.It Ar debug_fs_validate
133+Enable file-system specific validation (ffs only).
134+.It Ar debug_fs_create_image
135+Print image file creation stats (ffs only).
136+.It Ar debug_fs_size_dir
137+Print directory size information (ffs only).
138+.It Ar debug_fs_size_dir_node
139+Print directory size information per node (ffs only).
140+.It Ar debug_fs_size_dir_add_dirent
141+Print directory size information as entries are added (ffs only).
142+.It Ar debug_fs_populate
143+Print information at each directory population pass (ffs only).
144+.It Ar debug_fs_populate_dirbuf
145+Dump the directory buffer (ffs only).
146+.It Ar debug_fs_populate_node
147+Print information about each file during directory population (ffs only).
148+.It Ar debug_fs_write_file
149+Print buffer informaion when writing files (ffs only).
150+.It Ar debug_fs_write_file_block
151+Print block information when writing files (ffs only).
152+.It Ar debug_fs_make_dirbuf
153+Print directory buffer information (ffs only).
154+.It Ar debug_fs_write_inode
155+Print inode information (ffs only).
156+.It Ar debug_buf_bread
157+Print block buffer information (ffs only).
158+.It Ar debug_buf_bwrite
159+Print block write information (ffs only).
160+.It Ar debug_buf_getblk
161+Print block allocaion information (ffs only).
162+.It Ar debug_apply_specfile
163+Print information about each directory in the specfile.
164+.It Ar debug_apply_specentry
165+Print information about each entry in the specfile.
166+.It Ar debug_apply_speconly
167+Debug the
168+.Fl x
169+special file exclusion.
170+.El
171+.It Fl F Ar mtree-specfile
172+Use
173+.Ar mtree-specfile
174+as an
175+.Xr mtree 8
176+.Sq specfile
177+specification.
178+.Pp
179+If a specfile entry exists in the underlying file system, its
180+permissions and modification time will be used unless specifically
181+overridden by the specfile.
182+An error will be raised if the type of entry in the specfile
183+conflicts with that of an existing entry.
184+.Pp
185+In the opposite case (where a specfile entry does not have an entry
186+in the underlying file system) the following occurs:
187+If the specfile entry is marked
188+.Sy optional ,
189+the specfile entry is ignored.
190+Otherwise, the entry will be created in the image, and it is
191+necessary to specify at least the following parameters in the
192+specfile:
193+.Sy type ,
194+.Sy mode ,
195+.Sy gname ,
196+or
197+.Sy gid ,
198+and
199+.Sy uname
200+or
201+.Sy uid ,
202+.Sy device
203+(in the case of block or character devices), and
204+.Sy link
205+(in the case of symbolic links).
206+If
207+.Sy time
208+isn't provided, the current time will be used.
209+If
210+.Sy flags
211+isn't provided, the current file flags will be used.
212+Missing regular file entries will be created as zero-length files.
213+.It Fl f Ar free-files
214+Ensure that a minimum of
215+.Ar free-files
216+free files (inodes) exist in the image.
217+An optional
218+.Ql %
219+suffix may be provided to indicate that
220+.Ar free-files
221+indicates a percentage of the calculated image size.
222+.It Fl L
223+All symbolic links are followed.
224+.It Fl M Ar minimum-size
225+Set the minimum size of the file system image to
226+.Ar minimum-size .
227+.It Fl m Ar maximum-size
228+Set the maximum size of the file system image to
229+.Ar maximum-size .
230+An error will be raised if the target file system needs to be larger
231+than this to accommodate the provided directory tree.
232+.It Fl N Ar userdb-dir
233+Use the user database text file
234+.Pa master.passwd
235+and group database text file
236+.Pa group
237+from
238+.Ar userdb-dir ,
239+rather than using the results from the system's
240+.Xr getpwnam 3
241+and
242+.Xr getgrnam 3
243+(and related) library calls.
244+.It Fl O Ar offset
245+Instead of creating the file system at the beginning of the file, start
246+at offset.
247+Valid only for
248+.Sy ffs
249+and
250+.Sy msdos .
251+.It Fl o Ar fs-options
252+Set file system specific options.
253+.Ar fs-options
254+is a comma separated list of options.
255+Valid file system specific options are detailed below.
256+.It Fl r
257+When merging multiple directories replace duplicate files with the last found.
258+.It Fl S Ar sector-size
259+Set the file system sector size to
260+.Ar sector-size .
261+Defaults to 512 for most file systems, but is 2048 for
262+.Sy cd9660
263+and
264+.Sy udf
265+for CD/DVD/BD optical media types.
266+.It Fl s Ar image-size
267+Set the size of the file system image to
268+.Ar image-size .
269+This is equivalent of setting both the minimum
270+.Fl ( M )
271+and the maximum
272+.Fl ( m )
273+sizes to
274+.Ar image-size .
275+For
276+.Sy ffs
277+and
278+.Sy msdos
279+the
280+.Ar offset
281+is not included on that size.
282+.It Fl T Ar timestamp
283+Specify a timestamp to be set for all file system files and directories
284+created so that repeatable builds are possible.
285+The
286+.Ar timestamp
287+can be a
288+.Pa pathname ,
289+where the timestamps are derived from that file, a parseable date
290+for
291+.Xr parsedate 3
292+(this option is not yet available in the tools build), or an integer
293+value interpreted as the number of seconds from the Epoch.
294+Note that timestamps specified in an
295+.Xr mtree 5
296+spec file, override the default timestamp.
297+When this option is enabled, file systems that regularly use
298+.Xr localtime 3
299+to convert times to the native format (such as udf and cd9660), use
300+.Xr gmtime 3
301+instead with the specified timestamps so that they are immune to
302+timezone changes and get consistent timestamps.
303+.It Fl t Ar fs-type
304+Create an
305+.Ar fs-type
306+file system image.
307+The following file system types are supported:
308+.Bl -tag -width cd9660 -offset indent
309+.It Sy cd9660
310+ISO 9660 file system.
311+.It Sy chfs
312+Chip flash file system.
313+.It Sy ffs
314+BSD fast file system (default).
315+.It Sy msdos
316+FAT12, FAT16, or FAT32 file system.
317+.It Sy udf
318+ISO/Ecma UDF file system.
319+.It Sy v7fs
320+7th Edition(V7) file system.
321+.El
322+.It Fl x
323+Exclude file system nodes not explicitly listed in the specfile.
324+Repeating this flag causes
325+.Nm
326+to print a warning for each missing system nodes and exit with an error code
327+if there are any missing.
328+.It Fl Z
329+Create a sparse file for
330+.Sy ffs .
331+This is useful for virtual machine images.
332+.El
333+.Pp
334+Where sizes are specified, a decimal number of bytes is expected.
335+Two or more numbers may be separated by an
336+.Sq x
337+to indicate a product.
338+Each number may have one of the following optional suffixes:
339+.Bl -tag -width 3n -offset indent -compact
340+.It b
341+Block; multiply by 512
342+.It k
343+Kibi; multiply by 1024 (1 KiB)
344+.It m
345+Mebi; multiply by 1048576 (1 MiB)
346+.It g
347+Gibi; multiply by 1073741824 (1 GiB)
348+.It t
349+Tebi; multiply by 1099511627776 (1 TiB)
350+.It w
351+Word; multiply by the number of bytes in an integer
352+.El
353+.\"
354+.\"
355+.Ss FFS-specific options
356+.Sy ffs
357+images have ffs-specific optional parameters that may be provided.
358+Each of the options consists of a keyword, an equal sign
359+.Pq Ql = ,
360+and a value.
361+The following keywords are supported:
362+.Pp
363+.Bl -tag -width optimization -offset indent -compact
364+.It Sy avgfilesize
365+Expected average file size.
366+.It Sy avgfpdir
367+Expected number of files per directory.
368+.It Sy bsize
369+Block size.
370+.It Sy density
371+Bytes per inode.
372+.It Sy extattr
373+UFS2 with extended attributes.
374+.It Sy extent
375+Maximum extent size.
376+.It Sy fsize
377+Fragment size.
378+.It Sy label
379+Label name of the image.
380+.It Sy maxbpcg
381+Maximum total number of blocks in a cylinder group.
382+.It Sy maxbpg
383+Maximum blocks per file in a cylinder group.
384+.It Sy minfree
385+Minimum % free.
386+.It Sy optimization
387+Optimization preference; one of
388+.Ql space
389+or
390+.Ql time .
391+.It Sy version
392+File system format version, compatible with the
393+.Fl O
394+option of
395+.Xr newfs 8 .
396+0 for FFSv1 with the old level 1 format.
397+1 for FFSv1 (default, level 4), and
398+2 for FFSv2 (UFS2).
399+.El
400+.Ss CD9660-specific options
401+.Sy cd9660
402+images have ISO9660-specific optional parameters that may be
403+provided.
404+The arguments consist of a keyword and, optionally, an equal sign
405+.Pq Ql = ,
406+and a value.
407+The following keywords are supported:
408+.Pp
409+.Bl -tag -width omit-trailing-period -offset indent -compact
410+.It Sy allow-deep-trees
411+Allow the directory structure to exceed the maximum specified in
412+the spec.
413+.\" .It Sy allow-illegal-chars
414+.\" Unknown
415+.\" .It Sy allow-lowercase
416+.\" Unknown
417+.It Sy allow-max-name
418+Allow 37 instead of 33 characters for filenames by omitting the
419+version ID.
420+.It Sy allow-multidot
421+Allow multiple dots in a filename.
422+.It Sy applicationid
423+Application ID of the image.
424+.It Sy archimedes
425+Use the
426+.Ql ARCHIMEDES
427+extension to encode
428+.Tn RISC OS
429+metadata.
430+.It Sy boot-load-segment
431+Set load segment for the boot image.
432+.It Sy bootimage
433+Filename of a boot image in the format
434+.Dq sysid;filename ,
435+where
436+.Dq sysid
437+is one of
438+.Ql efi ,
439+.Ql i386 ,
440+.Ql mac68k ,
441+.Ql macppc ,
442+or
443+.Ql powerpc .
444+.It Sy chrp-boot
445+Write an MBR partition table to the image to allow older CHRP hardware to
446+boot.
447+.It Sy generic-bootimage
448+Load a generic boot image into the first 32K of the cd9660 image.
449+.It Sy hard-disk-boot
450+Boot image is a hard disk image.
451+.It Sy keep-bad-images
452+Don't throw away images whose write was aborted due to an error.
453+For debugging purposes.
454+.It Sy label
455+Label name of the image.
456+.It Sy no-boot
457+Boot image is not bootable.
458+.It Sy no-emul-boot
459+Boot image is a
460+.Dq no emulation
461+ElTorito image.
462+.It Sy no-trailing-padding
463+Do not pad the image (apparently Linux needs the padding).
464+.\" .It Sy omit-trailing-period
465+.\" Unknown
466+.It Sy platformid
467+Set platform ID of section header entry of the boot image.
468+.It Sy preparer
469+Preparer ID of the image.
470+.It Sy publisher
471+Publisher ID of the image.
472+.It Sy rockridge
473+Use RockRidge extensions (for longer filenames, etc.).
474+.It Sy volumeid
475+Volume set identifier of the image.
476+.El
477+.Ss CHFS-specific options
478+.Sy chfs
479+images have chfs-specific optional parameters that may be provided.
480+Each of the options consists of a keyword, an equal sign
481+.Pq Ql = ,
482+and a value.
483+The following keywords are supported:
484+.Pp
485+.Bl -tag -width optimization -offset indent -compact
486+.It Sy pagesize
487+Pagesize.
488+.It Sy erasesize
489+Erase block size of the media.
490+.It Sy mediatype
491+Type of the media.
492+NOR: 0 or NAND: 1.
493+.El
494+.Ss msdos-specific options
495+.Sy msdos
496+images have MS-DOS-specific optional parameters that may be
497+provided.
498+The arguments consist of a keyword, an equal sign
499+.Pq Ql = ,
500+and a value.
501+The following keywords are supported (see
502+.Xr newfs_msdos 8
503+for more details):
504+.Pp
505+.Bl -tag -width omit-trailing-period -offset indent -compact
506+.It Cm backup_sector
507+Location of the backup boot sector.
508+.It Cm block_size
509+Block size.
510+.It Cm bootstrap
511+Bootstrap file.
512+.It Cm bytes_per_sector
513+Bytes per sector.
514+.It Cm create_size
515+Create file size.
516+.It Cm directory_entries
517+Directory entries.
518+.It Cm drive_heads
519+Drive heads.
520+.It Cm fat_type
521+FAT type (12, 16, or 32).
522+.It Cm floppy
523+Preset drive parameters for standard format floppy disks
524+(160, 180, 320, 360, 640, 720, 1200, 1232, 1440, or 2880).
525+.It Cm hidden_sectors
526+Hidden sectors.
527+.It Cm info_sector
528+Location of the info sector.
529+.It Cm media_descriptor
530+Media descriptor.
531+.It Cm num_FAT
532+Number of FATs.
533+.It Cm OEM_string
534+OEM string.
535+.It Cm offset
536+Offset in device.
537+.It Cm reserved_sectors
538+Reserved sectors.
539+.It Cm sectors_per_cluster
540+Sectors per cluster.
541+.It Cm sectors_per_fat
542+Sectors per FAT.
543+.It Cm sectors_per_track
544+Sectors per track.
545+.It Cm size
546+File System size.
547+.It Cm volume_id
548+Volume ID.
549+.It Cm volume_label
550+Volume Label.
551+.El
552+.Ss V7FS-specific options
553+The following keywords are supported:
554+.Pp
555+.Bl -tag -width optimization -offset indent -compact
556+.It Sy pdp
557+PDP endian.
558+.It Sy progress
559+Display a progress meter for the file system construction and file
560+population.
561+.El
562+.Ss UDF-specific options
563+.Nm
564+supports besides writing to image files also direct formatting of disc
565+partitions and optical media.
566+Optical media will auto configure settings.
567+The following udf-specific optional parameters may be provided.
568+Each of the options consists of a keyword, an equal sign
569+.Pq Ql = ,
570+and a value.
571+The following keywords are supported:
572+.Pp
573+.Bl -tag -width optimization -offset indent -compact
574+.It Sy disctype
575+This can have the following values:
576+.Bl -tag -width cdromXdvdromXbdromXXX -compact
577+.It Sy cdrom , Sy dvdrom , Sy bdrom
578+create a read-only fs
579+.It Sy dvdram , Sy bdre , Sy disk
580+create a rewritable fs without sparing for defective sectors
581+.It Sy cdr , Sy dvdr , Sy bdr
582+create a rewritable fs on once recordable media using a VAT
583+.It Sy cdrw , Sy dvdrw
584+create a rewritable fs with sparing for defective sectors
585+.El
586+The sectorsize is set for the selected media and the default maximum disc size
587+is assumed unless overridden.
588+For CD-ROM, DVD-ROM and BD-ROM images, the disc
589+size is the minimum size needed.
590+Note that the size estimator can
591+under-estimate in some cases; specify extra free blocks if encountering this.
592+.It Sy loglabel
593+Set the logical volume label of the disc to the specified argument.
594+.It Sy discid
595+Set the physical volume label of the disc to the specified argument.
596+Prepend the physical volume label with a volumeset label separated
597+with a ':' if wanted.
598+For strict conformance and interchange, don't set the volumeset label
599+manually unless it has an unique hex number in the first 8 character
600+positions.
601+.It Sy minver
602+Set the minimum UDF version to be used.
603+Choose UDF version numbers from 0x102, 0x150, 0x200, 0x201, and 0x250.
604+Version 0x260 is currently not supported
605+in
606+.Nm .
607+.It Sy maxver
608+Set the maximum UDF version to be used.
609+Choose UDF version numbers from 0x102, 0x150, 0x200, 0x201, and 0x250.
610+Version 0x260 is currently not supported
611+in
612+.Nm .
613+.It Sy metaperc
614+Set the minimum amount of free metadata space.
615+This is only applicable on UDF 0x250 on rewritable media.
616+.It Sy checksurface
617+Check the surface of non error-free rewritable media for remapping.
618+Note this is a destructive test and can take quite a while!
619+.It Sy forceformat
620+Force formatting on non-empty recordable media.
621+.El
622+.Sh SEE ALSO
623+.Xr strsuftoll 3 ,
624+.Xr installboot 8 ,
625+.Xr mtree 8 ,
626+.Xr newfs 8
627+.Sh HISTORY
628+The
629+.Nm
630+utility appeared in
631+.Nx 1.6 .
632+.Sh AUTHORS
633+.An Luke Mewburn
634+.Aq lukem@NetBSD.org
635+(original program),
636+.An Daniel Watt ,
637+.An Walter Deignan ,
638+.An Ryan Gabrys ,
639+.An Alan Perez-Rathke ,
640+.An Ram Vedam
641+(cd9660 support),
642+.An UCHIYAMA Yasushi
643+(v7fs support),
644+.An Tamas Toth
645+(chfs support),
646+.An Christos Zoulas
647+(msdos support),
648+.An Reinoud Zandijk
649+(udf support).
+561, -0
  1@@ -0,0 +1,561 @@
  2+/*	$NetBSD: makefs.c,v 1.59 2024/10/27 18:35:52 christos Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2001-2003 Wasabi Systems, Inc.
  6+ * All rights reserved.
  7+ *
  8+ * Written by Luke Mewburn for Wasabi Systems, Inc.
  9+ *
 10+ * Redistribution and use in source and binary forms, with or without
 11+ * modification, are permitted provided that the following conditions
 12+ * are met:
 13+ * 1. Redistributions of source code must retain the above copyright
 14+ *    notice, this list of conditions and the following disclaimer.
 15+ * 2. Redistributions in binary form must reproduce the above copyright
 16+ *    notice, this list of conditions and the following disclaimer in the
 17+ *    documentation and/or other materials provided with the distribution.
 18+ * 3. All advertising materials mentioning features or use of this software
 19+ *    must display the following acknowledgement:
 20+ *      This product includes software developed for the NetBSD Project by
 21+ *      Wasabi Systems, Inc.
 22+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 23+ *    or promote products derived from this software without specific prior
 24+ *    written permission.
 25+ *
 26+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
 27+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 28+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 29+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
 30+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 31+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 32+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 33+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 34+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 35+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 36+ * POSSIBILITY OF SUCH DAMAGE.
 37+ */
 38+
 39+#if HAVE_NBTOOL_CONFIG_H
 40+#include "nbtool_config.h"
 41+#endif
 42+
 43+#include <sys/cdefs.h>
 44+#if defined(__RCSID) && !defined(__lint)
 45+__RCSID("$NetBSD: makefs.c,v 1.59 2024/10/27 18:35:52 christos Exp $");
 46+#endif	/* !__lint */
 47+
 48+#include <assert.h>
 49+#include <ctype.h>
 50+#include <errno.h>
 51+#include <limits.h>
 52+#include <stdio.h>
 53+#include <stdlib.h>
 54+#include <string.h>
 55+#include <unistd.h>
 56+#include <stdbool.h>
 57+#include <util.h>
 58+
 59+#include "makefs.h"
 60+#include "mtree.h"
 61+#include "cd9660.h"
 62+
 63+/*
 64+ * list of supported file systems and dispatch functions
 65+ */
 66+typedef struct {
 67+	const char	*type;
 68+	void		(*prepare_options)(fsinfo_t *);
 69+	int		(*parse_options)(const char *, fsinfo_t *);
 70+	void		(*cleanup_options)(fsinfo_t *);
 71+	void		(*make_fs)(const char *, const char *, fsnode *,
 72+				fsinfo_t *);
 73+} fstype_t;
 74+
 75+static fstype_t fstypes[] = {
 76+#define ENTRY(name) { \
 77+	# name, name ## _prep_opts, name ## _parse_opts, \
 78+	name ## _cleanup_opts, name ## _makefs  \
 79+}
 80+	ENTRY(cd9660),
 81+	{ .type = NULL	},
 82+};
 83+
 84+u_int		debug;
 85+struct timespec	start_time;
 86+struct stat stampst;
 87+
 88+static fstype_t *get_fstype(const char *);
 89+static int get_tstamp(const char *, struct stat *);
 90+static void usage(fstype_t *, fsinfo_t *) __dead;
 91+static u_int parse_debug(char *);
 92+
 93+int
 94+main(int argc, char *argv[])
 95+{
 96+	struct timeval	 start;
 97+	fstype_t	*fstype;
 98+	fsinfo_t	 fsoptions;
 99+	fsnode		*root;
100+	int	 	 ch, i;
101+	size_t		 len;
102+	char		*specfile;
103+
104+	setprogname(argv[0]);
105+
106+	debug = 0;
107+	if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL)
108+		errx(EXIT_FAILURE,
109+		    "Unknown default fs type `%s'.", DEFAULT_FSTYPE);
110+
111+		/* set default fsoptions */
112+	(void)memset(&fsoptions, 0, sizeof(fsoptions));
113+	fsoptions.fd = -1;
114+	fsoptions.sectorsize = -1;
115+
116+	if (fstype->prepare_options)
117+		fstype->prepare_options(&fsoptions);
118+
119+	specfile = NULL;
120+#ifdef CLOCK_REALTIME
121+	ch = clock_gettime(CLOCK_REALTIME, &start_time);
122+#else
123+	ch = gettimeofday(&start, NULL);
124+	start_time.tv_sec = start.tv_sec;
125+	start_time.tv_nsec = start.tv_usec * 1000;
126+#endif
127+	if (ch == -1)
128+		err(EXIT_FAILURE, "Unable to get system time");
129+
130+
131+	while ((ch = getopt(argc, argv, "B:b:d:f:F:LM:m:N:O:o:rs:S:t:T:xZ")) != -1) {
132+		switch (ch) {
133+
134+		case 'B':
135+			if (strcmp(optarg, "be") == 0 ||
136+			    strcmp(optarg, "4321") == 0 ||
137+			    strcmp(optarg, "big") == 0) {
138+#if BYTE_ORDER == LITTLE_ENDIAN
139+				fsoptions.needswap = 1;
140+#endif
141+			} else if (strcmp(optarg, "le") == 0 ||
142+			    strcmp(optarg, "1234") == 0 ||
143+			    strcmp(optarg, "little") == 0) {
144+#if BYTE_ORDER == BIG_ENDIAN
145+				fsoptions.needswap = 1;
146+#endif
147+			} else {
148+				warnx("Invalid endian `%s'.", optarg);
149+				usage(fstype, &fsoptions);
150+			}
151+			break;
152+
153+		case 'b':
154+			len = strlen(optarg) - 1;
155+			if (optarg[len] == '%') {
156+				optarg[len] = '\0';
157+				fsoptions.freeblockpc = (int)
158+				    strsuftoll("free block percentage",
159+					optarg, 0, 99);
160+			} else {
161+				fsoptions.freeblocks =
162+				    strsuftoll("free blocks",
163+					optarg, 0, LLONG_MAX);
164+			}
165+			break;
166+
167+		case 'd':
168+			debug = parse_debug(optarg);
169+			break;
170+
171+		case 'f':
172+			len = strlen(optarg) - 1;
173+			if (optarg[len] == '%') {
174+				optarg[len] = '\0';
175+				fsoptions.freefilepc = (int)
176+				    strsuftoll("free file percentage",
177+					optarg, 0, 99);
178+			} else {
179+				fsoptions.freefiles =
180+				    strsuftoll("free files",
181+					optarg, 0, LLONG_MAX);
182+			}
183+			break;
184+
185+		case 'F':
186+			specfile = optarg;
187+			break;
188+
189+		case 'L':
190+			fsoptions.follow = true;
191+			break;
192+
193+		case 'M':
194+			fsoptions.minsize =
195+			    strsuftoll("minimum size", optarg, 1LL, LLONG_MAX);
196+			break;
197+
198+		case 'N':
199+			if (! setup_getid(optarg))
200+				errx(EXIT_FAILURE,
201+			    "Unable to use user and group databases in `%s'",
202+				    optarg);
203+			break;
204+
205+		case 'm':
206+			fsoptions.maxsize =
207+			    strsuftoll("maximum size", optarg, 1LL, LLONG_MAX);
208+			break;
209+
210+		case 'O':
211+			fsoptions.offset =
212+			    strsuftoll("offset", optarg, 0LL, LLONG_MAX);
213+			break;
214+
215+		case 'o':
216+		{
217+			char *p;
218+
219+			while ((p = strsep(&optarg, ",")) != NULL) {
220+				if (*p == '\0')
221+					errx(EXIT_FAILURE, "Empty option");
222+				if (! fstype->parse_options(p, &fsoptions))
223+					usage(fstype, &fsoptions);
224+			}
225+			break;
226+		}
227+
228+		case 'r':
229+			fsoptions.replace = 1;
230+			break;
231+
232+		case 's':
233+			fsoptions.minsize = fsoptions.maxsize =
234+			    strsuftoll("size", optarg, 1LL, LLONG_MAX);
235+			break;
236+
237+		case 'S':
238+			fsoptions.sectorsize =
239+			    (int)strsuftoll("sector size", optarg,
240+				1LL, INT_MAX);
241+			break;
242+
243+		case 't':
244+			/* Check current one and cleanup if necessary. */
245+			if (fstype->cleanup_options)
246+				fstype->cleanup_options(&fsoptions);
247+			fsoptions.fs_specific = NULL;
248+			if ((fstype = get_fstype(optarg)) == NULL)
249+				errx(EXIT_FAILURE,
250+				    "Unknown fs type `%s'.", optarg);
251+			fstype->prepare_options(&fsoptions);
252+			break;
253+
254+		case 'T':
255+			if (get_tstamp(optarg, &stampst) == -1)
256+				errx(EXIT_FAILURE,
257+				    "Cannot get timestamp from `%s'", optarg);
258+			break;
259+
260+		case 'x':
261+			fsoptions.onlyspec++;
262+			break;
263+
264+		case 'Z':
265+			fsoptions.sparse = 1;
266+			break;
267+
268+		case '?':
269+		default:
270+			usage(fstype, &fsoptions);
271+			/* NOTREACHED */
272+
273+		}
274+	}
275+	if (debug) {
276+		printf("debug mask: 0x%08x\n", debug);
277+		printf("start time: %ld.%ld, %s",
278+		    (long)start_time.tv_sec, (long)start_time.tv_nsec,
279+		    ctime(&start_time.tv_sec));
280+	}
281+	argc -= optind;
282+	argv += optind;
283+
284+	if (argc < 2)
285+		usage(fstype, &fsoptions);
286+
287+	/* -x must be accompanied by -F */
288+	if (fsoptions.onlyspec != 0 && specfile == NULL)
289+		errx(EXIT_FAILURE, "-x requires -F mtree-specfile.");
290+
291+				/* walk the tree */
292+	TIMER_START(start);
293+	root = walk_dir(argv[1], ".", NULL, NULL, fsoptions.replace,
294+	    fsoptions.follow);
295+	TIMER_RESULTS(start, "walk_dir");
296+
297+	/* append extra directory */
298+	for (i = 2; i < argc; i++) {
299+		struct stat sb;
300+		if (stat(argv[i], &sb) == -1)
301+			err(EXIT_FAILURE, "Can't stat `%s'", argv[i]);
302+		if (!S_ISDIR(sb.st_mode))
303+			errx(EXIT_FAILURE, "%s: not a directory", argv[i]);
304+		TIMER_START(start);
305+		root = walk_dir(argv[i], ".", NULL, root, fsoptions.replace,
306+		    fsoptions.follow);
307+		TIMER_RESULTS(start, "walk_dir2");
308+	}
309+
310+	if (specfile) {		/* apply a specfile */
311+		TIMER_START(start);
312+		apply_specfile(specfile, argv[1], root, fsoptions.onlyspec);
313+		TIMER_RESULTS(start, "apply_specfile");
314+	}
315+
316+	if (debug & DEBUG_DUMP_FSNODES) {
317+		printf("\nparent: %s\n", argv[1]);
318+		dump_fsnodes(root);
319+		putchar('\n');
320+	}
321+
322+				/* build the file system */
323+	TIMER_START(start);
324+	fstype->make_fs(argv[0], argv[1], root, &fsoptions);
325+	TIMER_RESULTS(start, "make_fs");
326+
327+	free_fsnodes(root);
328+
329+	exit(EXIT_SUCCESS);
330+	/* NOTREACHED */
331+}
332+
333+int
334+set_option(const option_t *options, const char *option, char *buf, size_t len)
335+{
336+	char *var, *val;
337+	int retval;
338+
339+	assert(option != NULL);
340+
341+	var = estrdup(option);
342+	for (val = var; *val; val++)
343+		if (*val == '=') {
344+			*val++ = '\0';
345+			break;
346+		}
347+	retval = set_option_var(options, var, val, buf, len);
348+	free(var);
349+	return retval;
350+}
351+
352+void
353+print_options(FILE *fp, const option_t *options)
354+{
355+	for (size_t i = 0; options[i].name != NULL; i++) {
356+		fprintf(fp, "%s=", options[i].name);
357+		switch (options[i].type) {
358+		case OPT_BOOL:
359+			fputs(*(bool *)options[i].value ? "true\n" : "false\n",
360+			    fp); 
361+			break;
362+		case OPT_STRARRAY:
363+		case OPT_STRPTR:
364+		case OPT_STRBUF:
365+			fprintf(fp, "%s\n", *(const char **)options[i].value);
366+			break;
367+		case OPT_INT64:
368+			fprintf(fp, "%" PRIu64 "\n",
369+			    *(uint64_t *)options[i].value);
370+			break;
371+		case OPT_INT32:
372+			fprintf(fp, "%" PRIu32 "\n",
373+			    *(uint32_t *)options[i].value);
374+			break;
375+		case OPT_INT16:
376+			fprintf(fp, "%" PRIu16 "\n",
377+			    *(uint16_t *)options[i].value);
378+			break;
379+		case OPT_INT8:
380+			fprintf(fp, "%" PRIu8 "\n",
381+			    *(uint8_t *)options[i].value);
382+			break;
383+		default:
384+			warnx("Unknown type %d in option %s", options[i].type,
385+			    options[i].name);
386+			return;
387+		}
388+	}
389+}
390+
391+int
392+set_option_var(const option_t *options, const char *var, const char *val,
393+    char *buf, size_t len)
394+{
395+	char *s;
396+	size_t i;
397+
398+#define NUM(type) \
399+	if (!*val) { \
400+		*(type *)options[i].value = 1; \
401+		break; \
402+	} \
403+	*(type *)options[i].value = (type)strsuftoll(options[i].desc, val, \
404+	    options[i].minimum, options[i].maximum); break
405+
406+	for (i = 0; options[i].name != NULL; i++) {
407+		if (var[1] == '\0') {
408+			if (options[i].letter != var[0])
409+				continue;
410+		} else if (strcmp(options[i].name, var) != 0)
411+			continue;
412+		switch (options[i].type) {
413+		case OPT_BOOL:
414+			*(bool *)options[i].value = 1;
415+			break;
416+		case OPT_STRARRAY:
417+			strlcpy((void *)options[i].value, val, (size_t)
418+			    options[i].maximum);
419+			break;
420+		case OPT_STRPTR:
421+			s = estrdup(val);
422+			*(char **)options[i].value = s;
423+			break;
424+		case OPT_STRBUF:
425+			if (buf == NULL)
426+				abort();
427+			strlcpy(buf, val, len);
428+			break;
429+		case OPT_INT64:
430+			NUM(uint64_t);
431+		case OPT_INT32:
432+			NUM(uint32_t);
433+		case OPT_INT16:
434+			NUM(uint16_t);
435+		case OPT_INT8:
436+			NUM(uint8_t);
437+		default:
438+			warnx("Unknown type %d in option %s", options[i].type,
439+			    val);
440+			return 0;
441+		}
442+		return (int)i;
443+	}
444+	warnx("Unknown option `%s'", var);
445+	return -1;
446+}
447+
448+
449+static fstype_t *
450+get_fstype(const char *type)
451+{
452+	int i;
453+
454+	for (i = 0; fstypes[i].type != NULL; i++)
455+		if (strcmp(fstypes[i].type, type) == 0)
456+			return (&fstypes[i]);
457+	return (NULL);
458+}
459+
460+option_t *
461+copy_opts(const option_t *o)
462+{
463+	size_t i;
464+	for (i = 0; o[i].name; i++)
465+		continue;
466+	i++;
467+	return memcpy(ecalloc(i, sizeof(*o)), o, i * sizeof(*o));
468+}
469+
470+static int
471+get_tstamp(const char *b, struct stat *st)
472+{
473+	time_t when;
474+	char *eb;
475+	long long l;
476+
477+	if (stat(b, st) != -1)
478+		return 0;
479+
480+#ifndef HAVE_NBTOOL_CONFIG_H
481+	errno = 0;
482+	if ((when = parsedate(b, NULL, NULL)) == -1 && errno != 0)
483+#endif
484+	{
485+		errno = 0;
486+		l = strtoll(b, &eb, 0);
487+		if (b == eb || *eb || errno)
488+			return -1;
489+		when = (time_t)l;
490+	}
491+
492+	st->st_ino = 1;
493+#if HAVE_STRUCT_STAT_BIRTHTIME
494+	st->st_birthtime =
495+#endif
496+	st->st_mtime = st->st_ctime = st->st_atime = when;
497+	return 0;
498+}
499+
500+static struct {
501+	const char *n;
502+	u_int v;
503+} nv[] = {
504+	DEBUG_STRINGS
505+};
506+
507+static void
508+usage(fstype_t *fstype, fsinfo_t *fsoptions)
509+{
510+	const char *prog;
511+
512+	prog = getprogname();
513+	fprintf(stderr,
514+"Usage: %s [-rxZ] [-B endian] [-b free-blocks] [-d debug-mask|comma-separated-option]\n"
515+"\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n"
516+"\t[-N userdb-dir] [-O offset] [-o fs-options] [-S sector-size]\n"
517+"\t[-s image-size] [-T <timestamp/file>] [-t fs-type]"
518+" image-file directory [extra-directory ...]\n",
519+	    prog);
520+
521+	fprintf(stderr, "\nDebugging options:\n");
522+	for (size_t i = 0; i < __arraycount(nv); i++)
523+		fprintf(stderr, "\t0x%8.8x\t%s\n", nv[i].v, nv[i].n);
524+
525+	if (fstype) {
526+		size_t i;
527+		option_t *o = fsoptions->fs_options;
528+
529+		fprintf(stderr, "\n%s specific options:\n", fstype->type);
530+		for (i = 0; o[i].name != NULL; i++)
531+			fprintf(stderr, "\t%c%c%20.20s\t%s\n",
532+			    o[i].letter ? o[i].letter : ' ',
533+			    o[i].letter ? ',' : ' ',
534+			    o[i].name, o[i].desc);
535+	}
536+	exit(EXIT_FAILURE);
537+}
538+
539+
540+static u_int
541+parse_debug(char *str)
542+{
543+	char *ep;
544+	u_int d;
545+	size_t i;
546+
547+	errno = 0;
548+	d = (u_int)strtoul(str, &ep, 0);
549+	if (str != ep && !*ep && errno == 0)
550+		return d;
551+	d = 0;
552+	for (char *a = strtok(str, ","); a != NULL; a = strtok(NULL, ",")) {
553+		for (i = 0; i < __arraycount(nv); i++)
554+			if (strcmp(nv[i].n, a) == 0) {
555+				d |= nv[i].v;
556+				break;
557+			}
558+		if (i == __arraycount(nv))
559+			errx(EXIT_FAILURE, "Unknown debug option `%s'", a);
560+	}
561+	return d;
562+}
+303, -0
  1@@ -0,0 +1,303 @@
  2+/*	$nEtBSD: makefs.h,v 1.38 2022/04/09 10:05:35 riastradh Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2001 Wasabi Systems, Inc.
  6+ * All rights reserved.
  7+ *
  8+ * Written by Luke Mewburn for Wasabi Systems, Inc.
  9+ *
 10+ * Redistribution and use in source and binary forms, with or without
 11+ * modification, are permitted provided that the following conditions
 12+ * are met:
 13+ * 1. Redistributions of source code must retain the above copyright
 14+ *    notice, this list of conditions and the following disclaimer.
 15+ * 2. Redistributions in binary form must reproduce the above copyright
 16+ *    notice, this list of conditions and the following disclaimer in the
 17+ *    documentation and/or other materials provided with the distribution.
 18+ * 3. All advertising materials mentioning features or use of this software
 19+ *    must display the following acknowledgement:
 20+ *      This product includes software developed for the NetBSD Project by
 21+ *      Wasabi Systems, Inc.
 22+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 23+ *    or promote products derived from this software without specific prior
 24+ *    written permission.
 25+ *
 26+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
 27+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 28+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 29+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
 30+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 31+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 32+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 33+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 34+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 35+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 36+ * POSSIBILITY OF SUCH DAMAGE.
 37+ */
 38+
 39+#ifndef	_MAKEFS_H
 40+#define	_MAKEFS_H
 41+
 42+#if HAVE_NBTOOL_CONFIG_H
 43+#include "nbtool_config.h"
 44+#else
 45+#define HAVE_STRUCT_STAT_ST_FLAGS 1
 46+#define HAVE_STRUCT_STAT_ST_GEN 1
 47+#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1
 48+#define HAVE_STRUCT_STATVFS_F_IOSIZE 1
 49+#define HAVE_STRUCT_STAT_BIRTHTIME 1
 50+#define HAVE_FSTATVFS 1
 51+#endif
 52+
 53+#include <stdio.h>
 54+#include <sys/stat.h>
 55+#include <err.h>
 56+
 57+/*
 58+ * fsnode -
 59+ *	a component of the tree; contains a filename, a pointer to
 60+ *	fsinode, optional symlink name, and tree pointers
 61+ *
 62+ * fsinode -
 63+ *	equivalent to an inode, containing target file system inode number,
 64+ *	refcount (nlink), and stat buffer
 65+ *
 66+ * A tree of fsnodes looks like this:
 67+ *
 68+ *	name	"."		"bin"		"netbsd"
 69+ *	type	S_IFDIR		S_IFDIR		S_IFREG
 70+ *	next 	  >		  >		NULL
 71+ *	parent	NULL		NULL		NULL
 72+ *	child	NULL		  v
 73+ *
 74+ *	name			"."		"ls"
 75+ *	type			S_IFDIR		S_IFREG
 76+ *	next			  >		NULL
 77+ *	parent			  ^		^ (to "bin")
 78+ *	child			NULL		NULL
 79+ *
 80+ * Notes:
 81+ *	-   first always points to first entry, at current level, which
 82+ *	    must be "." when the tree has been built; during build it may
 83+ *	    not be if "." hasn't yet been found by readdir(2).
 84+ */
 85+
 86+enum fi_flags {
 87+	FI_SIZED =	1<<0,		/* inode sized */
 88+	FI_ALLOCATED =	1<<1,		/* fsinode->ino allocated */
 89+	FI_WRITTEN =	1<<2,		/* inode written */
 90+};
 91+
 92+typedef struct {
 93+	uint64_t	 ino;		/* inode number used on target fs */
 94+	uint32_t	 nlink;		/* number of links to this entry */
 95+	enum fi_flags	 flags;		/* flags used by fs specific code */
 96+	struct stat	 st;		/* stat entry */
 97+	void		*fsuse;		/* for storing FS dependent info */
 98+#if !HAVE_STRUCT_STAT_ST_FLAGS
 99+	uint32_t	 st_flags;	/* stand-in for st.st_flags */
100+#endif
101+} fsinode;
102+
103+#if HAVE_STRUCT_STAT_ST_FLAGS
104+#define	FSINODE_ST_FLAGS(inode)	(inode).st.st_flags
105+#else
106+#define	FSINODE_ST_FLAGS(inode)	(inode).st_flags
107+#endif
108+
109+typedef struct _fsnode {
110+	struct _fsnode	*parent;	/* parent (NULL if root) */
111+	struct _fsnode	*child;		/* child (if type == S_IFDIR) */
112+	struct _fsnode	*next;		/* next */
113+	struct _fsnode	*first;		/* first node of current level (".") */
114+	uint32_t	 type;		/* type of entry */
115+	fsinode		*inode;		/* actual inode data */
116+	char		*symlink;	/* symlink target */
117+	const char	*root;		/* root path */
118+	char		*path;		/* directory name */
119+	char		*name;		/* file name */
120+	int		flags;		/* misc flags */
121+} fsnode;
122+
123+#define	FSNODE_F_HASSPEC	0x01	/* fsnode has a spec entry */
124+
125+/*
126+ * option_t - contains option name, description, pointer to location to store
127+ * result, and range checks for the result. Used to simplify fs specific
128+ * option setting
129+ */
130+typedef enum {
131+	OPT_STRARRAY,
132+	OPT_STRPTR,
133+	OPT_STRBUF,
134+	OPT_BOOL,
135+	OPT_INT8,
136+	OPT_INT16,
137+	OPT_INT32,
138+	OPT_INT64
139+} opttype_t;
140+
141+typedef struct {
142+	char		letter;		/* option letter NUL for none */
143+	const char	*name;		/* option name */
144+	void		*value;		/* where to stuff the value */
145+	opttype_t	type;		/* type of entry */
146+	long long	minimum;	/* minimum for value */
147+	long long	maximum;	/* maximum for value */
148+	const char	*desc;		/* option description */
149+} option_t;
150+
151+/*
152+ * fsinfo_t - contains various settings and parameters pertaining to
153+ * the image, including current settings, global options, and fs
154+ * specific options
155+ */
156+typedef struct makefs_fsinfo {
157+		/* current settings */
158+	off_t	size;		/* total size */
159+	off_t	inodes;		/* number of inodes */
160+	uint32_t curinode;	/* current inode */
161+
162+		/* image settings */
163+	int	fd;		/* file descriptor of image */
164+	void	*superblock;	/* superblock */
165+	int	onlyspec;	/* only add entries in specfile */
166+
167+
168+		/* global options */
169+	off_t	minsize;	/* minimum size image should be */
170+	off_t	maxsize;	/* maximum size image can be */
171+	off_t	freefiles;	/* free file entries to leave */
172+	off_t	freeblocks;	/* free blocks to leave */
173+	off_t	offset;		/* offset from start of file */
174+	int	freefilepc;	/* free file % */
175+	int	freeblockpc;	/* free block % */
176+	int	needswap;	/* non-zero if byte swapping needed */
177+	int	sectorsize;	/* sector size */
178+	int	sparse;		/* sparse image, don't fill it with zeros */
179+	int	replace;	/* replace files when merging */
180+	int	follow;		/* follow symlinks */
181+
182+	void	*fs_specific;	/* File system specific additions. */
183+	option_t *fs_options;	/* File system specific options */
184+} fsinfo_t;
185+
186+
187+
188+
189+void		apply_specfile(const char *, const char *, fsnode *, int);
190+void		dump_fsnodes(fsnode *);
191+const char *	inode_type(mode_t);
192+int		set_option(const option_t *, const char *, char *, size_t);
193+void		print_options(FILE *, const option_t *);
194+int		set_option_var(const option_t *, const char *, const char *,
195+    char *, size_t);
196+fsnode *	walk_dir(const char *, const char *, fsnode *, fsnode *, int,
197+    int);
198+void		free_fsnodes(fsnode *);
199+option_t *	copy_opts(const option_t *);
200+
201+#define DECLARE_FUN(fs)							\
202+void		fs ## _prep_opts(fsinfo_t *);				\
203+int		fs ## _parse_opts(const char *, fsinfo_t *);		\
204+void		fs ## _cleanup_opts(fsinfo_t *);			\
205+void		fs ## _makefs(const char *, const char *, fsnode *, fsinfo_t *)
206+
207+DECLARE_FUN(cd9660);
208+
209+extern	u_int		debug;
210+extern	struct timespec	start_time;
211+extern	struct stat stampst;
212+
213+/*
214+ * If -x is specified, we want to exclude nodes which do not appear
215+ * in the spec file.
216+ */
217+#define	FSNODE_EXCLUDE_P(opts, fsnode)	\
218+	((opts)->onlyspec != 0 && ((fsnode)->flags & FSNODE_F_HASSPEC) == 0)
219+
220+#define	DEBUG_TIME			0x00000001
221+		/* debug bits 1..3 unused at this time */
222+#define	DEBUG_WALK_DIR			0x00000010
223+#define	DEBUG_WALK_DIR_NODE		0x00000020
224+#define	DEBUG_WALK_DIR_LINKCHECK	0x00000040
225+#define	DEBUG_DUMP_FSNODES		0x00000080
226+#define	DEBUG_DUMP_FSNODES_VERBOSE	0x00000100
227+#define	DEBUG_FS_PARSE_OPTS		0x00000200
228+#define	DEBUG_FS_MAKEFS			0x00000400
229+#define	DEBUG_FS_VALIDATE		0x00000800
230+#define	DEBUG_FS_CREATE_IMAGE		0x00001000
231+#define	DEBUG_FS_SIZE_DIR		0x00002000
232+#define	DEBUG_FS_SIZE_DIR_NODE		0x00004000
233+#define	DEBUG_FS_SIZE_DIR_ADD_DIRENT	0x00008000
234+#define	DEBUG_FS_POPULATE		0x00010000
235+#define	DEBUG_FS_POPULATE_DIRBUF	0x00020000
236+#define	DEBUG_FS_POPULATE_NODE		0x00040000
237+#define	DEBUG_FS_WRITE_FILE		0x00080000
238+#define	DEBUG_FS_WRITE_FILE_BLOCK	0x00100000
239+#define	DEBUG_FS_MAKE_DIRBUF		0x00200000
240+#define	DEBUG_FS_WRITE_INODE		0x00400000
241+#define	DEBUG_BUF_BREAD			0x00800000
242+#define	DEBUG_BUF_BWRITE		0x01000000
243+#define	DEBUG_BUF_GETBLK		0x02000000
244+#define	DEBUG_APPLY_SPECFILE		0x04000000
245+#define	DEBUG_APPLY_SPECENTRY		0x08000000
246+#define	DEBUG_APPLY_SPECONLY		0x10000000
247+
248+#define DEBUG_STRINGS \
249+	{ "time",	DEBUG_TIME }, \
250+	{ "walk_dir",	DEBUG_WALK_DIR }, \
251+	{ "walk_dir_node",	DEBUG_WALK_DIR_NODE }, \
252+	{ "walk_dir_linkcheck",	DEBUG_WALK_DIR_LINKCHECK }, \
253+	{ "dump_fsnodes",	DEBUG_DUMP_FSNODES }, \
254+	{ "dump_fsnodes_verbose",	DEBUG_DUMP_FSNODES_VERBOSE }, \
255+	{ "fs_parse_opts",	DEBUG_FS_PARSE_OPTS }, \
256+	{ "fs_makefs",	DEBUG_FS_MAKEFS }, \
257+	{ "fs_validate",	DEBUG_FS_VALIDATE }, \
258+	{ "fs_create_image",	DEBUG_FS_CREATE_IMAGE }, \
259+	{ "fs_size_dir",	DEBUG_FS_SIZE_DIR }, \
260+	{ "fs_size_dir_node",	DEBUG_FS_SIZE_DIR_NODE }, \
261+	{ "fs_size_dir_add_dirent",	DEBUG_FS_SIZE_DIR_ADD_DIRENT }, \
262+	{ "fs_populate",	DEBUG_FS_POPULATE }, \
263+	{ "fs_populate_dirbuf",	DEBUG_FS_POPULATE_DIRBUF }, \
264+	{ "fs_populate_node",	DEBUG_FS_POPULATE_NODE }, \
265+	{ "fs_write_file",	DEBUG_FS_WRITE_FILE }, \
266+	{ "fs_write_file_block",	DEBUG_FS_WRITE_FILE_BLOCK }, \
267+	{ "fs_make_dirbuf",	DEBUG_FS_MAKE_DIRBUF }, \
268+	{ "fs_write_inode",	DEBUG_FS_WRITE_INODE }, \
269+	{ "buf_bread",	DEBUG_BUF_BREAD }, \
270+	{ "buf_bwrite",	DEBUG_BUF_BWRITE }, \
271+	{ "buf_getblk",	DEBUG_BUF_GETBLK }, \
272+	{ "apply_specfile",	DEBUG_APPLY_SPECFILE }, \
273+	{ "apply_specentry",	DEBUG_APPLY_SPECENTRY }, \
274+	{ "apply_speconly",	DEBUG_APPLY_SPECONLY },
275+
276+#define	TIMER_START(x)				\
277+	if (debug & DEBUG_TIME)			\
278+		gettimeofday(&(x), NULL)
279+
280+#define	TIMER_RESULTS(x,d)				\
281+	if (debug & DEBUG_TIME) {			\
282+		struct timeval end, td;			\
283+		gettimeofday(&end, NULL);		\
284+		timersub(&end, &(x), &td);		\
285+		printf("%s took %lld.%06ld seconds\n",	\
286+		    (d), (long long)td.tv_sec,		\
287+		    (long)td.tv_usec);			\
288+	}
289+
290+
291+#ifndef	DEFAULT_FSTYPE
292+#define	DEFAULT_FSTYPE	"cd9660"
293+#endif
294+
295+
296+/*
297+ *	ffs specific settings
298+ *	---------------------
299+ */
300+
301+#define	FFS_EI		/* for opposite endian support in ffs headers */
302+
303+
304+#endif	/* _MAKEFS_H */
+97, -0
 1@@ -0,0 +1,97 @@
 2+/*	$NetBSD: extern.h,v 1.41 2024/12/05 17:17:43 christos Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 1991, 1993
 6+ *	The Regents of the University of California.  All rights reserved.
 7+ *
 8+ * Redistribution and use in source and binary forms, with or without
 9+ * modification, are permitted provided that the following conditions
10+ * are met:
11+ * 1. Redistributions of source code must retain the above copyright
12+ *    notice, this list of conditions and the following disclaimer.
13+ * 2. Redistributions in binary form must reproduce the above copyright
14+ *    notice, this list of conditions and the following disclaimer in the
15+ *    documentation and/or other materials provided with the distribution.
16+ * 3. Neither the name of the University nor the names of its contributors
17+ *    may be used to endorse or promote products derived from this software
18+ *    without specific prior written permission.
19+ *
20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30+ * SUCH DAMAGE.
31+ *
32+ *	@(#)extern.h	8.1 (Berkeley) 6/6/93
33+ */
34+
35+#include "mtree.h"
36+
37+#if HAVE_NBTOOL_CONFIG_H
38+#include "nbtool_config.h"
39+#else 
40+#define HAVE_STRUCT_STAT_ST_FLAGS 1
41+#endif
42+ 
43+#include <err.h> 
44+#include <fts.h>
45+#include <util.h>
46+#include <stdbool.h>
47+
48+#if HAVE_NETDB_H
49+/* For MAXHOSTNAMELEN on some platforms. */
50+#include <netdb.h>
51+#endif
52+
53+#if defined(__FreeBSD__) && !defined(HAVE_NBTOOL_CONFIG_H)
54+#define    FTS_CONST const
55+#else
56+#define    FTS_CONST
57+#endif
58+
59+#ifndef MAXHOSTNAMELEN
60+#define MAXHOSTNAMELEN 256
61+#endif
62+
63+enum flavor {
64+	F_MTREE,
65+	F_FREEBSD9,
66+	F_NETBSD6
67+};
68+
69+void	 addtag(slist_t *, char *);
70+int	 check_excludes(const char *, const char *);
71+int	 compare(NODE *, FTSENT *);
72+int	 crc(int, uint32_t *, uint32_t *);
73+void	 cwalk(FILE *);
74+int	dcmp(const FTSENT *FTS_CONST *, const FTSENT *FTS_CONST *);
75+void	 dump_nodes(FILE *, const char *, NODE *, int);
76+void	 init_excludes(void);
77+int	 matchtags(NODE *);
78+__dead __printflike(1,2) void	 mtree_err(const char *, ...);
79+const char *nodetype(u_int);
80+u_int	 parsekey(const char *, int *);
81+void	 parsetags(slist_t *, char *);
82+u_int	 parsetype(const char *);
83+void	 read_excludes_file(const char *);
84+const char *rlink(const char *);
85+int	 verify(FILE *);
86+void	 load_only(const char *fname);
87+bool	 find_only(const char *path);
88+
89+extern int	bflag, dflag, eflag, iflag, jflag, lflag, mflag,
90+		nflag, qflag, rflag, sflag, tflag, uflag;
91+extern int	mtree_Mflag, mtree_Sflag, mtree_Wflag;
92+extern size_t	mtree_lineno;
93+extern enum flavor	flavor;
94+extern uint32_t crc_total;
95+extern int	ftsoptions, keys;
96+extern char	fullpath[];
97+extern slist_t	includetags, excludetags;
98+
+446, -0
  1@@ -0,0 +1,446 @@
  2+/*	$NetBSD: getid.c,v 1.10 2014/10/27 21:46:45 christos Exp $	*/
  3+/*	from: NetBSD: getpwent.c,v 1.48 2000/10/03 03:22:26 enami Exp */
  4+/*	from: NetBSD: getgrent.c,v 1.41 2002/01/12 23:51:30 lukem Exp */
  5+
  6+/*
  7+ * Copyright (c) 1987, 1988, 1989, 1993, 1994, 1995
  8+ *	The Regents of the University of California.  All rights reserved.
  9+ *
 10+ * Redistribution and use in source and binary forms, with or without
 11+ * modification, are permitted provided that the following conditions
 12+ * are met:
 13+ * 1. Redistributions of source code must retain the above copyright
 14+ *    notice, this list of conditions and the following disclaimer.
 15+ * 2. Redistributions in binary form must reproduce the above copyright
 16+ *    notice, this list of conditions and the following disclaimer in the
 17+ *    documentation and/or other materials provided with the distribution.
 18+ * 3. Neither the name of the University nor the names of its contributors
 19+ *    may be used to endorse or promote products derived from this software
 20+ *    without specific prior written permission.
 21+ *
 22+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 23+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 24+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 25+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 26+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 27+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 28+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 29+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 30+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 31+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 32+ * SUCH DAMAGE.
 33+ */
 34+
 35+/*-
 36+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
 37+ * All rights reserved.
 38+ *
 39+ * This code is derived from software contributed to The NetBSD Foundation
 40+ * by Luke Mewburn of Wasabi Systems.
 41+ *
 42+ * Redistribution and use in source and binary forms, with or without
 43+ * modification, are permitted provided that the following conditions
 44+ * are met:
 45+ * 1. Redistributions of source code must retain the above copyright
 46+ *    notice, this list of conditions and the following disclaimer.
 47+ * 2. Redistributions in binary form must reproduce the above copyright
 48+ *    notice, this list of conditions and the following disclaimer in the
 49+ *    documentation and/or other materials provided with the distribution.
 50+ *
 51+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 52+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 53+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 54+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 55+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 56+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 57+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 58+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 59+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 60+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 61+ * POSSIBILITY OF SUCH DAMAGE.
 62+ */
 63+
 64+#if HAVE_NBTOOL_CONFIG_H
 65+#include "nbtool_config.h"
 66+#endif
 67+
 68+#include <sys/cdefs.h>
 69+__RCSID("$NetBSD: getid.c,v 1.10 2014/10/27 21:46:45 christos Exp $");
 70+
 71+#include <sys/param.h>
 72+
 73+#include <grp.h>
 74+#include <limits.h>
 75+#include <pwd.h>
 76+#include <stdlib.h>
 77+#include <stdio.h>
 78+#include <string.h>
 79+#include <time.h>
 80+#include <unistd.h>
 81+
 82+#include "extern.h"
 83+
 84+static	struct group *	gi_getgrnam(const char *);
 85+static	struct group *	gi_getgrgid(gid_t);
 86+static	int		gi_setgroupent(int);
 87+static	void		gi_endgrent(void);
 88+static	int		grstart(void);
 89+static	int		grscan(int, gid_t, const char *);
 90+static	int		grmatchline(int, gid_t, const char *);
 91+
 92+static	struct passwd *	gi_getpwnam(const char *);
 93+static	struct passwd *	gi_getpwuid(uid_t);
 94+static	int		gi_setpassent(int);
 95+static	void		gi_endpwent(void);
 96+static	int		pwstart(void);
 97+static	int		pwscan(int, uid_t, const char *);
 98+static	int		pwmatchline(int, uid_t, const char *);
 99+
100+#define	MAXGRP		200
101+#define	MAXLINELENGTH	1024
102+
103+static	FILE		*_gr_fp;
104+static	struct group	_gr_group;
105+static	int		_gr_stayopen;
106+static	int		_gr_filesdone;
107+static	FILE		*_pw_fp;
108+static	struct passwd	_pw_passwd;	/* password structure */
109+static	int		_pw_stayopen;	/* keep fd's open */
110+static	int		_pw_filesdone;
111+
112+static	char		grfile[MAXPATHLEN];
113+static	char		pwfile[MAXPATHLEN];
114+
115+static	char		*members[MAXGRP];
116+static	char		grline[MAXLINELENGTH];
117+static	char		pwline[MAXLINELENGTH];
118+
119+int
120+setup_getid(const char *dir)
121+{
122+	if (dir == NULL)
123+		return (0);
124+
125+				/* close existing databases */
126+	gi_endgrent();
127+	gi_endpwent();
128+
129+				/* build paths to new databases */
130+	snprintf(grfile, sizeof(grfile), "%s/group", dir);
131+	snprintf(pwfile, sizeof(pwfile), "%s/master.passwd", dir);
132+
133+				/* try to open new databases */
134+	if (!grstart() || !pwstart())
135+		return (0);
136+
137+				/* switch pwcache(3) lookup functions */
138+	if (pwcache_groupdb(gi_setgroupent, gi_endgrent,
139+			    gi_getgrnam, gi_getgrgid) == -1
140+	    || pwcache_userdb(gi_setpassent, gi_endpwent,
141+			    gi_getpwnam, gi_getpwuid) == -1)
142+		return (0);
143+
144+	return (1);
145+}
146+
147+
148+/*
149+ * group lookup functions
150+ */
151+
152+static struct group *
153+gi_getgrnam(const char *name)
154+{
155+	int rval;
156+
157+	if (!grstart())
158+		return NULL;
159+	rval = grscan(1, 0, name);
160+	if (!_gr_stayopen)
161+		endgrent();
162+	return (rval) ? &_gr_group : NULL;
163+}
164+
165+static struct group *
166+gi_getgrgid(gid_t gid)
167+{
168+	int rval;
169+
170+	if (!grstart())
171+		return NULL;
172+	rval = grscan(1, gid, NULL);
173+	if (!_gr_stayopen)
174+		endgrent();
175+	return (rval) ? &_gr_group : NULL;
176+}
177+
178+static int
179+gi_setgroupent(int stayopen)
180+{
181+
182+	if (!grstart())
183+		return 0;
184+	_gr_stayopen = stayopen;
185+	return 1;
186+}
187+
188+static void
189+gi_endgrent(void)
190+{
191+
192+	_gr_filesdone = 0;
193+	if (_gr_fp) {
194+		(void)fclose(_gr_fp);
195+		_gr_fp = NULL;
196+	}
197+}
198+
199+static int
200+grstart(void)
201+{
202+
203+	_gr_filesdone = 0;
204+	if (_gr_fp) {
205+		rewind(_gr_fp);
206+		return 1;
207+	}
208+	if (grfile[0] == '\0')			/* sanity check */
209+		return 0;
210+
211+	_gr_fp = fopen(grfile, "r");
212+	if (_gr_fp != NULL)
213+		return 1;
214+	warn("Can't open `%s'", grfile);
215+	return 0;
216+}
217+
218+
219+static int
220+grscan(int search, gid_t gid, const char *name)
221+{
222+
223+	if (_gr_filesdone)
224+		return 0;
225+	for (;;) {
226+		if (!fgets(grline, sizeof(grline), _gr_fp)) {
227+			if (!search)
228+				_gr_filesdone = 1;
229+			return 0;
230+		}
231+		/* skip lines that are too big */
232+		if (!strchr(grline, '\n')) {
233+			int ch;
234+
235+			while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
236+				;
237+			continue;
238+		}
239+		/* skip comments */
240+		if (grline[0] == '#')
241+			continue;
242+		if (grmatchline(search, gid, name))
243+			return 1;
244+	}
245+	/* NOTREACHED */
246+}
247+
248+static int
249+grmatchline(int search, gid_t gid, const char *name)
250+{
251+	unsigned long	id;
252+	char		**m;
253+	char		*cp, *bp, *ep;
254+
255+	/* name may be NULL if search is nonzero */
256+
257+	bp = grline;
258+	memset(&_gr_group, 0, sizeof(_gr_group));
259+	_gr_group.gr_name = strsep(&bp, ":\n");
260+	if (search && name && strcmp(_gr_group.gr_name, name))
261+		return 0;
262+	_gr_group.gr_passwd = strsep(&bp, ":\n");
263+	if (!(cp = strsep(&bp, ":\n")))
264+		return 0;
265+	id = strtoul(cp, &ep, 10);
266+	if (id > GID_MAX || *ep != '\0')
267+		return 0;
268+	_gr_group.gr_gid = (gid_t)id;
269+	if (search && name == NULL && _gr_group.gr_gid != gid)
270+		return 0;
271+	cp = NULL;
272+	if (bp == NULL)
273+		return 0;
274+	for (_gr_group.gr_mem = m = members;; bp++) {
275+		if (m == &members[MAXGRP - 1])
276+			break;
277+		if (*bp == ',') {
278+			if (cp) {
279+				*bp = '\0';
280+				*m++ = cp;
281+				cp = NULL;
282+			}
283+		} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
284+			if (cp) {
285+				*bp = '\0';
286+				*m++ = cp;
287+			}
288+			break;
289+		} else if (cp == NULL)
290+			cp = bp;
291+	}
292+	*m = NULL;
293+	return 1;
294+}
295+
296+
297+/*
298+ * user lookup functions
299+ */
300+
301+static struct passwd *
302+gi_getpwnam(const char *name)
303+{
304+	int rval;
305+
306+	if (!pwstart())
307+		return NULL;
308+	rval = pwscan(1, 0, name);
309+	if (!_pw_stayopen)
310+		endpwent();
311+	return (rval) ? &_pw_passwd : NULL;
312+}
313+
314+static struct passwd *
315+gi_getpwuid(uid_t uid)
316+{
317+	int rval;
318+
319+	if (!pwstart())
320+		return NULL;
321+	rval = pwscan(1, uid, NULL);
322+	if (!_pw_stayopen)
323+		endpwent();
324+	return (rval) ? &_pw_passwd : NULL;
325+}
326+
327+static int
328+gi_setpassent(int stayopen)
329+{
330+
331+	if (!pwstart())
332+		return 0;
333+	_pw_stayopen = stayopen;
334+	return 1;
335+}
336+
337+static void
338+gi_endpwent(void)
339+{
340+
341+	_pw_filesdone = 0;
342+	if (_pw_fp) {
343+		(void)fclose(_pw_fp);
344+		_pw_fp = NULL;
345+	}
346+}
347+
348+static int
349+pwstart(void)
350+{
351+
352+	_pw_filesdone = 0;
353+	if (_pw_fp) {
354+		rewind(_pw_fp);
355+		return 1;
356+	}
357+	if (pwfile[0] == '\0')			/* sanity check */
358+		return 0;
359+	_pw_fp = fopen(pwfile, "r");
360+	if (_pw_fp != NULL)
361+		return 1;
362+	warn("Can't open `%s'", pwfile);
363+	return 0;
364+}
365+
366+
367+static int
368+pwscan(int search, uid_t uid, const char *name)
369+{
370+
371+	if (_pw_filesdone)
372+		return 0;
373+	for (;;) {
374+		if (!fgets(pwline, sizeof(pwline), _pw_fp)) {
375+			if (!search)
376+				_pw_filesdone = 1;
377+			return 0;
378+		}
379+		/* skip lines that are too big */
380+		if (!strchr(pwline, '\n')) {
381+			int ch;
382+
383+			while ((ch = getc(_pw_fp)) != '\n' && ch != EOF)
384+				;
385+			continue;
386+		}
387+		/* skip comments */
388+		if (pwline[0] == '#')
389+			continue;
390+		if (pwmatchline(search, uid, name))
391+			return 1;
392+	}
393+	/* NOTREACHED */
394+}
395+
396+static int
397+pwmatchline(int search, uid_t uid, const char *name)
398+{
399+	unsigned long	id;
400+	char		*cp, *bp, *ep;
401+
402+	/* name may be NULL if search is nonzero */
403+
404+	bp = pwline;
405+	memset(&_pw_passwd, 0, sizeof(_pw_passwd));
406+	_pw_passwd.pw_name = strsep(&bp, ":\n");		/* name */
407+	if (search && name && strcmp(_pw_passwd.pw_name, name))
408+		return 0;
409+
410+	_pw_passwd.pw_passwd = strsep(&bp, ":\n");		/* passwd */
411+
412+	if (!(cp = strsep(&bp, ":\n")))				/* uid */
413+		return 0;
414+	id = strtoul(cp, &ep, 10);
415+	if (id > UID_MAX || *ep != '\0')
416+		return 0;
417+	_pw_passwd.pw_uid = (uid_t)id;
418+	if (search && name == NULL && _pw_passwd.pw_uid != uid)
419+		return 0;
420+
421+	if (!(cp = strsep(&bp, ":\n")))				/* gid */
422+		return 0;
423+	id = strtoul(cp, &ep, 10);
424+	if (id > GID_MAX || *ep != '\0')
425+		return 0;
426+	_pw_passwd.pw_gid = (gid_t)id;
427+
428+	if (!(ep = strsep(&bp, ":")))				/* class */
429+		return 0;
430+	if (!(ep = strsep(&bp, ":")))				/* change */
431+		return 0;
432+	if (!(ep = strsep(&bp, ":")))				/* expire */
433+		return 0;
434+
435+	if (!(_pw_passwd.pw_gecos = strsep(&bp, ":\n")))	/* gecos */
436+		return 0;
437+	if (!(_pw_passwd.pw_dir = strsep(&bp, ":\n")))		/* directory */
438+		return 0;
439+	if (!(_pw_passwd.pw_shell = strsep(&bp, ":\n")))	/* shell */
440+		return 0;
441+
442+	if (strchr(bp, ':') != NULL)
443+		return 0;
444+
445+	return 1;
446+}
447+
+312, -0
  1@@ -0,0 +1,312 @@
  2+/*	$NetBSD: misc.c,v 1.35 2024/12/05 17:17:43 christos Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1991, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. Neither the name of the University nor the names of its contributors
 17+ *    may be used to endorse or promote products derived from this software
 18+ *    without specific prior written permission.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 30+ * SUCH DAMAGE.
 31+ *
 32+ *	@(#)misc.c	8.1 (Berkeley) 6/6/93
 33+ */
 34+
 35+#if HAVE_NBTOOL_CONFIG_H
 36+#include "nbtool_config.h"
 37+#endif
 38+
 39+#include <sys/cdefs.h>
 40+#if defined(__RCSID) && !defined(lint)
 41+__RCSID("$NetBSD: misc.c,v 1.35 2024/12/05 17:17:43 christos Exp $");
 42+#endif /* not lint */
 43+
 44+#include <sys/types.h>
 45+#include <sys/stat.h>
 46+
 47+#include <stdarg.h>
 48+#include <stdio.h>
 49+#include <stdlib.h>
 50+#include <string.h>
 51+
 52+#include "extern.h"
 53+
 54+enum flavor	flavor = F_MTREE;
 55+
 56+typedef struct _key {
 57+	const char	*name;		/* key name */
 58+	u_int		val;		/* value */
 59+
 60+#define	NEEDVALUE	0x01
 61+	u_int		flags;
 62+} KEY;
 63+
 64+/* NB: the following tables must be sorted lexically. */
 65+static KEY keylist[] = {
 66+	{"cksum",	F_CKSUM,	NEEDVALUE},
 67+	{"device",	F_DEV,		NEEDVALUE},
 68+	{"flags",	F_FLAGS,	NEEDVALUE},
 69+	{"gid",		F_GID,		NEEDVALUE},
 70+	{"gname",	F_GNAME,	NEEDVALUE},
 71+	{"ignore",	F_IGN,		0},
 72+	{"link",	F_SLINK,	NEEDVALUE},
 73+	{"md5",		F_MD5,		NEEDVALUE},
 74+	{"md5digest",	F_MD5,		NEEDVALUE},
 75+	{"mode",	F_MODE,		NEEDVALUE},
 76+	{"nlink",	F_NLINK,	NEEDVALUE},
 77+	{"nochange",	F_NOCHANGE,	0},
 78+	{"optional",	F_OPT,		0},
 79+	{"ripemd160digest", F_RMD160,	NEEDVALUE},
 80+	{"rmd160",	F_RMD160,	NEEDVALUE},
 81+	{"rmd160digest",F_RMD160,	NEEDVALUE},
 82+	{"sha1",	F_SHA1,		NEEDVALUE},
 83+	{"sha1digest",	F_SHA1,		NEEDVALUE},
 84+	{"sha256",	F_SHA256,	NEEDVALUE},
 85+	{"sha256digest",F_SHA256,	NEEDVALUE},
 86+	{"sha384",	F_SHA384,	NEEDVALUE},
 87+	{"sha384digest",F_SHA384,	NEEDVALUE},
 88+	{"sha512",	F_SHA512,	NEEDVALUE},
 89+	{"sha512digest",F_SHA512,	NEEDVALUE},
 90+	{"size",	F_SIZE,		NEEDVALUE},
 91+	{"tags",	F_TAGS,		NEEDVALUE},
 92+	{"time",	F_TIME,		NEEDVALUE},
 93+	{"type",	F_TYPE,		NEEDVALUE},
 94+	{"uid",		F_UID,		NEEDVALUE},
 95+	{"uname",	F_UNAME,	NEEDVALUE}
 96+};
 97+
 98+static KEY typelist[] = {
 99+	{"block",	F_BLOCK,	0},
100+	{"char",	F_CHAR,		0},
101+	{"dir",		F_DIR,		0},
102+#ifdef S_IFDOOR
103+	{"door",	F_DOOR,		0},
104+#endif
105+	{"fifo",	F_FIFO,		0},
106+	{"file",	F_FILE,		0},
107+	{"link",	F_LINK,		0},
108+	{"socket",	F_SOCK,		0},
109+};
110+
111+slist_t	excludetags, includetags;
112+int	keys = KEYDEFAULT;
113+
114+
115+static int keycompare(const void *, const void *);
116+
117+u_int
118+parsekey(const char *name, int *needvaluep)
119+{
120+	static int allbits;
121+	KEY *k, tmp;
122+
123+	if (allbits == 0) {
124+		size_t i;
125+
126+		for (i = 0; i < sizeof(keylist) / sizeof(KEY); i++)
127+			allbits |= keylist[i].val;
128+	}
129+	tmp.name = name;
130+	if (strcmp(name, "all") == 0)
131+		return (allbits);
132+	k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY),
133+	    sizeof(KEY), keycompare);
134+	if (k == NULL)
135+		mtree_err("unknown keyword `%s'", name);
136+
137+	if (needvaluep)
138+		*needvaluep = k->flags & NEEDVALUE ? 1 : 0;
139+
140+	return (k->val);
141+}
142+
143+u_int
144+parsetype(const char *name)
145+{
146+	KEY *k, tmp;
147+
148+	tmp.name = name;
149+	k = (KEY *)bsearch(&tmp, typelist, sizeof(typelist) / sizeof(KEY),
150+	    sizeof(KEY), keycompare);
151+	if (k == NULL)
152+		mtree_err("unknown file type `%s'", name);
153+
154+	return (k->val);
155+}
156+
157+static int
158+keycompare(const void *a, const void *b)
159+{
160+
161+	return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name));
162+}
163+
164+void
165+mtree_err(const char *fmt, ...)
166+{
167+	va_list ap;
168+
169+	va_start(ap, fmt);
170+	vwarnx(fmt, ap);
171+	va_end(ap);
172+	if (mtree_lineno)
173+		warnx("failed at line %lu of the specification",
174+		    (u_long) mtree_lineno);
175+	exit(1);
176+	/* NOTREACHED */
177+}
178+
179+void
180+addtag(slist_t *list, char *elem)
181+{
182+
183+#define	TAG_CHUNK 20
184+
185+	if ((list->count % TAG_CHUNK) == 0) {
186+		char **new;
187+
188+		new = (char **)realloc(list->list, (list->count + TAG_CHUNK)
189+		    * sizeof(char *));
190+		if (new == NULL)
191+			mtree_err("memory allocation error");
192+		list->list = new;
193+	}
194+	list->list[list->count] = elem;
195+	list->count++;
196+}
197+
198+void
199+parsetags(slist_t *list, char *args)
200+{
201+	char	*p, *e;
202+	size_t	len;
203+
204+	if (args == NULL) {
205+		addtag(list, NULL);
206+		return;
207+	}
208+	while ((p = strsep(&args, ",")) != NULL) {
209+		if (*p == '\0')
210+			continue;
211+		len = strlen(p) + 3;	/* "," + p + ",\0" */
212+		if ((e = malloc(len)) == NULL)
213+			mtree_err("memory allocation error");
214+		snprintf(e, len, ",%s,", p);
215+		addtag(list, e);
216+	}
217+}
218+
219+/*
220+ * matchtags
221+ *	returns 0 if there's a match from the exclude list in the node's tags,
222+ *	or there's an include list and no match.
223+ *	return 1 otherwise.
224+ */
225+int
226+matchtags(NODE *node)
227+{
228+	int	i;
229+
230+	if (node->tags) {
231+		for (i = 0; i < excludetags.count; i++)
232+			if (strstr(node->tags, excludetags.list[i]))
233+				break;
234+		if (i < excludetags.count)
235+			return (0);
236+
237+		for (i = 0; i < includetags.count; i++)
238+			if (strstr(node->tags, includetags.list[i]))
239+				break;
240+		if (i > 0 && i == includetags.count)
241+			return (0);
242+	} else if (includetags.count > 0) {
243+		return (0);
244+	}
245+	return (1);
246+}
247+
248+u_int
249+nodetoino(u_int type)
250+{
251+
252+	switch (type) {
253+	case F_BLOCK:
254+		return S_IFBLK;
255+	case F_CHAR:
256+		return S_IFCHR;
257+	case F_DIR:
258+		return S_IFDIR;
259+	case F_FIFO:
260+		return S_IFIFO;
261+	case F_FILE:
262+		return S_IFREG;
263+	case F_LINK:
264+		return S_IFLNK;
265+#ifdef S_IFSOCK
266+	case F_SOCK:
267+		return S_IFSOCK;
268+#endif
269+	default:
270+		printf("unknown type %d", type);
271+		abort();
272+	}
273+	/* NOTREACHED */
274+}
275+
276+const char *
277+nodetype(u_int type)
278+{
279+
280+	return (inotype(nodetoino(type)));
281+}
282+
283+
284+const char *
285+inotype(u_int type)
286+{
287+
288+	switch (type & S_IFMT) {
289+	case S_IFBLK:
290+		return ("block");
291+	case S_IFCHR:
292+		return ("char");
293+	case S_IFDIR:
294+		return ("dir");
295+	case S_IFIFO:
296+		return ("fifo");
297+	case S_IFREG:
298+		return ("file");
299+	case S_IFLNK:
300+		return ("link");
301+#ifdef S_IFSOCK
302+	case S_IFSOCK:
303+		return ("socket");
304+#endif
305+#ifdef S_IFDOOR
306+	case S_IFDOOR:
307+		return ("door");
308+#endif
309+	default:
310+		return ("unknown");
311+	}
312+	/* NOTREACHED */
313+}
+158, -0
  1@@ -0,0 +1,158 @@
  2+/*	$NetBSD: mtree.h,v 1.31 2012/10/05 09:17:29 wiz Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1990, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. Neither the name of the University nor the names of its contributors
 17+ *    may be used to endorse or promote products derived from this software
 18+ *    without specific prior written permission.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 30+ * SUCH DAMAGE.
 31+ *
 32+ *	@(#)mtree.h	8.1 (Berkeley) 6/6/93
 33+ */
 34+
 35+#ifndef _MTREE_H_
 36+#define	_MTREE_H_
 37+
 38+#define	KEYDEFAULT	(F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | \
 39+			F_TIME | F_TYPE | F_UID | F_FLAGS)
 40+
 41+#define	MISMATCHEXIT	2
 42+
 43+typedef struct _node {
 44+	struct _node	*parent, *child;	/* up, down */
 45+	struct _node	*prev, *next;		/* left, right */
 46+	off_t	st_size;			/* size */
 47+	struct timespec	st_mtimespec;		/* last modification time */
 48+	char	*slink;				/* symbolic link reference */
 49+	uid_t	st_uid;				/* uid */
 50+	gid_t	st_gid;				/* gid */
 51+#define	MBITS	(S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
 52+	mode_t	st_mode;			/* mode */
 53+	dev_t	st_rdev;			/* device type */
 54+	u_long	st_flags;			/* flags */
 55+	nlink_t	st_nlink;			/* link count */
 56+	u_long	cksum;				/* check sum */
 57+	char	*md5digest;			/* MD5 digest */
 58+	char	*rmd160digest;			/* RMD-160 digest */
 59+	char	*sha1digest;			/* SHA1 digest */
 60+	char	*sha256digest;			/* SHA256 digest */
 61+	char	*sha384digest;			/* SHA384 digest */
 62+	char	*sha512digest;			/* SHA512 digest */
 63+	char	*tags;				/* tags, comma delimited,
 64+						 * also with leading and
 65+						 * trailing commas */
 66+	size_t	lineno;				/* line # entry came from */
 67+
 68+#define	F_CKSUM		0x00000001		/* cksum(1) check sum */
 69+#define	F_DEV		0x00000002		/* device type */
 70+#define	F_DONE		0x00000004		/* directory done */
 71+#define	F_FLAGS		0x00000008		/* file flags */
 72+#define	F_GID		0x00000010		/* gid */
 73+#define	F_GNAME		0x00000020		/* group name */
 74+#define	F_IGN		0x00000040		/* ignore */
 75+#define	F_MAGIC		0x00000080		/* name has magic chars */
 76+#define	F_MD5		0x00000100		/* MD5 digest */
 77+#define	F_MODE		0x00000200		/* mode */
 78+#define	F_NLINK		0x00000400		/* number of links */
 79+#define	F_OPT		0x00000800		/* existence optional */
 80+#define	F_RMD160	0x00001000		/* RMD-160 digest */
 81+#define	F_SHA1		0x00002000		/* SHA1 digest */
 82+#define	F_SIZE		0x00004000		/* size */
 83+#define	F_SLINK		0x00008000		/* symbolic link */
 84+#define	F_TAGS		0x00010000		/* tags */
 85+#define	F_TIME		0x00020000		/* modification time */
 86+#define	F_TYPE		0x00040000		/* file type */
 87+#define	F_UID		0x00080000		/* uid */
 88+#define	F_UNAME		0x00100000		/* user name */
 89+#define	F_VISIT		0x00200000		/* file visited */
 90+#define	F_NOCHANGE	0x00400000		/* check existence, but not */
 91+						/* other properties */
 92+#define	F_SHA256	0x00800000		/* SHA256 digest */
 93+#define	F_SHA384	0x01000000		/* SHA384 digest */
 94+#define	F_SHA512	0x02000000		/* SHA512 digest */
 95+
 96+	int	flags;				/* items set */
 97+
 98+#define	F_BLOCK	0x001				/* block special */
 99+#define	F_CHAR	0x002				/* char special */
100+#define	F_DIR	0x004				/* directory */
101+#define	F_FIFO	0x008				/* fifo */
102+#define	F_FILE	0x010				/* regular file */
103+#define	F_LINK	0x020				/* symbolic link */
104+#define	F_SOCK	0x040				/* socket */
105+#define	F_DOOR	0x080				/* door */
106+	int	type;				/* file type */
107+
108+	char	name[1];			/* file name (must be last) */
109+} NODE;
110+
111+
112+typedef struct {
113+	char  **list;
114+	int	count;
115+} slist_t;
116+
117+
118+/*
119+ * prototypes for functions published to other programs which want to use
120+ * the specfile parser but don't want to pull in all of "extern.h"
121+ */
122+const char	*inotype(u_int);
123+u_int		 nodetoino(u_int);
124+int		 setup_getid(const char *);
125+NODE		*spec(FILE *);
126+int		 mtree_specspec(FILE *, FILE *);
127+void		 free_nodes(NODE *);
128+char		*vispath(const char *);
129+
130+#ifdef __FreeBSD__
131+#define KEY_DIGEST "digest"
132+#else
133+#define KEY_DIGEST
134+#endif
135+
136+#define	MD5KEY		"md5"		KEY_DIGEST
137+#ifdef __FreeBSD__
138+#define	RMD160KEY	"ripemd160"	KEY_DIGEST
139+#else
140+#define	RMD160KEY	"rmd160"	KEY_DIGEST
141+#endif
142+#define	SHA1KEY		"sha1"		KEY_DIGEST
143+#define	SHA256KEY	"sha256"	KEY_DIGEST
144+#define	SHA384KEY	"sha384"
145+#define	SHA512KEY	"sha512"
146+
147+#define	RP(p)	\
148+	((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \
149+	    (p)->fts_path + 2 : (p)->fts_path)
150+
151+#define	UF_MASK ((UF_NODUMP | UF_IMMUTABLE |   \
152+                  UF_APPEND | UF_OPAQUE)       \
153+                    & UF_SETTABLE)              /* user settable flags */
154+#define	SF_MASK ((SF_ARCHIVED | SF_IMMUTABLE | \
155+                  SF_APPEND) & SF_SETTABLE)     /* root settable flags */
156+#define	CH_MASK  (UF_MASK | SF_MASK)            /* all settable flags */
157+#define	SP_FLGS  (SF_IMMUTABLE | SF_APPEND)     /* special flags */
158+
159+#endif /* _MTREE_H_ */
+290, -0
  1@@ -0,0 +1,290 @@
  2+/*	$NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
  6+ * All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to The NetBSD Foundation
  9+ * by Charles M. Hannum.
 10+ *
 11+ * Redistribution and use in source and binary forms, with or without
 12+ * modification, are permitted provided that the following conditions
 13+ * are met:
 14+ * 1. Redistributions of source code must retain the above copyright
 15+ *    notice, this list of conditions and the following disclaimer.
 16+ * 2. Redistributions in binary form must reproduce the above copyright
 17+ *    notice, this list of conditions and the following disclaimer in the
 18+ *    documentation and/or other materials provided with the distribution.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 21+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 22+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 23+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 24+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 25+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 26+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 27+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 28+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 29+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 30+ * POSSIBILITY OF SUCH DAMAGE.
 31+ */
 32+
 33+#if HAVE_NBTOOL_CONFIG_H
 34+#include "nbtool_config.h"
 35+#endif
 36+
 37+#include <sys/cdefs.h>
 38+#if !defined(lint)
 39+__RCSID("$NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $");
 40+#endif /* not lint */
 41+
 42+#include <sys/types.h>
 43+#include <sys/stat.h>
 44+
 45+#include <limits.h>
 46+#include <stdio.h>
 47+#include <stdlib.h>
 48+#include <string.h>
 49+#include <unistd.h>
 50+
 51+#include "pack_dev.h"
 52+
 53+static	pack_t	pack_netbsd;
 54+static	pack_t	pack_freebsd;
 55+static	pack_t	pack_8_8;
 56+static	pack_t	pack_12_20;
 57+static	pack_t	pack_14_18;
 58+static	pack_t	pack_8_24;
 59+static	pack_t	pack_bsdos;
 60+static	int	compare_format(const void *, const void *);
 61+
 62+static const char iMajorError[] = "invalid major number";
 63+static const char iMinorError[] = "invalid minor number";
 64+static const char tooManyFields[] = "too many fields for format";
 65+
 66+	/* exported */
 67+dev_t
 68+pack_native(int n, u_long numbers[], const char **error)
 69+{
 70+	dev_t dev = 0;
 71+
 72+	if (n == 2) {
 73+		dev = makedev(numbers[0], numbers[1]);
 74+		if ((u_long)major(dev) != numbers[0])
 75+			*error = iMajorError;
 76+		else if ((u_long)minor(dev) != numbers[1])
 77+			*error = iMinorError;
 78+	} else
 79+		*error = tooManyFields;
 80+	return (dev);
 81+}
 82+
 83+
 84+static dev_t
 85+pack_netbsd(int n, u_long numbers[], const char **error)
 86+{
 87+	dev_t dev = 0;
 88+
 89+	if (n == 2) {
 90+		dev = makedev_netbsd(numbers[0], numbers[1]);
 91+		if ((u_long)major_netbsd(dev) != numbers[0])
 92+			*error = iMajorError;
 93+		else if ((u_long)minor_netbsd(dev) != numbers[1])
 94+			*error = iMinorError;
 95+	} else
 96+		*error = tooManyFields;
 97+	return (dev);
 98+}
 99+
100+
101+#define	major_freebsd(x)	((int32_t)(((x) & 0x0000ff00) >> 8))
102+#define	minor_freebsd(x)	((int32_t)(((x) & 0xffff00ff) >> 0))
103+#define	makedev_freebsd(x,y)	((dev_t)((((x) << 8) & 0x0000ff00) | \
104+					 (((y) << 0) & 0xffff00ff)))
105+
106+static dev_t
107+pack_freebsd(int n, u_long numbers[], const char **error)
108+{
109+	dev_t dev = 0;
110+
111+	if (n == 2) {
112+		dev = makedev_freebsd(numbers[0], numbers[1]);
113+		if ((u_long)major_freebsd(dev) != numbers[0])
114+			*error = iMajorError;
115+		if ((u_long)minor_freebsd(dev) != numbers[1])
116+			*error = iMinorError;
117+	} else
118+		*error = tooManyFields;
119+	return (dev);
120+}
121+
122+
123+#define	major_8_8(x)		((int32_t)(((x) & 0x0000ff00) >> 8))
124+#define	minor_8_8(x)		((int32_t)(((x) & 0x000000ff) >> 0))
125+#define	makedev_8_8(x,y)	((dev_t)((((x) << 8) & 0x0000ff00) | \
126+					 (((y) << 0) & 0x000000ff)))
127+
128+static dev_t
129+pack_8_8(int n, u_long numbers[], const char **error)
130+{
131+	dev_t dev = 0;
132+
133+	if (n == 2) {
134+		dev = makedev_8_8(numbers[0], numbers[1]);
135+		if ((u_long)major_8_8(dev) != numbers[0])
136+			*error = iMajorError;
137+		if ((u_long)minor_8_8(dev) != numbers[1])
138+			*error = iMinorError;
139+	} else
140+		*error = tooManyFields;
141+	return (dev);
142+}
143+
144+
145+#define	major_12_20(x)		((int32_t)(((x) & 0xfff00000) >> 20))
146+#define	minor_12_20(x)		((int32_t)(((x) & 0x000fffff) >>  0))
147+#define	makedev_12_20(x,y)	((dev_t)((((x) << 20) & 0xfff00000) | \
148+					 (((y) <<  0) & 0x000fffff)))
149+
150+static dev_t
151+pack_12_20(int n, u_long numbers[], const char **error)
152+{
153+	dev_t dev = 0;
154+
155+	if (n == 2) {
156+		dev = makedev_12_20(numbers[0], numbers[1]);
157+		if ((u_long)major_12_20(dev) != numbers[0])
158+			*error = iMajorError;
159+		if ((u_long)minor_12_20(dev) != numbers[1])
160+			*error = iMinorError;
161+	} else
162+		*error = tooManyFields;
163+	return (dev);
164+}
165+
166+
167+#define	major_14_18(x)		((int32_t)(((x) & 0xfffc0000) >> 18))
168+#define	minor_14_18(x)		((int32_t)(((x) & 0x0003ffff) >>  0))
169+#define	makedev_14_18(x,y)	((dev_t)((((x) << 18) & 0xfffc0000) | \
170+					 (((y) <<  0) & 0x0003ffff)))
171+
172+static dev_t
173+pack_14_18(int n, u_long numbers[], const char **error)
174+{
175+	dev_t dev = 0;
176+
177+	if (n == 2) {
178+		dev = makedev_14_18(numbers[0], numbers[1]);
179+		if ((u_long)major_14_18(dev) != numbers[0])
180+			*error = iMajorError;
181+		if ((u_long)minor_14_18(dev) != numbers[1])
182+			*error = iMinorError;
183+	} else
184+		*error = tooManyFields;
185+	return (dev);
186+}
187+
188+
189+#define	major_8_24(x)		((int32_t)(((x) & 0xff000000) >> 24))
190+#define	minor_8_24(x)		((int32_t)(((x) & 0x00ffffff) >>  0))
191+#define	makedev_8_24(x,y)	((dev_t)((((x) << 24) & 0xff000000) | \
192+					 (((y) <<  0) & 0x00ffffff)))
193+
194+static dev_t
195+pack_8_24(int n, u_long numbers[], const char **error)
196+{
197+	dev_t dev = 0;
198+
199+	if (n == 2) {
200+		dev = makedev_8_24(numbers[0], numbers[1]);
201+		if ((u_long)major_8_24(dev) != numbers[0])
202+			*error = iMajorError;
203+		if ((u_long)minor_8_24(dev) != numbers[1])
204+			*error = iMinorError;
205+	} else
206+		*error = tooManyFields;
207+	return (dev);
208+}
209+
210+
211+#define	major_12_12_8(x)	((int32_t)(((x) & 0xfff00000) >> 20))
212+#define	unit_12_12_8(x)		((int32_t)(((x) & 0x000fff00) >>  8))
213+#define	subunit_12_12_8(x)	((int32_t)(((x) & 0x000000ff) >>  0))
214+#define	makedev_12_12_8(x,y,z)	((dev_t)((((x) << 20) & 0xfff00000) | \
215+					 (((y) <<  8) & 0x000fff00) | \
216+					 (((z) <<  0) & 0x000000ff)))
217+
218+static dev_t
219+pack_bsdos(int n, u_long numbers[], const char **error)
220+{
221+	dev_t dev = 0;
222+
223+	if (n == 2) {
224+		dev = makedev_12_20(numbers[0], numbers[1]);
225+		if ((u_long)major_12_20(dev) != numbers[0])
226+			*error = iMajorError;
227+		if ((u_long)minor_12_20(dev) != numbers[1])
228+			*error = iMinorError;
229+	} else if (n == 3) {
230+		dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]);
231+		if ((u_long)major_12_12_8(dev) != numbers[0])
232+			*error = iMajorError;
233+		if ((u_long)unit_12_12_8(dev) != numbers[1])
234+			*error = "invalid unit number";
235+		if ((u_long)subunit_12_12_8(dev) != numbers[2])
236+			*error = "invalid subunit number";
237+	} else
238+		*error = tooManyFields;
239+	return (dev);
240+}
241+
242+
243+		/* list of formats and pack functions */
244+		/* this list must be sorted lexically */
245+static struct format {
246+	const char	*name;
247+	pack_t		*pack;
248+} formats[] = {
249+	{"386bsd",  pack_8_8},
250+	{"4bsd",    pack_8_8},
251+	{"bsdos",   pack_bsdos},
252+	{"freebsd", pack_freebsd},
253+	{"hpux",    pack_8_24},
254+	{"isc",     pack_8_8},
255+	{"linux",   pack_8_8},
256+	{"native",  pack_native},
257+	{"netbsd",  pack_netbsd},
258+	{"osf1",    pack_12_20},
259+	{"sco",     pack_8_8},
260+	{"solaris", pack_14_18},
261+	{"sunos",   pack_8_8},
262+	{"svr3",    pack_8_8},
263+	{"svr4",    pack_14_18},
264+	{"ultrix",  pack_8_8},
265+};
266+
267+static int
268+compare_format(const void *key, const void *element)
269+{
270+	const char		*name;
271+	const struct format	*format;
272+
273+	name = key;
274+	format = element;
275+
276+	return (strcmp(name, format->name));
277+}
278+
279+
280+pack_t *
281+pack_find(const char *name)
282+{
283+	struct format	*format;
284+
285+	format = bsearch(name, formats,
286+	    sizeof(formats)/sizeof(formats[0]),
287+	    sizeof(formats[0]), compare_format);
288+	if (format == 0)
289+		return (NULL);
290+	return (format->pack);
291+}
+47, -0
 1@@ -0,0 +1,47 @@
 2+/*	$NetBSD: pack_dev.h,v 1.8 2013/06/14 16:28:20 tsutsui Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
 6+ * All rights reserved.
 7+ *
 8+ * This code is derived from software contributed to The NetBSD Foundation
 9+ * by Charles M. Hannum.
10+ *
11+ * Redistribution and use in source and binary forms, with or without
12+ * modification, are permitted provided that the following conditions
13+ * are met:
14+ * 1. Redistributions of source code must retain the above copyright
15+ *    notice, this list of conditions and the following disclaimer.
16+ * 2. Redistributions in binary form must reproduce the above copyright
17+ *    notice, this list of conditions and the following disclaimer in the
18+ *    documentation and/or other materials provided with the distribution.
19+ *
20+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30+ * POSSIBILITY OF SUCH DAMAGE.
31+ */
32+
33+#ifndef	_PACK_DEV_H
34+#define	_PACK_DEV_H
35+
36+typedef	dev_t pack_t(int, u_long [], const char **);
37+
38+pack_t	*pack_find(const char *);
39+pack_t	 pack_native;
40+
41+#define	major_netbsd(x)		((int32_t)((((x) & 0x000fff00) >>  8)))
42+#define	minor_netbsd(x)		((int32_t)((((x) & 0xfff00000) >> 12) | \
43+					   (((x) & 0x000000ff) >>  0)))
44+#define	makedev_netbsd(x,y)	((dev_t)((((x) <<  8) & 0x000fff00) | \
45+					 (((y) << 12) & 0xfff00000) | \
46+					 (((y) <<  0) & 0x000000ff)))
47+
48+#endif	/* _PACK_DEV_H */
+878, -0
  1@@ -0,0 +1,878 @@
  2+/*	$NetBSD: spec.c,v 1.94 2025/12/18 14:05:41 christos Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1989, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. Neither the name of the University nor the names of its contributors
 17+ *    may be used to endorse or promote products derived from this software
 18+ *    without specific prior written permission.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 30+ * SUCH DAMAGE.
 31+ */
 32+
 33+/*-
 34+ * Copyright (c) 2001-2004 The NetBSD Foundation, Inc.
 35+ * All rights reserved.
 36+ *
 37+ * This code is derived from software contributed to The NetBSD Foundation
 38+ * by Luke Mewburn of Wasabi Systems.
 39+ *
 40+ * Redistribution and use in source and binary forms, with or without
 41+ * modification, are permitted provided that the following conditions
 42+ * are met:
 43+ * 1. Redistributions of source code must retain the above copyright
 44+ *    notice, this list of conditions and the following disclaimer.
 45+ * 2. Redistributions in binary form must reproduce the above copyright
 46+ *    notice, this list of conditions and the following disclaimer in the
 47+ *    documentation and/or other materials provided with the distribution.
 48+ *
 49+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 50+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 51+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 52+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 53+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 54+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 55+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 56+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 57+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 58+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 59+ * POSSIBILITY OF SUCH DAMAGE.
 60+ */
 61+
 62+#if HAVE_NBTOOL_CONFIG_H
 63+#include "nbtool_config.h"
 64+#endif
 65+
 66+#include <sys/cdefs.h>
 67+#if defined(__RCSID) && !defined(lint)
 68+#if 0
 69+static char sccsid[] = "@(#)spec.c	8.2 (Berkeley) 4/28/95";
 70+#else
 71+__RCSID("$NetBSD: spec.c,v 1.94 2025/12/18 14:05:41 christos Exp $");
 72+#endif
 73+#endif /* not lint */
 74+
 75+#include <sys/param.h>
 76+#include <sys/stat.h>
 77+
 78+#include <assert.h>
 79+#include <ctype.h>
 80+#include <errno.h>
 81+#include <grp.h>
 82+#include <pwd.h>
 83+#include <stdarg.h>
 84+#include <stdio.h>
 85+#include <stdint.h>
 86+#include <stdlib.h>
 87+#include <string.h>
 88+#include <unistd.h>
 89+#include <vis.h>
 90+#include <util.h>
 91+
 92+#include "extern.h"
 93+#include "pack_dev.h"
 94+
 95+size_t	mtree_lineno;			/* Current spec line number */
 96+int	mtree_Mflag;			/* Merge duplicate entries */
 97+int	mtree_Wflag;			/* Don't "whack" permissions */
 98+int	mtree_Sflag;			/* Sort entries */
 99+
100+static	dev_t	parsedev(char *);
101+static	void	replacenode(NODE *, NODE *);
102+static	void	set(char *, NODE *);
103+static	void	unset(char *, NODE *);
104+static	NODE	*addchild(NODE *, NODE *);
105+static	int	nodecmp(const NODE *, const NODE *);
106+static	int	appendfield(FILE *, int, const char *, ...) __printflike(3, 4);
107+
108+#define REPLACEPTR(x,v)	do { if ((x)) free((x)); (x) = (v); } while (0)
109+
110+NODE *
111+spec(FILE *fp)
112+{
113+	NODE *centry, *last, *pathparent, *cur;
114+	char *p, *e, *next;
115+	NODE ginfo, *root;
116+	char *buf, *tname, *ntname;
117+	size_t tnamelen, plen;
118+
119+	root = NULL;
120+	centry = last = NULL;
121+	tname = NULL;
122+	tnamelen = 0;
123+	memset(&ginfo, 0, sizeof(ginfo));
124+	for (mtree_lineno = 0;
125+	    (buf = fparseln(fp, NULL, &mtree_lineno, NULL,
126+		FPARSELN_UNESCCOMM));
127+	    free(buf)) {
128+		/* Skip leading whitespace. */
129+		for (p = buf; *p && isspace((unsigned char)*p); ++p)
130+			continue;
131+
132+		/* If nothing but whitespace, continue. */
133+		if (!*p)
134+			continue;
135+
136+#ifdef DEBUG
137+		fprintf(stderr, "line %lu: {%s}\n",
138+		    (u_long)mtree_lineno, p);
139+#endif
140+		/* Grab file name, "$", "set", or "unset". */
141+		next = buf;
142+		while ((p = strsep(&next, " \t")) != NULL && *p == '\0')
143+			continue;
144+		if (p == NULL)
145+			mtree_err("missing field");
146+
147+		if (p[0] == '/') {
148+			if (strcmp(p + 1, "set") == 0)
149+				set(next, &ginfo);
150+			else if (strcmp(p + 1, "unset") == 0)
151+				unset(next, &ginfo);
152+			else
153+				mtree_err("invalid specification `%s'", p);
154+			continue;
155+		}
156+
157+		if (strcmp(p, "..") == 0) {
158+			/* Don't go up, if haven't gone down. */
159+			if (root == NULL)
160+				goto noparent;
161+			if (last->type != F_DIR || last->flags & F_DONE) {
162+				if (last == root)
163+					goto noparent;
164+				last = last->parent;
165+			}
166+			last->flags |= F_DONE;
167+			continue;
168+
169+noparent:		mtree_err("no parent node");
170+		}
171+
172+		plen = strlen(p) + 1;
173+		if (plen > tnamelen) {
174+			if ((ntname = realloc(tname, plen)) == NULL)
175+				mtree_err("realloc: %s", strerror(errno));
176+			tname = ntname;
177+			tnamelen = plen;
178+		}
179+		if (strunvis(tname, p) == -1)
180+			mtree_err("strunvis failed on `%s'", p);
181+		p = tname;
182+
183+		pathparent = NULL;
184+		if (strchr(p, '/') != NULL) {
185+			cur = root;
186+			for (; (e = strchr(p, '/')) != NULL; p = e+1) {
187+				if (p == e)
188+					continue;	/* handle // */
189+				*e = '\0';
190+				if (strcmp(p, ".") != 0) {
191+					while (cur &&
192+					    strcmp(cur->name, p) != 0) {
193+						cur = cur->next;
194+					}
195+				}
196+				if (cur == NULL || cur->type != F_DIR) {
197+					mtree_err("%s: %s", tname,
198+					"missing directory in specification");
199+				}
200+				*e = '/';
201+				pathparent = cur;
202+				cur = cur->child;
203+			}
204+			if (*p == '\0')
205+				mtree_err("%s: empty leaf element", tname);
206+		}
207+
208+		if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
209+			mtree_err("%s", strerror(errno));
210+		*centry = ginfo;
211+		centry->lineno = mtree_lineno;
212+		strcpy(centry->name, p);
213+#define	MAGIC	"?*["
214+		if (strpbrk(p, MAGIC))
215+			centry->flags |= F_MAGIC;
216+		set(next, centry);
217+
218+		if (root == NULL) {
219+				/*
220+				 * empty tree
221+				 */
222+			/*
223+			 * Allow a bare "." root node by forcing it to
224+			 * type=dir for compatibility with FreeBSD.
225+			 */
226+			if (strcmp(centry->name, ".") == 0 && centry->type == 0)
227+				centry->type = F_DIR;
228+			if (strcmp(centry->name, ".") != 0)
229+				mtree_err(
230+				    "root node must be the directory `.',"
231+				    " found `%s'", centry->name);
232+			if (centry->type != F_DIR)
233+				mtree_err(
234+				    "root node must type %#x != %#x",
235+				    F_DIR, centry->type);
236+			last = root = centry;
237+			root->parent = root;
238+		} else if (pathparent != NULL) {
239+				/*
240+				 * full path entry; add or replace
241+				 */
242+			centry->parent = pathparent;
243+			last = addchild(pathparent, centry);
244+		} else if (strcmp(centry->name, ".") == 0) {
245+				/*
246+				 * duplicate "." entry; always replace
247+				 */
248+			replacenode(root, centry);
249+		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
250+				/*
251+				 * new relative child in current dir;
252+				 * add or replace
253+				 */
254+			centry->parent = last;
255+			last = addchild(last, centry);
256+		} else {
257+				/*
258+				 * new relative child in parent dir
259+				 * (after encountering ".." entry);
260+				 * add or replace
261+				 */
262+			centry->parent = last->parent;
263+			last = addchild(last->parent, centry);
264+		}
265+	}
266+	return (root);
267+}
268+
269+void
270+free_nodes(NODE *root)
271+{
272+	NODE	*cur, *next;
273+
274+	if (root == NULL)
275+		return;
276+
277+	next = NULL;
278+	for (cur = root; cur != NULL; cur = next) {
279+		next = cur->next;
280+		free_nodes(cur->child);
281+		REPLACEPTR(cur->slink, NULL);
282+		REPLACEPTR(cur->md5digest, NULL);
283+		REPLACEPTR(cur->rmd160digest, NULL);
284+		REPLACEPTR(cur->sha1digest, NULL);
285+		REPLACEPTR(cur->sha256digest, NULL);
286+		REPLACEPTR(cur->sha384digest, NULL);
287+		REPLACEPTR(cur->sha512digest, NULL);
288+		REPLACEPTR(cur->tags, NULL);
289+		REPLACEPTR(cur, NULL);
290+	}
291+}
292+
293+/*
294+ * appendfield --
295+ *	Like fprintf(), but output a space either before or after
296+ *	the regular output, according to the pathlast flag.
297+ */
298+static int
299+appendfield(FILE *fp, int pathlast, const char *fmt, ...)
300+{
301+	va_list ap;
302+	int result;
303+
304+	va_start(ap, fmt);
305+	if (!pathlast)
306+		fprintf(fp, " ");
307+	result = vprintf(fmt, ap);
308+	if (pathlast)
309+		fprintf(fp, " ");
310+	va_end(ap);
311+	return result;
312+}
313+
314+/*
315+ * dump_nodes --
316+ *	dump the NODEs from `cur', based in the directory `dir'.
317+ *	if pathlast is none zero, print the path last, otherwise print
318+ *	it first.
319+ */
320+void
321+dump_nodes(FILE *fp, const char *dir, NODE *root, int pathlast)
322+{
323+	NODE	*cur;
324+	char	path[MAXPATHLEN];
325+	const char *name;
326+	char	*str;
327+	char	*p, *q;
328+
329+	for (cur = root; cur != NULL; cur = cur->next) {
330+		if (cur->type != F_DIR && !matchtags(cur))
331+			continue;
332+
333+		if (snprintf(path, sizeof(path), "%s%s%s",
334+		    dir, *dir ? "/" : "", cur->name)
335+		    >= (int)sizeof(path))
336+			mtree_err("Pathname too long.");
337+
338+		if (!pathlast)
339+			fprintf(fp, "%s", vispath(path));
340+
341+#define MATCHFLAG(f)	((keys & (f)) && (cur->flags & (f)))
342+		if (MATCHFLAG(F_TYPE))
343+			appendfield(fp, pathlast, "type=%s",
344+			    nodetype(cur->type));
345+		if (MATCHFLAG(F_UID | F_UNAME)) {
346+			if (keys & F_UNAME &&
347+			    (name = user_from_uid(cur->st_uid, 1)) != NULL)
348+				appendfield(fp, pathlast, "uname=%s", name);
349+			else
350+				appendfield(fp, pathlast, "uid=%u",
351+				    cur->st_uid);
352+		}
353+		if (MATCHFLAG(F_GID | F_GNAME)) {
354+			if (keys & F_GNAME &&
355+			    (name = group_from_gid(cur->st_gid, 1)) != NULL)
356+				appendfield(fp, pathlast, "gname=%s", name);
357+			else
358+				appendfield(fp, pathlast, "gid=%u",
359+				    cur->st_gid);
360+		}
361+		if (MATCHFLAG(F_MODE))
362+			appendfield(fp, pathlast, "mode=%#o", cur->st_mode);
363+		if (MATCHFLAG(F_DEV) &&
364+		    (cur->type == F_BLOCK || cur->type == F_CHAR))
365+			appendfield(fp, pathlast, "device=%#jx",
366+			    (uintmax_t)cur->st_rdev);
367+		if (MATCHFLAG(F_NLINK))
368+			appendfield(fp, pathlast, "nlink=%ju",
369+			    (uintmax_t)cur->st_nlink);
370+		if (MATCHFLAG(F_SLINK))
371+			appendfield(fp, pathlast, "link=%s",
372+			    vispath(cur->slink));
373+		if (MATCHFLAG(F_SIZE))
374+			appendfield(fp, pathlast, "size=%ju",
375+			    (uintmax_t)cur->st_size);
376+		if (MATCHFLAG(F_TIME))
377+			appendfield(fp, pathlast, "time=%jd.%09ld",
378+			    (intmax_t)cur->st_mtimespec.tv_sec,
379+			    cur->st_mtimespec.tv_nsec);
380+		if (MATCHFLAG(F_CKSUM))
381+			appendfield(fp, pathlast, "cksum=%lu", cur->cksum);
382+		if (MATCHFLAG(F_MD5))
383+			appendfield(fp, pathlast, "%s=%s", MD5KEY,
384+			    cur->md5digest);
385+		if (MATCHFLAG(F_RMD160))
386+			appendfield(fp, pathlast, "%s=%s", RMD160KEY,
387+			    cur->rmd160digest);
388+		if (MATCHFLAG(F_SHA1))
389+			appendfield(fp, pathlast, "%s=%s", SHA1KEY,
390+			    cur->sha1digest);
391+		if (MATCHFLAG(F_SHA256))
392+			appendfield(fp, pathlast, "%s=%s", SHA256KEY,
393+			    cur->sha256digest);
394+		if (MATCHFLAG(F_SHA384))
395+			appendfield(fp, pathlast, "%s=%s", SHA384KEY,
396+			    cur->sha384digest);
397+		if (MATCHFLAG(F_SHA512))
398+			appendfield(fp, pathlast, "%s=%s", SHA512KEY,
399+			    cur->sha512digest);
400+		if (MATCHFLAG(F_FLAGS)) {
401+			str = flags_to_string(cur->st_flags, "none");
402+			appendfield(fp, pathlast, "flags=%s", str);
403+			free(str);
404+		}
405+		if (MATCHFLAG(F_IGN))
406+			appendfield(fp, pathlast, "ignore");
407+		if (MATCHFLAG(F_OPT))
408+			appendfield(fp, pathlast, "optional");
409+		if (MATCHFLAG(F_TAGS)) {
410+			/* don't output leading or trailing commas */
411+			p = cur->tags;
412+			while (*p == ',')
413+				p++;
414+			q = p + strlen(p);
415+			while(q > p && q[-1] == ',')
416+				q--;
417+			appendfield(fp, pathlast, "tags=%.*s", (int)(q - p), p);
418+		}
419+		puts(pathlast ? vispath(path) : "");
420+
421+		if (cur->child)
422+			dump_nodes(fp, path, cur->child, pathlast);
423+	}
424+}
425+
426+/*
427+ * vispath --
428+ *	strsvis(3) encodes path, which must not be longer than MAXPATHLEN
429+ *	characters long, and returns a pointer to a static buffer containing
430+ *	the result.
431+ */
432+char *
433+vispath(const char *path)
434+{
435+	static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
436+	static const char extra_glob[] = { ' ', '\t', '\n', '\\', '#', '*',
437+	    '?', '[', '\0' };
438+	static char pathbuf[4*MAXPATHLEN + 1];
439+
440+	if (flavor == F_NETBSD6)
441+		strsvis(pathbuf, path, VIS_CSTYLE, extra);
442+	else
443+		strsvis(pathbuf, path, VIS_OCTAL, extra_glob);
444+	return pathbuf;
445+}
446+
447+
448+static dev_t
449+parsedev(char *arg)
450+{
451+#define MAX_PACK_ARGS	3
452+	u_long	numbers[MAX_PACK_ARGS];
453+	char	*p, *ep, *dev;
454+	int	argc;
455+	pack_t	*pack;
456+	dev_t	result;
457+	const char *error = NULL;
458+
459+	if ((dev = strchr(arg, ',')) != NULL) {
460+		*dev++='\0';
461+		if ((pack = pack_find(arg)) == NULL)
462+			mtree_err("unknown format `%s'", arg);
463+		argc = 0;
464+		while ((p = strsep(&dev, ",")) != NULL) {
465+			if (*p == '\0')
466+				mtree_err("missing number");
467+			numbers[argc++] = strtoul(p, &ep, 0);
468+			if (*ep != '\0')
469+				mtree_err("invalid number `%s'",
470+				    p);
471+			if (argc > MAX_PACK_ARGS)
472+				mtree_err("too many arguments");
473+		}
474+		if (argc < 2)
475+			mtree_err("not enough arguments");
476+		result = (*pack)(argc, numbers, &error);
477+		if (error != NULL)
478+			mtree_err("%s", error);
479+	} else {
480+		result = (dev_t)strtoul(arg, &ep, 0);
481+		if (*ep != '\0')
482+			mtree_err("invalid device `%s'", arg);
483+	}
484+	return (result);
485+}
486+
487+static void
488+replacenode(NODE *cur, NODE *new)
489+{
490+
491+#define REPLACE(x)	cur->x = new->x
492+#define REPLACESTR(x)	REPLACEPTR(cur->x,new->x)
493+
494+	if (cur->type != new->type) {
495+		if (mtree_Mflag) {
496+				/*
497+				 * merge entries with different types; we
498+				 * don't want children retained in this case.
499+				 */
500+			REPLACE(type);
501+			free_nodes(cur->child);
502+			cur->child = NULL;
503+		} else {
504+			mtree_err(
505+			    "existing entry for `%s', type `%s'"
506+			    " does not match type `%s'",
507+			    cur->name, nodetype(cur->type),
508+			    nodetype(new->type));
509+		}
510+	}
511+
512+	REPLACE(st_size);
513+	REPLACE(st_mtimespec);
514+	REPLACESTR(slink);
515+	if (cur->slink != NULL) {
516+		if ((cur->slink = strdup(new->slink)) == NULL)
517+			mtree_err("memory allocation error");
518+		if (strunvis(cur->slink, new->slink) == -1)
519+			mtree_err("strunvis failed on `%s'", new->slink);
520+		free(new->slink);
521+	}
522+	REPLACE(st_uid);
523+	REPLACE(st_gid);
524+	REPLACE(st_mode);
525+	REPLACE(st_rdev);
526+	REPLACE(st_flags);
527+	REPLACE(st_nlink);
528+	REPLACE(cksum);
529+	REPLACESTR(md5digest);
530+	REPLACESTR(rmd160digest);
531+	REPLACESTR(sha1digest);
532+	REPLACESTR(sha256digest);
533+	REPLACESTR(sha384digest);
534+	REPLACESTR(sha512digest);
535+	REPLACESTR(tags);
536+	REPLACE(lineno);
537+	REPLACE(flags);
538+	free(new);
539+}
540+
541+static void
542+set(char *t, NODE *ip)
543+{
544+	int	type, value;
545+	size_t	len;
546+	gid_t	gid;
547+	uid_t	uid;
548+	char	*kw, *val, *md, *ep;
549+	void	*m;
550+
551+	while ((kw = strsep(&t, "= \t")) != NULL) {
552+		if (*kw == '\0')
553+			continue;
554+		if (strcmp(kw, "all") == 0)
555+			mtree_err("invalid keyword `all'");
556+		ip->flags |= type = parsekey(kw, &value);
557+		if (!value)
558+			/* Just set flag bit (F_IGN and F_OPT) */
559+			continue;
560+		while ((val = strsep(&t, " \t")) != NULL && *val == '\0')
561+			continue;
562+		if (val == NULL)
563+			mtree_err("missing value");
564+		switch (type) {
565+		case F_CKSUM:
566+			ip->cksum = strtoul(val, &ep, 10);
567+			if (*ep)
568+				mtree_err("invalid checksum `%s'", val);
569+			break;
570+		case F_DEV:
571+			ip->st_rdev = parsedev(val);
572+			break;
573+		case F_FLAGS:
574+			if (strcmp("none", val) == 0)
575+				ip->st_flags = 0;
576+			else if (string_to_flags(&val, &ip->st_flags, NULL)
577+			    != 0)
578+				mtree_err("invalid flag `%s'", val);
579+			break;
580+		case F_GID:
581+			ip->st_gid = (gid_t)strtoul(val, &ep, 10);
582+			if (*ep)
583+				mtree_err("invalid gid `%s'", val);
584+			break;
585+		case F_GNAME:
586+			if (mtree_Wflag)	/* don't parse if whacking */
587+				break;
588+			if (gid_from_group(val, &gid) == -1)
589+				mtree_err("unknown group `%s'", val);
590+			ip->st_gid = gid;
591+			break;
592+		case F_MD5:
593+			if (val[0]=='0' && val[1]=='x')
594+				md=&val[2];
595+			else
596+				md=val;
597+			if ((ip->md5digest = strdup(md)) == NULL)
598+				mtree_err("memory allocation error");
599+			break;
600+		case F_MODE:
601+			if ((m = setmode(val)) == NULL)
602+				mtree_err("cannot set file mode `%s' (%s)",
603+				    val, strerror(errno));
604+			ip->st_mode = getmode(m, 0);
605+			free(m);
606+			break;
607+		case F_NLINK:
608+			ip->st_nlink = (nlink_t)strtoul(val, &ep, 10);
609+			if (*ep)
610+				mtree_err("invalid link count `%s'", val);
611+			break;
612+		case F_RMD160:
613+			if (val[0]=='0' && val[1]=='x')
614+				md=&val[2];
615+			else
616+				md=val;
617+			if ((ip->rmd160digest = strdup(md)) == NULL)
618+				mtree_err("memory allocation error");
619+			break;
620+		case F_SHA1:
621+			if (val[0]=='0' && val[1]=='x')
622+				md=&val[2];
623+			else
624+				md=val;
625+			if ((ip->sha1digest = strdup(md)) == NULL)
626+				mtree_err("memory allocation error");
627+			break;
628+		case F_SIZE:
629+			ip->st_size = (off_t)strtoll(val, &ep, 10);
630+			if (*ep)
631+				mtree_err("invalid size `%s'", val);
632+			break;
633+		case F_SLINK:
634+			if ((ip->slink = strdup(val)) == NULL)
635+				mtree_err("memory allocation error");
636+			if (strunvis(ip->slink, val) == -1)
637+				mtree_err("strunvis failed on `%s'", val);
638+			break;
639+		case F_TAGS:
640+			len = strlen(val) + 3;	/* "," + str + ",\0" */
641+			if ((ip->tags = malloc(len)) == NULL)
642+				mtree_err("memory allocation error");
643+			snprintf(ip->tags, len, ",%s,", val);
644+			break;
645+		case F_TIME:
646+			ip->st_mtimespec.tv_sec =
647+			    (time_t)strtoll(val, &ep, 10);
648+			if (*ep != '.')
649+				mtree_err("invalid time `%s'", val);
650+			val = ep + 1;
651+			ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10);
652+			if (*ep)
653+				mtree_err("invalid time `%s'", val);
654+			break;
655+		case F_TYPE:
656+			ip->type = parsetype(val);
657+			break;
658+		case F_UID:
659+			ip->st_uid = (uid_t)strtoul(val, &ep, 10);
660+			if (*ep)
661+				mtree_err("invalid uid `%s'", val);
662+			break;
663+		case F_UNAME:
664+			if (mtree_Wflag)	/* don't parse if whacking */
665+				break;
666+			if (uid_from_user(val, &uid) == -1)
667+				mtree_err("unknown user `%s'", val);
668+			ip->st_uid = uid;
669+			break;
670+		case F_SHA256:
671+			if (val[0]=='0' && val[1]=='x')
672+				md=&val[2];
673+			else
674+				md=val;
675+			if ((ip->sha256digest = strdup(md)) == NULL)
676+				mtree_err("memory allocation error");
677+			break;
678+		case F_SHA384:
679+			if (val[0]=='0' && val[1]=='x')
680+				md=&val[2];
681+			else
682+				md=val;
683+			if ((ip->sha384digest = strdup(md)) == NULL)
684+				mtree_err("memory allocation error");
685+			break;
686+		case F_SHA512:
687+			if (val[0]=='0' && val[1]=='x')
688+				md=&val[2];
689+			else
690+				md=val;
691+			if ((ip->sha512digest = strdup(md)) == NULL)
692+				mtree_err("memory allocation error");
693+			break;
694+		default:
695+			mtree_err(
696+			    "set(): unsupported key type 0x%x (INTERNAL ERROR)",
697+			    type);
698+			/* NOTREACHED */
699+		}
700+	}
701+}
702+
703+static void
704+unset(char *t, NODE *ip)
705+{
706+	char *p;
707+
708+	while ((p = strsep(&t, " \t")) != NULL) {
709+		if (*p == '\0')
710+			continue;
711+		ip->flags &= ~parsekey(p, NULL);
712+	}
713+}
714+
715+/*
716+ * addchild --
717+ *	Add the centry node as a child of the pathparent node.	If
718+ *	centry is a duplicate, call replacenode().  If centry is not
719+ *	a duplicate, insert it into the linked list referenced by
720+ *	pathparent->child.  Keep the list sorted if Sflag is set.
721+ */
722+static NODE *
723+addchild(NODE *pathparent, NODE *centry)
724+{
725+	NODE *samename;      /* node with the same name as centry */
726+	NODE *replacepos;    /* if non-NULL, centry should replace this node */
727+	NODE *insertpos;     /* if non-NULL, centry should be inserted
728+			      * after this node */
729+	NODE *cur;           /* for stepping through the list */
730+	NODE *last;          /* the last node in the list */
731+	int cmp;
732+
733+	samename = NULL;
734+	replacepos = NULL;
735+	insertpos = NULL;
736+	last = NULL;
737+	cur = pathparent->child;
738+	if (cur == NULL) {
739+		/* centry is pathparent's first and only child node so far */
740+		pathparent->child = centry;
741+		return centry;
742+	}
743+
744+	/*
745+	 * pathparent already has at least one other child, so add the
746+	 * centry node to the list.
747+	 *
748+	 * We first scan through the list looking for an existing node
749+	 * with the same name (setting samename), and also looking
750+	 * for the correct position to replace or insert the new node
751+	 * (setting replacepos and/or insertpos).
752+	 */
753+	for (; cur != NULL; last = cur, cur = cur->next) {
754+		if (strcmp(centry->name, cur->name) == 0) {
755+			samename = cur;
756+		}
757+		if (mtree_Sflag) {
758+			cmp = nodecmp(centry, cur);
759+			if (cmp == 0) {
760+				replacepos = cur;
761+			} else if (cmp > 0) {
762+				insertpos = cur;
763+			}
764+		}
765+	}
766+	if (! mtree_Sflag) {
767+		if (samename != NULL) {
768+			/* replace node with same name */
769+			replacepos = samename;
770+		} else {
771+			/* add new node at end of list */
772+			insertpos = last;
773+		}
774+	}
775+
776+	if (samename != NULL) {
777+		/*
778+		 * We found a node with the same name above.  Call
779+		 * replacenode(), which will either exit with an error,
780+		 * or replace the information in the samename node and
781+		 * free the information in the centry node.
782+		 */
783+		replacenode(samename, centry);
784+		if (samename == replacepos) {
785+			/* The just-replaced node was in the correct position */
786+			return samename;
787+		}
788+		if (samename == insertpos || samename->prev == insertpos) {
789+			/*
790+			 * We thought the new node should be just before
791+			 * or just after the replaced node, but that would
792+			 * be equivalent to just retaining the replaced node.
793+			 */
794+			return samename;
795+		}
796+
797+		/*
798+		 * The just-replaced node is in the wrong position in
799+		 * the list.  This can happen if sort order depends on
800+		 * criteria other than the node name.
801+		 *
802+		 * Make centry point to the just-replaced node.	 Unlink
803+		 * the just-replaced node from the list, and allow it to
804+		 * be inserted in the correct position later.
805+		 */
806+		centry = samename;
807+		if (centry->prev)
808+			centry->prev->next = centry->next;
809+		else {
810+			/* centry->next is the new head of the list */
811+			pathparent->child = centry->next;
812+			assert(centry->next != NULL);
813+		}
814+		if (centry->next)
815+			centry->next->prev = centry->prev;
816+		centry->prev = NULL;
817+		centry->next = NULL;
818+	}
819+
820+	if (insertpos == NULL) {
821+		/* insert centry at the beginning of the list */
822+		pathparent->child->prev = centry;
823+		centry->next = pathparent->child;
824+		centry->prev = NULL;
825+		pathparent->child = centry;
826+	} else {
827+		/* insert centry into the list just after insertpos */
828+		centry->next = insertpos->next;
829+		insertpos->next = centry;
830+		centry->prev = insertpos;
831+		if (centry->next)
832+			centry->next->prev = centry;
833+	}
834+	return centry;
835+}
836+
837+/*
838+ * nodecmp --
839+ *	used as a comparison function by addchild() to control the order
840+ *	in which entries appear within a list of sibling nodes.	 We make
841+ *	directories sort after non-directories, but otherwise sort in
842+ *	strcmp() order.
843+ *
844+ * Keep this in sync with dcmp() below.
845+ */
846+static int
847+nodecmp(const NODE *a, const NODE *b)
848+{
849+
850+	if ((a->type & F_DIR) != 0) {
851+		if ((b->type & F_DIR) == 0)
852+			return 1;
853+	} else if ((b->type & F_DIR) != 0) {
854+		return -1;
855+	}
856+	return strcmp(a->name, b->name);
857+}
858+
859+/*
860+ * dcmp --
861+ *    used as a comparison function passed to fts_open() to control
862+ *    the order in which fts_read() returns results.    We make
863+ *    directories sort after non-directories, but otherwise sort in
864+ *    strcmp() order.
865+ *
866+ * Keep this in sync with nodecmp() above.
867+ */
868+int
869+dcmp(const FTSENT *FTS_CONST *a, const FTSENT *FTS_CONST *b)
870+{
871+
872+	if (S_ISDIR((*a)->fts_statp->st_mode)) {
873+		if (!S_ISDIR((*b)->fts_statp->st_mode))
874+			return 1;
875+	} else if (S_ISDIR((*b)->fts_statp->st_mode)) {
876+		return -1;
877+	}
878+	return strcmp((*a)->fts_name, (*b)->fts_name);
879+}
+186, -0
  1@@ -0,0 +1,186 @@
  2+/*	$NetBSD: stat_flags.c,v 1.3 2022/04/19 20:32:17 rillig Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. Neither the name of the University nor the names of its contributors
 17+ *    may be used to endorse or promote products derived from this software
 18+ *    without specific prior written permission.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 30+ * SUCH DAMAGE.
 31+ */
 32+
 33+#if HAVE_NBTOOL_CONFIG_H
 34+#include "nbtool_config.h"
 35+#else
 36+#define HAVE_STRUCT_STAT_ST_FLAGS 1
 37+#endif
 38+
 39+#include <sys/cdefs.h>
 40+#if !defined(lint)
 41+#if 0
 42+static char sccsid[] = "@(#)stat_flags.c	8.2 (Berkeley) 7/28/94";
 43+#else
 44+__RCSID("$NetBSD: stat_flags.c,v 1.3 2022/04/19 20:32:17 rillig Exp $");
 45+#endif
 46+#endif /* not lint */
 47+
 48+#include <sys/types.h>
 49+#include <sys/stat.h>
 50+#include <fts.h>
 51+#include <stddef.h>
 52+#include <string.h>
 53+#include <stdlib.h>
 54+
 55+#include "util.h"
 56+
 57+#define	SAPPEND(s) do {							\
 58+	if (prefix != NULL)						\
 59+		(void)strlcat(string, prefix, sizeof(string));		\
 60+	(void)strlcat(string, s, sizeof(string));			\
 61+	prefix = ",";							\
 62+} while (0)
 63+
 64+/*
 65+ * flags_to_string --
 66+ *	Convert stat flags to a comma-separated string.  If no flags
 67+ *	are set, return the default string.
 68+ */
 69+char *
 70+flags_to_string(u_long flags, const char *def)
 71+{
 72+	char string[128];
 73+	const char *prefix;
 74+
 75+	string[0] = '\0';
 76+	prefix = NULL;
 77+#if HAVE_STRUCT_STAT_ST_FLAGS
 78+	if (flags & UF_APPEND)
 79+		SAPPEND("uappnd");
 80+	if (flags & UF_IMMUTABLE)
 81+		SAPPEND("uchg");
 82+	if (flags & UF_NODUMP)
 83+		SAPPEND("nodump");
 84+	if (flags & UF_OPAQUE)
 85+		SAPPEND("opaque");
 86+	if (flags & SF_APPEND)
 87+		SAPPEND("sappnd");
 88+	if (flags & SF_ARCHIVED)
 89+		SAPPEND("arch");
 90+	if (flags & SF_IMMUTABLE)
 91+		SAPPEND("schg");
 92+#ifdef SF_SNAPSHOT
 93+	if (flags & SF_SNAPSHOT)
 94+		SAPPEND("snap");
 95+#endif
 96+#endif
 97+	if (prefix != NULL)
 98+		return strdup(string);
 99+	return strdup(def);
100+}
101+
102+#define	TEST(a, b, f) {							\
103+	if (!strcmp(a, b)) {						\
104+		if (clear) {						\
105+			if (clrp)					\
106+				*clrp |= (f);				\
107+			if (setp)					\
108+				*setp &= ~(f);				\
109+		} else {						\
110+			if (setp)					\
111+				*setp |= (f);				\
112+			if (clrp)					\
113+				*clrp &= ~(f);				\
114+		}							\
115+		break;							\
116+	}								\
117+}
118+
119+/*
120+ * string_to_flags --
121+ *	Take string of arguments and return stat flags.  Return 0 on
122+ *	success, 1 on failure.  On failure, stringp is set to point
123+ *	to the offending token.
124+ */
125+int
126+string_to_flags(char **stringp, u_long *setp, u_long *clrp)
127+{
128+	int clear;
129+	char *string, *p;
130+
131+	if (setp)
132+		*setp = 0;
133+	if (clrp)
134+		*clrp = 0;
135+
136+#if HAVE_STRUCT_STAT_ST_FLAGS
137+	string = *stringp;
138+	while ((p = strsep(&string, "\t ,")) != NULL) {
139+		clear = 0;
140+		*stringp = p;
141+		if (*p == '\0')
142+			continue;
143+		if (p[0] == 'n' && p[1] == 'o') {
144+			clear = 1;
145+			p += 2;
146+		}
147+		switch (p[0]) {
148+		case 'a':
149+			TEST(p, "arch", SF_ARCHIVED);
150+			TEST(p, "archived", SF_ARCHIVED);
151+			return (1);
152+		case 'd':
153+			clear = !clear;
154+			TEST(p, "dump", UF_NODUMP);
155+			return (1);
156+		case 'n':
157+				/*
158+				 * Support `nonodump'. Note that
159+				 * the state of clear is not changed.
160+				 */
161+			TEST(p, "nodump", UF_NODUMP);
162+			return (1);
163+		case 'o':
164+			TEST(p, "opaque", UF_OPAQUE);
165+			return (1);
166+		case 's':
167+			TEST(p, "sappnd", SF_APPEND);
168+			TEST(p, "sappend", SF_APPEND);
169+			TEST(p, "schg", SF_IMMUTABLE);
170+			TEST(p, "schange", SF_IMMUTABLE);
171+			TEST(p, "simmutable", SF_IMMUTABLE);
172+			return (1);
173+		case 'u':
174+			TEST(p, "uappnd", UF_APPEND);
175+			TEST(p, "uappend", UF_APPEND);
176+			TEST(p, "uchg", UF_IMMUTABLE);
177+			TEST(p, "uchange", UF_IMMUTABLE);
178+			TEST(p, "uimmutable", UF_IMMUTABLE);
179+			return (1);
180+		default:
181+			return (1);
182+		}
183+	}
184+#endif
185+
186+	return (0);
187+}
+35, -0
 1@@ -0,0 +1,35 @@
 2+/*	$NetBSD: stat_flags.h,v 1.4 2003/08/07 09:05:16 agc Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 1991, 1993
 6+ *	The Regents of the University of California.  All rights reserved.
 7+ *
 8+ * Redistribution and use in source and binary forms, with or without
 9+ * modification, are permitted provided that the following conditions
10+ * are met:
11+ * 1. Redistributions of source code must retain the above copyright
12+ *    notice, this list of conditions and the following disclaimer.
13+ * 2. Redistributions in binary form must reproduce the above copyright
14+ *    notice, this list of conditions and the following disclaimer in the
15+ *    documentation and/or other materials provided with the distribution.
16+ * 3. Neither the name of the University nor the names of its contributors
17+ *    may be used to endorse or promote products derived from this software
18+ *    without specific prior written permission.
19+ *
20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30+ * SUCH DAMAGE.
31+ *
32+ *	@(#)extern.h	8.1 (Berkeley) 5/31/93
33+ */
34+
35+char	*flags_to_string(u_long, const char *);
36+int	 string_to_flags(char **, u_long *, u_long *);
+1476, -0
   1@@ -0,0 +1,1476 @@
   2+/*	$NetBSD: bootblock.h,v 1.62 2026/01/09 15:43:07 nia Exp $	*/
   3+
   4+/*-
   5+ * Copyright (c) 2002-2004 The NetBSD Foundation, Inc.
   6+ * All rights reserved.
   7+ *
   8+ * Redistribution and use in source and binary forms, with or without
   9+ * modification, are permitted provided that the following conditions
  10+ * are met:
  11+ * 1. Redistributions of source code must retain the above copyright
  12+ *    notice, this list of conditions and the following disclaimer.
  13+ * 2. Redistributions in binary form must reproduce the above copyright
  14+ *    notice, this list of conditions and the following disclaimer in the
  15+ *    documentation and/or other materials provided with the distribution.
  16+ *
  17+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  18+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  19+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  20+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  21+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27+ * POSSIBILITY OF SUCH DAMAGE.
  28+ */
  29+/*-
  30+ * Copyright (C) 1993	Allen K. Briggs, Chris P. Caputo,
  31+ *			Michael L. Finch, Bradley A. Grantham, and
  32+ *			Lawrence A. Kesteloot
  33+ * All rights reserved.
  34+ *
  35+ * Redistribution and use in source and binary forms, with or without
  36+ * modification, are permitted provided that the following conditions
  37+ * are met:
  38+ * 1. Redistributions of source code must retain the above copyright
  39+ *    notice, this list of conditions and the following disclaimer.
  40+ * 2. Redistributions in binary form must reproduce the above copyright
  41+ *    notice, this list of conditions and the following disclaimer in the
  42+ *    documentation and/or other materials provided with the distribution.
  43+ * 3. All advertising materials mentioning features or use of this software
  44+ *    must display the following acknowledgement:
  45+ *	This product includes software developed by the Alice Group.
  46+ * 4. The names of the Alice Group or any of its members may not be used
  47+ *    to endorse or promote products derived from this software without
  48+ *    specific prior written permission.
  49+ *
  50+ * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR
  51+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  52+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  53+ * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT,
  54+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  55+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  56+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  57+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  58+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  59+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  60+ *
  61+ */
  62+/*
  63+ * Copyright (c) 1994, 1999 Christopher G. Demetriou
  64+ * All rights reserved.
  65+ *
  66+ * Redistribution and use in source and binary forms, with or without
  67+ * modification, are permitted provided that the following conditions
  68+ * are met:
  69+ * 1. Redistributions of source code must retain the above copyright
  70+ *    notice, this list of conditions and the following disclaimer.
  71+ * 2. Redistributions in binary form must reproduce the above copyright
  72+ *    notice, this list of conditions and the following disclaimer in the
  73+ *    documentation and/or other materials provided with the distribution.
  74+ * 3. All advertising materials mentioning features or use of this software
  75+ *    must display the following acknowledgement:
  76+ *      This product includes software developed by Christopher G. Demetriou
  77+ *      for the NetBSD Project.
  78+ * 4. The name of the author may not be used to endorse or promote products
  79+ *    derived from this software without specific prior written permission
  80+ *
  81+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  82+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  83+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  84+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  85+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  86+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  87+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  88+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  89+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  90+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  91+ */
  92+/*
  93+ * Copyright (c) 1994 Rolf Grossmann
  94+ * All rights reserved.
  95+ *
  96+ * Redistribution and use in source and binary forms, with or without
  97+ * modification, are permitted provided that the following conditions
  98+ * are met:
  99+ * 1. Redistributions of source code must retain the above copyright
 100+ *    notice, this list of conditions and the following disclaimer.
 101+ * 2. Redistributions in binary form must reproduce the above copyright
 102+ *    notice, this list of conditions and the following disclaimer in the
 103+ *    documentation and/or other materials provided with the distribution.
 104+ * 3. All advertising materials mentioning features or use of this software
 105+ *    must display the following acknowledgement:
 106+ *      This product includes software developed by Rolf Grossmann.
 107+ * 4. The name of the author may not be used to endorse or promote products
 108+ *    derived from this software without specific prior written permission
 109+ *
 110+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 111+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 112+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 113+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 114+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 115+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 116+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 117+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 118+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 119+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 120+ */
 121+
 122+#ifndef _SYS_BOOTBLOCK_H
 123+#define	_SYS_BOOTBLOCK_H
 124+
 125+#if !defined(__ASSEMBLER__)
 126+#include <sys/cdefs.h>
 127+#include <sys/endian.h>
 128+#if defined(_KERNEL) || defined(_STANDALONE)
 129+#include <sys/stdint.h>
 130+#else
 131+#include <stdint.h>
 132+#endif
 133+#endif	/* !defined(__ASSEMBLER__) */
 134+
 135+/* ------------------------------------------
 136+ * MBR (Master Boot Record) --
 137+ *	definitions for systems that use MBRs
 138+ */
 139+
 140+/*
 141+ * Layout of boot records:
 142+ *
 143+ *	Byte range	Use	Description
 144+ *	----------	---	-----------
 145+ *
 146+ *	0 - 2		FMP	JMP xxx, NOP
 147+ *	3 - 10		FP	OEM Name
 148+ *
 149+ *	11 - 61		FMP	FAT12/16 BPB
 150+ *				Whilst not strictly necessary for MBR,
 151+ *				GRUB reserves this area
 152+ *
 153+ *	11 - 89		P	FAT32 BPB
 154+ *				(are we ever going to boot off this?)
 155+ *
 156+ *
 157+ *	62 - 217	FMP	Boot code
 158+ *
 159+ *	90 - 217	P	FAT32 boot code
 160+ *
 161+ *	218 - 223	M	Win95b/98/me "drive time"
 162+ *		http://www.geocities.com/thestarman3/asm/mbr/95BMEMBR.htm#MYST
 163+ *				only changed if all 6 bytes are 0
 164+ *
 165+ *	224 - 436	FMP	boot code (continued)
 166+ *
 167+ *	437 - 439	M	WinNT/2K/XP MBR "boot language"
 168+ *		http://www.geocities.com/thestarman3/asm/mbr/Win2kmbr.htm
 169+ *				not needed by us
 170+ *
 171+ *	400 - 439	MP	NetBSD: mbr_bootsel
 172+ *
 173+ *	424 - 439	M	NetBSD: bootptn_guid (in GPT PMBR only)
 174+ *
 175+ *	440 - 443	M	WinNT/2K/XP Drive Serial Number (NT DSN)
 176+ *		http://www.geocities.com/thestarman3/asm/mbr/Win2kmbr.htm
 177+ *
 178+ *	444 - 445	FMP	bootcode or unused
 179+ *				NetBSD: mbr_bootsel_magic
 180+ *
 181+ *	446 - 509	M	partition table
 182+ *
 183+ *	510 - 511	FMP	magic number (0xAA55)
 184+ *
 185+ *	Use:
 186+ *	----
 187+ *	F	Floppy boot sector
 188+ *	M	Master Boot Record
 189+ *	P	Partition Boot record
 190+ *
 191+ */
 192+
 193+/*
 194+ * MBR (Master Boot Record)
 195+ */
 196+#define	MBR_BBSECTOR		0	/* MBR relative sector # */
 197+#define	MBR_BPB_OFFSET		11	/* offsetof(mbr_sector, mbr_bpb) */
 198+#define	MBR_BOOTCODE_OFFSET	90	/* offsetof(mbr_sector, mbr_bootcode) */
 199+#define	MBR_BS_OFFSET		400	/* offsetof(mbr_sector, mbr_bootsel) */
 200+#define	MBR_BS_OLD_OFFSET	404	/* where mbr_bootsel used to be */
 201+#define	MBR_GPT_GUID_OFFSET	424	/* location of partition GUID to boot */
 202+#define	MBR_GPT_GUID_DEFAULT		/* default uninitialized GUID */ \
 203+	{0xeee69d04,0x02f4,0x11e0,0x8f,0x5d,{0x00,0xe0,0x81,0x52,0x9a,0x6b}}
 204+#define	MBR_DSN_OFFSET		440	/* offsetof(mbr_sector, mbr_dsn) */
 205+#define	MBR_BS_MAGIC_OFFSET	444	/* offsetof(mbr_sector, mbr_bootsel_magic) */
 206+#define	MBR_PART_OFFSET		446	/* offsetof(mbr_sector, mbr_part[0]) */
 207+#define	MBR_MAGIC_OFFSET	510	/* offsetof(mbr_sector, mbr_magic) */
 208+#define	MBR_MAGIC		0xaa55	/* MBR magic number */
 209+#define	MBR_BS_MAGIC		0xb5e1	/* mbr_bootsel magic number */
 210+#define	MBR_PART_COUNT		4	/* Number of partitions in MBR */
 211+#define	MBR_BS_PARTNAMESIZE	8	/* Size of name mbr_bootsel nametab */
 212+					/* (excluding trailing NUL) */
 213+
 214+		/* values for mbr_partition.mbrp_flag */
 215+#define	MBR_PFLAG_ACTIVE	0x80	/* The active partition */
 216+
 217+		/* values for mbr_partition.mbrp_type */
 218+#define	MBR_PTYPE_UNUSED	0x00	/* Unused */
 219+#define	MBR_PTYPE_FAT12		0x01	/* 12-bit FAT */
 220+#define	MBR_PTYPE_XENIX_ROOT	0x02	/* XENIX / */
 221+#define	MBR_PTYPE_XENIX_USR	0x03	/* XENIX /usr */
 222+#define	MBR_PTYPE_FAT16S	0x04	/* 16-bit FAT, less than 32M */
 223+#define	MBR_PTYPE_EXT		0x05	/* extended partition */
 224+#define	MBR_PTYPE_FAT16B	0x06	/* 16-bit FAT, more than 32M */
 225+#define	MBR_PTYPE_NTFS		0x07	/* OS/2 HPFS, NTFS, QNX2, Adv. UNIX */
 226+#define	MBR_PTYPE_DELL		0x08	/* AIX or os, or etc. */
 227+#define MBR_PTYPE_AIX_BOOT	0x09	/* AIX boot partition or Coherent */
 228+#define MBR_PTYPE_OS2_BOOT	0x0a	/* O/2 boot manager or Coherent swap */
 229+#define	MBR_PTYPE_FAT32		0x0b	/* 32-bit FAT */
 230+#define	MBR_PTYPE_FAT32L	0x0c	/* 32-bit FAT, LBA-mapped */
 231+#define	MBR_PTYPE_7XXX		0x0d	/* 7XXX, LBA-mapped */
 232+#define	MBR_PTYPE_FAT16L	0x0e	/* 16-bit FAT, LBA-mapped */
 233+#define	MBR_PTYPE_EXT_LBA	0x0f	/* extended partition, LBA-mapped */
 234+#define	MBR_PTYPE_OPUS		0x10	/* OPUS */
 235+#define MBR_PTYPE_OS2_DOS12	0x11 	/* OS/2 DOS 12-bit FAT */
 236+#define MBR_PTYPE_COMPAQ_DIAG	0x12 	/* Compaq diagnostics */
 237+#define MBR_PTYPE_OS2_DOS16S	0x14 	/* OS/2 DOS 16-bit FAT <32M */
 238+#define MBR_PTYPE_OS2_DOS16B	0x16 	/* OS/2 DOS 16-bit FAT >=32M */
 239+#define MBR_PTYPE_OS2_IFS	0x17 	/* OS/2 hidden IFS */
 240+#define MBR_PTYPE_AST_SWAP	0x18 	/* AST Windows swapfile */
 241+#define MBR_PTYPE_WILLOWTECH	0x19 	/* Willowtech Photon coS */
 242+#define MBR_PTYPE_HID_FAT32	0x1b 	/* hidden win95 fat 32 */
 243+#define MBR_PTYPE_HID_FAT32_LBA	0x1c 	/* hidden win95 fat 32 lba */
 244+#define MBR_PTYPE_HID_FAT16_LBA	0x1d	/* hidden win95 fat 16 lba */
 245+#define MBR_PTYPE_WILLOWSOFT	0x20 	/* Willowsoft OFS1 */
 246+#define MBR_PTYPE_RESERVED_x21	0x21 	/* reserved */
 247+#define MBR_PTYPE_RESERVED_x23	0x23 	/* reserved */
 248+#define MBR_PTYPE_RESERVED_x24	0x24	/* NEC DOS */
 249+#define MBR_PTYPE_RESERVED_x26	0x26 	/* reserved */
 250+#define MBR_PTYPE_RESERVED_x31	0x31 	/* reserved */
 251+#define MBR_PTYPE_NOS		0x32	/* Alien Internet Services NOS */
 252+#define MBR_PTYPE_RESERVED_x33	0x33 	/* reserved */
 253+#define MBR_PTYPE_RESERVED_x34	0x34 	/* reserved */
 254+#define MBR_PTYPE_OS2_JFS	0x35 	/* JFS on OS2 */
 255+#define MBR_PTYPE_RESERVED_x36	0x36 	/* reserved */
 256+#define MBR_PTYPE_THEOS		0x38 	/* Theos */
 257+#define MBR_PTYPE_PLAN9		0x39 	/* Plan 9, or Theos spanned */
 258+#define MBR_PTYPE_THEOS_4GB	0x3a 	/* Theos ver 4 4gb partition */
 259+#define MBR_PTYPE_THEOS_EXT	0x3b 	/* Theos ve 4 extended partition */
 260+#define MBR_PTYPE_PMRECOVERY	0x3c 	/* PartitionMagic recovery */
 261+#define MBR_PTYPE_HID_NETWARE	0x3d 	/* Hidden Netware */
 262+#define MBR_PTYPE_VENIX		0x40 	/* VENIX 286 or LynxOS */
 263+#define	MBR_PTYPE_PREP		0x41	/* PReP */
 264+#define	MBR_PTYPE_DRDOS_LSWAP	0x42	/* linux swap sharing DRDOS disk */
 265+#define	MBR_PTYPE_DRDOS_LINUX	0x43	/* linux sharing DRDOS disk */
 266+#define	MBR_PTYPE_GOBACK	0x44	/* GoBack change utility */
 267+#define	MBR_PTYPE_BOOT_US	0x45	/* Boot US Boot manager */
 268+#define	MBR_PTYPE_EUMEL_x46	0x46	/* EUMEL/Elan or Ergos 3 */
 269+#define	MBR_PTYPE_EUMEL_x47	0x47	/* EUMEL/Elan or Ergos 3 */
 270+#define	MBR_PTYPE_EUMEL_x48	0x48	/* EUMEL/Elan or Ergos 3 */
 271+#define	MBR_PTYPE_ALFS_THIN	0x4a	/* ALFX/THIN filesystem for DOS */
 272+#define	MBR_PTYPE_OBERON	0x4c	/* Oberon partition */
 273+#define MBR_PTYPE_QNX4X		0x4d 	/* QNX4.x */
 274+#define MBR_PTYPE_QNX4X_2	0x4e 	/* QNX4.x 2nd part */
 275+#define MBR_PTYPE_QNX4X_3	0x4f 	/* QNX4.x 3rd part */
 276+#define MBR_PTYPE_DM		0x50 	/* DM (disk manager) */
 277+#define MBR_PTYPE_DM6_AUX1	0x51 	/* DM6 Aux1 (or Novell) */
 278+#define MBR_PTYPE_CPM		0x52 	/* CP/M or Microport SysV/AT */
 279+#define MBR_PTYPE_DM6_AUX3	0x53 	/* DM6 Aux3 */
 280+#define	MBR_PTYPE_DM6_DDO	0x54	/* DM6 DDO */
 281+#define MBR_PTYPE_EZDRIVE	0x55	/* EZ-Drive (disk manager) */
 282+#define MBR_PTYPE_GOLDEN_BOW	0x56	/* Golden Bow (disk manager) */
 283+#define MBR_PTYPE_DRIVE_PRO	0x57	/* Drive PRO */
 284+#define MBR_PTYPE_PRIAM_EDISK	0x5c	/* Priam Edisk (disk manager) */
 285+#define MBR_PTYPE_SPEEDSTOR	0x61	/* SpeedStor */
 286+#define MBR_PTYPE_HURD		0x63	/* GNU HURD or Mach or Sys V/386 */
 287+#define MBR_PTYPE_NOVELL_2XX	0x64	/* Novell Netware 2.xx or Speedstore */
 288+#define MBR_PTYPE_NOVELL_3XX	0x65	/* Novell Netware 3.xx */
 289+#define MBR_PTYPE_NOVELL_386	0x66	/* Novell 386 Netware */
 290+#define MBR_PTYPE_NOVELL_x67	0x67	/* Novell */
 291+#define MBR_PTYPE_NOVELL_x68	0x68	/* Novell */
 292+#define MBR_PTYPE_NOVELL_x69	0x69	/* Novell */
 293+#define MBR_PTYPE_DISKSECURE	0x70	/* DiskSecure Multi-Boot */
 294+#define MBR_PTYPE_RESERVED_x71	0x71	/* reserved */
 295+#define MBR_PTYPE_RESERVED_x73	0x73	/* reserved */
 296+#define MBR_PTYPE_RESERVED_x74	0x74	/* reserved */
 297+#define MBR_PTYPE_PCIX		0x75	/* PC/IX */
 298+#define MBR_PTYPE_RESERVED_x76	0x76	/* reserved */
 299+#define MBR_PTYPE_M2FS_M2CS	0x77	/* M2FS/M2CS partition */
 300+#define MBR_PTYPE_XOSL_FS	0x78	/* XOSL boot loader filesystem */
 301+#define MBR_PTYPE_MINIX_14A	0x80	/* MINIX until 1.4a */
 302+#define MBR_PTYPE_MINIX_14B	0x81	/* MINIX since 1.4b */
 303+#define	MBR_PTYPE_LNXSWAP	0x82	/* Linux swap or Solaris */
 304+#define	MBR_PTYPE_LNXEXT2	0x83	/* Linux native */
 305+#define MBR_PTYPE_OS2_C		0x84	/* OS/2 hidden C: drive */
 306+#define	MBR_PTYPE_EXT_LNX	0x85	/* Linux extended partition */
 307+#define	MBR_PTYPE_NTFATVOL 	0x86	/* NT FAT volume set */
 308+#define	MBR_PTYPE_NTFSVOL	0x87	/* NTFS volume set or HPFS mirrored */
 309+#define	MBR_PTYPE_LNX_KERNEL	0x8a	/* Linux Kernel AiR-BOOT partition */
 310+#define	MBR_PTYPE_FT_FAT32	0x8b	/* Legacy Fault tolerant FAT32 */
 311+#define	MBR_PTYPE_FT_FAT32_EXT	0x8c	/* Legacy Fault tolerant FAT32 ext */
 312+#define	MBR_PTYPE_HID_FR_FD_12	0x8d	/* Hidden free FDISK FAT12 */
 313+#define	MBR_PTYPE_LNX_LVM	0x8e	/* Linux Logical Volume Manager */
 314+#define	MBR_PTYPE_HID_FR_FD_16	0x90	/* Hidden free FDISK FAT16 */
 315+#define	MBR_PTYPE_HID_FR_FD_EXT	0x91	/* Hidden free FDISK DOS EXT */
 316+#define	MBR_PTYPE_HID_FR_FD_16B	0x92	/* Hidden free FDISK FAT16 Big */
 317+#define MBR_PTYPE_AMOEBA_FS 	0x93	/* Amoeba filesystem */
 318+#define MBR_PTYPE_AMOEBA_BAD 	0x94	/* Amoeba bad block table */
 319+#define MBR_PTYPE_MIT_EXOPC 	0x95	/* MIT EXOPC native partitions */
 320+#define	MBR_PTYPE_HID_FR_FD_32	0x97	/* Hidden free FDISK FAT32 */
 321+#define	MBR_PTYPE_DATALIGHT	0x98	/* Datalight ROM-DOS Super-Boot */
 322+#define MBR_PTYPE_MYLEX 	0x99	/* Mylex EISA SCSI */
 323+#define	MBR_PTYPE_HID_FR_FD_16L	0x9a	/* Hidden free FDISK FAT16 LBA */
 324+#define	MBR_PTYPE_HID_FR_FD_EXL	0x9b	/* Hidden free FDISK EXT LBA */
 325+#define MBR_PTYPE_BSDI	 	0x9f	/* BSDI? */
 326+#define MBR_PTYPE_IBM_HIB	0xa0	/* IBM Thinkpad hibernation */
 327+#define MBR_PTYPE_HP_VOL_xA1	0xa1	/* HP Volume expansion (SpeedStor) */
 328+#define MBR_PTYPE_HP_VOL_xA3	0xa3	/* HP Volume expansion (SpeedStor) */
 329+#define MBR_PTYPE_HP_VOL_xA4	0xa4	/* HP Volume expansion (SpeedStor) */
 330+#define	MBR_PTYPE_386BSD	0xa5	/* 386BSD partition type */
 331+#define	MBR_PTYPE_OPENBSD	0xa6	/* OpenBSD partition type */
 332+#define	MBR_PTYPE_NEXTSTEP_486 	0xa7	/* NeXTSTEP 486 */
 333+#define	MBR_PTYPE_APPLE_UFS 	0xa8	/* Apple UFS */
 334+#define	MBR_PTYPE_NETBSD	0xa9	/* NetBSD partition type */
 335+#define MBR_PTYPE_OLIVETTI	0xaa	/* Olivetty Fat12 1.44MB Service part */
 336+#define MBR_PTYPE_APPLE_BOOT	0xab	/* Apple Boot */
 337+#define MBR_PTYPE_SHAG_OS	0xae	/* SHAG OS filesystem */
 338+#define MBR_PTYPE_APPLE_HFS	0xaf	/* Apple HFS */
 339+#define MBR_PTYPE_BOOTSTAR_DUM	0xb0	/* BootStar Dummy */
 340+#define MBR_PTYPE_RESERVED_xB1	0xb1	/* reserved */
 341+#define MBR_PTYPE_RESERVED_xB3	0xb3	/* reserved */
 342+#define MBR_PTYPE_RESERVED_xB4	0xb4	/* reserved */
 343+#define MBR_PTYPE_RESERVED_xB6	0xb6	/* reserved */
 344+#define MBR_PTYPE_BSDI_386	0xb7	/* BSDI BSD/386 filesystem */
 345+#define MBR_PTYPE_BSDI_SWAP	0xb8	/* BSDI BSD/386 swap */
 346+#define	MBR_PTYPE_BOOT_WIZARD	0xbb	/* Boot Wizard Hidden */
 347+#define	MBR_PTYPE_SOLARIS_8	0xbe	/* Solaris 8 partition type */
 348+#define	MBR_PTYPE_SOLARIS	0xbf	/* Solaris partition type */
 349+#define MBR_PTYPE_CTOS		0xc0 	/* CTOS */
 350+#define MBR_PTYPE_DRDOS_FAT12	0xc1 	/* DRDOS/sec (FAT-12) */
 351+#define MBR_PTYPE_HID_LNX	0xc2 	/* Hidden Linux */
 352+#define MBR_PTYPE_HID_LNX_SWAP	0xc3 	/* Hidden Linux swap */
 353+#define MBR_PTYPE_DRDOS_FAT16S	0xc4 	/* DRDOS/sec (FAT-16, < 32M) */
 354+#define MBR_PTYPE_DRDOS_EXT	0xc5 	/* DRDOS/sec (EXT) */
 355+#define MBR_PTYPE_DRDOS_FAT16B	0xc6 	/* DRDOS/sec (FAT-16, >= 32M) */
 356+#define MBR_PTYPE_SYRINX	0xc7 	/* Syrinx (Cyrnix?) or HPFS disabled */
 357+#define MBR_PTYPE_DRDOS_8_xC8	0xc8 	/* Reserved for DR-DOS 8.0+ */
 358+#define MBR_PTYPE_DRDOS_8_xC9	0xc9 	/* Reserved for DR-DOS 8.0+ */
 359+#define MBR_PTYPE_DRDOS_8_xCA	0xca 	/* Reserved for DR-DOS 8.0+ */
 360+#define MBR_PTYPE_DRDOS_74_CHS	0xcb 	/* DR-DOS 7.04+ Secured FAT32 CHS */
 361+#define MBR_PTYPE_DRDOS_74_LBA	0xcc 	/* DR-DOS 7.04+ Secured FAT32 LBA */
 362+#define MBR_PTYPE_CTOS_MEMDUMP	0xcd	/* CTOS Memdump */
 363+#define MBR_PTYPE_DRDOS_74_16X	0xce 	/* DR-DOS 7.04+ FAT16X LBA */
 364+#define MBR_PTYPE_DRDOS_74_EXT	0xcf 	/* DR-DOS 7.04+ EXT LBA */
 365+#define MBR_PTYPE_REAL32	0xd0 	/* REAL/32 secure big partition */
 366+#define MBR_PTYPE_MDOS_FAT12	0xd1 	/* Old Multiuser DOS FAT12 */
 367+#define MBR_PTYPE_MDOS_FAT16S	0xd4 	/* Old Multiuser DOS FAT16 Small */
 368+#define MBR_PTYPE_MDOS_EXT	0xd5 	/* Old Multiuser DOS Extended */
 369+#define MBR_PTYPE_MDOS_FAT16B	0xd6 	/* Old Multiuser DOS FAT16 Big */
 370+#define MBR_PTYPE_CPM_86	0xd8 	/* CP/M 86 */
 371+#define MBR_PTYPE_CONCURRENT	0xdb 	/* CP/M or Concurrent CP/M */
 372+#define MBR_PTYPE_HID_CTOS_MEM	0xdd 	/* Hidden CTOS Memdump */
 373+#define MBR_PTYPE_DELL_UTIL	0xde 	/* Dell PowerEdge Server utilities */
 374+#define MBR_PTYPE_DGUX_VIRTUAL	0xdf 	/* DG/UX virtual disk manager */
 375+#define MBR_PTYPE_STMICROELEC	0xe0 	/* STMicroelectronics ST AVFS */
 376+#define MBR_PTYPE_DOS_ACCESS	0xe1 	/* DOS access or SpeedStor 12-bit */
 377+#define MBR_PTYPE_STORDIM	0xe3 	/* DOS R/O or Storage Dimensions */
 378+#define MBR_PTYPE_SPEEDSTOR_16S	0xe4 	/* SpeedStor 16-bit FAT < 1024 cyl. */
 379+#define MBR_PTYPE_RESERVED_xE5	0xe5	/* reserved */
 380+#define MBR_PTYPE_RESERVED_xE6	0xe6	/* reserved */
 381+#define MBR_PTYPE_BEOS		0xeb 	/* BeOS */
 382+#define	MBR_PTYPE_PMBR		0xee	/* GPT Protective MBR */
 383+#define	MBR_PTYPE_EFI		0xef	/* EFI system partition */
 384+#define MBR_PTYPE_LNX_PA_RISC	0xf0 	/* Linux PA-RISC boot loader */
 385+#define MBR_PTYPE_SPEEDSTOR_X	0xf1 	/* SpeedStor or Storage Dimensions */
 386+#define MBR_PTYPE_DOS33_SEC	0xf2 	/* DOS 3.3+ Secondary */
 387+#define MBR_PTYPE_RESERVED_xF3	0xf3	/* reserved */
 388+#define MBR_PTYPE_SPEEDSTOR_L	0xf4	/* SpeedStor large partition */
 389+#define MBR_PTYPE_PROLOGUE	0xf5	/* Prologue multi-volumen partition */
 390+#define MBR_PTYPE_RESERVED_xF6	0xf6 	/* reserved */
 391+#define MBR_PTYPE_PCACHE	0xf9 	/* pCache: ext2/ext3 persistent cache */
 392+#define MBR_PTYPE_BOCHS		0xfa 	/* Bochs x86 emulator */
 393+#define MBR_PTYPE_VMWARE	0xfb 	/* VMware File System */
 394+#define MBR_PTYPE_VMWARE_SWAP	0xfc 	/* VMware Swap */
 395+#define MBR_PTYPE_LNX_RAID	0xfd 	/* Linux RAID partition persistent sb */
 396+#define MBR_PTYPE_LANSTEP	0xfe	/* LANstep or IBM PS/2 IML */
 397+#define MBR_PTYPE_XENIX_BAD	0xff 	/* Xenix Bad Block Table */
 398+
 399+#ifdef MBRPTYPENAMES
 400+static const struct mbr_ptype {
 401+	int id;
 402+	const char *name;
 403+} mbr_ptypes[] = {
 404+	{ MBR_PTYPE_UNUSED, "<UNUSED>" },
 405+	{ MBR_PTYPE_FAT12, "Primary DOS with 12 bit FAT" },
 406+	{ MBR_PTYPE_XENIX_ROOT, "XENIX / filesystem" },
 407+	{ MBR_PTYPE_XENIX_USR, "XENIX /usr filesystem" },
 408+	{ MBR_PTYPE_FAT16S, "Primary DOS with 16 bit FAT <32M" },
 409+	{ MBR_PTYPE_EXT, "Extended partition" },
 410+	{ MBR_PTYPE_FAT16B, "Primary 'big' DOS, 16-bit FAT (> 32MB)" },
 411+	{ MBR_PTYPE_NTFS, "NTFS, OS/2 HPFS, QNX2 or Advanced UNIX" },
 412+	{ MBR_PTYPE_DELL, "AIX filesystem or OS/2 (thru v1.3) or DELL "
 413+			  "multiple drives or Commodore DOS or SplitDrive" },
 414+	{ MBR_PTYPE_AIX_BOOT, "AIX boot partition or Coherent" },
 415+	{ MBR_PTYPE_OS2_BOOT, "OS/2 Boot Manager or Coherent swap or OPUS" },
 416+	{ MBR_PTYPE_FAT32, "Primary DOS with 32 bit FAT" },
 417+	{ MBR_PTYPE_FAT32L, "Primary DOS with 32 bit FAT - LBA" },
 418+	{ MBR_PTYPE_7XXX, "Type 7??? - LBA" },
 419+	{ MBR_PTYPE_FAT16L, "DOS (16-bit FAT) - LBA" },
 420+	{ MBR_PTYPE_EXT_LBA, "Ext. partition - LBA" },
 421+	{ MBR_PTYPE_OPUS, "OPUS" },
 422+	{ MBR_PTYPE_OS2_DOS12, "OS/2 BM: hidden DOS 12-bit FAT" },
 423+	{ MBR_PTYPE_COMPAQ_DIAG, "Compaq diagnostics" },
 424+	{ MBR_PTYPE_OS2_DOS16S, "OS/2 BM: hidden DOS 16-bit FAT <32M "
 425+				"or Novell DOS 7.0 bug" },
 426+	{ MBR_PTYPE_OS2_DOS16B, "OS/2 BM: hidden DOS 16-bit FAT >=32M" },
 427+	{ MBR_PTYPE_OS2_IFS, "OS/2 BM: hidden IFS" },
 428+	{ MBR_PTYPE_AST_SWAP, "AST Windows swapfile" },
 429+	{ MBR_PTYPE_WILLOWTECH, "Willowtech Photon coS" },
 430+	{ MBR_PTYPE_HID_FAT32, "hidden Windows/95 FAT32" },
 431+	{ MBR_PTYPE_HID_FAT32_LBA, "hidden Windows/95 FAT32 LBA" },
 432+	{ MBR_PTYPE_HID_FAT16_LBA, "hidden Windows/95 FAT16 LBA" },
 433+	{ MBR_PTYPE_WILLOWSOFT, "Willowsoft OFS1" },
 434+	{ MBR_PTYPE_RESERVED_x21, "reserved" },
 435+	{ MBR_PTYPE_RESERVED_x23, "reserved" },
 436+	{ MBR_PTYPE_RESERVED_x24, "NEC DOS"},
 437+	{ MBR_PTYPE_RESERVED_x26, "reserved" },
 438+	{ MBR_PTYPE_RESERVED_x31, "reserved" },
 439+	{ MBR_PTYPE_NOS, "Alien Internet Services NOS" },
 440+	{ MBR_PTYPE_RESERVED_x33, "reserved" },
 441+	{ MBR_PTYPE_RESERVED_x34, "reserved" },
 442+	{ MBR_PTYPE_OS2_JFS, "JFS on OS2" },
 443+	{ MBR_PTYPE_RESERVED_x36, "reserved" },
 444+	{ MBR_PTYPE_THEOS, "Theos" },
 445+	{ MBR_PTYPE_PLAN9, "Plan 9" },
 446+	{ MBR_PTYPE_PLAN9, "Plan 9, or Theos spanned" },
 447+	{ MBR_PTYPE_THEOS_4GB,	"Theos ver 4 4gb partition" },
 448+	{ MBR_PTYPE_THEOS_EXT,	"Theos ve 4 extended partition" },
 449+	{ MBR_PTYPE_PMRECOVERY, "PartitionMagic recovery" },
 450+	{ MBR_PTYPE_HID_NETWARE, "Hidden Netware" },
 451+	{ MBR_PTYPE_VENIX, "VENIX 286 or LynxOS" },
 452+	{ MBR_PTYPE_PREP, "Linux/MINIX (sharing disk with DRDOS) "
 453+			  "or Personal RISC boot" },
 454+	{ MBR_PTYPE_DRDOS_LSWAP, "SFS or Linux swap "
 455+				 "(sharing disk with DRDOS)" },
 456+	{ MBR_PTYPE_DRDOS_LINUX, "Linux native (sharing disk with DRDOS)" },
 457+	{ MBR_PTYPE_GOBACK, "GoBack change utility" },
 458+	{ MBR_PTYPE_BOOT_US, "Boot US Boot manager" },
 459+	{ MBR_PTYPE_EUMEL_x46, "EUMEL/Elan or Ergos 3" },
 460+	{ MBR_PTYPE_EUMEL_x47, "EUMEL/Elan or Ergos 3" },
 461+	{ MBR_PTYPE_EUMEL_x48, "EUMEL/Elan or Ergos 3" },
 462+	{ MBR_PTYPE_ALFS_THIN, "ALFX/THIN filesystem for DOS" },
 463+	{ MBR_PTYPE_OBERON, "Oberon partition" },
 464+	{ MBR_PTYPE_QNX4X, "QNX4.x" },
 465+	{ MBR_PTYPE_QNX4X_2, "QNX4.x 2nd part" },
 466+	{ MBR_PTYPE_QNX4X_3, "QNX4.x 3rd part" },
 467+	{ MBR_PTYPE_DM, "DM (disk manager)" },
 468+	{ MBR_PTYPE_DM6_AUX1, "DM6 Aux1 (or Novell)" },
 469+	{ MBR_PTYPE_CPM, "CP/M or Microport SysV/AT" },
 470+	{ MBR_PTYPE_DM6_AUX3, "DM6 Aux3" },
 471+	{ MBR_PTYPE_DM6_DDO, "DM6 DDO" },
 472+	{ MBR_PTYPE_EZDRIVE, "EZ-Drive (disk manager)" },
 473+	{ MBR_PTYPE_GOLDEN_BOW, "Golden Bow (disk manager)" },
 474+	{ MBR_PTYPE_DRIVE_PRO, "Drive PRO" },
 475+	{ MBR_PTYPE_PRIAM_EDISK, "Priam Edisk (disk manager)" },
 476+	{ MBR_PTYPE_SPEEDSTOR, "SpeedStor" },
 477+	{ MBR_PTYPE_HURD, "GNU HURD or Mach or Sys V/386 "
 478+			  "(such as ISC UNIX) or MtXinu" },
 479+	{ MBR_PTYPE_NOVELL_2XX, "Novell Netware 2.xx or Speedstore" },
 480+	{ MBR_PTYPE_NOVELL_3XX, "Novell Netware 3.xx" },
 481+	{ MBR_PTYPE_NOVELL_386, "Novell 386 Netware" },
 482+	{ MBR_PTYPE_NOVELL_x67, "Novell" },
 483+	{ MBR_PTYPE_NOVELL_x68, "Novell" },
 484+	{ MBR_PTYPE_NOVELL_x69, "Novell" },
 485+	{ MBR_PTYPE_DISKSECURE, "DiskSecure Multi-Boot" },
 486+	{ MBR_PTYPE_RESERVED_x71, "reserved" },
 487+	{ MBR_PTYPE_RESERVED_x73, "reserved" },
 488+	{ MBR_PTYPE_RESERVED_x74, "reserved" },
 489+	{ MBR_PTYPE_PCIX, "PC/IX" },
 490+	{ MBR_PTYPE_RESERVED_x76, "reserved" },
 491+	{ MBR_PTYPE_M2FS_M2CS,	"M2FS/M2CS partition" },
 492+	{ MBR_PTYPE_XOSL_FS, "XOSL boot loader filesystem" },
 493+	{ MBR_PTYPE_MINIX_14A, "MINIX until 1.4a" },
 494+	{ MBR_PTYPE_MINIX_14B, "MINIX since 1.4b, early Linux, Mitac dmgr" },
 495+	{ MBR_PTYPE_LNXSWAP, "Linux swap or Prime or Solaris" },
 496+	{ MBR_PTYPE_LNXEXT2, "Linux native" },
 497+	{ MBR_PTYPE_OS2_C, "OS/2 hidden C: drive" },
 498+	{ MBR_PTYPE_EXT_LNX, "Linux extended" },
 499+	{ MBR_PTYPE_NTFATVOL, "NT FAT volume set" },
 500+	{ MBR_PTYPE_NTFSVOL, "NTFS volume set or HPFS mirrored" },
 501+	{ MBR_PTYPE_LNX_KERNEL,	"Linux Kernel AiR-BOOT partition" },
 502+	{ MBR_PTYPE_FT_FAT32, "Legacy Fault tolerant FAT32" },
 503+	{ MBR_PTYPE_FT_FAT32_EXT, "Legacy Fault tolerant FAT32 ext" },
 504+	{ MBR_PTYPE_HID_FR_FD_12, "Hidden free FDISK FAT12" },
 505+	{ MBR_PTYPE_LNX_LVM, "Linux Logical Volume Manager" },
 506+	{ MBR_PTYPE_HID_FR_FD_16, "Hidden free FDISK FAT16" },
 507+	{ MBR_PTYPE_HID_FR_FD_EXT, "Hidden free FDISK DOS EXT" },
 508+	{ MBR_PTYPE_HID_FR_FD_16L, "Hidden free FDISK FAT16 Large" },
 509+	{ MBR_PTYPE_AMOEBA_FS, "Amoeba filesystem" },
 510+	{ MBR_PTYPE_AMOEBA_BAD, "Amoeba bad block table" },
 511+	{ MBR_PTYPE_MIT_EXOPC, "MIT EXOPC native partitions" },
 512+	{ MBR_PTYPE_HID_FR_FD_32, "Hidden free FDISK FAT32" },
 513+	{ MBR_PTYPE_DATALIGHT, "Datalight ROM-DOS Super-Boot" },
 514+	{ MBR_PTYPE_MYLEX, "Mylex EISA SCSI" },
 515+	{ MBR_PTYPE_HID_FR_FD_16L, "Hidden free FDISK FAT16 LBA" },
 516+	{ MBR_PTYPE_HID_FR_FD_EXL, "Hidden free FDISK EXT LBA" },
 517+	{ MBR_PTYPE_BSDI, "BSDI?" },
 518+	{ MBR_PTYPE_IBM_HIB, "IBM Thinkpad hibernation" },
 519+	{ MBR_PTYPE_HP_VOL_xA1, "HP Volume expansion (SpeedStor)" },
 520+	{ MBR_PTYPE_HP_VOL_xA3, "HP Volume expansion (SpeedStor)" },
 521+	{ MBR_PTYPE_HP_VOL_xA4, "HP Volume expansion (SpeedStor)" },
 522+	{ MBR_PTYPE_386BSD, "FreeBSD or 386BSD or old NetBSD" },
 523+	{ MBR_PTYPE_OPENBSD, "OpenBSD" },
 524+	{ MBR_PTYPE_NEXTSTEP_486, "NeXTSTEP 486" },
 525+	{ MBR_PTYPE_APPLE_UFS, "Apple UFS" },
 526+	{ MBR_PTYPE_NETBSD, "NetBSD" },
 527+	{ MBR_PTYPE_OLIVETTI, "Olivetty Fat12 1.44MB Service part" },
 528+	{ MBR_PTYPE_SHAG_OS, "SHAG OS filesystem" },
 529+	{ MBR_PTYPE_BOOTSTAR_DUM, "BootStar Dummy" },
 530+	{ MBR_PTYPE_BOOT_WIZARD, "Boot Wizard Hidden" },
 531+	{ MBR_PTYPE_APPLE_BOOT, "Apple Boot" },
 532+	{ MBR_PTYPE_APPLE_HFS, "Apple HFS" },
 533+	{ MBR_PTYPE_RESERVED_xB6, "reserved" },
 534+	{ MBR_PTYPE_RESERVED_xB6, "reserved" },
 535+	{ MBR_PTYPE_RESERVED_xB6, "reserved" },
 536+	{ MBR_PTYPE_RESERVED_xB6, "reserved" },
 537+	{ MBR_PTYPE_BSDI_386, "BSDI BSD/386 filesystem" },
 538+	{ MBR_PTYPE_BSDI_SWAP, "BSDI BSD/386 swap" },
 539+	{ MBR_PTYPE_SOLARIS_8, "Solaris 8 boot partition" },
 540+	{ MBR_PTYPE_SOLARIS, "Solaris boot partition" },
 541+	{ MBR_PTYPE_CTOS, "CTOS" },
 542+	{ MBR_PTYPE_DRDOS_FAT12, "DRDOS/sec (FAT-12)" },
 543+	{ MBR_PTYPE_HID_LNX, "Hidden Linux" },
 544+	{ MBR_PTYPE_HID_LNX_SWAP, "Hidden Linux Swap" },
 545+	{ MBR_PTYPE_DRDOS_FAT16S, "DRDOS/sec (FAT-16, < 32M)" },
 546+	{ MBR_PTYPE_DRDOS_EXT, "DRDOS/sec (EXT)" },
 547+	{ MBR_PTYPE_DRDOS_FAT16B, "DRDOS/sec (FAT-16, >= 32M)" },
 548+	{ MBR_PTYPE_SYRINX, "Syrinx (Cyrnix?) or HPFS disabled" },
 549+	{ MBR_PTYPE_DRDOS_8_xC8, "Reserved for DR-DOS 8.0+" },
 550+	{ MBR_PTYPE_DRDOS_8_xC9, "Reserved for DR-DOS 8.0+" },
 551+	{ MBR_PTYPE_DRDOS_8_xCA, "Reserved for DR-DOS 8.0+" },
 552+	{ MBR_PTYPE_DRDOS_74_CHS, "DR-DOS 7.04+ Secured FAT32 CHS" },
 553+	{ MBR_PTYPE_DRDOS_74_LBA, "DR-DOS 7.04+ Secured FAT32 LBA" },
 554+	{ MBR_PTYPE_CTOS_MEMDUMP, "CTOS Memdump" },
 555+	{ MBR_PTYPE_DRDOS_74_16X, "DR-DOS 7.04+ FAT16X LBA" },
 556+	{ MBR_PTYPE_DRDOS_74_EXT, "DR-DOS 7.04+ EXT LBA" },
 557+	{ MBR_PTYPE_REAL32, "REAL/32 secure big partition" },
 558+	{ MBR_PTYPE_MDOS_FAT12, "Old Multiuser DOS FAT12" },
 559+	{ MBR_PTYPE_MDOS_FAT16S, "Old Multiuser DOS FAT16 Small" },
 560+	{ MBR_PTYPE_MDOS_EXT, "Old Multiuser DOS Extended" },
 561+	{ MBR_PTYPE_MDOS_FAT16B, "Old Multiuser DOS FAT16 Big" },
 562+	{ MBR_PTYPE_CPM_86, "CP/M 86" },
 563+	{ MBR_PTYPE_CONCURRENT, "CP/M or Concurrent CP/M or Concurrent DOS "
 564+				"or CTOS" },
 565+	{ MBR_PTYPE_HID_CTOS_MEM, "Hidden CTOS Memdump" },
 566+	{ MBR_PTYPE_DELL_UTIL, "Dell PowerEdge Server utilities" },
 567+	{ MBR_PTYPE_DGUX_VIRTUAL, "DG/UX virtual disk manager" },
 568+	{ MBR_PTYPE_STMICROELEC, "STMicroelectronics ST AVFS" },
 569+	{ MBR_PTYPE_DOS_ACCESS, "DOS access or SpeedStor 12-bit FAT "
 570+				"extended partition" },
 571+	{ MBR_PTYPE_STORDIM, "DOS R/O or SpeedStor or Storage Dimensions" },
 572+	{ MBR_PTYPE_SPEEDSTOR_16S, "SpeedStor 16-bit FAT extended partition "
 573+				   "< 1024 cyl." },
 574+	{ MBR_PTYPE_RESERVED_xE5, "reserved" },
 575+	{ MBR_PTYPE_RESERVED_xE6, "reserved" },
 576+	{ MBR_PTYPE_BEOS, "BeOS" },
 577+	{ MBR_PTYPE_PMBR, "GPT Protective MBR" },
 578+	{ MBR_PTYPE_EFI, "EFI system partition" },
 579+	{ MBR_PTYPE_LNX_PA_RISC, "Linux PA-RISC boot loader" },
 580+	{ MBR_PTYPE_SPEEDSTOR_X, "SpeedStor or Storage Dimensions" },
 581+	{ MBR_PTYPE_DOS33_SEC, "DOS 3.3+ Secondary" },
 582+	{ MBR_PTYPE_RESERVED_xF3, "reserved" },
 583+	{ MBR_PTYPE_SPEEDSTOR_L, "SpeedStor large partition or "
 584+				 "Storage Dimensions" },
 585+	{ MBR_PTYPE_PROLOGUE, "Prologue multi-volumen partition" },
 586+	{ MBR_PTYPE_RESERVED_xF6, "reserved" },
 587+	{ MBR_PTYPE_PCACHE, "pCache: ext2/ext3 persistent cache" },
 588+	{ MBR_PTYPE_BOCHS, "Bochs x86 emulator" },
 589+	{ MBR_PTYPE_VMWARE, "VMware File System" },
 590+	{ MBR_PTYPE_VMWARE_SWAP, "VMware Swap" },
 591+	{ MBR_PTYPE_LNX_RAID, "Linux RAID partition persistent sb" },
 592+	{ MBR_PTYPE_LANSTEP, "SpeedStor >1024 cyl. or LANstep "
 593+			     "or IBM PS/2 IML" },
 594+	{ MBR_PTYPE_XENIX_BAD, "Xenix Bad Block Table" },
 595+};
 596+#endif
 597+
 598+#define	MBR_PSECT(s)		((s) & 0x3f)
 599+#define	MBR_PCYL(c, s)		((c) + (((s) & 0xc0) << 2))
 600+
 601+#define	MBR_IS_EXTENDED(x)	((x) == MBR_PTYPE_EXT || \
 602+				 (x) == MBR_PTYPE_EXT_LBA || \
 603+				 (x) == MBR_PTYPE_EXT_LNX)
 604+
 605+		/* values for mbr_bootsel.mbrbs_flags */
 606+#define	MBR_BS_ACTIVE	0x01	/* Bootselector active (or code present) */
 607+#define	MBR_BS_EXTINT13	0x02	/* Set by fdisk if LBA needed (deprecated) */
 608+#define	MBR_BS_READ_LBA	0x04	/* Force LBA reads (deprecated) */
 609+#define	MBR_BS_EXTLBA	0x08	/* Extended ptn capable (LBA reads) */
 610+#define	MBR_BS_ASCII	0x10	/* Bootselect code needs ascii key code */
 611+/* This is always set, the bootsel is located using the magic number...  */
 612+#define	MBR_BS_NEWMBR	0x80	/* New bootsel at offset 440 */
 613+
 614+#if !defined(__ASSEMBLER__)					/* { */
 615+
 616+/*
 617+ * (x86) BIOS Parameter Block for FAT12
 618+ */
 619+struct mbr_bpbFAT12 {
 620+	uint16_t	bpbBytesPerSec;	/* bytes per sector */
 621+	uint8_t		bpbSecPerClust;	/* sectors per cluster */
 622+	uint16_t	bpbResSectors;	/* number of reserved sectors */
 623+	uint8_t		bpbFATs;	/* number of FATs */
 624+	uint16_t	bpbRootDirEnts;	/* number of root directory entries */
 625+	uint16_t	bpbSectors;	/* total number of sectors */
 626+	uint8_t		bpbMedia;	/* media descriptor */
 627+	uint16_t	bpbFATsecs;	/* number of sectors per FAT */
 628+	uint16_t	bpbSecPerTrack;	/* sectors per track */
 629+	uint16_t	bpbHeads;	/* number of heads */
 630+	uint16_t	bpbHiddenSecs;	/* # of hidden sectors */
 631+} __packed;
 632+
 633+/*
 634+ * (x86) BIOS Parameter Block for FAT16
 635+ */
 636+struct mbr_bpbFAT16 {
 637+	uint16_t	bpbBytesPerSec;	/* bytes per sector */
 638+	uint8_t		bpbSecPerClust;	/* sectors per cluster */
 639+	uint16_t	bpbResSectors;	/* number of reserved sectors */
 640+	uint8_t		bpbFATs;	/* number of FATs */
 641+	uint16_t	bpbRootDirEnts;	/* number of root directory entries */
 642+	uint16_t	bpbSectors;	/* total number of sectors */
 643+	uint8_t		bpbMedia;	/* media descriptor */
 644+	uint16_t	bpbFATsecs;	/* number of sectors per FAT */
 645+	uint16_t	bpbSecPerTrack;	/* sectors per track */
 646+	uint16_t	bpbHeads;	/* number of heads */
 647+	uint32_t	bpbHiddenSecs;	/* # of hidden sectors */
 648+	uint32_t	bpbHugeSectors;	/* # of sectors if bpbSectors == 0 */
 649+	uint8_t		bsDrvNum;	/* Int 0x13 drive number (e.g. 0x80) */
 650+	uint8_t		bsReserved1;	/* Reserved; set to 0 */
 651+	uint8_t		bsBootSig;	/* 0x29 if next 3 fields are present */
 652+	uint8_t		bsVolID[4];	/* Volume serial number */
 653+	uint8_t		bsVolLab[11];	/* Volume label */
 654+	uint8_t		bsFileSysType[8];
 655+					/* "FAT12   ", "FAT16   ", "FAT     " */
 656+} __packed;
 657+
 658+/*
 659+ * (x86) BIOS Parameter Block for FAT32
 660+ */
 661+struct mbr_bpbFAT32 {
 662+	uint16_t	bpbBytesPerSec;	/* bytes per sector */
 663+	uint8_t		bpbSecPerClust;	/* sectors per cluster */
 664+	uint16_t	bpbResSectors;	/* number of reserved sectors */
 665+	uint8_t		bpbFATs;	/* number of FATs */
 666+	uint16_t	bpbRootDirEnts;	/* number of root directory entries */
 667+	uint16_t	bpbSectors;	/* total number of sectors */
 668+	uint8_t		bpbMedia;	/* media descriptor */
 669+	uint16_t	bpbFATsecs;	/* number of sectors per FAT */
 670+	uint16_t	bpbSecPerTrack;	/* sectors per track */
 671+	uint16_t	bpbHeads;	/* number of heads */
 672+	uint32_t	bpbHiddenSecs;	/* # of hidden sectors */
 673+	uint32_t	bpbHugeSectors;	/* # of sectors if bpbSectors == 0 */
 674+	uint32_t	bpbBigFATsecs;	/* like bpbFATsecs for FAT32 */
 675+	uint16_t	bpbExtFlags;	/* extended flags: */
 676+#define	MBR_FAT32_FATNUM	0x0F	/*   mask for numbering active FAT */
 677+#define	MBR_FAT32_FATMIRROR	0x80	/*   FAT is mirrored (as previously) */
 678+	uint16_t	bpbFSVers;	/* filesystem version */
 679+#define	MBR_FAT32_FSVERS	0	/*   currently only 0 is understood */
 680+	uint32_t	bpbRootClust;	/* start cluster for root directory */
 681+	uint16_t	bpbFSInfo;	/* filesystem info structure sector */
 682+	uint16_t	bpbBackup;	/* backup boot sector */
 683+	uint8_t		bsReserved[12];	/* Reserved for future expansion */
 684+	uint8_t		bsDrvNum;	/* Int 0x13 drive number (e.g. 0x80) */
 685+	uint8_t		bsReserved1;	/* Reserved; set to 0 */
 686+	uint8_t		bsBootSig;	/* 0x29 if next 3 fields are present */
 687+	uint8_t		bsVolID[4];	/* Volume serial number */
 688+	uint8_t		bsVolLab[11];	/* Volume label */
 689+	uint8_t		bsFileSysType[8]; /* "FAT32   " */
 690+} __packed;
 691+
 692+/*
 693+ * (x86) MBR boot selector
 694+ */
 695+struct mbr_bootsel {
 696+	uint8_t		mbrbs_defkey;
 697+	uint8_t		mbrbs_flags;
 698+	uint16_t	mbrbs_timeo;
 699+	char		mbrbs_nametab[MBR_PART_COUNT][MBR_BS_PARTNAMESIZE + 1];
 700+} __packed;
 701+
 702+/*
 703+ * MBR partition
 704+ */
 705+struct mbr_partition {
 706+	uint8_t		mbrp_flag;	/* MBR partition flags */
 707+	uint8_t		mbrp_shd;	/* Starting head */
 708+	uint8_t		mbrp_ssect;	/* Starting sector */
 709+	uint8_t		mbrp_scyl;	/* Starting cylinder */
 710+	uint8_t		mbrp_type;	/* Partition type (see below) */
 711+	uint8_t		mbrp_ehd;	/* End head */
 712+	uint8_t		mbrp_esect;	/* End sector */
 713+	uint8_t		mbrp_ecyl;	/* End cylinder */
 714+	uint32_t	mbrp_start;	/* Absolute starting sector number */
 715+	uint32_t	mbrp_size;	/* Partition size in sectors */
 716+} __packed;
 717+
 718+int xlat_mbr_fstype(int);	/* in sys/lib/libkern/xlat_mbr_fstype.c */
 719+
 720+/*
 721+ * MBR boot sector.
 722+ * This is used by both the MBR (Master Boot Record) in sector 0 of the disk
 723+ * and the PBR (Partition Boot Record) in sector 0 of an MBR partition.
 724+ */
 725+struct mbr_sector {
 726+					/* Jump instruction to boot code.  */
 727+					/* Usually 0xE9nnnn or 0xEBnn90 */
 728+	uint8_t			mbr_jmpboot[3];
 729+					/* OEM name and version */
 730+	uint8_t			mbr_oemname[8];
 731+	union {				/* BIOS Parameter Block */
 732+		struct mbr_bpbFAT12	bpb12;
 733+		struct mbr_bpbFAT16	bpb16;
 734+		struct mbr_bpbFAT32	bpb32;
 735+	} mbr_bpb;
 736+					/* Boot code */
 737+	uint8_t			mbr_bootcode[310];
 738+					/* Config for /usr/mdec/mbr_bootsel */
 739+	struct mbr_bootsel	mbr_bootsel;
 740+					/* NT Drive Serial Number */
 741+	uint32_t		mbr_dsn;
 742+					/* mbr_bootsel magic */
 743+	uint16_t		mbr_bootsel_magic;
 744+					/* MBR partition table */
 745+	struct mbr_partition	mbr_parts[MBR_PART_COUNT];
 746+					/* MBR magic (0xaa55) */
 747+	uint16_t		mbr_magic;
 748+} __packed;
 749+
 750+#endif	/* !defined(__ASSEMBLER__) */				/* } */
 751+
 752+
 753+/* ------------------------------------------
 754+ * shared --
 755+ *	definitions shared by many platforms
 756+ */
 757+
 758+#if !defined(__ASSEMBLER__)					/* { */
 759+
 760+	/* Maximum # of blocks in bbi_block_table, each bbi_block_size long */
 761+#define	SHARED_BBINFO_MAXBLOCKS	118	/* so sizeof(shared_bbinfo) == 512 */
 762+
 763+struct shared_bbinfo {
 764+	uint8_t bbi_magic[32];
 765+	int32_t bbi_block_size;
 766+	int32_t bbi_block_count;
 767+	int32_t bbi_block_table[SHARED_BBINFO_MAXBLOCKS];
 768+};
 769+
 770+/* ------------------------------------------
 771+ * alpha --
 772+ *	Alpha (disk, but also tape) Boot Block.
 773+ *
 774+ *	See Section (III) 3.6.1 of the Alpha Architecture Reference Manual.
 775+ */
 776+
 777+struct alpha_boot_block {
 778+	uint64_t bb_data[63];		/* data (disklabel, also as below) */
 779+	uint64_t bb_cksum;		/* checksum of the boot block,
 780+					 * taken as uint64_t's
 781+					 */
 782+};
 783+#define	bb_secsize	bb_data[60]	/* secondary size (blocks) */
 784+#define	bb_secstart	bb_data[61]	/* secondary start (blocks) */
 785+#define	bb_flags	bb_data[62]	/* unknown flags (set to zero) */
 786+
 787+#define	ALPHA_BOOT_BLOCK_OFFSET		0	/* offset of boot block. */
 788+#define	ALPHA_BOOT_BLOCK_BLOCKSIZE	512	/* block size for sector
 789+						 * size/start, and for boot
 790+						 * block itself.
 791+						 */
 792+
 793+#define	ALPHA_BOOT_BLOCK_CKSUM(bb,cksum)				\
 794+	do {								\
 795+		const struct alpha_boot_block *_bb = (bb);		\
 796+		uint64_t _cksum;					\
 797+		size_t _i;						\
 798+									\
 799+		_cksum = 0;						\
 800+		for (_i = 0;						\
 801+		    _i < (sizeof _bb->bb_data / sizeof _bb->bb_data[0]); \
 802+		    _i++)						\
 803+			_cksum += le64toh(_bb->bb_data[_i]);		\
 804+		*(cksum) = htole64(_cksum);				\
 805+	} while (0)
 806+
 807+/* ------------------------------------------
 808+ * apple --
 809+ *	Apple computers boot block related information
 810+ */
 811+
 812+/*
 813+ *	Driver Descriptor Map, from Inside Macintosh: Devices, SCSI Manager
 814+ *	pp 12-13.  The driver descriptor map always resides on physical block 0.
 815+ */
 816+struct apple_drvr_descriptor {
 817+	uint32_t	descBlock;	/* first block of driver */
 818+	uint16_t	descSize;	/* driver size in blocks */
 819+	uint16_t	descType;	/* system type */
 820+} __packed;
 821+
 822+/*
 823+ *	system types; Apple reserves 0-15
 824+ */
 825+#define	APPLE_DRVR_TYPE_MACINTOSH	1
 826+
 827+#define	APPLE_DRVR_MAP_MAGIC		0x4552
 828+#define	APPLE_DRVR_MAP_MAX_DESCRIPTORS	61
 829+
 830+struct apple_drvr_map {
 831+	uint16_t	sbSig;		/* map signature */
 832+	uint16_t	sbBlockSize;	/* block size of device */
 833+	uint32_t	sbBlkCount;	/* number of blocks on device */
 834+	uint16_t	sbDevType;	/* (used internally by ROM) */
 835+	uint16_t	sbDevID;	/* (used internally by ROM) */
 836+	uint32_t	sbData;		/* (used internally by ROM) */
 837+	uint16_t	sbDrvrCount;	/* number of driver descriptors */
 838+	struct apple_drvr_descriptor sb_dd[APPLE_DRVR_MAP_MAX_DESCRIPTORS];
 839+	uint16_t	pad[3];
 840+} __packed;
 841+
 842+/*
 843+ *	Partition map structure from Inside Macintosh: Devices, SCSI Manager
 844+ *	pp. 13-14.  The partition map always begins on physical block 1.
 845+ *
 846+ *	With the exception of block 0, all blocks on the disk must belong to
 847+ *	exactly one partition.  The partition map itself belongs to a partition
 848+ *	of type `APPLE_PARTITION_MAP', and is not limited in size by anything
 849+ *	other than available disk space.  The partition map is not necessarily
 850+ *	the first partition listed.
 851+ */
 852+#define	APPLE_PART_MAP_ENTRY_MAGIC	0x504d
 853+
 854+struct apple_part_map_entry {
 855+	uint16_t	pmSig;		/* partition signature */
 856+	uint16_t	pmSigPad;	/* (reserved) */
 857+	uint32_t	pmMapBlkCnt;	/* number of blocks in partition map */
 858+	uint32_t	pmPyPartStart;	/* first physical block of partition */
 859+	uint32_t	pmPartBlkCnt;	/* number of blocks in partition */
 860+	uint8_t		pmPartName[32];	/* partition name */
 861+	uint8_t		pmPartType[32];	/* partition type */
 862+	uint32_t	pmLgDataStart;	/* first logical block of data area */
 863+	uint32_t	pmDataCnt;	/* number of blocks in data area */
 864+	uint32_t	pmPartStatus;	/* partition status information */
 865+/*
 866+ * Partition Status Information from Apple Tech Note 1189
 867+ */
 868+#define	APPLE_PS_VALID		0x00000001	/* Entry is valid */
 869+#define	APPLE_PS_ALLOCATED	0x00000002	/* Entry is allocated */
 870+#define	APPLE_PS_IN_USE		0x00000004	/* Entry in use */
 871+#define	APPLE_PS_BOOT_INFO	0x00000008	/* Entry contains boot info */
 872+#define	APPLE_PS_READABLE	0x00000010	/* Entry is readable */
 873+#define	APPLE_PS_WRITABLE	0x00000020	/* Entry is writable */
 874+#define	APPLE_PS_BOOT_CODE_PIC	0x00000040	/* Boot code has position
 875+						 * independent code */
 876+#define	APPLE_PS_CC_DRVR	0x00000100	/* Partition contains chain-
 877+						 * compatible driver */
 878+#define	APPLE_PS_RL_DRVR	0x00000200	/* Partition contains real
 879+						 * driver */
 880+#define	APPLE_PS_CH_DRVR	0x00000400	/* Partition contains chain
 881+						 * driver */
 882+#define	APPLE_PS_AUTO_MOUNT	0x40000000	/* Mount automatically at
 883+						 * startup */
 884+#define	APPLE_PS_STARTUP	0x80000000	/* Is the startup partition */
 885+	uint32_t	pmLgBootStart;	/* first logical block of boot code */
 886+	uint32_t	pmBootSize;	/* size of boot code, in bytes */
 887+	uint32_t	pmBootLoad;	/* boot code load address */
 888+	uint32_t	pmBootLoad2;	/* (reserved) */
 889+	uint32_t	pmBootEntry;	/* boot code entry point */
 890+	uint32_t	pmBootEntry2;	/* (reserved) */
 891+	uint32_t	pmBootCksum;	/* boot code checksum */
 892+	int8_t		pmProcessor[16]; /* processor type (e.g. "68020") */
 893+	uint8_t		pmBootArgs[128]; /* A/UX boot arguments */
 894+	uint8_t		pad[248];	/* pad to end of block */
 895+};
 896+
 897+#define	APPLE_PART_TYPE_DRIVER		"APPLE_DRIVER"
 898+#define	APPLE_PART_TYPE_DRIVER43	"APPLE_DRIVER43"
 899+#define	APPLE_PART_TYPE_DRIVERATA	"APPLE_DRIVER_ATA"
 900+#define	APPLE_PART_TYPE_DRIVERIOKIT	"APPLE_DRIVER_IOKIT"
 901+#define	APPLE_PART_TYPE_FWDRIVER	"APPLE_FWDRIVER"
 902+#define	APPLE_PART_TYPE_FWB_COMPONENT	"FWB DRIVER COMPONENTS"
 903+#define	APPLE_PART_TYPE_FREE		"APPLE_FREE"
 904+#define	APPLE_PART_TYPE_MAC		"APPLE_HFS"
 905+#define	APPLE_PART_TYPE_NETBSD		"NETBSD"
 906+#define	APPLE_PART_TYPE_NBSD_PPCBOOT	"NETBSD/MACPPC"
 907+#define	APPLE_PART_TYPE_NBSD_68KBOOT	"NETBSD/MAC68K"
 908+#define	APPLE_PART_TYPE_PATCHES		"APPLE_PATCHES"
 909+#define	APPLE_PART_TYPE_PARTMAP		"APPLE_PARTITION_MAP"
 910+#define	APPLE_PART_TYPE_PATCHES		"APPLE_PATCHES"
 911+#define	APPLE_PART_TYPE_SCRATCH		"APPLE_SCRATCH"
 912+#define	APPLE_PART_TYPE_UNIX		"APPLE_UNIX_SVR2"
 913+
 914+/*
 915+ * "pmBootArgs" for APPLE_UNIX_SVR2 partition.
 916+ * NetBSD/mac68k only uses Magic, Cluster, Type, and Flags.
 917+ */
 918+struct apple_blockzeroblock {
 919+	uint32_t       bzbMagic;
 920+	uint8_t        bzbCluster;
 921+	uint8_t        bzbType;
 922+	uint16_t       bzbBadBlockInode;
 923+	uint16_t       bzbFlags;
 924+	uint16_t       bzbReserved;
 925+	uint32_t       bzbCreationTime;
 926+	uint32_t       bzbMountTime;
 927+	uint32_t       bzbUMountTime;
 928+};
 929+
 930+#define	APPLE_BZB_MAGIC		0xABADBABE
 931+#define	APPLE_BZB_TYPEFS	1
 932+#define	APPLE_BZB_TYPESWAP	3
 933+#define	APPLE_BZB_ROOTFS	0x8000
 934+#define	APPLE_BZB_USRFS		0x4000
 935+
 936+/* ------------------------------------------
 937+ * ews4800mips
 938+ *
 939+ */
 940+
 941+#define	EWS4800MIPS_BBINFO_MAGIC		"NetBSD/ews4800mips     20040611"
 942+#define	EWS4800MIPS_BOOT_BLOCK_OFFSET		0
 943+#define	EWS4800MIPS_BOOT_BLOCK_BLOCKSIZE	512
 944+#define	EWS4800MIPS_BOOT_BLOCK_MAX_SIZE		(512 * 8)
 945+
 946+/* ------------------------------------------
 947+ * hp300
 948+ *
 949+ */
 950+
 951+/* volume header for "LIF" format volumes */
 952+
 953+struct	hp300_lifvol {
 954+	uint16_t	vol_id;
 955+	char		vol_label[6];
 956+	uint32_t	vol_addr;
 957+	uint16_t	vol_oct;
 958+	uint16_t	vol_dummy;
 959+	uint32_t	vol_dirsize;
 960+	uint16_t	vol_version;
 961+	uint16_t	vol_zero;
 962+	uint32_t	vol_huh1;
 963+	uint32_t	vol_huh2;
 964+	uint32_t	vol_length;
 965+};
 966+
 967+/* LIF directory entry format */
 968+
 969+struct	hp300_lifdir {
 970+	char		dir_name[10];
 971+	uint16_t	dir_type;
 972+	uint32_t	dir_addr;
 973+	uint32_t	dir_length;
 974+	char		dir_toc[6];
 975+	uint16_t	dir_flag;
 976+	uint32_t	dir_exec;
 977+};
 978+
 979+/* load header for boot rom */
 980+struct hp300_load {
 981+	uint32_t	address;
 982+	uint32_t	count;
 983+};
 984+
 985+#define	HP300_VOL_ID		0x8000	/* always $8000 */
 986+#define	HP300_VOL_OCT		4096
 987+#define	HP300_DIR_TYPE		0xe942	/* "SYS9k Series 9000" */
 988+#define	HP300_DIR_FLAG		0x8001	/* don't ask me! */
 989+#define	HP300_SECTSIZE		256
 990+
 991+#define	HP300_LIF_NUMDIR	8
 992+
 993+#define	HP300_LIF_VOLSTART	0
 994+#define	HP300_LIF_VOLSIZE	sizeof(struct hp300_lifvol)
 995+#define	HP300_LIF_DIRSTART	512
 996+#define	HP300_LIF_DIRSIZE	(HP300_LIF_NUMDIR * sizeof(struct hp300_lifdir))
 997+#define	HP300_LIF_FILESTART	8192
 998+
 999+#define	hp300_btolifs(b)	(((b) + (HP300_SECTSIZE - 1)) / HP300_SECTSIZE)
1000+#define	hp300_lifstob(s)	((s) * HP300_SECTSIZE)
1001+
1002+
1003+/* ------------------------------------------
1004+ * hppa
1005+ *
1006+ */
1007+
1008+/*
1009+ * volume header for "LIF" format volumes
1010+ */
1011+struct	hppa_lifvol {
1012+	uint16_t	vol_id;
1013+	uint8_t		vol_label[6];
1014+	uint32_t	vol_addr;
1015+	uint16_t	vol_oct;
1016+	uint16_t	vol_dummy;
1017+
1018+	uint32_t	vol_dirsize;
1019+	uint16_t	vol_version;
1020+	uint16_t	vol_zero;
1021+	uint32_t	vol_number;
1022+	uint32_t	vol_lastvol;
1023+
1024+	uint32_t	vol_length;
1025+	uint8_t		vol_toc[6];
1026+	uint8_t		vol_dummy1[198];
1027+
1028+	uint32_t	ipl_addr;
1029+	uint32_t	ipl_size;
1030+	uint32_t	ipl_entry;
1031+
1032+	uint32_t	vol_dummy2;
1033+};
1034+
1035+struct	hppa_lifdir {
1036+	uint8_t		dir_name[10];
1037+	uint16_t	dir_type;
1038+	uint32_t	dir_addr;
1039+	uint32_t	dir_length;
1040+	uint8_t		dir_toc[6];
1041+	uint16_t	dir_flag;
1042+	uint32_t	dir_implement;
1043+};
1044+
1045+struct hppa_lifload {
1046+	int address;
1047+	int count;
1048+};
1049+
1050+#define	HPPA_LIF_VOL_ID	0x8000
1051+#define	HPPA_LIF_VOL_OCT	0x1000
1052+#define	HPPA_LIF_DIR_SWAP	0x5243
1053+#define	HPPA_LIF_DIR_FS	0xcd38
1054+#define	HPPA_LIF_DIR_IOMAP	0xcd60
1055+#define	HPPA_LIF_DIR_HPUX	0xcd80
1056+#define	HPPA_LIF_DIR_ISL	0xce00
1057+#define	HPPA_LIF_DIR_PAD	0xcffe
1058+#define	HPPA_LIF_DIR_AUTO	0xcfff
1059+#define	HPPA_LIF_DIR_EST	0xd001
1060+#define	HPPA_LIF_DIR_TYPE	0xe942
1061+
1062+#define	HPPA_LIF_DIR_FLAG	0x8001	/* dont ask me! */
1063+#define	HPPA_LIF_SECTSIZE	256
1064+
1065+#define	HPPA_LIF_NUMDIR	8
1066+
1067+#define	HPPA_LIF_VOLSTART	0
1068+#define	HPPA_LIF_VOLSIZE	sizeof(struct hppa_lifvol)
1069+#define	HPPA_LIF_DIRSTART	2048
1070+#define	HPPA_LIF_DIRSIZE	(HPPA_LIF_NUMDIR * sizeof(struct hppa_lifdir))
1071+#define	HPPA_LIF_FILESTART	4096
1072+
1073+#define	hppa_btolifs(b)	(((b) + (HPPA_LIF_SECTSIZE - 1)) / HPPA_LIF_SECTSIZE)
1074+#define	hppa_lifstob(s)	((s) * HPPA_LIF_SECTSIZE)
1075+#define	hppa_lifstodb(s)	((s) * HPPA_LIF_SECTSIZE / DEV_BSIZE)
1076+
1077+
1078+/* ------------------------------------------
1079+ * x86
1080+ *
1081+ */
1082+
1083+/*
1084+ * Parameters for NetBSD /boot written to start of pbr code by installboot
1085+ */
1086+
1087+struct x86_boot_params {
1088+	uint32_t	bp_length;	/* length of patchable data */
1089+	uint32_t	bp_flags;
1090+	uint32_t	bp_timeout;	/* boot timeout in seconds */
1091+	uint32_t	bp_consdev;
1092+	uint32_t	bp_conspeed;
1093+	uint8_t		bp_password[16];	/* md5 hash of password */
1094+	char		bp_keymap[64];	/* keyboard translation map */
1095+	uint32_t	bp_consaddr;	/* ioaddr for console */
1096+};
1097+
1098+#endif	/* !defined(__ASSEMBLER__) */				/* } */
1099+
1100+#define	X86_BOOT_MAGIC(n)	('x' << 24 | 0x86b << 12 | 'm' << 4 | (n))
1101+#define	X86_BOOT_MAGIC_1	X86_BOOT_MAGIC(1)	/* pbr.S */
1102+#define	X86_BOOT_MAGIC_2	X86_BOOT_MAGIC(2)	/* bootxx.S */
1103+#define	X86_BOOT_MAGIC_PXE	X86_BOOT_MAGIC(3)	/* start_pxe.S */
1104+#define	X86_BOOT_MAGIC_FAT	X86_BOOT_MAGIC(4)	/* fatboot.S */
1105+#define	X86_BOOT_MAGIC_EFI	X86_BOOT_MAGIC(5)	/* efiboot/start.S */
1106+#define	X86_MBR_GPT_MAGIC	0xedb88320		/* gpt.S */
1107+
1108+		/* values for bp_flags */
1109+#define	X86_BP_FLAGS_RESET_VIDEO	1
1110+#define	X86_BP_FLAGS_PASSWORD		2
1111+#define	X86_BP_FLAGS_NOMODULES		4
1112+#define	X86_BP_FLAGS_NOBOOTCONF		8
1113+#define	X86_BP_FLAGS_LBA64VALID		0x10
1114+
1115+		/* values for bp_consdev */
1116+#define	X86_BP_CONSDEV_PC	0
1117+#define	X86_BP_CONSDEV_COM0	1
1118+#define	X86_BP_CONSDEV_COM1	2
1119+#define	X86_BP_CONSDEV_COM2	3
1120+#define	X86_BP_CONSDEV_COM3	4
1121+#define	X86_BP_CONSDEV_COM0KBD	5
1122+#define	X86_BP_CONSDEV_COM1KBD	6
1123+#define	X86_BP_CONSDEV_COM2KBD	7
1124+#define	X86_BP_CONSDEV_COM3KBD	8
1125+
1126+/* ------------------------------------------
1127+ * landisk
1128+ */
1129+
1130+#if !defined(__ASSEMBLER__)					/* { */
1131+
1132+/*
1133+ * Parameters for NetBSD /boot written to start of pbr code by installboot
1134+ */
1135+struct landisk_boot_params {
1136+	uint32_t	bp_length;	/* length of patchable data */
1137+	uint32_t	bp_flags;
1138+	uint32_t	bp_timeout;	/* boot timeout in seconds */
1139+	uint32_t	bp_consdev;
1140+	uint32_t	bp_conspeed;
1141+};
1142+
1143+#endif	/* !defined(__ASSEMBLER__) */				/* } */
1144+
1145+#define	LANDISK_BOOT_MAGIC_1	0x20031125
1146+#define	LANDISK_BOOT_MAGIC_2	0x20041110
1147+
1148+#if !defined(__ASSEMBLER__)					/* { */
1149+
1150+/* ------------------------------------------
1151+ * macppc
1152+ */
1153+
1154+#define	MACPPC_BOOT_BLOCK_OFFSET	2048
1155+#define	MACPPC_BOOT_BLOCK_BLOCKSIZE	512
1156+#define	MACPPC_BOOT_BLOCK_MAX_SIZE	2048	/* XXX: could be up to 6144 */
1157+	/* Magic string -- 32 bytes long (including the NUL) */
1158+#define	MACPPC_BBINFO_MAGIC		"NetBSD/macppc bootxx   20020515"
1159+
1160+/* ------------------------------------------
1161+ * news68k, newsmips
1162+ */
1163+
1164+#define	NEWS_BOOT_BLOCK_LABELOFFSET	64 /* XXX from <machine/disklabel.h> */
1165+#define	NEWS_BOOT_BLOCK_OFFSET		0
1166+#define	NEWS_BOOT_BLOCK_BLOCKSIZE	512
1167+#define	NEWS_BOOT_BLOCK_MAX_SIZE	(512 * 16)
1168+
1169+	/* Magic string -- 32 bytes long (including the NUL) */
1170+#define	NEWS68K_BBINFO_MAGIC		"NetBSD/news68k bootxx  20020518"
1171+#define	NEWSMIPS_BBINFO_MAGIC		"NetBSD/newsmips bootxx 20020518"
1172+
1173+/* ------------------------------------------
1174+ * next68k
1175+ */
1176+
1177+#define	NEXT68K_LABEL_MAXPARTITIONS	8	/* number of partitions in next68k_disklabel */
1178+#define	NEXT68K_LABEL_CPULBLLEN		24
1179+#define	NEXT68K_LABEL_MAXDNMLEN		24
1180+#define	NEXT68K_LABEL_MAXTYPLEN		24
1181+#define	NEXT68K_LABEL_MAXBFLEN		24
1182+#define	NEXT68K_LABEL_MAXHNLEN		32
1183+#define	NEXT68K_LABEL_MAXMPTLEN		16
1184+#define	NEXT68K_LABEL_MAXFSTLEN		8
1185+#define	NEXT68K_LABEL_NBAD		1670	/* sized to make label ~= 8KB */
1186+
1187+struct next68k_partition {
1188+	int32_t	cp_offset;		/* starting sector */
1189+	int32_t	cp_size;		/* number of sectors in partition */
1190+	int16_t	cp_bsize;		/* block size in bytes */
1191+	int16_t	cp_fsize;		/* filesystem basic fragment size */
1192+	char	cp_opt;			/* optimization type: 's'pace/'t'ime */
1193+	char	cp_pad1;
1194+	int16_t	cp_cpg;			/* filesystem cylinders per group */
1195+	int16_t	cp_density;		/* bytes per inode density */
1196+	int8_t	cp_minfree;		/* minfree (%) */
1197+	int8_t	cp_newfs;		/* run newfs during init */
1198+	char	cp_mountpt[NEXT68K_LABEL_MAXMPTLEN];
1199+					/* default/standard mount point */
1200+	int8_t	cp_automnt;		/* auto-mount when inserted */
1201+	char	cp_type[NEXT68K_LABEL_MAXFSTLEN]; /* file system type name */
1202+	char	cp_pad2;
1203+} __packed;
1204+
1205+/* The disklabel the way it is on the disk */
1206+struct next68k_disklabel {
1207+	int32_t	cd_version;		/* label version */
1208+	int32_t	cd_label_blkno;		/* block # of this label */
1209+	int32_t	cd_size;		/* size of media area (sectors) */
1210+	char	cd_label[NEXT68K_LABEL_CPULBLLEN]; /* disk name (label) */
1211+	uint32_t cd_flags;		/* flags */
1212+	uint32_t cd_tag;		/* volume tag */
1213+	char	cd_name[NEXT68K_LABEL_MAXDNMLEN]; /* drive (hardware) name */
1214+	char	cd_type[NEXT68K_LABEL_MAXTYPLEN]; /* drive type */
1215+	int32_t	cd_secsize;		/* # of bytes per sector */
1216+	int32_t	cd_ntracks;		/* # of tracks per cylinder */
1217+	int32_t	cd_nsectors;		/* # of data sectors per track */
1218+	int32_t	cd_ncylinders;		/* # of data cylinders per unit */
1219+	int32_t	cd_rpm;			/* rotational speed */
1220+	int16_t	cd_front;		/* # of sectors in "front porch" */
1221+	int16_t	cd_back;		/* # of sectors in "back porch" */
1222+	int16_t	cd_ngroups;		/* # of alt groups */
1223+	int16_t	cd_ag_size;		/* alt group size (sectors) */
1224+	int16_t	cd_ag_alts;		/* alternate sectors / alt group */
1225+	int16_t	cd_ag_off;		/* sector offset to first alternate */
1226+	int32_t	cd_boot_blkno[2];	/* boot program locations */
1227+	char	cd_kernel[NEXT68K_LABEL_MAXBFLEN]; /* default kernel name */
1228+	char	cd_hostname[NEXT68K_LABEL_MAXHNLEN];
1229+				/* host name (usu. where disk was labeled) */
1230+	char	cd_rootpartition;	/* root partition letter e.g. 'a' */
1231+	char	cd_rwpartition;		/* r/w partition letter e.g. 'b' */
1232+	struct next68k_partition cd_partitions[NEXT68K_LABEL_MAXPARTITIONS];
1233+
1234+	union {
1235+		uint16_t CD_v3_checksum; /* label version 3 checksum */
1236+		int32_t	CD_bad[NEXT68K_LABEL_NBAD];
1237+					/* block number that is bad */
1238+	} cd_un;
1239+	uint16_t cd_checksum;		/* label version 1 or 2 checksum */
1240+} __packed;
1241+
1242+#define	NEXT68K_LABEL_cd_checksum	cd_checksum
1243+#define	NEXT68K_LABEL_cd_v3_checksum	cd_un.CD_v3_checksum
1244+#define	NEXT68K_LABEL_cd_bad		cd_un.CD_bad
1245+
1246+#define	NEXT68K_LABEL_SECTOR		0	/* sector containing label */
1247+#define	NEXT68K_LABEL_OFFSET		0	/* offset of label in sector */
1248+#define	NEXT68K_LABEL_SIZE		8192	/* size of label */
1249+#define	NEXT68K_LABEL_CD_V1		0x4e655854 /* version #1: "NeXT" */
1250+#define	NEXT68K_LABEL_CD_V2		0x646c5632 /* version #2: "dlV2" */
1251+#define	NEXT68K_LABEL_CD_V3		0x646c5633 /* version #3: "dlV3" */
1252+#define	NEXT68K_LABEL_DEFAULTFRONTPORCH	(160 * 2)
1253+#define	NEXT68K_LABEL_DEFAULTBOOT0_1	(32 * 2)
1254+#define	NEXT68K_LABEL_DEFAULTBOOT0_2	(96 * 2)
1255+
1256+/* ------------------------------------------
1257+ * pmax --
1258+ *	PMAX (DECstation / MIPS) boot block information
1259+ */
1260+
1261+/*
1262+ * If mode is 0, there is just one sequence of blocks and one Dec_BootMap
1263+ * is used.  If mode is 1, there are multiple sequences of blocks
1264+ * and multiple Dec_BootMaps are used, the last with numBlocks = 0.
1265+ */
1266+struct pmax_boot_map {
1267+	int32_t	num_blocks;		/* Number of blocks to read. */
1268+	int32_t	start_block;		/* Starting block on disk. */
1269+};
1270+
1271+/*
1272+ * This is the structure of a disk or tape boot block.  The boot_map
1273+ * can either be a single boot count and start block (contiguous mode)
1274+ * or a list of up to 61 (to fill a 512 byte sector) block count and
1275+ * start block pairs.  Under NetBSD, contiguous mode is always used.
1276+ */
1277+struct pmax_boot_block {
1278+	uint8_t		pad[8];
1279+	int32_t		magic;			/* PMAX_BOOT_MAGIC */
1280+	int32_t		mode;			/* Mode for boot info. */
1281+	uint32_t	load_addr;		/* Address to start loading. */
1282+	uint32_t	exec_addr;		/* Address to start execing. */
1283+	struct		pmax_boot_map map[61];	/* boot program section(s). */
1284+} __packed;
1285+
1286+#define	PMAX_BOOT_MAGIC			0x0002757a
1287+#define	PMAX_BOOTMODE_CONTIGUOUS	0
1288+#define	PMAX_BOOTMODE_SCATTERED		1
1289+
1290+#define	PMAX_BOOT_BLOCK_OFFSET		0
1291+#define	PMAX_BOOT_BLOCK_BLOCKSIZE	512
1292+
1293+
1294+/* ------------------------------------------
1295+ * sgimips
1296+ */
1297+
1298+/*
1299+ * Some IRIX man pages refer to the size being a multiple of whole cylinders.
1300+ * Later ones only refer to the size being "typically" 2MB.  IRIX fx(1)
1301+ * uses a default drive geometry if one can't be determined, suggesting
1302+ * that "whole cylinder" multiples are not required.
1303+ */
1304+
1305+#define SGI_BOOT_BLOCK_SIZE_VOLHDR	3135
1306+#define SGI_BOOT_BLOCK_MAGIC		0xbe5a941
1307+#define SGI_BOOT_BLOCK_MAXPARTITIONS	16
1308+#define SGI_BOOT_BLOCK_MAXVOLDIRS	15
1309+#define SGI_BOOT_BLOCK_BLOCKSIZE	512
1310+
1311+/*
1312+ * SGI partition conventions:
1313+ *
1314+ * Partition 0 - root
1315+ * Partition 1 - swap
1316+ * Partition 6 - usr
1317+ * Partition 7 - volume body
1318+ * Partition 8 - volume header
1319+ * Partition 10 - whole disk
1320+ */
1321+
1322+struct sgi_boot_devparms {
1323+	uint8_t		dp_skew;
1324+	uint8_t		dp_gap1;
1325+	uint8_t		dp_gap2;
1326+	uint8_t		dp_spares_cyl;
1327+	uint16_t	dp_cyls;
1328+	uint16_t	dp_shd0;
1329+	uint16_t	dp_trks0;
1330+	uint8_t		dp_ctq_depth;
1331+	uint8_t		dp_cylshi;
1332+	uint16_t	dp_unused;
1333+	uint16_t	dp_secs;
1334+	uint16_t	dp_secbytes;
1335+	uint16_t	dp_interleave;
1336+	uint32_t	dp_flags;
1337+	uint32_t	dp_datarate;
1338+	uint32_t	dp_nretries;
1339+	uint32_t	dp_mspw;
1340+	uint16_t	dp_xgap1;
1341+	uint16_t	dp_xsync;
1342+	uint16_t	dp_xrdly;
1343+	uint16_t	dp_xgap2;
1344+	uint16_t	dp_xrgate;
1345+	uint16_t	dp_xwcont;
1346+} __packed;
1347+
1348+struct sgi_boot_block {
1349+	uint32_t	magic;
1350+	int16_t		root;
1351+	int16_t		swap;
1352+	char		bootfile[16];
1353+	struct sgi_boot_devparms dp;
1354+	struct {
1355+		char		name[8];
1356+		int32_t		block;
1357+		int32_t		bytes;
1358+	}		voldir[SGI_BOOT_BLOCK_MAXVOLDIRS];
1359+	struct {
1360+		int32_t		blocks;
1361+		int32_t		first;
1362+		int32_t		type;
1363+	}		partitions[SGI_BOOT_BLOCK_MAXPARTITIONS];
1364+	int32_t		checksum;
1365+	int32_t		_pad;
1366+} __packed;
1367+
1368+#define SGI_PTYPE_VOLHDR	0
1369+#define SGI_PTYPE_TRKREPL	1
1370+#define SGI_PTYPE_SECREPL	2
1371+#define SGI_PTYPE_RAW		3
1372+#define SGI_PTYPE_BSD		4
1373+#define SGI_PTYPE_SYSV		5
1374+#define SGI_PTYPE_VOLUME	6
1375+#define SGI_PTYPE_EFS		7
1376+#define SGI_PTYPE_LVOL		8
1377+#define SGI_PTYPE_RLVOL		9
1378+#define SGI_PTYPE_XFS		10
1379+#define SGI_PTYPE_XFSLOG	11
1380+#define SGI_PTYPE_XLV		12
1381+#define SGI_PTYPE_XVM		13
1382+
1383+/* ------------------------------------------
1384+ * sparc
1385+ */
1386+
1387+#define	SPARC_BOOT_BLOCK_OFFSET		512
1388+#define	SPARC_BOOT_BLOCK_BLOCKSIZE	512
1389+#define	SPARC_BOOT_BLOCK_MAX_SIZE	(512 * 15)
1390+	/* Magic string -- 32 bytes long (including the NUL) */
1391+#define	SPARC_BBINFO_MAGIC		"NetBSD/sparc bootxx    20020515"
1392+
1393+
1394+/* ------------------------------------------
1395+ * sparc64
1396+ */
1397+
1398+#define	SPARC64_BOOT_BLOCK_OFFSET	512
1399+#define	SPARC64_BOOT_BLOCK_BLOCKSIZE	512
1400+#define	SPARC64_BOOT_BLOCK_MAX_SIZE	(512 * 15)
1401+
1402+
1403+/* ------------------------------------------
1404+ * sun68k (sun2, sun3)
1405+ */
1406+
1407+#define	SUN68K_BOOT_BLOCK_OFFSET	512
1408+#define	SUN68K_BOOT_BLOCK_BLOCKSIZE	512
1409+#define	SUN68K_BOOT_BLOCK_MAX_SIZE	(512 * 15)
1410+	/* Magic string -- 32 bytes long (including the NUL) */
1411+#define	SUN68K_BBINFO_MAGIC		"NetBSD/sun68k bootxx   20020515"
1412+
1413+
1414+/* ------------------------------------------
1415+ * vax --
1416+ *	VAX boot block information
1417+ */
1418+
1419+struct vax_boot_block {
1420+/* Note that these don't overlap any of the pmax boot block */
1421+	uint8_t		pad0[2];
1422+	uint8_t		bb_id_offset;	/* offset in words to id (magic1)*/
1423+	uint8_t		bb_mbone;	/* must be one */
1424+	uint16_t	bb_lbn_hi;	/* lbn (hi word) of bootstrap */
1425+	uint16_t	bb_lbn_low;	/* lbn (low word) of bootstrap */
1426+	uint8_t		pad1[406];
1427+	/* disklabel offset is 64 from base, or 56 from start of pad1 */
1428+
1429+	/* The rest of these fields are identification area and describe
1430+	 * the secondary block for uVAX VMB.
1431+	 */
1432+	uint8_t		bb_magic1;	/* magic number */
1433+	uint8_t		bb_mbz1;	/* must be zero */
1434+	uint8_t		bb_pad1;	/* any value */
1435+	uint8_t		bb_sum1;	/* ~(magic1 + mbz1 + pad1) */
1436+
1437+	uint8_t		bb_mbz2;	/* must be zero */
1438+	uint8_t		bb_volinfo;	/* volinfo */
1439+	uint8_t		bb_pad2a;	/* any value */
1440+	uint8_t		bb_pad2b;	/* any value */
1441+
1442+	uint32_t	bb_size;	/* size in blocks of bootstrap */
1443+	uint32_t	bb_load;	/* load offset to bootstrap */
1444+	uint32_t	bb_entry;	/* byte offset in bootstrap */
1445+	uint32_t	bb_sum3;	/* sum of previous 3 fields */
1446+
1447+	/* The rest is unused.
1448+	 */
1449+	uint8_t		pad2[74];
1450+} __packed;
1451+
1452+#define	VAX_BOOT_MAGIC1			0x18	/* size of BB info? */
1453+#define	VAX_BOOT_VOLINFO_NONE		0x00	/* no special info */
1454+#define	VAX_BOOT_VOLINFO_SS		0x01	/* single sided */
1455+#define	VAX_BOOT_VOLINFO_DS		0x81	/* double sided */
1456+
1457+#define	VAX_BOOT_SIZE			15	/* 15 blocks */
1458+#define	VAX_BOOT_LOAD			0	/* no load offset */
1459+#define	VAX_BOOT_ENTRY			0x200	/* one block in */
1460+
1461+#define	VAX_BOOT_BLOCK_OFFSET		0
1462+#define	VAX_BOOT_BLOCK_BLOCKSIZE	512
1463+
1464+
1465+/* ------------------------------------------
1466+ * x68k
1467+ */
1468+
1469+#define	X68K_BOOT_BLOCK_OFFSET		0
1470+#define	X68K_BOOT_BLOCK_BLOCKSIZE	512
1471+#define	X68K_BOOT_BLOCK_MAX_SIZE	(512 * 16)
1472+	/* Magic string -- 32 bytes long (including the NUL) */
1473+#define	X68K_BBINFO_MAGIC		"NetBSD/x68k bootxx     20020601"
1474+
1475+#endif	/* !defined(__ASSEMBLER__) */				/* } */
1476+
1477+#endif	/* !_SYS_BOOTBLOCK_H */
+826, -0
  1@@ -0,0 +1,826 @@
  2+/*	$NetBSD: walk.c,v 1.43 2025/05/01 14:14:36 andvar Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2001 Wasabi Systems, Inc.
  6+ * All rights reserved.
  7+ *
  8+ * Written by Luke Mewburn for Wasabi Systems, Inc.
  9+ *
 10+ * Redistribution and use in source and binary forms, with or without
 11+ * modification, are permitted provided that the following conditions
 12+ * are met:
 13+ * 1. Redistributions of source code must retain the above copyright
 14+ *    notice, this list of conditions and the following disclaimer.
 15+ * 2. Redistributions in binary form must reproduce the above copyright
 16+ *    notice, this list of conditions and the following disclaimer in the
 17+ *    documentation and/or other materials provided with the distribution.
 18+ * 3. All advertising materials mentioning features or use of this software
 19+ *    must display the following acknowledgement:
 20+ *      This product includes software developed for the NetBSD Project by
 21+ *      Wasabi Systems, Inc.
 22+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 23+ *    or promote products derived from this software without specific prior
 24+ *    written permission.
 25+ *
 26+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
 27+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 28+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 29+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
 30+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 31+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 32+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 33+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 34+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 35+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 36+ * POSSIBILITY OF SUCH DAMAGE.
 37+ */
 38+
 39+#if HAVE_NBTOOL_CONFIG_H
 40+#include "nbtool_config.h"
 41+#endif
 42+
 43+#include <sys/cdefs.h>
 44+#if defined(__RCSID) && !defined(__lint)
 45+__RCSID("$NetBSD: walk.c,v 1.43 2025/05/01 14:14:36 andvar Exp $");
 46+#endif	/* !__lint */
 47+
 48+#include <sys/param.h>
 49+#include <sys/stat.h>
 50+
 51+#include <assert.h>
 52+#include <errno.h>
 53+#include <fcntl.h>
 54+#include <stdio.h>
 55+#include <dirent.h>
 56+#include <stdlib.h>
 57+#include <string.h>
 58+#include <unistd.h>
 59+#include <util.h>
 60+
 61+#include "makefs.h"
 62+#include "mtree.h"
 63+
 64+static	void	 apply_specdir(const char *, NODE *, fsnode *, int);
 65+static	void	 apply_specentry(const char *, NODE *, fsnode *);
 66+static	fsnode	*create_fsnode(const char *, const char *, const char *,
 67+			       struct stat *);
 68+static	fsinode	*link_check(fsinode *);
 69+static size_t missing = 0;
 70+
 71+/*
 72+ * fsnode_cmp --
 73+ *	This function is used by `qsort` so sort one directory's
 74+ *	entries.  `.` is always first, followed by anything else
 75+ *	as compared by `strcmp()`.
 76+ */
 77+static int
 78+fsnode_cmp(const void *vleft, const void *vright)
 79+{
 80+	const fsnode * const *left  = vleft;
 81+	const fsnode * const *right = vright;
 82+	const char *lname = (*left)->name, *rname = (*right)->name;
 83+
 84+	if (strcmp(lname, ".") == 0)
 85+		return -1;
 86+	if (strcmp(rname, ".") == 0)
 87+		return 1;
 88+	return strcmp(lname, rname);
 89+}
 90+
 91+static fsnode *
 92+fsnode_sort(fsnode *first, const char *root, const char *dir)
 93+{
 94+	fsnode **list, **listptr;
 95+	size_t num = 0;
 96+
 97+	for (fsnode *tmp = first; tmp; tmp = tmp->next, num++) {
 98+		if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
 99+			printf("%s: pre sort: %s %s %s\n",
100+			    __func__, root, dir, tmp->name);
101+	}
102+
103+	list = listptr = ecalloc(num, sizeof(*list));
104+	for (fsnode *tmp = first; tmp; tmp = tmp->next)
105+		*listptr++ = tmp;
106+
107+	qsort(list, num, sizeof(*list), fsnode_cmp);
108+
109+	for (size_t i = 0; i < num - 1; ++i)
110+		list[i]->next = list[i + 1];
111+	list[num - 1]->next = NULL;
112+	first = list[0];
113+	assert(strcmp(first->name, ".") == 0);
114+	free(list);
115+	if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
116+		for (fsnode *tmp = first; tmp; tmp = tmp->next)
117+			printf("%s: post sort: %s %s %s\n",
118+			    __func__, root, dir, tmp->name);
119+
120+	return first;
121+}
122+
123+/*
124+ * join current entry with the list. Return the current entry to replace
125+ * in cur, and 1 if it is a directory and we need to add or 0 if we need
126+ * to replace it.
127+ */
128+static int
129+fsnode_join(fsnode **curp, fsnode *join, fsnode *last, const char *path,
130+    const char *name, const struct stat *st, int replace)
131+{
132+	fsnode *cur;
133+
134+	/* Look for the entry to replace by name */
135+	cur = join->next;
136+	for (;;) {
137+		if (cur == NULL || strcmp(cur->name, name) == 0)
138+			break;
139+		if (cur == last) {
140+			cur = NULL;
141+			break;
142+		}
143+		cur = cur->next;
144+	}
145+	if (cur == NULL) {
146+		/* Not found */
147+		*curp = NULL;
148+		return 0;
149+	}
150+	if (S_ISDIR(cur->type) && S_ISDIR(st->st_mode)) {
151+		/*
152+                 * both the entry to join and this entry are directories
153+		 * need to merge the two directories
154+		 */
155+		if (debug & DEBUG_WALK_DIR_NODE)
156+			printf("%s: merging %s with %p\n",
157+			    __func__, path, cur->child);
158+		*curp = cur;
159+		return 1;
160+	}
161+	if (!replace) {
162+		/*
163+		 * if they are not both directories and replace is not
164+		 * specified, bail out
165+		 */
166+		errx(EXIT_FAILURE, "Can't merge %s `%s' with existing %s",
167+		    inode_type(st->st_mode), path, inode_type(cur->type));
168+	}
169+
170+	if (debug & DEBUG_WALK_DIR_NODE)
171+		printf("%s: replacing %s %s\n",
172+		    __func__, inode_type(st->st_mode), path);
173+
174+	/* merge the join list */
175+	if (cur == join->next)
176+		join->next = cur->next;
177+	else {
178+		fsnode *p;
179+		for (p = join->next;
180+		    p->next != cur; p = p->next)
181+			continue;
182+		p->next = cur->next;
183+	}
184+	/* return the entry to be replaced */
185+	*curp = cur;
186+	return 0;
187+}
188+
189+/*
190+ * walk_dir --
191+ *	build a tree of fsnodes from `root' and `dir', with a parent
192+ *	fsnode of `parent' (which may be NULL for the root of the tree).
193+ *	append the tree to a fsnode of `join' if it is not NULL.
194+ *	each "level" is a directory, with the "." entry guaranteed to be
195+ *	at the start of the list, and without ".." entries.
196+ */
197+fsnode *
198+walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join,
199+    int replace, int follow)
200+{
201+	fsnode		*first, *cur, *prev, *last;
202+	DIR		*dirp;
203+	struct dirent	*dent;
204+	char		path[MAXPATHLEN + 1];
205+	struct stat	stbuf;
206+	char		*name, *rp;
207+	int		dot, len;
208+
209+	assert(root != NULL);
210+	assert(dir != NULL);
211+
212+	len = snprintf(path, sizeof(path), "%s/%s", root, dir);
213+	if ((size_t)len >= sizeof(path))
214+		errx(EXIT_FAILURE, "Pathname too long.");
215+	if (debug & DEBUG_WALK_DIR)
216+		printf("%s: %s %p\n", __func__, path, parent);
217+	if ((dirp = opendir(path)) == NULL)
218+		err(EXIT_FAILURE, "Can't opendir `%s'", path);
219+	rp = path + strlen(root) + 1;
220+	if (join != NULL) {
221+		first = cur = join;
222+		while (cur->next != NULL)
223+			cur = cur->next;
224+		prev = last = cur;
225+	} else
226+		last = first = prev = NULL;
227+	while ((dent = readdir(dirp)) != NULL) {
228+		name = dent->d_name;
229+		dot = 0;
230+		if (name[0] == '.')
231+			switch (name[1]) {
232+			case '\0':	/* "." */
233+				if (join != NULL)
234+					continue;
235+				dot = 1;
236+				break;
237+			case '.':	/* ".." */
238+				if (name[2] == '\0')
239+					continue;
240+				/* FALLTHROUGH */
241+			default:
242+				dot = 0;
243+			}
244+		if (debug & DEBUG_WALK_DIR_NODE)
245+			printf("%s: scanning %s/%s/%s\n",
246+			    __func__, root, dir, name);
247+		if (snprintf(path + len, sizeof(path) - len, "/%s", name) >=
248+		    (int)sizeof(path) - len)
249+			errx(EXIT_FAILURE, "Pathname too long.");
250+		if (follow) {
251+			if (stat(path, &stbuf) == -1)
252+				err(EXIT_FAILURE, "Can't stat `%s'", path);
253+		} else {
254+			if (lstat(path, &stbuf) == -1)
255+				err(EXIT_FAILURE, "Can't lstat `%s'", path);
256+			/*
257+			 * Symlink permission bits vary between filesystems/OSs
258+			 * (ie. 0755 on FFS/NetBSD, 0777 for ext[234]/Linux),
259+			 * force them to 0755.
260+			 */
261+			if (S_ISLNK(stbuf.st_mode)) {
262+				stbuf.st_mode &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
263+				stbuf.st_mode |= S_IRWXU
264+				    | S_IRGRP | S_IXGRP
265+				    | S_IROTH | S_IXOTH;
266+			}
267+		}
268+#ifdef S_ISSOCK
269+		if (S_ISSOCK(stbuf.st_mode & S_IFMT)) {
270+			if (debug & DEBUG_WALK_DIR_NODE)
271+				printf("%s: skipping socket %s\n", __func__,
272+				     path);
273+			continue;
274+		}
275+#endif
276+
277+		if (join != NULL) {
278+			if (fsnode_join(&cur, join, last, path, name, &stbuf,
279+			    replace)) {
280+				cur->child = walk_dir(root, rp, cur,
281+				    cur->child, replace, follow);
282+				continue;
283+			} else if (cur) {
284+				if (prev == cur) {
285+					fsnode *p = join;
286+					while (p->next != NULL)
287+					     p = p->next;
288+					prev = p;
289+				}
290+				free(cur->name);
291+				free(cur->path);
292+				free(cur);
293+			}
294+		}
295+
296+		cur = create_fsnode(root, dir, name, &stbuf);
297+		cur->parent = parent;
298+		if (dot) {
299+				/* ensure "." is at the start of the list */
300+			cur->next = first;
301+			first = cur;
302+			if (! prev)
303+				prev = cur;
304+			cur->first = first;
305+		} else {			/* not "." */
306+			if (prev)
307+				prev->next = cur;
308+			prev = cur;
309+			if (!first)
310+				first = cur;
311+			cur->first = first;
312+			if (S_ISDIR(cur->type)) {
313+				cur->child = walk_dir(root, rp, cur, NULL,
314+				    replace, follow);
315+				continue;
316+			}
317+		}
318+		if (stbuf.st_nlink > 1) {
319+			fsinode	*curino;
320+
321+			curino = link_check(cur->inode);
322+			if (curino != NULL) {
323+				free(cur->inode);
324+				cur->inode = curino;
325+				cur->inode->nlink++;
326+				if (debug & DEBUG_WALK_DIR_LINKCHECK)
327+					printf("%s: link check found [%ju, %ju]\n",
328+					    __func__,
329+					    (uintmax_t)curino->st.st_dev,
330+					    (uintmax_t)curino->st.st_ino);
331+			}
332+		}
333+		if (S_ISLNK(cur->type)) {
334+			char	slink[PATH_MAX+1];
335+			ssize_t	llen;
336+
337+			llen = readlink(path, slink, sizeof(slink) - 1);
338+			if (llen == -1)
339+				err(EXIT_FAILURE, "Readlink `%s'", path);
340+			slink[llen] = '\0';
341+			cur->symlink = estrdup(slink);
342+		}
343+	}
344+	assert(first != NULL);
345+	if (join == NULL)
346+		for (cur = first->next; cur != NULL; cur = cur->next)
347+			cur->first = first;
348+	if (closedir(dirp) == -1)
349+		err(EXIT_FAILURE, "Can't closedir `%s/%s'", root, dir);
350+
351+	return fsnode_sort(first, root, dir);
352+}
353+
354+static fsnode *
355+create_fsnode(const char *root, const char *path, const char *name,
356+    struct stat *stbuf)
357+{
358+	fsnode *cur;
359+
360+	cur = ecalloc(1, sizeof(*cur));
361+	cur->path = estrdup(path);
362+	cur->name = estrdup(name);
363+	cur->inode = ecalloc(1, sizeof(*cur->inode));
364+	cur->root = root;
365+	cur->type = stbuf->st_mode & S_IFMT;
366+	cur->inode->nlink = 1;
367+	cur->inode->st = *stbuf;
368+	if (stampst.st_ino) {
369+		cur->inode->st.st_atime = stampst.st_atime;
370+		cur->inode->st.st_mtime = stampst.st_mtime;
371+		cur->inode->st.st_ctime = stampst.st_ctime;
372+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
373+		cur->inode->st.st_atimensec = stampst.st_atimensec;
374+		cur->inode->st.st_mtimensec = stampst.st_mtimensec;
375+		cur->inode->st.st_ctimensec = stampst.st_ctimensec;
376+#endif
377+#if HAVE_STRUCT_STAT_BIRTHTIME
378+		cur->inode->st.st_birthtime = stampst.st_birthtime;
379+		cur->inode->st.st_birthtimensec = stampst.st_birthtimensec;
380+#endif
381+	}
382+	return (cur);
383+}
384+
385+/*
386+ * free_fsnodes --
387+ *	Removes node from tree and frees it and all of
388+ *   its descendents.
389+ */
390+void
391+free_fsnodes(fsnode *node)
392+{
393+	fsnode	*cur, *next;
394+
395+	assert(node != NULL);
396+
397+	/* for ".", start with actual parent node */
398+	if (node->first == node) {
399+		assert(node->name[0] == '.' && node->name[1] == '\0');
400+		if (node->parent) {
401+			assert(node->parent->child == node);
402+			node = node->parent;
403+		}
404+	}
405+
406+	/* Find ourselves in our sibling list and unlink */
407+	if (node->first != node) {
408+		for (cur = node->first; cur->next; cur = cur->next) {
409+			if (cur->next == node) {
410+				cur->next = node->next;
411+				node->next = NULL;
412+				break;
413+			}
414+		}
415+	}
416+
417+	for (cur = node; cur != NULL; cur = next) {
418+		next = cur->next;
419+		if (cur->child) {
420+			cur->child->parent = NULL;
421+			free_fsnodes(cur->child);
422+		}
423+		if (cur->inode->nlink-- == 1)
424+			free(cur->inode);
425+		if (cur->symlink)
426+			free(cur->symlink);
427+		free(cur->path);
428+		free(cur->name);
429+		free(cur);
430+	}
431+}
432+
433+/*
434+ * apply_specfile --
435+ *	read in the mtree(8) specfile, and apply it to the tree
436+ *	at dir,parent. parameters in parent on equivalent types
437+ *	will be changed to those found in specfile, and missing
438+ *	entries will be added.
439+ */
440+void
441+apply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly)
442+{
443+	struct timeval	 start;
444+	FILE	*fp;
445+	NODE	*root;
446+
447+	assert(specfile != NULL);
448+	assert(parent != NULL);
449+
450+	if (debug & DEBUG_APPLY_SPECFILE)
451+		printf("%s: %s, %s %p\n", __func__, specfile, dir, parent);
452+
453+				/* read in the specfile */
454+	if ((fp = fopen(specfile, "r")) == NULL)
455+		err(EXIT_FAILURE, "Can't open `%s'", specfile);
456+	TIMER_START(start);
457+	root = spec(fp);
458+	TIMER_RESULTS(start, "spec");
459+	if (fclose(fp) == EOF)
460+		err(EXIT_FAILURE, "Can't close `%s'", specfile);
461+
462+				/* perform some sanity checks */
463+	if (root == NULL)
464+		errx(EXIT_FAILURE,
465+		    "Specfile `%s' did not contain a tree", specfile);
466+	assert(strcmp(root->name, ".") == 0);
467+	assert(root->type == F_DIR);
468+
469+				/* merge in the changes */
470+	apply_specdir(dir, root, parent, speconly);
471+
472+	free_nodes(root);
473+	if (missing)
474+		errx(EXIT_FAILURE, "Add %zu missing entries in `%s'", 
475+		    missing, specfile);
476+}
477+
478+static void
479+apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly)
480+{
481+	char	 path[MAXPATHLEN + 1];
482+	NODE	*curnode;
483+	fsnode	*curfsnode;
484+
485+	assert(specnode != NULL);
486+	assert(dirnode != NULL);
487+
488+	if (debug & DEBUG_APPLY_SPECFILE)
489+		printf("%s: %s %p %p\n", __func__, dir, specnode, dirnode);
490+
491+	if (specnode->type != F_DIR)
492+		errx(EXIT_FAILURE, "Specfile node `%s/%s' is not a directory",
493+		    dir, specnode->name);
494+	if (dirnode->type != S_IFDIR)
495+		errx(EXIT_FAILURE, "Directory node `%s/%s' is not a directory",
496+		    dir, dirnode->name);
497+
498+	apply_specentry(dir, specnode, dirnode);
499+
500+	/* Remove any filesystem nodes not found in specfile */
501+	/* XXX inefficient.  This is O^2 in each dir and it would
502+	 * have been better never to have walked this part of the tree
503+	 * to begin with
504+	 */
505+	if (speconly) {
506+		fsnode *next;
507+		assert(dirnode->name[0] == '.' && dirnode->name[1] == '\0');
508+		for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = next) {
509+			next = curfsnode->next;
510+			for (curnode = specnode->child; curnode != NULL;
511+			     curnode = curnode->next) {
512+				if (strcmp(curnode->name, curfsnode->name) == 0)
513+					break;
514+			}
515+			if (curnode == NULL) {
516+				if (speconly > 1) {
517+					warnx("missing specfile entry for %s/%s",
518+					    dir, curfsnode->name);
519+					missing++;
520+				}
521+				if (debug & DEBUG_APPLY_SPECONLY) {
522+					printf("%s: trimming %s/%s %p\n",
523+					    __func__, dir, curfsnode->name,
524+					    curfsnode);
525+				}
526+				free_fsnodes(curfsnode);
527+			}
528+		}
529+	}
530+
531+			/* now walk specnode->child matching up with dirnode */
532+	for (curnode = specnode->child; curnode != NULL;
533+	    curnode = curnode->next) {
534+		if (debug & DEBUG_APPLY_SPECENTRY)
535+			printf("%s:  spec %s\n", __func__, curnode->name);
536+		for (curfsnode = dirnode->next; curfsnode != NULL;
537+		    curfsnode = curfsnode->next) {
538+#if 0	/* too verbose for now */
539+			if (debug & DEBUG_APPLY_SPECENTRY)
540+				printf("%s: dirent %s\n", __func__,
541+				    curfsnode->name);
542+#endif
543+			if (strcmp(curnode->name, curfsnode->name) == 0)
544+				break;
545+		}
546+		if ((size_t)snprintf(path, sizeof(path), "%s/%s",
547+		    dir, curnode->name) >= sizeof(path))
548+			errx(EXIT_FAILURE, "Pathname too long.");
549+		if (curfsnode == NULL) {	/* need new entry */
550+			struct stat	stbuf;
551+
552+					    /*
553+					     * don't add optional spec entries
554+					     * that lack an existing fs entry
555+					     */
556+			if ((curnode->flags & F_OPT) &&
557+			    lstat(path, &stbuf) == -1)
558+					continue;
559+
560+					/* check that enough info is provided */
561+#define NODETEST(t, m)							\
562+			if (!(t))					\
563+				errx(EXIT_FAILURE,			\
564+				    "`%s': %s not provided", path, m)
565+			NODETEST(curnode->flags & F_TYPE, "type");
566+			NODETEST(curnode->flags & F_MODE, "mode");
567+				/* XXX: require F_TIME ? */
568+			NODETEST(curnode->flags & F_GID ||
569+			    curnode->flags & F_GNAME, "group");
570+			NODETEST(curnode->flags & F_UID ||
571+			    curnode->flags & F_UNAME, "user");
572+			if (curnode->type == F_BLOCK || curnode->type == F_CHAR)
573+				NODETEST(curnode->flags & F_DEV,
574+				    "device number");
575+#undef NODETEST
576+
577+			if (debug & DEBUG_APPLY_SPECFILE)
578+				printf("%s: adding %s\n", __func__, curnode->name);
579+					/* build minimal fsnode */
580+			memset(&stbuf, 0, sizeof(stbuf));
581+			stbuf.st_mode = nodetoino(curnode->type);
582+			stbuf.st_nlink = 1;
583+			stbuf.st_mtime = stbuf.st_atime =
584+			    stbuf.st_ctime = start_time.tv_sec;
585+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
586+			stbuf.st_mtimensec = stbuf.st_atimensec =
587+			    stbuf.st_ctimensec = start_time.tv_nsec;
588+#endif
589+			curfsnode = create_fsnode(".", ".", curnode->name,
590+			    &stbuf);
591+			curfsnode->parent = dirnode->parent;
592+			curfsnode->first = dirnode;
593+			curfsnode->next = dirnode->next;
594+			dirnode->next = curfsnode;
595+			if (curfsnode->type == S_IFDIR) {
596+					/* for dirs, make "." entry as well */
597+				curfsnode->child = create_fsnode(".", ".", ".",
598+				    &stbuf);
599+				curfsnode->child->parent = curfsnode;
600+				curfsnode->child->first = curfsnode->child;
601+			}
602+			if (curfsnode->type == S_IFLNK) {
603+				assert(curnode->slink != NULL);
604+					/* for symlinks, copy the target */
605+				curfsnode->symlink = estrdup(curnode->slink);
606+			}
607+		}
608+		apply_specentry(dir, curnode, curfsnode);
609+		if (curnode->type == F_DIR) {
610+			if (curfsnode->type != S_IFDIR)
611+				errx(EXIT_FAILURE,
612+				    "`%s' is not a directory", path);
613+			assert(curfsnode->child != NULL);
614+			apply_specdir(path, curnode, curfsnode->child, speconly);
615+		}
616+	}
617+}
618+
619+static void
620+apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode)
621+{
622+
623+	assert(specnode != NULL);
624+	assert(dirnode != NULL);
625+
626+	if (nodetoino(specnode->type) != dirnode->type)
627+		errx(EXIT_FAILURE,
628+		    "`%s/%s' type mismatch: specfile %s, tree %s",
629+		    dir, specnode->name, inode_type(nodetoino(specnode->type)),
630+		    inode_type(dirnode->type));
631+
632+	if (debug & DEBUG_APPLY_SPECENTRY)
633+		printf("%s: %s/%s\n", dir, __func__, dirnode->name);
634+
635+#define ASEPRINT(t, b, o, n) \
636+		if (debug & DEBUG_APPLY_SPECENTRY) \
637+			printf("\t\t\tchanging %s from " b " to " b "\n", \
638+			    t, o, n)
639+
640+	if (specnode->flags & (F_GID | F_GNAME)) {
641+		ASEPRINT("gid", "%d",
642+		    dirnode->inode->st.st_gid, specnode->st_gid);
643+		dirnode->inode->st.st_gid = specnode->st_gid;
644+	}
645+	if (specnode->flags & F_MODE) {
646+		ASEPRINT("mode", "%#o",
647+		    dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode);
648+		dirnode->inode->st.st_mode &= ~ALLPERMS;
649+		dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS);
650+	}
651+		/* XXX: ignoring F_NLINK for now */
652+	if (specnode->flags & F_SIZE) {
653+		ASEPRINT("size", "%jd",
654+		    (intmax_t)dirnode->inode->st.st_size,
655+		    (intmax_t)specnode->st_size);
656+		dirnode->inode->st.st_size = specnode->st_size;
657+	}
658+	if (specnode->flags & F_SLINK) {
659+		assert(dirnode->symlink != NULL);
660+		assert(specnode->slink != NULL);
661+		ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink);
662+		free(dirnode->symlink);
663+		dirnode->symlink = estrdup(specnode->slink);
664+	}
665+	if (specnode->flags & F_TIME) {
666+		ASEPRINT("time", "%ld",
667+		    (long)dirnode->inode->st.st_mtime,
668+		    (long)specnode->st_mtimespec.tv_sec);
669+		dirnode->inode->st.st_mtime =		specnode->st_mtimespec.tv_sec;
670+		dirnode->inode->st.st_atime =		specnode->st_mtimespec.tv_sec;
671+		dirnode->inode->st.st_ctime =		start_time.tv_sec;
672+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
673+		dirnode->inode->st.st_mtimensec =	specnode->st_mtimespec.tv_nsec;
674+		dirnode->inode->st.st_atimensec =	specnode->st_mtimespec.tv_nsec;
675+		dirnode->inode->st.st_ctimensec =	start_time.tv_nsec;
676+#endif
677+	}
678+	if (specnode->flags & (F_UID | F_UNAME)) {
679+		ASEPRINT("uid", "%d",
680+		    dirnode->inode->st.st_uid, specnode->st_uid);
681+		dirnode->inode->st.st_uid = specnode->st_uid;
682+	}
683+	if (specnode->flags & F_FLAGS) {
684+		ASEPRINT("flags", "%#lX",
685+		    (unsigned long)FSINODE_ST_FLAGS(*dirnode->inode),
686+		    (unsigned long)specnode->st_flags);
687+		FSINODE_ST_FLAGS(*dirnode->inode) = specnode->st_flags;
688+	}
689+	if (specnode->flags & F_DEV) {
690+		ASEPRINT("rdev", "%#jx",
691+		    (uintmax_t)dirnode->inode->st.st_rdev,
692+		    (uintmax_t)specnode->st_rdev);
693+		dirnode->inode->st.st_rdev = specnode->st_rdev;
694+	}
695+#undef ASEPRINT
696+
697+	dirnode->flags |= FSNODE_F_HASSPEC;
698+}
699+
700+
701+/*
702+ * dump_fsnodes --
703+ *	dump the fsnodes from `cur'
704+ */
705+void
706+dump_fsnodes(fsnode *root)
707+{
708+	fsnode	*cur;
709+	char	path[MAXPATHLEN + 1];
710+
711+	printf("%s: %s %p\n", __func__, root->path, root);
712+	for (cur = root; cur != NULL; cur = cur->next) {
713+		if (snprintf(path, sizeof(path), "%s/%s", cur->path,
714+		    cur->name) >= (int)sizeof(path))
715+			errx(EXIT_FAILURE, "Pathname too long.");
716+
717+		if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
718+			printf("%s: cur=%8p parent=%8p first=%8p ", __func__,
719+			    cur, cur->parent, cur->first);
720+		printf("%7s: %s", inode_type(cur->type), path);
721+		if (S_ISLNK(cur->type)) {
722+			assert(cur->symlink != NULL);
723+			printf(" -> %s", cur->symlink);
724+		} else {
725+			assert(cur->symlink == NULL);
726+		}
727+		if (cur->inode->nlink > 1)
728+			printf(", nlinks=%d", cur->inode->nlink);
729+		putchar('\n');
730+
731+		if (cur->child) {
732+			assert(cur->type == S_IFDIR);
733+			dump_fsnodes(cur->child);
734+		}
735+	}
736+	printf("%s: finished %s/%s\n", __func__, root->path, root->name);
737+}
738+
739+
740+/*
741+ * inode_type --
742+ *	for a given inode type `mode', return a descriptive string.
743+ *	for most cases, uses inotype() from mtree/misc.c
744+ */
745+const char *
746+inode_type(mode_t mode)
747+{
748+
749+	if (S_ISLNK(mode))
750+		return ("symlink");	/* inotype() returns "link"...  */
751+	return (inotype(mode));
752+}
753+
754+
755+/*
756+ * link_check --
757+ *	return pointer to fsinode matching `entry's st_ino & st_dev if it exists,
758+ *	otherwise add `entry' to table and return NULL
759+ */
760+/* This was borrowed from du.c and tweaked to keep an fsnode
761+ * pointer instead. -- dbj@netbsd.org
762+ */
763+static fsinode *
764+link_check(fsinode *entry)
765+{
766+	static struct entry {
767+		fsinode *data;
768+	} *htable;
769+	static size_t htshift;  /* log(allocated size) */
770+	static size_t htmask;   /* allocated size - 1 */
771+	static size_t htused;   /* 2*number of insertions */
772+	size_t h, h2;
773+	uint64_t tmp;
774+	/* this constant is (1<<64)/((1+sqrt(5))/2)
775+	 * aka (word size)/(golden ratio)
776+	 */
777+	const uint64_t HTCONST = 11400714819323198485ULL;
778+	const size_t HTBITS = 64;
779+
780+	/* Never store zero in hashtable */
781+	assert(entry);
782+
783+	/* Extend hash table if necessary, keep load under 0.5 */
784+	if (htused<<1 >= htmask) {
785+		struct entry *ohtable;
786+
787+		if (!htable)
788+			htshift = 10;   /* starting hashtable size */
789+		else
790+			htshift++;   /* exponential hashtable growth */
791+
792+		htmask  = (1 << htshift) - 1;
793+		htused = 0;
794+
795+		ohtable = htable;
796+		htable = ecalloc(htmask+1, sizeof(*htable));
797+		/* populate newly allocated hashtable */
798+		if (ohtable) {
799+			for (size_t i = 0; i <= htmask>>1; i++)
800+				if (ohtable[i].data)
801+					link_check(ohtable[i].data);
802+			free(ohtable);
803+		}
804+	}
805+
806+	/* multiplicative hashing */
807+	tmp = entry->st.st_dev;
808+	tmp <<= HTBITS>>1;
809+	tmp |=  entry->st.st_ino;
810+	tmp *= HTCONST;
811+	h  = tmp >> (HTBITS - htshift);
812+	h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */
813+
814+	/* open address hashtable search with double hash probing */
815+	while (htable[h].data) {
816+		if ((htable[h].data->st.st_ino == entry->st.st_ino) &&
817+		    (htable[h].data->st.st_dev == entry->st.st_dev)) {
818+			return htable[h].data;
819+		}
820+		h = (h + h2) & htmask;
821+	}
822+
823+	/* Insert the current entry into hashtable */
824+	htable[h].data = entry;
825+	htused++;
826+	return NULL;
827+}
+7, -3
 1@@ -1,7 +1,8 @@
 2 .POSIX:
 3 
 4 PROG = pr
 5-SRCS = pr.c egetopt.c ../compat/raise_default_signal.c
 6+SRCS = pr.c egetopt.c
 7+COMPATLIB = ../compat/libnetcompat.a
 8 MAN = pr.1
 9 
10 CC = cc
11@@ -13,8 +14,11 @@ MANDIR = /usr/local/share/man
12 
13 all: $(PROG)
14 
15-$(PROG): $(SRCS)
16-	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
17+$(PROG): $(SRCS) $(COMPATLIB)
18+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
19+
20+$(COMPATLIB):
21+	(cd ../compat && $(MAKE) all)
22 
23 install: $(PROG)
24 	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
+6, -2
 1@@ -2,6 +2,7 @@
 2 
 3 PROG = rs
 4 SRCS = rs.c
 5+COMPATLIB = ../compat/libnetcompat.a
 6 MAN = rs.1
 7 
 8 CC = cc
 9@@ -13,8 +14,11 @@ MANDIR = /usr/local/share/man
10 
11 all: $(PROG)
12 
13-$(PROG): $(SRCS)
14-	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
15+$(PROG): $(SRCS) $(COMPATLIB)
16+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
17+
18+$(COMPATLIB):
19+	(cd ../compat && $(MAKE) all)
20 
21 install: $(PROG)
22 	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
+7, -3
 1@@ -1,7 +1,8 @@
 2 .POSIX:
 3 
 4 PROG = script
 5-SRCS = script.c ../compat/getprogname.c
 6+SRCS = script.c
 7+COMPATLIB = ../compat/libnetcompat.a
 8 MAN = script.1
 9 
10 CC = cc
11@@ -14,8 +15,11 @@ MANDIR = /usr/local/share/man
12 
13 all: $(PROG)
14 
15-$(PROG): $(SRCS)
16-	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
17+$(PROG): $(SRCS) $(COMPATLIB)
18+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(COMPATLIB) $(LDLIBS)
19+
20+$(COMPATLIB):
21+	(cd ../compat && $(MAKE) all)
22 
23 install: $(PROG)
24 	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1