commit b63693d

shrub  ·  2026-05-26 14:34:59 +0000 UTC
parent b63693d
initial commit
66 files changed,  +11949, -0
+24, -0
 1@@ -0,0 +1,24 @@
 2+.POSIX:
 3+
 4+#RECURSIVE MAKE CONSIDERED USEFUL?
 5+SUBDIRS = apply col ctags finger jot pr rs script
 6+all:
 7+	@set -e; \
 8+	for dir in $(SUBDIRS); do \
 9+		echo "> $$dir"; \
10+		(cd "$$dir" && $(MAKE) all); \
11+	done
12+
13+clean:
14+	@set -e; \
15+	for dir in $(SUBDIRS); do \
16+		echo "> $$dir"; \
17+		(cd "$$dir" && $(MAKE) clean); \
18+	done
19+
20+install:
21+	@set -e; \
22+	for dir in $(SUBDIRS); do \
23+		echo "> $$dir"; \
24+		(cd "$$dir" && $(MAKE) install); \
25+	done
+8, -0
1@@ -0,0 +1,8 @@
2+netmisc
3+-------
4+
5+misc programs from netbsd with some compatibilty shim and portability bits.
6+
7+you need cc, make, yacc, etc to build, and a reasonably posix-compliant system. 
8+
9+all code under it's original license
+27, -0
 1@@ -0,0 +1,27 @@
 2+.POSIX:
 3+
 4+PROG = apply
 5+SRCS = apply.c ../compat/getprogname.c ../compat/setprogname.c
 6+MAN = apply.1
 7+
 8+CC = cc
 9+CFLAGS = -O2
10+CPPFLAGS = -I../compat -include ../compat/netcompat.h
11+DESTDIR =
12+BINDIR = /usr/local/bin
13+MANDIR = /usr/local/share/man
14+
15+all: $(PROG)
16+
17+$(PROG): $(SRCS)
18+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
19+
20+install: $(PROG)
21+	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
22+	cp $(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
23+	chmod 755 $(DESTDIR)$(BINDIR)/$(PROG)
24+	cp $(MAN) $(DESTDIR)$(MANDIR)/man1/$(MAN)
25+	chmod 644 $(DESTDIR)$(MANDIR)/man1/$(MAN)
26+
27+clean:
28+	rm -f $(PROG) *.o
+161, -0
  1@@ -0,0 +1,161 @@
  2+.\"	$NetBSD: apply.1,v 1.15 2016/03/14 09:53:37 wiz Exp $
  3+.\"
  4+.\" Copyright (c) 1983, 1990, 1993
  5+.\"	The Regents of the University of California.  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. Neither the name of the University nor the names of its contributors
 16+.\"    may be used to endorse or promote products derived from this software
 17+.\"    without specific prior written permission.
 18+.\"
 19+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 20+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 21+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 22+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 23+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 24+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 25+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 26+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 27+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 28+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 29+.\" SUCH DAMAGE.
 30+.\"
 31+.\"     @(#)apply.1	8.2 (Berkeley) 4/4/94
 32+.\"
 33+.Dd March 12, 2016
 34+.Dt APPLY 1
 35+.Os
 36+.Sh NAME
 37+.Nm apply
 38+.Nd apply a command to a list of arguments
 39+.Sh SYNOPSIS
 40+.Nm
 41+.Op Fl a Ns Ar c
 42+.Op Fl Ns Ar #
 43+.Ar command arguments ...
 44+.Sh DESCRIPTION
 45+.Nm
 46+divides its
 47+.Ar arguments
 48+into fixed-size groups and runs
 49+.Ar command
 50+in turn on each group.
 51+.Pp
 52+On each execution of
 53+.Ar command ,
 54+each character sequence of the form
 55+.Dq Li \&%d
 56+in
 57+.Ar command ,
 58+where
 59+.Ar d
 60+is a digit from 1 to 9, is replaced with the
 61+.Ar d Ns \'th
 62+argument from the current argument group.
 63+The argument group size is set to the largest such
 64+.Ar d
 65+found.
 66+Any given argument number can be used arbitrarily many times.
 67+(Including zero.)
 68+.Pp
 69+If no explicit substitution sequences are found in
 70+.Ar command ,
 71+the current argument group is substituted after
 72+.Ar command
 73+delimited by spaces, and the argument group size defaults to 1 and can
 74+be set with the
 75+.Fl #
 76+option.
 77+.Pp
 78+If the argument group size is set to 0, one argument from
 79+.Ar arguments
 80+is taken for each execution of
 81+.Ar command
 82+anyway, but is discarded and not substituted; thus,
 83+.Ar command
 84+is run verbatim once for every argument.
 85+.Pp
 86+The options are as follows:
 87+.Bl -tag -width "-ac"
 88+.It Fl Ns Ar #
 89+Set the argument group size.
 90+Ignored if explicit substitutions are used.
 91+.It Fl a Ns Ar c
 92+Change the magic substitution character from the default
 93+.Dq Li %
 94+to
 95+.Ar c .
 96+.El
 97+.Sh ENVIRONMENT
 98+The following environment variable affects the execution of
 99+.Nm :
100+.Bl -tag -width SHELL
101+.It Ev SHELL
102+Pathname of the shell to use to execute
103+.Ar command .
104+If this variable is not defined, the Bourne shell is used.
105+.El
106+.Sh FILES
107+.Bl -tag -width /bin/sh -compact
108+.It Pa /bin/sh
109+Default shell.
110+.El
111+.Sh EXAMPLES
112+.Bl -tag -width apply -compact
113+.It Li "apply echo *"
114+Prints the name of every file in the current directory.
115+.It Li "apply \-2 diff a1 b1 a2 b2 a3 b3"
116+Compares the `a' files to the `b' files.
117+.It Li "apply \-0 who 1 2 3 4 5"
118+Runs
119+.Xr who 1
120+5 times.
121+.It Li "apply \'ln %1 /home/joe/joe.%1\'" *
122+Hard-links all files in the current directory into the directory
123+.Pa /home/joe ,
124+with their names prefixed with "joe.".
125+.It Li "apply \'cvs diff %1 > %1.diff'" *.c
126+Diff all C sources in the current directory against the last
127+checked-in version and store each result in its own output file.
128+.El
129+.Sh HISTORY
130+The
131+.Nm
132+command appeared in
133+.Bx 4.2 .
134+.Sh AUTHORS
135+.An Rob Pike
136+.Sh RESTRICTIONS
137+The complete command to be executed on each iteration is assembled as
138+a string without additional quoting and then passed to a copy of the
139+shell for parsing and execution.
140+Thus, commands or arguments that contain spaces or shell
141+metacharacters may behave in unexpected ways.
142+.Pp
143+To protect a shell metacharacter fully it must be quoted twice, once
144+against the current shell and once against the subshell used for
145+execution.
146+Similarly, for a shell metacharacter to be interpreted by the subshell
147+it must be quoted to protect it from the current shell.
148+A simple rule of thumb is to enclose the entire
149+.Ar command
150+in single quotes
151+.Pq ''
152+so that the current shell does not interpret any of it.
153+.Sh BUGS
154+There is no easy way to produce the literal string
155+.Dq %1
156+in
157+.Ar command .
158+.Pp
159+.Nm
160+unconditionally inserts "exec" at the beginning of each copy of
161+.Ar command
162+so compound commands may not behave as intended.
+266, -0
  1@@ -0,0 +1,266 @@
  2+/*	$NetBSD: apply.c,v 1.19 2016/03/12 22:28:04 dholland 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 by
  9+ * Jan-Simon Pendry.
 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. Neither the name of the University nor the names of its contributors
 20+ *    may be used to endorse or promote products derived from this software
 21+ *    without specific prior written permission.
 22+ *
 23+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 24+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 25+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 26+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 27+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 29+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 30+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 31+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 33+ * SUCH DAMAGE.
 34+ */
 35+
 36+#include <sys/cdefs.h>
 37+#ifndef lint
 38+#if 0
 39+static char sccsid[] = "@(#)apply.c	8.4 (Berkeley) 4/4/94";
 40+#else
 41+__RCSID("$NetBSD: apply.c,v 1.19 2016/03/12 22:28:04 dholland Exp $");
 42+#endif
 43+#endif /* not lint */
 44+
 45+#include <sys/wait.h>
 46+
 47+#include <ctype.h>
 48+#include <err.h>
 49+#include <paths.h>
 50+#include <signal.h>
 51+#include <stdio.h>
 52+#include <stdlib.h>
 53+#include <string.h>
 54+#include <unistd.h>
 55+
 56+static __dead void usage(void);
 57+static int shell_system(const char *);
 58+
 59+int
 60+main(int argc, char *argv[])
 61+{
 62+	size_t clen, l;
 63+	int ch, debug, i, magic, n, nargs, rval;
 64+	char *c, *cmd, *p, *q, *nc;
 65+
 66+	(void)setprogname(argv[0]);	/* for portability */
 67+
 68+	/* Option defaults */
 69+	debug = 0;
 70+	magic = '%';
 71+	nargs = -1;
 72+
 73+	while ((ch = getopt(argc, argv, "a:d0123456789")) != -1) {
 74+		switch (ch) {
 75+		case 'a':
 76+			if (optarg[1] != '\0')
 77+				errx(EXIT_FAILURE,
 78+				    "Illegal magic character specification.");
 79+			magic = optarg[0];
 80+			break;
 81+		case 'd':
 82+			debug = 1;
 83+			break;
 84+		case '0': case '1': case '2': case '3': case '4':
 85+		case '5': case '6': case '7': case '8': case '9':
 86+			if (nargs != -1)
 87+				errx(EXIT_FAILURE,
 88+				    "Only one -# argument may be specified.");
 89+			nargs = optopt - '0';
 90+			break;
 91+		default:
 92+			usage();
 93+		}
 94+	}
 95+	argc -= optind;
 96+	argv += optind;
 97+
 98+	if (argc < 2)
 99+		usage();
100+
101+	/*
102+	 * The command to run is now argv[0], and the args are argv[1+].
103+	 * Look for %digit references in the command, remembering the
104+	 * largest one.
105+	 */
106+	n = 0;
107+	for (p = argv[0]; p[0] != '\0'; ++p) {
108+		if (p[0] == magic && p[1] != '\0' &&
109+		    isdigit((unsigned char)p[1]) && p[1] != '0') {
110+			++p;
111+			if (p[0] - '0' > n)
112+				n = p[0] - '0';
113+		}
114+	}
115+
116+	/*
117+	 * If there were any %digit references, then use those, otherwise
118+	 * build a new command string with sufficient %digit references at
119+	 * the end to consume (nargs) arguments each time round the loop.
120+	 * Allocate enough space to hold the maximum command.
121+	 */
122+	if ((cmd = malloc(sizeof("exec ") - 1 +
123+	    strlen(argv[0]) + 9 * (sizeof(" %1") - 1) + 1)) == NULL)
124+		err(EXIT_FAILURE, "malloc");
125+
126+	if (n == 0) {
127+		/* If nargs not set, default to a single argument. */
128+		if (nargs == -1)
129+			nargs = 1;
130+
131+		p = cmd;
132+		p += sprintf(cmd, "exec %s", argv[0]);
133+		for (i = 1; i <= nargs; i++)
134+			p += sprintf(p, " %c%d", magic, i);
135+
136+		/*
137+		 * If nargs set to the special value 0, eat a single
138+		 * argument for each command execution.
139+		 */
140+		if (nargs == 0)
141+			nargs = 1;
142+	} else {
143+		(void)sprintf(cmd, "exec %s", argv[0]);
144+		nargs = n;
145+	}
146+
147+	/*
148+	 * Grab some space in which to build the command.  Allocate
149+	 * as necessary later, but no reason to build it up slowly
150+	 * for the normal case.
151+	 */
152+	if ((c = malloc(clen = 1024)) == NULL)
153+		err(EXIT_FAILURE, "malloc");
154+
155+	/*
156+	 * (argc) and (argv) are still offset by one to make it simpler to
157+	 * expand %digit references.  At the end of the loop check for (argc)
158+	 * equals 1 means that all the (argv) has been consumed.
159+	 */
160+	for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
161+		/*
162+		 * Find a max value for the command length, and ensure
163+		 * there's enough space to build it.
164+		 */
165+		for (l = strlen(cmd), i = 0; i < nargs; i++)
166+			l += strlen(argv[i+1]);
167+		if (l > clen) {
168+			nc = realloc(c, l);
169+			if (nc == NULL)
170+				err(EXIT_FAILURE, "malloc");
171+			c = nc;
172+			clen = l;
173+		}
174+
175+		/* Expand command argv references. */
176+		for (p = cmd, q = c; *p != '\0'; ++p) {
177+			if (p[0] == magic && isdigit((unsigned char)p[1]) &&
178+			    p[1] != '0')
179+				q += sprintf(q, "%s", argv[(++p)[0] - '0']);
180+			else
181+				*q++ = *p;
182+		}
183+
184+		/* Terminate the command string. */
185+		*q = '\0';
186+
187+		/* Run the command. */
188+		if (debug)
189+			(void)printf("%s\n", c);
190+		else if (shell_system(c))
191+			rval = 1;
192+	}
193+
194+	if (argc != 1)
195+		errx(EXIT_FAILURE,
196+		    "Expecting additional argument%s after \"%s\"",
197+		    (nargs - argc) ? "s" : "", argv[argc - 1]);
198+	return rval;
199+}
200+
201+/*
202+ * shell_system --
203+ * 	Private version of system(3).  Use the user's SHELL environment
204+ *	variable as the shell to execute.
205+ */
206+static int
207+shell_system(const char *command)
208+{
209+	static const char *name, *shell;
210+	int status;
211+	pid_t pid;
212+	sigset_t chldmask, omask;
213+	sig_t intsave, quitsave;
214+
215+	if (shell == NULL) {
216+		if ((shell = getenv("SHELL")) == NULL)
217+			shell = _PATH_BSHELL;
218+		if ((name = strrchr(shell, '/')) == NULL)
219+			name = shell;
220+		else
221+			++name;
222+	}
223+
224+	if (!command) {
225+		/* just checking... */
226+		return(1);
227+	}
228+
229+	(void)sigemptyset(&chldmask);
230+	(void)sigaddset(&chldmask, SIGCHLD);
231+	if (sigprocmask(SIG_BLOCK, &chldmask, &omask) == -1)
232+		err(EXIT_FAILURE, "sigprocmask");
233+
234+	switch (pid = fork()) {
235+	case -1:
236+		/* error */
237+		err(EXIT_FAILURE, "fork");
238+		/*NOTREACHED*/
239+	case 0:
240+		/* child */
241+		(void)sigprocmask(SIG_SETMASK, &omask, NULL);
242+		(void)execl(shell, name, "-c", command, (char *)NULL);
243+		warn("%s", shell);
244+		_exit(1);
245+		/*NOTREACHED*/
246+	default:
247+		/* parent */
248+		intsave = signal(SIGINT, SIG_IGN);
249+		quitsave = signal(SIGQUIT, SIG_IGN);
250+		pid = waitpid(pid, &status, 0);
251+		(void)sigprocmask(SIG_SETMASK, &omask, NULL);
252+		(void)signal(SIGINT, intsave);
253+		(void)signal(SIGQUIT, quitsave);
254+		return pid == -1 ? -1 : status;
255+	}
256+	/*NOTREACHED*/
257+}
258+
259+static __dead void
260+usage(void)
261+{
262+
263+	(void)fprintf(stderr,
264+	    "usage: %s [-a magic] [-0123456789] command arguments ...\n",
265+	    getprogname());
266+	exit(1);
267+}
+53, -0
 1@@ -0,0 +1,53 @@
 2+apply.o: apply.c \
 3+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/cdefs.h \
 4+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/cdefs.h \
 5+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/cdefs_elf.h \
 6+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/wait.h \
 7+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/featuretest.h \
 8+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/types.h \
 9+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/types.h \
10+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/int_types.h \
11+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/common_int_types.h \
12+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/ansi.h \
13+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/common_ansi.h \
14+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/ansi.h \
15+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/stdint.h \
16+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/int_mwgwtypes.h \
17+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/common_int_mwgwtypes.h \
18+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/int_limits.h \
19+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/common_int_limits.h \
20+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/int_const.h \
21+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/common_int_const.h \
22+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/wchar_limits.h \
23+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/fd_set.h \
24+  /home/srb/random/netbsd/destdir-minimal/usr/include/pthread_types.h \
25+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/sigtypes.h \
26+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/siginfo.h \
27+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/signal.h \
28+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/trap.h \
29+  /home/srb/random/netbsd/destdir-minimal/usr/include/x86/trap.h \
30+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/mcontext.h \
31+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/frame_regs.h \
32+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/idtype.h \
33+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/endian.h \
34+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/endian_machdep.h \
35+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/bswap.h \
36+  /home/srb/random/netbsd/destdir-minimal/usr/include/machine/byte_swap.h \
37+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/bswap.h \
38+  /home/srb/random/netbsd/destdir-minimal/usr/include/ctype.h \
39+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/ctype_inline.h \
40+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/ctype_bits.h \
41+  /home/srb/random/netbsd/destdir-minimal/usr/include/err.h \
42+  /home/srb/random/netbsd/destdir-minimal/usr/include/stdarg.h \
43+  /home/srb/random/netbsd/destdir-minimal/usr/include/paths.h \
44+  /home/srb/random/netbsd/destdir-minimal/usr/include/signal.h \
45+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/signal.h \
46+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/ucontext.h \
47+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/timespec.h \
48+  /home/srb/random/netbsd/destdir-minimal/usr/include/stdio.h \
49+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/null.h \
50+  /home/srb/random/netbsd/destdir-minimal/usr/include/stdlib.h \
51+  /home/srb/random/netbsd/destdir-minimal/usr/include/string.h \
52+  /home/srb/random/netbsd/destdir-minimal/usr/include/strings.h \
53+  /home/srb/random/netbsd/destdir-minimal/usr/include/unistd.h \
54+  /home/srb/random/netbsd/destdir-minimal/usr/include/sys/unistd.h
+29, -0
 1@@ -0,0 +1,29 @@
 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+MAN = col.1
 9+
10+CC = cc
11+CFLAGS = -O2
12+CPPFLAGS = -I../compat -include ../compat/netcompat.h
13+DESTDIR =
14+BINDIR = /usr/local/bin
15+MANDIR = /usr/local/share/man
16+
17+all: $(PROG)
18+
19+$(PROG): $(SRCS)
20+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
21+
22+install: $(PROG)
23+	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
24+	cp $(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
25+	chmod 755 $(DESTDIR)$(BINDIR)/$(PROG)
26+	cp $(MAN) $(DESTDIR)$(MANDIR)/man1/$(MAN)
27+	chmod 644 $(DESTDIR)$(MANDIR)/man1/$(MAN)
28+
29+clean:
30+	rm -f $(PROG) *.o
+48, -0
 1@@ -0,0 +1,48 @@
 2+#	@(#)README	8.1 (Berkeley) 6/6/93
 3+
 4+col - filter out reverse line feeds.
 5+
 6+Options are:
 7+	-b	do not print any backspaces (last character written is printed)
 8+	-f	allow half line feeds in output, by default characters between
 9+		lines are pushed to the line below
10+	-x	do not compress spaces into tabs.
11+	-l num	keep (at least) num lines in memory, 128 are kept by default
12+
13+In the 32V source code to col(1) the default behavior was to NOT compress
14+spaces into tabs.  There was a -h option which caused it to compress spaces
15+into tabs.  There was no -x flag.
16+
17+The 32V documentation, however, was consistent with the SVID (actually, V7
18+at the time) and documented a -x flag (as defined above) while making no
19+mention of a -h flag.  Just before 4.3BSD went out, CSRG updated the manual
20+page to reflect the way the code worked.  Suspecting that this was probably
21+the wrong way to go, this version adopts the SVID defaults, and no longer
22+documents the -h option.
23+
24+The S5 -p flag is not supported because it isn't clear what it does (looks
25+like a kludge introduced for a particular printer).
26+
27+Known differences between AT&T's col and this one (# is delimiter):
28+	Input			AT&T col		this col
29+	#\nabc\E7def\n#		#   def\nabc\r#		#   def\nabc\n#
30+	#a#			##			#a\n#
31+		- last line always ends with at least one \n (or \E9)
32+	#1234567 8\n#		#1234567\t8\n#		#1234567 8\n#
33+		- single space not expanded to tab
34+     -f #a\E8b\n#		#ab\n#			# b\E9\ra\n#
35+		- can back up past first line (as far as you want) so you
36+		  *can* have a super script on the first line
37+	#\E9_\ba\E8\nb\n#	#\n_\bb\ba\n#		#\n_\ba\bb\n#
38+		- always print last character written to a position,
39+		  AT&T col claims to do this but doesn't.
40+
41+If a character is to be placed on a line that has been flushed, a warning
42+is produced (the AT&T col is silent).   The -l flag (not in AT&T col) can
43+be used to increase the number of lines buffered to avoid the problem.
44+
45+General algorithm: a limited number of lines are buffered in a linked
46+list.  When a printable character is read, it is put in the buffer of
47+the current line along with the column it's supposed to be in.  When
48+a line is flushed, the characters in the line are sorted according to
49+column and then printed.
+172, -0
  1@@ -0,0 +1,172 @@
  2+.\" $NetBSD: col.1,v 1.11 2020/11/01 22:27:15 christos Exp $
  3+.\" Copyright (c) 1990, 1993
  4+.\"	The Regents of the University of California.  All rights reserved.
  5+.\"
  6+.\" This code is derived from software contributed to Berkeley by
  7+.\" Michael Rendell.
  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. Neither the name of the University nor the names of its contributors
 18+.\"    may be used to endorse or promote products derived from this software
 19+.\"    without specific prior written permission.
 20+.\"
 21+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 22+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 23+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 24+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 25+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 26+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 27+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 28+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 29+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 30+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 31+.\" SUCH DAMAGE.
 32+.\"
 33+.\"     @(#)col.1	8.1 (Berkeley) 6/29/93
 34+.\" $FreeBSD: head/usr.bin/col/col.1 366913 2020-10-21 16:30:34Z fernape $
 35+.\"
 36+.Dd November 1, 2020
 37+.Dt COL 1
 38+.Os
 39+.Sh NAME
 40+.Nm col
 41+.Nd filter reverse line feeds from input
 42+.Sh SYNOPSIS
 43+.Nm
 44+.Op Fl bfhpx
 45+.Op Fl l Ar num
 46+.Sh DESCRIPTION
 47+The
 48+.Nm
 49+utility filters out reverse (and half reverse) line feeds so that the output is
 50+in the correct order with only forward and half forward line
 51+feeds, and replaces white-space characters with tabs where possible.
 52+.Pp
 53+The
 54+.Nm
 55+utility reads from the standard input and writes to the standard output.
 56+.Pp
 57+The options are as follows:
 58+.Bl -tag -width indent
 59+.It Fl b
 60+Do not output any backspaces, printing only the last character
 61+written to each column position.
 62+.It Fl f
 63+Forward half line feeds are permitted (``fine'' mode).
 64+Normally characters printed on a half line boundary are printed
 65+on the following line.
 66+.It Fl h
 67+Do not output multiple spaces instead of tabs (default).
 68+.It Fl l Ar num
 69+Buffer at least
 70+.Ar num
 71+lines in memory.
 72+By default, 128 lines are buffered.
 73+.It Fl p
 74+Force unknown control sequences to be passed through unchanged.
 75+Normally,
 76+.Nm
 77+will filter out any control sequences from the input other than those
 78+recognized and interpreted by itself, which are listed below.
 79+.It Fl x
 80+Output multiple spaces instead of tabs.
 81+.El
 82+.Pp
 83+In the input stream,
 84+.Nm
 85+understands both the escape sequences of the form escape-digit
 86+mandated by
 87+.St -susv2
 88+and the traditional
 89+.Bx
 90+format escape-control-character.
 91+The control sequences for carriage motion and their ASCII values
 92+are as follows:
 93+.Pp
 94+.Bl -tag -width "carriage return" -compact
 95+.It ESC\-BELL
 96+reverse line feed (escape then bell).
 97+.It ESC\-7
 98+reverse line feed (escape then 7).
 99+.It ESC\-BACKSPACE
100+half reverse line feed (escape then backspace).
101+.It ESC\-8
102+half reverse line feed (escape then 8).
103+.It ESC\-TAB
104+half forward line feed (escape than tab).
105+.It ESC\-9
106+half forward line feed (escape then 9).
107+In
108+.Fl f
109+mode, this sequence may also occur in the output stream.
110+.It backspace
111+moves back one column (8); ignored in the first column
112+.It carriage return
113+(13)
114+.It newline
115+forward line feed (10); also does carriage return
116+.It shift in
117+shift to normal character set (15)
118+.It shift out
119+shift to alternate character set (14)
120+.It space
121+moves forward one column (32)
122+.It tab
123+moves forward to next tab stop (9)
124+.It vertical tab
125+reverse line feed (11)
126+.El
127+.Pp
128+All unrecognized control characters and escape sequences are
129+discarded.
130+.Pp
131+The
132+.Nm
133+utility keeps track of the character set as characters are read and makes
134+sure the character set is correct when they are output.
135+.Pp
136+If the input attempts to back up to the last flushed line,
137+.Nm
138+will display a warning message.
139+.Sh ENVIRONMENT
140+The
141+.Ev LANG , LC_ALL
142+and
143+.Ev LC_CTYPE
144+environment variables affect the execution of
145+.Nm
146+as described in
147+.Xr environ 7 .
148+.Sh EXIT STATUS
149+.Ex -std
150+.Sh EXAMPLES
151+We can use
152+.Nm
153+to filter the output of
154+.Xr man 1
155+and remove the backspace characters (
156+.Em ^H
157+) before searching for some text:
158+.Bd -literal -offset indent
159+man ls | col -b | grep HISTORY
160+.Ed
161+.Sh SEE ALSO
162+.Xr expand 1
163+.Sh STANDARDS
164+The
165+.Nm
166+utility conforms to
167+.St -susv2 .
168+.Sh HISTORY
169+A
170+.Nm
171+command
172+appeared in
173+.At v6 .
+603, -0
  1@@ -0,0 +1,603 @@
  2+/*	$NetBSD: col.c,v 1.20 2021/09/10 21:52:17 rillig Exp $	*/
  3+
  4+/*-
  5+ * SPDX-License-Identifier: BSD-3-Clause
  6+ *
  7+ * Copyright (c) 1990, 1993, 1994
  8+ *	The Regents of the University of California.  All rights reserved.
  9+ *
 10+ * This code is derived from software contributed to Berkeley by
 11+ * Michael Rendell of the Memorial University of Newfoundland.
 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+
 38+#include <sys/cdefs.h>
 39+#ifndef lint
 40+__COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\
 41+ The Regents of the University of California.  All rights reserved.");
 42+#endif /* not lint */
 43+ 
 44+#ifndef lint
 45+#if 0
 46+static char sccsid[] = "@(#)col.c	8.5 (Berkeley) 5/4/95";
 47+__FBSDID("$FreeBSD: head/usr.bin/col/col.c 366577 2020-10-09 15:27:37Z markj $")
 48+;
 49+
 50+#endif
 51+__RCSID("$NetBSD: col.c,v 1.20 2021/09/10 21:52:17 rillig Exp $");
 52+#endif /* not lint */
 53+
 54+#include <err.h>
 55+#include <errno.h>
 56+#include <inttypes.h>
 57+#include <limits.h>
 58+#include <locale.h>
 59+#include <stdio.h>
 60+#include <stdlib.h>
 61+#include <string.h>
 62+#include <termios.h>
 63+#include <unistd.h>
 64+#include <wchar.h>
 65+#include <wctype.h>
 66+
 67+#define	BS	'\b'		/* backspace */
 68+#define	TAB	'\t'		/* tab */
 69+#define	SPACE	' '		/* space */
 70+#define	NL	'\n'		/* newline */
 71+#define	CR	'\r'		/* carriage return */
 72+#define	ESC	'\033'		/* escape */
 73+#define	SI	'\017'		/* shift in to normal character set */
 74+#define	SO	'\016'		/* shift out to alternate character set */
 75+#define	VT	'\013'		/* vertical tab (aka reverse line feed) */
 76+#define	RLF	'7'		/* ESC-7 reverse line feed */
 77+#define	RHLF	'8'		/* ESC-8 reverse half-line feed */
 78+#define	FHLF	'9'		/* ESC-9 forward half-line feed */
 79+
 80+/* build up at least this many lines before flushing them out */
 81+#define	BUFFER_MARGIN		32
 82+
 83+typedef char CSET;
 84+
 85+typedef struct char_str {
 86+#define	CS_NORMAL	1
 87+#define	CS_ALTERNATE	2
 88+	int		c_column;	/* column character is in */
 89+	CSET		c_set;		/* character set (currently only 2) */
 90+	wchar_t		c_char;		/* character in question */
 91+	int		c_width;	/* character width */
 92+} CHAR;
 93+
 94+typedef struct line_str LINE;
 95+struct line_str {
 96+	CHAR	*l_line;		/* characters on the line */
 97+	LINE	*l_prev;		/* previous line */
 98+	LINE	*l_next;		/* next line */
 99+	int	l_lsize;		/* allocated sizeof l_line */
100+	int	l_line_len;		/* strlen(l_line) */
101+	int	l_needs_sort;		/* set if chars went in out of order */
102+	int	l_max_col;		/* max column in the line */
103+};
104+
105+static void	addto_lineno(int *, int);
106+static LINE	*alloc_line(void);
107+static void	dowarn(int);
108+static void	flush_line(LINE *);
109+static void	flush_lines(int);
110+static void	flush_blanks(void);
111+static void	free_line(LINE *);
112+__dead static void	usage(void);
113+
114+static CSET	last_set;		/* char_set of last char printed */
115+static LINE	*lines;
116+static int	compress_spaces;	/* if doing space -> tab conversion */
117+static int	fine;			/* if `fine' resolution (half lines) */
118+static int	max_bufd_lines;		/* max # of half lines to keep in memory */
119+static int	nblank_lines;		/* # blanks after last flushed line */
120+static int	no_backspaces;		/* if not to output any backspaces */
121+static int	pass_unknown_seqs;	/* pass unknown control sequences */
122+
123+#define	PUTC(ch) \
124+	do {					\
125+		if (putwchar(ch) == WEOF)	\
126+			errx(EXIT_FAILURE, "write error");	\
127+	} while (0)
128+
129+int
130+main(int argc, char **argv)
131+{
132+	wint_t ch;
133+	CHAR *c;
134+	CSET cur_set;			/* current character set */
135+	LINE *l;			/* current line */
136+	int extra_lines;		/* # of lines above first line */
137+	int cur_col;			/* current column */
138+	int cur_line;			/* line number of current position */
139+	int max_line;			/* max value of cur_line */
140+	int this_line;			/* line l points to */
141+	int nflushd_lines;		/* number of lines that were flushed */
142+	int adjust, opt, warned, width;
143+	int e;
144+
145+	(void)setlocale(LC_CTYPE, "");
146+
147+	max_bufd_lines = 256;
148+	compress_spaces = 1;		/* compress spaces into tabs */
149+	while ((opt = getopt(argc, argv, "bfhl:px")) != -1)
150+		switch (opt) {
151+		case 'b':		/* do not output backspaces */
152+			no_backspaces = 1;
153+			break;
154+		case 'f':		/* allow half forward line feeds */
155+			fine = 1;
156+			break;
157+		case 'h':		/* compress spaces into tabs */
158+			compress_spaces = 1;
159+			break;
160+		case 'l':		/* buffered line count */
161+			max_bufd_lines = (int)strtoi(optarg, NULL, 0, 1,
162+			    (INT_MAX - BUFFER_MARGIN) / 2, &e) * 2;
163+			if (e)
164+				errc(EXIT_FAILURE, e, "bad -l argument `%s'",
165+				    optarg);
166+			break;
167+		case 'p':		/* pass unknown control sequences */
168+			pass_unknown_seqs = 1;
169+			break;
170+		case 'x':		/* do not compress spaces into tabs */
171+			compress_spaces = 0;
172+			break;
173+		case '?':
174+		default:
175+			usage();
176+		}
177+
178+	if (optind != argc)
179+		usage();
180+
181+	adjust = cur_col = extra_lines = warned = 0;
182+	cur_line = max_line = nflushd_lines = this_line = 0;
183+	cur_set = last_set = CS_NORMAL;
184+	lines = l = alloc_line();
185+
186+	while ((ch = getwchar()) != WEOF) {
187+		if (!iswgraph(ch)) {
188+			switch (ch) {
189+			case BS:		/* can't go back further */
190+				if (cur_col == 0)
191+					continue;
192+				--cur_col;
193+				continue;
194+			case CR:
195+				cur_col = 0;
196+				continue;
197+			case ESC:		/* just ignore EOF */
198+				switch(getwchar()) {
199+				/*
200+				 * In the input stream, accept both the
201+				 * XPG5 sequences ESC-digit and the
202+				 * traditional BSD sequences ESC-ctrl.
203+				 */
204+				case '\007':
205+					/* FALLTHROUGH */
206+				case RLF:
207+					addto_lineno(&cur_line, -2);
208+					break;
209+				case '\010':
210+					/* FALLTHROUGH */
211+				case RHLF:
212+					addto_lineno(&cur_line, -1);
213+					break;
214+				case '\011':
215+					/* FALLTHROUGH */
216+				case FHLF:
217+					addto_lineno(&cur_line, 1);
218+					if (cur_line > max_line)
219+						max_line = cur_line;
220+				}
221+				continue;
222+			case NL:
223+				addto_lineno(&cur_line, 2);
224+				if (cur_line > max_line)
225+					max_line = cur_line;
226+				cur_col = 0;
227+				continue;
228+			case SPACE:
229+				++cur_col;
230+				continue;
231+			case SI:
232+				cur_set = CS_NORMAL;
233+				continue;
234+			case SO:
235+				cur_set = CS_ALTERNATE;
236+				continue;
237+			case TAB:		/* adjust column */
238+				cur_col |= 7;
239+				++cur_col;
240+				continue;
241+			case VT:
242+				addto_lineno(&cur_line, -2);
243+				continue;
244+			}
245+			if (iswspace(ch)) {
246+				if ((width = wcwidth(ch)) > 0)
247+					cur_col += width;
248+				continue;
249+			}
250+			if (!pass_unknown_seqs)
251+				continue;
252+		}
253+
254+		/* Must stuff ch in a line - are we at the right one? */
255+		if (cur_line + adjust != this_line) {
256+			LINE *lnew;
257+
258+			/* round up to next line */
259+			adjust = !fine && (cur_line & 1);
260+
261+			if (cur_line + adjust < this_line) {
262+				while (cur_line + adjust < this_line &&
263+				    l->l_prev != NULL) {
264+					l = l->l_prev;
265+					this_line--;
266+				}
267+				if (cur_line + adjust < this_line) {
268+					if (nflushd_lines == 0) {
269+						/*
270+						 * Allow backup past first
271+						 * line if nothing has been
272+						 * flushed yet.
273+						 */
274+						while (cur_line + adjust
275+						    < this_line) {
276+							lnew = alloc_line();
277+							l->l_prev = lnew;
278+							lnew->l_next = l;
279+							l = lines = lnew;
280+							extra_lines++;
281+							this_line--;
282+						}
283+					} else {
284+						if (!warned++)
285+							dowarn(cur_line);
286+						cur_line = this_line - adjust;
287+					}
288+				}
289+			} else {
290+				/* may need to allocate here */
291+				while (cur_line + adjust > this_line) {
292+					if (l->l_next == NULL) {
293+						l->l_next = alloc_line();
294+						l->l_next->l_prev = l;
295+					}
296+					l = l->l_next;
297+					this_line++;
298+				}
299+			}
300+			if (this_line > nflushd_lines &&
301+			    this_line - nflushd_lines >=
302+			    max_bufd_lines + BUFFER_MARGIN) {
303+				if (extra_lines) {
304+					flush_lines(extra_lines);
305+					extra_lines = 0;
306+				}
307+				flush_lines(this_line - nflushd_lines -
308+				    max_bufd_lines);
309+				nflushd_lines = this_line - max_bufd_lines;
310+			}
311+		}
312+		/* grow line's buffer? */
313+		if (l->l_line_len + 1 >= l->l_lsize) {
314+			int need;
315+
316+			need = l->l_lsize ? l->l_lsize * 2 : 90;
317+			if ((l->l_line = realloc(l->l_line,
318+			    (unsigned)need * sizeof(CHAR))) == NULL)
319+				err(EXIT_FAILURE, NULL);
320+			l->l_lsize = need;
321+		}
322+		c = &l->l_line[l->l_line_len++];
323+		c->c_char = ch;
324+		c->c_set = cur_set;
325+		c->c_column = cur_col;
326+		c->c_width = wcwidth(ch);
327+		/*
328+		 * If things are put in out of order, they will need sorting
329+		 * when it is flushed.
330+		 */
331+		if (cur_col < l->l_max_col)
332+			l->l_needs_sort = 1;
333+		else
334+			l->l_max_col = cur_col;
335+		if (c->c_width > 0)
336+			cur_col += c->c_width;
337+	}
338+	if (ferror(stdin))
339+		err(EXIT_FAILURE, NULL);
340+	if (extra_lines) {
341+		/*
342+		 * Extra lines only exist if no lines have been flushed
343+		 * yet. This means that 'lines' must point to line zero
344+		 * after we flush the extra lines.
345+		 */
346+		flush_lines(extra_lines);
347+		l = lines;
348+		this_line = 0;
349+	}
350+
351+	/* goto the last line that had a character on it */
352+	for (; l->l_next; l = l->l_next)
353+		this_line++;
354+	flush_lines(this_line - nflushd_lines + 1);
355+
356+	/* make sure we leave things in a sane state */
357+	if (last_set != CS_NORMAL)
358+		PUTC(SI);
359+
360+	/* flush out the last few blank lines */
361+	if (max_line >= this_line)
362+		nblank_lines = max_line - this_line + (max_line & 1);
363+	if (nblank_lines == 0)
364+		/* end with a newline even if the source doesn't */
365+		nblank_lines = 2;
366+	flush_blanks();
367+	exit(EXIT_SUCCESS);
368+}
369+
370+/*
371+ * Prints the first 'nflush' lines. Printed lines are freed.
372+ * After this function returns, 'lines' points to the first
373+ * of the remaining lines, and 'nblank_lines' will have the
374+ * number of half line feeds between the final flushed line
375+ * and the first remaining line.
376+ */
377+static void
378+flush_lines(int nflush)
379+{
380+	LINE *l;
381+
382+	while (--nflush >= 0) {
383+		l = lines;
384+		lines = l->l_next;
385+		if (l->l_line) {
386+			flush_blanks();
387+			flush_line(l);
388+			free(l->l_line);
389+		}
390+		if (l->l_next)
391+			nblank_lines++;
392+		free_line(l);
393+	}
394+	if (lines)
395+		lines->l_prev = NULL;
396+}
397+
398+/*
399+ * Print a number of newline/half newlines.
400+ * nblank_lines is the number of half line feeds.
401+ */
402+static void
403+flush_blanks(void)
404+{
405+	int half, i, nb;
406+
407+	half = 0;
408+	nb = nblank_lines;
409+	if (nb & 1) {
410+		if (fine)
411+			half = 1;
412+		else
413+			nb++;
414+	}
415+	nb /= 2;
416+	for (i = nb; --i >= 0;)
417+		PUTC('\n');
418+	if (half) {
419+		PUTC(ESC);
420+		PUTC(FHLF);
421+		if (!nb)
422+			PUTC('\r');
423+	}
424+	nblank_lines = 0;
425+}
426+
427+/*
428+ * Write a line to stdout taking care of space to tab conversion (-h flag)
429+ * and character set shifts.
430+ */
431+static void
432+flush_line(LINE *l)
433+{
434+	CHAR *c, *endc;
435+	int i, j, nchars, last_col, save, this_col, tot;
436+
437+	last_col = 0;
438+	nchars = l->l_line_len;
439+
440+	if (l->l_needs_sort) {
441+		static CHAR *sorted;
442+		static int count_size, *count, sorted_size;
443+
444+		/*
445+		 * Do an O(n) sort on l->l_line by column being careful to
446+		 * preserve the order of characters in the same column.
447+		 */
448+		if (l->l_lsize > sorted_size) {
449+			sorted_size = l->l_lsize;
450+			if ((sorted = realloc(sorted,
451+			    sizeof(CHAR) * (size_t)sorted_size)) == NULL)
452+				err(EXIT_FAILURE, NULL);
453+		}
454+		if (l->l_max_col >= count_size) {
455+			count_size = l->l_max_col + 1;
456+			if ((count = realloc(count,
457+			    sizeof(int) * (size_t)count_size)) == NULL)
458+				err(EXIT_FAILURE, NULL);
459+		}
460+		memset(count, 0, sizeof(int) * (size_t)l->l_max_col + 1);
461+		for (i = nchars, c = l->l_line; --i >= 0; c++)
462+			count[c->c_column]++;
463+
464+		/*
465+		 * calculate running total (shifted down by 1) to use as
466+		 * indices into new line.
467+		 */
468+		for (tot = 0, i = 0; i <= l->l_max_col; i++) {
469+			save = count[i];
470+			count[i] = tot;
471+			tot += save;
472+		}
473+
474+		for (i = nchars, c = l->l_line; --i >= 0; c++)
475+			sorted[count[c->c_column]++] = *c;
476+		c = sorted;
477+	} else
478+		c = l->l_line;
479+	while (nchars > 0) {
480+		this_col = c->c_column;
481+		endc = c;
482+		do {
483+			++endc;
484+		} while (--nchars > 0 && this_col == endc->c_column);
485+
486+		/* if -b only print last character */
487+		if (no_backspaces) {
488+			c = endc - 1;
489+			if (nchars > 0 &&
490+			    this_col + c->c_width > endc->c_column)
491+				continue;
492+		}
493+
494+		if (this_col > last_col) {
495+			int nspace = this_col - last_col;
496+
497+			if (compress_spaces && nspace > 1) {
498+				while (1) {
499+					int tab_col, tab_size;
500+
501+					tab_col = (last_col + 8) & ~7;
502+					if (tab_col > this_col)
503+						break;
504+					tab_size = tab_col - last_col;
505+					if (tab_size == 1)
506+						PUTC(' ');
507+					else
508+						PUTC('\t');
509+					nspace -= tab_size;
510+					last_col = tab_col;
511+				}
512+			}
513+			while (--nspace >= 0)
514+				PUTC(' ');
515+			last_col = this_col;
516+		}
517+
518+		for (;;) {
519+			if (c->c_set != last_set) {
520+				switch (c->c_set) {
521+				case CS_NORMAL:
522+					PUTC(SI);
523+					break;
524+				case CS_ALTERNATE:
525+					PUTC(SO);
526+				}
527+				last_set = c->c_set;
528+			}
529+			PUTC(c->c_char);
530+			if ((c + 1) < endc)
531+				for (j = 0; j < c->c_width; j++)
532+					PUTC('\b');
533+			if (++c >= endc)
534+				break;
535+		}
536+		last_col += (c - 1)->c_width;
537+	}
538+}
539+
540+/*
541+ * Increment or decrement a line number, checking for overflow.
542+ * Stop one below INT_MAX such that the adjust variable is safe.
543+ */
544+void
545+addto_lineno(int *lno, int offset)
546+{
547+	if (offset > 0) {
548+		if (*lno >= INT_MAX - offset)
549+			errx(EXIT_FAILURE, "too many lines");
550+	} else {
551+		if (*lno < INT_MIN - offset)
552+			errx(EXIT_FAILURE, "too many reverse line feeds");
553+	}
554+	*lno += offset;
555+}
556+
557+#define	NALLOC 64
558+
559+static LINE *line_freelist;
560+
561+static LINE *
562+alloc_line(void)
563+{
564+	LINE *l;
565+	int i;
566+
567+	if (!line_freelist) {
568+		if ((l = realloc(NULL, sizeof(LINE) * NALLOC)) == NULL)
569+			err(EXIT_FAILURE, NULL);
570+		line_freelist = l;
571+		for (i = 1; i < NALLOC; i++, l++)
572+			l->l_next = l + 1;
573+		l->l_next = NULL;
574+	}
575+	l = line_freelist;
576+	line_freelist = l->l_next;
577+
578+	memset(l, 0, sizeof(LINE));
579+	return (l);
580+}
581+
582+static void
583+free_line(LINE *l)
584+{
585+
586+	l->l_next = line_freelist;
587+	line_freelist = l;
588+}
589+
590+static void
591+usage(void)
592+{
593+
594+	(void)fprintf(stderr, "Usage: %s [-bfhpx] [-l nline]\n", getprogname());
595+	exit(EXIT_FAILURE);
596+}
597+
598+static void
599+dowarn(int line)
600+{
601+
602+	warnx("warning: can't back up %s",
603+		line < 0 ? "past first line" : "-- line already flushed");
604+}
+160, -0
  1@@ -0,0 +1,160 @@
  2+/*	$NetBSD: _strtoi.h,v 1.5 2024/07/24 09:11:27 kre 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+ * Original version ID:
 33+ * NetBSD: src/lib/libc/locale/_wcstoul.h,v 1.2 2003/08/07 16:43:03 agc Exp
 34+ *
 35+ * Created by Kamil Rytarowski, based on ID:
 36+ * NetBSD: src/common/lib/libc/stdlib/_strtoul.h,v 1.7 2013/05/17 12:55:56 joerg Exp
 37+ */
 38+
 39+/*
 40+ * function template for strtoi and strtou
 41+ *
 42+ * parameters:
 43+ *	_FUNCNAME    : function name
 44+ *      __TYPE       : return and range limits type
 45+ *      __WRAPPED    : wrapped function, strtoimax or strtoumax
 46+ */
 47+
 48+#define __WRAPPED_L_(x) x ## _l
 49+#define __WRAPPED_L__(x) __WRAPPED_L_(x)
 50+#define __WRAPPED_L __WRAPPED_L__(__WRAPPED)
 51+
 52+#if defined(_KERNEL) || defined(_STANDALONE) || \
 53+    defined(HAVE_NBTOOL_CONFIG_H) || defined(BCS_ONLY)
 54+__TYPE
 55+_FUNCNAME(const char * __restrict nptr, char ** __restrict endptr, int base,
 56+          __TYPE lo, __TYPE hi, int * rstatus)
 57+#else
 58+#include <locale.h>
 59+#include "setlocale_local.h"
 60+#define INT_FUNCNAME_(pre, name, post)	pre ## name ## post
 61+#define INT_FUNCNAME(pre, name, post)	INT_FUNCNAME_(pre, name, post)
 62+
 63+static __TYPE
 64+INT_FUNCNAME(_int_, _FUNCNAME, _l)(const char * __restrict nptr,
 65+    char ** __restrict endptr, int base,
 66+    __TYPE lo, __TYPE hi, int * rstatus, locale_t loc)
 67+#endif
 68+{
 69+#if !defined(_KERNEL) && !defined(_STANDALONE)
 70+	int serrno;
 71+#endif
 72+	__TYPE im;
 73+	char *ep;
 74+	int rep;
 75+
 76+	_DIAGASSERT(hi >= lo);
 77+
 78+	_DIAGASSERT(nptr != NULL);
 79+	/* endptr may be NULL */
 80+
 81+	if (endptr == NULL)
 82+		endptr = &ep;
 83+
 84+	if (rstatus == NULL)
 85+		rstatus = &rep;
 86+
 87+	*rstatus = 0;		/* assume there will be no errors */
 88+
 89+	if (base != 0 && (base < 2 || base > 36)) {
 90+#if !defined(_KERNEL) && !defined(_STANDALONE)
 91+		*rstatus = EINVAL;
 92+		if (endptr != NULL)
 93+			/* LINTED interface specification */
 94+			*endptr = __UNCONST(nptr);
 95+		return 0;
 96+#else
 97+		panic("%s: invalid base %d", __func__, base);
 98+#endif
 99+	}
100+
101+#if !defined(_KERNEL) && !defined(_STANDALONE)
102+	serrno = errno;
103+	errno = 0;
104+#endif
105+
106+#if defined(_KERNEL) || defined(_STANDALONE) || \
107+    defined(HAVE_NBTOOL_CONFIG_H) || defined(BCS_ONLY)
108+	im = __WRAPPED(nptr, endptr, base);
109+#else
110+	im = __WRAPPED_L(nptr, endptr, base, loc);
111+#endif
112+
113+#if !defined(_KERNEL) && !defined(_STANDALONE)
114+	/* EINVAL here can only mean "nothing converted" */
115+	if (errno != EINVAL)
116+		*rstatus = errno;
117+	errno = serrno;
118+#endif
119+
120+	/* No digits were found */
121+	if (*rstatus == 0 && nptr == *endptr)
122+		*rstatus = ECANCELED;
123+
124+	if (im < lo) {
125+		if (*rstatus == 0)
126+			*rstatus = ERANGE;
127+		return lo;
128+	}
129+
130+	if (im > hi) {
131+		if (*rstatus == 0)
132+			*rstatus = ERANGE;
133+		return hi;
134+	}
135+
136+	/* There are further characters after number */
137+	if (*rstatus == 0 && **endptr != '\0')
138+		*rstatus = ENOTSUP;
139+
140+	return im;
141+}
142+
143+#if !defined(_KERNEL) && !defined(_STANDALONE) && \
144+    !defined(HAVE_NBTOOL_CONFIG_H) && !defined(BCS_ONLY)
145+__TYPE
146+_FUNCNAME(const char * __restrict nptr, char ** __restrict endptr, int base,
147+    __TYPE lo, __TYPE hi, int * rstatus)
148+{
149+	return INT_FUNCNAME(_int_, _FUNCNAME, _l)(nptr, endptr, base, lo, hi,
150+	    rstatus, _current_locale());
151+}
152+
153+__TYPE
154+INT_FUNCNAME(, _FUNCNAME, _l)(const char * __restrict nptr,
155+    char ** __restrict endptr, int base,
156+    __TYPE lo, __TYPE hi, int * rstatus, locale_t loc)
157+{
158+	return INT_FUNCNAME(_int_, _FUNCNAME, _l)(nptr, endptr, base, lo, hi,
159+	    rstatus, loc);
160+}
161+#endif
+158, -0
  1@@ -0,0 +1,158 @@
  2+#include <sys/types.h>
  3+
  4+#include <errno.h>
  5+#include <stdlib.h>
  6+#include <string.h>
  7+
  8+#include "db.h"
  9+
 10+struct compat_db_entry {
 11+	DBT key;
 12+	DBT value;
 13+	struct compat_db_entry *next;
 14+};
 15+
 16+struct compat_db_state {
 17+	struct compat_db_entry *head;
 18+	struct compat_db_entry *iter;
 19+};
 20+
 21+static int
 22+compat_db_close(DB *db)
 23+{
 24+	struct compat_db_state *state;
 25+	struct compat_db_entry *entry;
 26+	struct compat_db_entry *next;
 27+
 28+	if (db == NULL)
 29+		return 0;
 30+	state = db->internal;
 31+	if (state != NULL) {
 32+		for (entry = state->head; entry != NULL; entry = next) {
 33+			next = entry->next;
 34+			free(entry->key.data);
 35+			free(entry->value.data);
 36+			free(entry);
 37+		}
 38+		free(state);
 39+	}
 40+	free(db);
 41+	return 0;
 42+}
 43+
 44+static int
 45+compat_db_get(DB *db, DBT *key, DBT *data, unsigned int flags)
 46+{
 47+	struct compat_db_state *state;
 48+	struct compat_db_entry *entry;
 49+
 50+	(void)flags;
 51+	state = db->internal;
 52+	for (entry = state->head; entry != NULL; entry = entry->next) {
 53+		if (entry->key.size == key->size &&
 54+		    memcmp(entry->key.data, key->data, key->size) == 0) {
 55+			data->data = entry->value.data;
 56+			data->size = entry->value.size;
 57+			return 0;
 58+		}
 59+	}
 60+	return 1;
 61+}
 62+
 63+static int
 64+compat_db_put(DB *db, DBT *key, DBT *data, unsigned int flags)
 65+{
 66+	struct compat_db_state *state;
 67+	struct compat_db_entry *entry;
 68+	void *copy;
 69+
 70+	(void)flags;
 71+	state = db->internal;
 72+	for (entry = state->head; entry != NULL; entry = entry->next) {
 73+		if (entry->key.size == key->size &&
 74+		    memcmp(entry->key.data, key->data, key->size) == 0) {
 75+			copy = malloc(data->size);
 76+			if (copy == NULL)
 77+				return -1;
 78+			memcpy(copy, data->data, data->size);
 79+			free(entry->value.data);
 80+			entry->value.data = copy;
 81+			entry->value.size = data->size;
 82+			return 0;
 83+		}
 84+	}
 85+
 86+	entry = calloc(1, sizeof(*entry));
 87+	if (entry == NULL)
 88+		return -1;
 89+	entry->key.data = malloc(key->size);
 90+	entry->value.data = malloc(data->size);
 91+	if (entry->key.data == NULL || entry->value.data == NULL) {
 92+		free(entry->key.data);
 93+		free(entry->value.data);
 94+		free(entry);
 95+		return -1;
 96+	}
 97+	memcpy(entry->key.data, key->data, key->size);
 98+	memcpy(entry->value.data, data->data, data->size);
 99+	entry->key.size = key->size;
100+	entry->value.size = data->size;
101+	entry->next = state->head;
102+	state->head = entry;
103+	return 0;
104+}
105+
106+static int
107+compat_db_seq(DB *db, DBT *key, DBT *data, unsigned int flags)
108+{
109+	struct compat_db_state *state;
110+	struct compat_db_entry *entry;
111+
112+	state = db->internal;
113+	if (flags == R_FIRST)
114+		state->iter = state->head;
115+	else if (flags == R_NEXT && state->iter != NULL)
116+		state->iter = state->iter->next;
117+
118+	entry = state->iter;
119+	if (entry == NULL)
120+		return 1;
121+
122+	key->data = entry->key.data;
123+	key->size = entry->key.size;
124+	data->data = entry->value.data;
125+	data->size = entry->value.size;
126+	return 0;
127+}
128+
129+DB *
130+dbopen(const char *fname, int flags, int mode, int type, const void *openinfo)
131+{
132+	DB *db;
133+	struct compat_db_state *state;
134+
135+	(void)flags;
136+	(void)mode;
137+	(void)type;
138+	(void)openinfo;
139+
140+	if (fname != NULL) {
141+		errno = ENOTSUP;
142+		return NULL;
143+	}
144+
145+	db = calloc(1, sizeof(*db));
146+	state = calloc(1, sizeof(*state));
147+	if (db == NULL || state == NULL) {
148+		free(db);
149+		free(state);
150+		return NULL;
151+	}
152+
153+	db->close = compat_db_close;
154+	db->get = compat_db_get;
155+	db->put = compat_db_put;
156+	db->seq = compat_db_seq;
157+	db->internal = state;
158+	return db;
159+}
+30, -0
 1@@ -0,0 +1,30 @@
 2+#ifndef COMPAT_DB_H
 3+#define COMPAT_DB_H
 4+
 5+#include <stddef.h>
 6+
 7+typedef struct {
 8+	void *data;
 9+	size_t size;
10+} DBT;
11+
12+typedef struct __db DB;
13+
14+struct __db {
15+	int (*close)(DB *);
16+	int (*del)(DB *, DBT *, unsigned int);
17+	int (*get)(DB *, DBT *, DBT *, unsigned int);
18+	int (*put)(DB *, DBT *, DBT *, unsigned int);
19+	int (*seq)(DB *, DBT *, DBT *, unsigned int);
20+	void *internal;
21+};
22+
23+#define DB_BTREE 1
24+#define DB_HASH 2
25+
26+#define R_FIRST 1
27+#define R_NEXT 2
28+
29+DB *dbopen(const char *, int, int, int, const void *);
30+
31+#endif
+49, -0
 1@@ -0,0 +1,49 @@
 2+/*	$NetBSD: errc.c,v 1.3 2014/06/06 11:38:41 joerg 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+#include <sys/cdefs.h>
34+#if defined(LIBC_SCCS) && !defined(lint)
35+__RCSID("$NetBSD: errc.c,v 1.3 2014/06/06 11:38:41 joerg Exp $");
36+#endif /* LIBC_SCCS and not lint */
37+
38+#include "netcompat.h"
39+#include <err.h>
40+#include <stdarg.h>
41+
42+__dead void
43+errc(int eval, int code, const char *fmt, ...)
44+{
45+	va_list ap;
46+
47+	va_start(ap, fmt);
48+	verrc(eval, code, fmt, ap);
49+	va_end(ap);
50+}
+56, -0
 1@@ -0,0 +1,56 @@
 2+/* $NetBSD: getprogname.c,v 1.5 2021/04/20 21:42:32 christos Exp $ */
 3+
 4+/*
 5+ * Copyright (c) 2001 Christopher G. Demetriou
 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. All advertising materials mentioning features or use of this software
17+ *    must display the following acknowledgement:
18+ *          This product includes software developed for the
19+ *          NetBSD Project.  See http://www.NetBSD.org/ for
20+ *          information about NetBSD.
21+ * 4. The name of the author may not be used to endorse or promote products
22+ *    derived from this software without specific prior written permission.
23+ * 
24+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34+ * 
35+ * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
36+ */
37+
38+#include <sys/cdefs.h>
39+#if defined(LIBC_SCCS) && !defined(lint)
40+__RCSID("$NetBSD: getprogname.c,v 1.5 2021/04/20 21:42:32 christos Exp $");
41+#endif /* LIBC_SCCS and not lint */
42+
43+#include <stdlib.h>
44+#include "netcompat.h"
45+
46+#ifdef __weak_alias
47+__weak_alias(getprogname,_getprogname)
48+#endif
49+
50+const char *netcompat_progname;
51+
52+const char *
53+getprogname(void)
54+{
55+
56+	return (netcompat_progname != NULL ? netcompat_progname : "");
57+}
+120, -0
  1@@ -0,0 +1,120 @@
  2+/*
  3+ * the shit we do to make it build. all code under it's original license
  4+ */
  5+
  6+#ifndef NETCOMPAT_H
  7+#define NETCOMPAT_H
  8+
  9+#ifndef _DEFAULT_SOURCE
 10+#define _DEFAULT_SOURCE 1
 11+#endif
 12+
 13+#ifndef _GNU_SOURCE
 14+#define _GNU_SOURCE 1
 15+#endif
 16+
 17+#ifndef _XOPEN_SOURCE
 18+#define _XOPEN_SOURCE 700
 19+#endif
 20+
 21+#include <signal.h>
 22+#include <stddef.h>
 23+#include <inttypes.h>
 24+#include <stdint.h>
 25+#include <stdarg.h>
 26+#include <time.h>
 27+#include <pwd.h>
 28+#include <byteswap.h>
 29+
 30+#ifndef __RCSID
 31+#define __RCSID(x)
 32+#endif
 33+
 34+#ifndef __COPYRIGHT
 35+#define __COPYRIGHT(x)
 36+#endif
 37+
 38+#ifndef __printflike
 39+#if defined(__GNUC__) || defined(__clang__)
 40+#define __printflike(fmtarg, firstvararg) \
 41+	__attribute__((__format__(__printf__, fmtarg, firstvararg)))
 42+#else
 43+#define __printflike(fmtarg, firstvararg)
 44+#endif
 45+#endif
 46+
 47+#ifndef __dead
 48+#if defined(__GNUC__) || defined(__clang__)
 49+#define __dead __attribute__((__noreturn__))
 50+#else
 51+#define __dead
 52+#endif
 53+#endif
 54+
 55+#ifndef __predict_false
 56+#define __predict_false(exp) (exp)
 57+#endif
 58+
 59+#ifndef __predict_true
 60+#define __predict_true(exp) (exp)
 61+#endif
 62+
 63+#ifndef _DIAGASSERT
 64+#define _DIAGASSERT(exp) ((void)0)
 65+#endif
 66+
 67+#ifndef __weak_alias
 68+#define __weak_alias(sym, alias)
 69+#endif
 70+
 71+#ifndef __UNCONST
 72+#define __UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
 73+#endif
 74+
 75+#ifndef sig_t
 76+typedef void (*sig_t)(int);
 77+#endif
 78+
 79+#ifndef timespeccmp
 80+#define timespeccmp(a, b, cmp) \
 81+	(((a)->tv_sec == (b)->tv_sec) ? \
 82+	 ((a)->tv_nsec cmp (b)->tv_nsec) : \
 83+	 ((a)->tv_sec cmp (b)->tv_sec))
 84+#endif
 85+
 86+#ifndef timespecclear
 87+#define timespecclear(tsp) \
 88+	do { \
 89+		(tsp)->tv_sec = 0; \
 90+		(tsp)->tv_nsec = 0; \
 91+	} while (0)
 92+#endif
 93+
 94+#ifndef setpassent
 95+#define setpassent(stayopen) ((void)(stayopen), setpwent())
 96+#endif
 97+
 98+#ifndef bswap32
 99+#define bswap32 bswap_32
100+#endif
101+
102+#ifndef bswap64
103+#define bswap64 bswap_64
104+#endif
105+
106+extern const char *netcompat_progname;
107+
108+const char *getprogname(void);
109+void setprogname(const char *);
110+size_t strlcpy(char * __restrict, const char * __restrict, size_t);
111+int reallocarr(void *, size_t, size_t);
112+intmax_t strtoi(const char * __restrict, char ** __restrict, int,
113+    intmax_t, intmax_t, int *);
114+size_t shquote(const char *, char *, size_t);
115+void vwarnc(int, const char *, va_list);
116+void warnc(int, const char *, ...);
117+__dead void verrc(int, int, const char *, va_list);
118+__dead void errc(int, int, const char *, ...);
119+int raise_default_signal(int);
120+
121+#endif
+110, -0
  1@@ -0,0 +1,110 @@
  2+/*	$NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $	 */
  3+
  4+/*-
  5+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
  6+ * All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to The NetBSD Foundation
  9+ * by Luke Mewburn.
 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+#include <sys/cdefs.h>
 34+#if defined(LIBC_SCCS) && !defined(lint)
 35+__RCSID("$NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $");
 36+#endif
 37+
 38+#include "netcompat.h"
 39+#include <errno.h>
 40+#include <signal.h>
 41+#include <stdio.h>
 42+#include <string.h>
 43+
 44+/*
 45+ * raise_default_signal sig
 46+ *	Raise the default signal handler for sig, by
 47+ *	- block all signals
 48+ *	- set the signal handler to SIG_DFL
 49+ *	- raise the signal
 50+ *	- unblock the signal to deliver it
 51+ *
 52+ *	The original signal mask and signal handler is restored on exit
 53+ *	(whether successful or not).
 54+ *
 55+ *	Returns 0 on success, or -1 on failure with errno set to
 56+ *	on of the values for sigemptyset(), sigaddset(), sigprocmask(),
 57+ *	sigaction(), or raise().
 58+ */
 59+int
 60+raise_default_signal(int sig)
 61+{
 62+	struct sigaction origact, act;
 63+	sigset_t origmask, fullmask, mask;
 64+	int retval, oerrno;
 65+
 66+	retval = -1;
 67+
 68+		/* Setup data structures */
 69+		/* XXX memset(3) isn't async-safe according to signal(7) */
 70+	(void)memset(&act, 0, sizeof(act));
 71+	act.sa_handler = SIG_DFL;
 72+	act.sa_flags = 0;
 73+	if ((sigemptyset(&act.sa_mask) == -1) ||
 74+	    (sigfillset(&fullmask) == -1) ||
 75+	    (sigemptyset(&mask) == -1) ||
 76+	    (sigaddset(&mask, sig) == -1))
 77+		goto restore_none;
 78+
 79+		/* Block all signals */
 80+	if (sigprocmask(SIG_BLOCK, &fullmask, &origmask) == -1)
 81+		goto restore_none;
 82+		/* (use 'goto restore_mask' to restore state) */
 83+
 84+		/* Enable the SIG_DFL handler */
 85+	if (sigaction(sig, &act, &origact) == -1)
 86+		goto restore_mask;
 87+		/* (use 'goto restore_act' to restore state) */
 88+
 89+		/* Raise the signal, and unblock the signal to deliver it */
 90+	if ((raise(sig) == -1) ||
 91+	    (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1))
 92+		goto restore_act;
 93+
 94+		/* Flag successful raise() */
 95+	retval = 0;
 96+
 97+		/* Restore the original handler */
 98+ restore_act:
 99+	oerrno = errno;
100+	(void)sigaction(sig, &origact, NULL);
101+	errno = oerrno;
102+
103+		/* Restore the original mask */
104+ restore_mask:
105+	oerrno = errno;
106+	(void)sigprocmask(SIG_SETMASK, &origmask, NULL);
107+	errno = oerrno;
108+
109+restore_none:
110+	return retval;
111+}
+83, -0
 1@@ -0,0 +1,83 @@
 2+/* $NetBSD: reallocarr.c,v 1.5 2015/08/20 22:27:49 kamil Exp $ */
 3+
 4+/*-
 5+ * Copyright (c) 2015 Joerg Sonnenberger <joerg@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+ *
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
16+ *    the documentation and/or other materials provided with the
17+ *    distribution.
18+ *
19+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
23+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
25+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30+ * SUCH DAMAGE.
31+ */
32+
33+#include <sys/cdefs.h>
34+__RCSID("$NetBSD: reallocarr.c,v 1.5 2015/08/20 22:27:49 kamil Exp $");
35+
36+#include "netcompat.h"
37+#include <errno.h>
38+/* Old POSIX has SIZE_MAX in limits.h */
39+#include <limits.h>
40+#include <stdint.h>
41+#include <stdlib.h>
42+#include <string.h>
43+
44+#define SQRT_SIZE_MAX (((size_t)1) << (sizeof(size_t) * CHAR_BIT / 2))
45+
46+int
47+reallocarr(void *ptr, size_t number, size_t size)
48+{
49+	int saved_errno, result;
50+	void *optr;
51+	void *nptr;
52+
53+	saved_errno = errno;
54+	memcpy(&optr, ptr, sizeof(ptr));
55+	if (number == 0 || size == 0) {
56+		free(optr);
57+		nptr = NULL;
58+		memcpy(ptr, &nptr, sizeof(ptr));
59+		errno = saved_errno;
60+		return 0;
61+	}
62+
63+	/*
64+	 * Try to avoid division here.
65+	 *
66+	 * It isn't possible to overflow during multiplication if neither
67+	 * operand uses any of the most significant half of the bits.
68+	 */
69+	if (__predict_false((number|size) >= SQRT_SIZE_MAX &&
70+	                    number > SIZE_MAX / size)) {
71+		errno = saved_errno;
72+		return EOVERFLOW;
73+	}
74+
75+	nptr = realloc(optr, number * size);
76+	if (__predict_false(nptr == NULL)) {
77+		result = errno;
78+	} else {
79+		result = 0;
80+		memcpy(ptr, &nptr, sizeof(ptr));
81+	}
82+	errno = saved_errno;
83+	return result;
84+}
+63, -0
 1@@ -0,0 +1,63 @@
 2+/* $NetBSD: setprogname.c,v 1.3 2003/07/26 19:24:44 salo Exp $ */
 3+
 4+/*
 5+ * Copyright (c) 2001 Christopher G. Demetriou
 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. All advertising materials mentioning features or use of this software
17+ *    must display the following acknowledgement:
18+ *          This product includes software developed for the
19+ *          NetBSD Project.  See http://www.NetBSD.org/ for
20+ *          information about NetBSD.
21+ * 4. The name of the author may not be used to endorse or promote products
22+ *    derived from this software without specific prior written permission.
23+ * 
24+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34+ * 
35+ * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
36+ */
37+
38+#include <sys/cdefs.h>
39+#if defined(LIBC_SCCS) && !defined(lint)
40+__RCSID("$NetBSD: setprogname.c,v 1.3 2003/07/26 19:24:44 salo Exp $");
41+#endif /* LIBC_SCCS and not lint */
42+
43+#define	REALLY_SET_PROGNAME
44+
45+#include <stdlib.h>
46+#include "netcompat.h"
47+
48+#ifdef REALLY_SET_PROGNAME
49+#include <string.h>
50+#endif
51+
52+/*ARGSUSED*/
53+void
54+setprogname(const char *progname)
55+{
56+
57+#ifdef REALLY_SET_PROGNAME
58+	netcompat_progname = strrchr(progname, '/');
59+	if (netcompat_progname == NULL)
60+		netcompat_progname = progname;
61+	else
62+		netcompat_progname++;
63+#endif
64+}
+185, -0
  1@@ -0,0 +1,185 @@
  2+/* $NetBSD: shquote.c,v 1.8 2006/03/19 02:33:02 christos Exp $ */
  3+
  4+/*
  5+ * Copyright (c) 2001 Christopher G. Demetriou
  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. All advertising materials mentioning features or use of this software
 17+ *    must display the following acknowledgement:
 18+ *          This product includes software developed for the
 19+ *          NetBSD Project.  See http://www.NetBSD.org/ for
 20+ *          information about NetBSD.
 21+ * 4. The name of the author may not be used to endorse or promote products
 22+ *    derived from this software without specific prior written permission.
 23+ * 
 24+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 25+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 26+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 27+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 28+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 29+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 30+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 31+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 32+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 33+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 34+ * 
 35+ * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
 36+ */
 37+
 38+#include <sys/cdefs.h>
 39+#if defined(LIBC_SCCS) && !defined(lint)
 40+__RCSID("$NetBSD: shquote.c,v 1.8 2006/03/19 02:33:02 christos Exp $");
 41+#endif /* LIBC_SCCS and not lint */
 42+
 43+/*
 44+ * Define SHQUOTE_USE_MULTIBYTE if you want shquote() to handle multibyte
 45+ * characters using mbrtowc().
 46+ *
 47+ * Please DO NOT rip this #ifdef out of the code.  It's also here to help
 48+ * portability.
 49+ */
 50+#undef	SHQUOTE_USE_MULTIBYTE
 51+
 52+#include "netcompat.h"
 53+#include <stdlib.h>
 54+#include <string.h>
 55+#ifdef SHQUOTE_USE_MULTIBYTE
 56+#include <limits.h>
 57+#include <stdio.h>
 58+#include <wchar.h>
 59+#endif
 60+
 61+/*
 62+ * shquote():
 63+ *
 64+ * Requotes arguments so that they'll be interpreted properly by the
 65+ * shell (/bin/sh).
 66+ *
 67+ * Wraps single quotes around the string, and replaces single quotes
 68+ * in the string with the sequence:
 69+ *	'\''
 70+ *
 71+ * Returns the number of characters required to hold the resulting quoted
 72+ * argument.
 73+ *
 74+ * The buffer supplied is filled in and NUL-terminated.  If 'bufsize'
 75+ * indicates that the buffer is too short to hold the output string, the
 76+ * first (bufsize - 1) bytes of quoted argument are filled in and the
 77+ * buffer is NUL-terminated.
 78+ *
 79+ * Changes could be made to optimize the length of strings output by this
 80+ * function:
 81+ *
 82+ *	* if there are no metacharacters or whitespace in the input,
 83+ *	  the output could be the input string.
 84+ */
 85+
 86+#ifdef SHQUOTE_USE_MULTIBYTE
 87+
 88+#define	XLATE_OUTCH(x)		wcrtomb(outch, (x), &mbso)
 89+#define	XLATE_INCH()						\
 90+    do {							\
 91+	n = mbrtowc(&c, arg, MB_CUR_MAX, &mbsi);		\
 92+    } while (/*LINTED const cond*/0)
 93+
 94+#else
 95+
 96+#define	XLATE_OUTCH(x)		(outch[0] = (x), 1)
 97+#define	XLATE_INCH()						\
 98+    do {							\
 99+	n = ((c = *arg) != '\0') ? 1 : 0;			\
100+    } while (/*LINTED const cond*/0)
101+
102+#endif
103+
104+#define	PUT(x)							\
105+    do {							\
106+	outchlen = XLATE_OUTCH(x);				\
107+	if (outchlen == (size_t)-1)				\
108+		goto bad;					\
109+	rv += outchlen;						\
110+	if (bufsize != 0) {					\
111+		if (bufsize < outchlen ||			\
112+		    (bufsize == outchlen &&			\
113+		     outch[outchlen - 1] != '\0')) {		\
114+			*buf = '\0';				\
115+			bufsize = 0;				\
116+		} else {					\
117+			memcpy(buf, outch, outchlen);		\
118+			buf += outchlen;			\
119+			bufsize -= outchlen;			\
120+		}						\
121+	}							\
122+    } while (/*LINTED const cond*/0)
123+
124+size_t
125+shquote(const char *arg, char *buf, size_t bufsize)
126+{
127+#ifdef SHQUOTE_USE_MULTIBYTE
128+	char outch[MB_LEN_MAX];
129+	mbstate_t mbsi, mbso;
130+	wchar_t c, lastc;
131+	size_t outchlen;
132+#else
133+	char outch[1];
134+	char c, lastc;
135+	size_t outchlen;
136+#endif
137+	size_t rv;
138+	int n;
139+
140+	rv = 0;
141+	lastc = 0;
142+#ifdef SHQUOTE_USE_MULTIBYTE
143+	memset(&mbsi, 0, sizeof mbsi);
144+	memset(&mbso, 0, sizeof mbso);
145+#endif
146+
147+	if (*arg != '\'')
148+		PUT('\'');
149+	for (;;) {
150+		XLATE_INCH();
151+		if (n <= 0)
152+			break;
153+		arg += n;
154+		lastc = c;
155+
156+		if (c == '\'') {
157+			if (rv != 0)
158+				PUT('\'');
159+			PUT('\\');
160+			PUT('\'');
161+			for (;;) {
162+				XLATE_INCH();
163+				if (n <= 0 || c != '\'')
164+					break;
165+				PUT('\\');
166+				PUT('\'');
167+				arg += n;
168+			}
169+			if (n > 0)
170+				PUT('\'');
171+		} else
172+			PUT(c);
173+	}
174+	if (lastc != '\'')
175+		PUT('\'');
176+
177+	/* Put multibyte or NUL terminator, but don't count the NUL. */
178+	PUT('\0');
179+	rv--;
180+
181+	return rv;
182+
183+bad:
184+	/* A multibyte character encoding or decoding error occurred. */
185+	return (size_t)-1;
186+}
+61, -0
 1@@ -0,0 +1,61 @@
 2+/*	$NetBSD: strlcpy.c,v 1.4 2024/11/01 21:11:37 riastradh Exp $	*/
 3+/*	$OpenBSD: strlcpy.c,v 1.7 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+#include <sys/cdefs.h>
22+#if defined(LIBC_SCCS) && !defined(lint)
23+__RCSID("$NetBSD: strlcpy.c,v 1.4 2024/11/01 21:11:37 riastradh Exp $");
24+#endif /* LIBC_SCCS and not lint */
25+
26+#include "netcompat.h"
27+#include <sys/types.h>
28+#include <assert.h>
29+#include <string.h>
30+/*
31+ * Copy src to string dst of size siz.  At most siz-1 characters
32+ * will be copied.  Always NUL terminates (unless siz == 0).
33+ * Returns strlen(src); if retval >= siz, truncation occurred.
34+ */
35+size_t
36+strlcpy(char *__restrict dst, const char *__restrict src, size_t siz)
37+{
38+	char *d = dst;
39+	const char *s = src;
40+	size_t n = siz;
41+
42+	_DIAGASSERT(dst != NULL);
43+	_DIAGASSERT(src != NULL);
44+
45+	/* Copy as many bytes as will fit */
46+	if (n != 0 && --n != 0) {
47+		do {
48+			if ((*d++ = *s++) == 0)
49+				break;
50+		} while (--n != 0);
51+	}
52+
53+	/* Not enough room in dst, add NUL and traverse rest of src */
54+	if (n == 0) {
55+		if (siz != 0)
56+			*d = '\0';		/* NUL-terminate dst */
57+		while (*s++)
58+			;
59+	}
60+
61+	return(s - src - 1);	/* count does not include NUL */
62+}
+60, -0
 1@@ -0,0 +1,60 @@
 2+/*	$NetBSD: strtoi.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+#include <sys/cdefs.h>
35+__RCSID("$NetBSD: strtoi.c,v 1.3 2019/11/28 12:33:23 roy Exp $");
36+
37+#include "netcompat.h"
38+
39+#define HAVE_NBTOOL_CONFIG_H 1
40+
41+#if defined(_KERNEL)
42+#include <sys/param.h>
43+#include <sys/types.h>
44+#include <lib/libkern/libkern.h>
45+#elif defined(_STANDALONE)
46+#include <sys/param.h>
47+#include <sys/types.h>
48+#include <lib/libkern/libkern.h>
49+#include <lib/libsa/stand.h>
50+#else
51+#include <stddef.h>
52+#include <assert.h>
53+#include <errno.h>
54+#include <inttypes.h>
55+#endif
56+
57+#define	_FUNCNAME	strtoi
58+#define	__TYPE		intmax_t
59+#define	__WRAPPED	strtoimax
60+
61+#include "_strtoi.h"
+43, -0
 1@@ -0,0 +1,43 @@
 2+#ifndef _SYS_CDEFS_H_
 3+#define _SYS_CDEFS_H_
 4+
 5+#if defined(__has_include_next)
 6+# if __has_include_next(<sys/cdefs.h>)
 7+#  include_next <sys/cdefs.h>
 8+# endif
 9+#endif
10+
11+#ifdef __cplusplus
12+# ifndef __BEGIN_DECLS
13+#  define __BEGIN_DECLS extern "C" {
14+# endif
15+# ifndef __END_DECLS
16+#  define __END_DECLS }
17+# endif
18+#else
19+# ifndef __BEGIN_DECLS
20+#  define __BEGIN_DECLS
21+# endif
22+# ifndef __END_DECLS
23+#  define __END_DECLS
24+# endif
25+#endif
26+
27+#ifndef __CONCAT
28+#define __CONCAT1(x, y) x ## y
29+#define __CONCAT(x, y) __CONCAT1(x, y)
30+#endif
31+
32+#ifndef __STRING
33+#define __STRING(x) #x
34+#endif
35+
36+#ifndef __UNCONST
37+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
38+#endif
39+
40+#ifndef __THROW
41+#define __THROW
42+#endif
43+
44+#endif
+12, -0
 1@@ -0,0 +1,12 @@
 2+#ifndef COMPAT_TZFILE_H
 3+#define COMPAT_TZFILE_H
 4+
 5+#define SECSPERMIN 60
 6+#define MINSPERHOUR 60
 7+#define HOURSPERDAY 24
 8+#define DAYSPERWEEK 7
 9+#define DAYSPERNYEAR 365
10+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
11+#define SECSPERDAY ((long)SECSPERHOUR * HOURSPERDAY)
12+
13+#endif
+58, -0
 1@@ -0,0 +1,58 @@
 2+/*	$NetBSD: verrc.c,v 1.3 2014/06/06 11:38:41 joerg 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+#include <sys/cdefs.h>
34+#if defined(LIBC_SCCS) && !defined(lint)
35+#if 0
36+static char sccsid[] = "@(#)err.c	8.1 (Berkeley) 6/4/93";
37+#else
38+__RCSID("$NetBSD: verrc.c,v 1.3 2014/06/06 11:38:41 joerg Exp $");
39+#endif
40+#endif /* LIBC_SCCS and not lint */
41+
42+#include "netcompat.h"
43+#include <err.h>
44+#include <stdarg.h>
45+#include <stdio.h>
46+#include <stdlib.h>
47+#include <string.h>
48+
49+__dead void
50+verrc(int eval, int code, const char *fmt, va_list ap)
51+{
52+	(void)fprintf(stderr, "%s: ", getprogname());
53+	if (fmt != NULL) {
54+		(void)vfprintf(stderr, fmt, ap);
55+		(void)fprintf(stderr, ": ");
56+	}
57+	(void)fprintf(stderr, "%s\n", strerror(code));
58+	exit(eval);
59+}
+120, -0
  1@@ -0,0 +1,120 @@
  2+#include <sys/cdefs.h>
  3+
  4+#include <ctype.h>
  5+#include <stddef.h>
  6+
  7+#include "vis.h"
  8+
  9+static int
 10+needs_vis(unsigned char c, int flags)
 11+{
 12+	if (c == '\\' && !(flags & VIS_NOSLASH))
 13+		return 1;
 14+	if ((flags & VIS_SP) && c == ' ')
 15+		return 1;
 16+	if ((flags & VIS_TAB) && c == '\t')
 17+		return 1;
 18+	if ((flags & VIS_NL) && c == '\n')
 19+		return 1;
 20+	if (!isprint(c))
 21+		return 1;
 22+	return 0;
 23+}
 24+
 25+static int
 26+putc_vis(char **dst, size_t *dlen, unsigned char c)
 27+{
 28+	if (*dlen <= 1)
 29+		return -1;
 30+	*(*dst)++ = (char)c;
 31+	(*dlen)--;
 32+	return 0;
 33+}
 34+
 35+static int
 36+put_oct(char **dst, size_t *dlen, unsigned char c)
 37+{
 38+	if (*dlen <= 4)
 39+		return -1;
 40+	*(*dst)++ = '\\';
 41+	*(*dst)++ = (char)(((c >> 6) & 0x07) + '0');
 42+	*(*dst)++ = (char)(((c >> 3) & 0x07) + '0');
 43+	*(*dst)++ = (char)((c & 0x07) + '0');
 44+	*dlen -= 4;
 45+	return 0;
 46+}
 47+
 48+static int
 49+put_cstyle(char **dst, size_t *dlen, unsigned char c)
 50+{
 51+	char esc;
 52+
 53+	switch (c) {
 54+	case '\n': esc = 'n'; break;
 55+	case '\r': esc = 'r'; break;
 56+	case '\b': esc = 'b'; break;
 57+	case '\a': esc = 'a'; break;
 58+	case '\v': esc = 'v'; break;
 59+	case '\t': esc = 't'; break;
 60+	case '\f': esc = 'f'; break;
 61+	case ' ':  esc = 's'; break;
 62+	case '\\': esc = '\\'; break;
 63+	default:
 64+		return put_oct(dst, dlen, c);
 65+	}
 66+
 67+	if (*dlen <= 2)
 68+		return -1;
 69+	*(*dst)++ = '\\';
 70+	*(*dst)++ = esc;
 71+	*dlen -= 2;
 72+	return 0;
 73+}
 74+
 75+int
 76+strnvisx(char *dst, size_t dlen, const char *src, size_t slen, int flags)
 77+{
 78+	char *start;
 79+	unsigned char c;
 80+
 81+	if (dlen == 0)
 82+		return -1;
 83+
 84+	start = dst;
 85+	while (slen-- > 0) {
 86+		c = (unsigned char)*src++;
 87+		if (!needs_vis(c, flags)) {
 88+			if (putc_vis(&dst, &dlen, c) == -1)
 89+				goto trunc;
 90+			continue;
 91+		}
 92+		if ((flags & VIS_CSTYLE) != 0) {
 93+			if (put_cstyle(&dst, &dlen, c) == -1)
 94+				goto trunc;
 95+		} else {
 96+			if (put_oct(&dst, &dlen, c) == -1)
 97+				goto trunc;
 98+		}
 99+	}
100+
101+	*dst = '\0';
102+	return (int)(dst - start);
103+
104+trunc:
105+	*dst = '\0';
106+	return -1;
107+}
108+
109+char *
110+vis(char *dst, int c, int flags, int nextc)
111+{
112+	char src[1];
113+
114+	(void)nextc;
115+	src[0] = (char)c;
116+	if (strnvisx(dst, 5, src, 1, flags) == -1)
117+		return NULL;
118+	while (*dst != '\0')
119+		dst++;
120+	return dst;
121+}
+22, -0
 1@@ -0,0 +1,22 @@
 2+#ifndef _VIS_H_
 3+#define _VIS_H_
 4+
 5+#include <sys/types.h>
 6+#include <sys/cdefs.h>
 7+
 8+#define	VIS_OCTAL	0x0001
 9+#define	VIS_CSTYLE	0x0002
10+#define	VIS_SP		0x0004
11+#define	VIS_TAB		0x0008
12+#define	VIS_NL		0x0010
13+#define	VIS_WHITE	(VIS_SP | VIS_TAB | VIS_NL)
14+#define	VIS_SAFE	0x0020
15+#define	VIS_DQ		0x8000
16+#define	VIS_NOSLASH	0x0040
17+
18+__BEGIN_DECLS
19+char	*vis(char *, int, int, int);
20+int	strnvisx(char *, size_t, const char *, size_t, int);
21+__END_DECLS
22+
23+#endif
+57, -0
 1@@ -0,0 +1,57 @@
 2+/*	$NetBSD: vwarnc.c,v 1.3 2014/06/06 11:38:41 joerg 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+#include <sys/cdefs.h>
34+#if defined(LIBC_SCCS) && !defined(lint)
35+#if 0
36+static char sccsid[] = "@(#)err.c	8.1 (Berkeley) 6/4/93";
37+#else
38+__RCSID("$NetBSD: vwarnc.c,v 1.3 2014/06/06 11:38:41 joerg Exp $");
39+#endif
40+#endif /* LIBC_SCCS and not lint */
41+
42+#include "netcompat.h"
43+#include <err.h>
44+#include <stdarg.h>
45+#include <stdio.h>
46+#include <stdlib.h>
47+#include <string.h>
48+
49+void
50+vwarnc(int code, const char *fmt, va_list ap)
51+{
52+	(void)fprintf(stderr, "%s: ", getprogname());
53+	if (fmt != NULL) {
54+		(void)vfprintf(stderr, fmt, ap);
55+		(void)fprintf(stderr, ": ");
56+	}
57+	(void)fprintf(stderr, "%s\n", strerror(code));
58+}
+53, -0
 1@@ -0,0 +1,53 @@
 2+/*	$NetBSD: warnc.c,v 1.3 2014/06/06 11:38:41 joerg 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+#include <sys/cdefs.h>
34+#if defined(LIBC_SCCS) && !defined(lint)
35+#if 0
36+static char sccsid[] = "@(#)err.c	8.1 (Berkeley) 6/4/93";
37+#else
38+__RCSID("$NetBSD: warnc.c,v 1.3 2014/06/06 11:38:41 joerg Exp $");
39+#endif
40+#endif /* LIBC_SCCS and not lint */
41+
42+#include "netcompat.h"
43+#include <err.h>
44+#include <stdarg.h>
45+
46+void
47+warnc(int code, const char *fmt, ...)
48+{
49+	va_list ap;
50+
51+	va_start(ap, fmt);
52+	vwarnc(code, fmt, ap);
53+	va_end(ap);
54+}
+573, -0
  1@@ -0,0 +1,573 @@
  2+/*	$NetBSD: C.c,v 1.19 2009/07/13 19:05:40 roy Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1987, 1993, 1994
  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+#endif
 36+
 37+#include <sys/cdefs.h>
 38+#if defined(__RCSID) && !defined(lint)
 39+#if 0
 40+static char sccsid[] = "@(#)C.c	8.4 (Berkeley) 4/2/94";
 41+#else
 42+__RCSID("$NetBSD: C.c,v 1.19 2009/07/13 19:05:40 roy Exp $");
 43+#endif
 44+#endif /* not lint */
 45+
 46+#include <limits.h>
 47+#include <stddef.h>
 48+#include <stdio.h>
 49+#include <string.h>
 50+
 51+#include "ctags.h"
 52+
 53+static int	func_entry(void);
 54+static void	hash_entry(void);
 55+static void	skip_string(int);
 56+static int	str_entry(int);
 57+
 58+/*
 59+ * c_entries --
 60+ *	read .c and .h files and call appropriate routines
 61+ */
 62+void
 63+c_entries(void)
 64+{
 65+	int	c;			/* current character */
 66+	int	level;			/* brace level */
 67+	int	token;			/* if reading a token */
 68+	int	t_def;			/* if reading a typedef */
 69+	int	t_level;		/* typedef's brace level */
 70+	char	*sp;			/* buffer pointer */
 71+	char	tok[MAXTOKEN];		/* token buffer */
 72+
 73+	lineftell = ftell(inf);
 74+	sp = tok; token = t_def = NO; t_level = -1; level = 0; lineno = 1;
 75+	while (GETC(!=, EOF)) {
 76+		switch (c) {
 77+		/*
 78+		 * Here's where it DOESN'T handle: {
 79+		 *	foo(a)
 80+		 *	{
 81+		 *	#ifdef notdef
 82+		 *		}
 83+		 *	#endif
 84+		 *		if (a)
 85+		 *			puts("hello, world");
 86+		 *	}
 87+		 */
 88+		case '{':
 89+			++level;
 90+			goto endtok;
 91+		case '}':
 92+			/*
 93+			 * if level goes below zero, try and fix
 94+			 * it, even though we've already messed up
 95+			 */
 96+			if (--level < 0)
 97+				level = 0;
 98+			goto endtok;
 99+
100+		case '\n':
101+			SETLINE;
102+			/*
103+			 * the above 3 cases are similar in that they
104+			 * are special characters that also end tokens.
105+			 */
106+	endtok:			if (sp > tok) {
107+				*sp = EOS;
108+				token = YES;
109+				sp = tok;
110+			}
111+			else
112+				token = NO;
113+			continue;
114+
115+		/*
116+		 * We ignore quoted strings and character constants
117+		 * completely.
118+		 */
119+		case '"':
120+		case '\'':
121+			(void)skip_string(c);
122+			break;
123+
124+		/*
125+		 * comments can be fun; note the state is unchanged after
126+		 * return, in case we found:
127+		 *	"foo() XX comment XX { int bar; }"
128+		 */
129+		case '/':
130+			if (GETC(==, '*')) {
131+				skip_comment(c);
132+				continue;
133+			} else if (c == '/') {
134+				skip_comment(c);
135+				continue;
136+			}
137+			(void)ungetc(c, inf);
138+			c = '/';
139+			goto storec;
140+
141+		/* hash marks flag #define's. */
142+		case '#':
143+			if (sp == tok) {
144+				hash_entry();
145+				break;
146+			}
147+			goto storec;
148+
149+		/*
150+		 * if we have a current token, parenthesis on
151+		 * level zero indicates a function.
152+		 */
153+		case '(':
154+			do c = getc(inf);
155+			while (c != EOF && iswhite(c));
156+			if (c == '*')
157+				break;
158+			if (c != EOF)
159+				ungetc(c, inf);
160+			if (!level && token) {
161+				int	curline;
162+
163+				if (sp != tok)
164+					*sp = EOS;
165+				/*
166+				 * grab the line immediately, we may
167+				 * already be wrong, for example,
168+				 *	foo\n
169+				 *	(arg1,
170+				 */
171+				get_line();
172+				curline = lineno;
173+				if (func_entry()) {
174+					++level;
175+					pfnote(tok, curline);
176+				}
177+				break;
178+			}
179+			goto storec;
180+
181+		/*
182+		 * semi-colons indicate the end of a typedef; if we find a
183+		 * typedef we search for the next semi-colon of the same
184+		 * level as the typedef.  Ignoring "structs", they are
185+		 * tricky, since you can find:
186+		 *
187+		 *	"typedef long time_t;"
188+		 *	"typedef unsigned int u_int;"
189+		 *	"typedef unsigned int u_int [10];"
190+		 *
191+		 * If looking at a typedef, we save a copy of the last token
192+		 * found.  Then, when we find the ';' we take the current
193+		 * token if it starts with a valid token name, else we take
194+		 * the one we saved.  There's probably some reasonable
195+		 * alternative to this...
196+		 */
197+		case ';':
198+			if (t_def && level == t_level) {
199+				t_def = NO;
200+				get_line();
201+				if (sp != tok)
202+					*sp = EOS;
203+				pfnote(tok, lineno);
204+				break;
205+			}
206+			goto storec;
207+
208+		/*
209+		 * store characters until one that can't be part of a token
210+		 * comes along; check the current token against certain
211+		 * reserved words.
212+		 */
213+		default:
214+	storec:		if (c == EOF)
215+				break;
216+			if (!intoken(c)) {
217+				if (sp == tok)
218+					break;
219+				*sp = EOS;
220+				if (tflag) {
221+					/* no typedefs inside typedefs */
222+					if (!t_def &&
223+						   !memcmp(tok, "typedef",8)) {
224+						t_def = YES;
225+						t_level = level;
226+						break;
227+					}
228+					/* catch "typedef struct" */
229+					if ((!t_def || t_level <= level)
230+					    && (!memcmp(tok, "struct", 7)
231+					    || !memcmp(tok, "union", 6)
232+					    || !memcmp(tok, "enum", 5))) {
233+						/*
234+						 * get line immediately;
235+						 * may change before '{'
236+						 */
237+						get_line();
238+						if (str_entry(c))
239+							++level;
240+						break;
241+						/* } */
242+					}
243+				}
244+				sp = tok;
245+			}
246+			else if (sp != tok || begtoken(c)) {
247+				if (sp < tok + sizeof tok)
248+					*sp++ = c;
249+				token = YES;
250+			}
251+			continue;
252+		}
253+
254+		sp = tok;
255+		token = NO;
256+	}
257+}
258+
259+/*
260+ * func_entry --
261+ *	handle a function reference
262+ */
263+static int
264+func_entry(void)
265+{
266+	int	c;			/* current character */
267+	int	level = 0;		/* for matching '()' */
268+	static char attribute[] = "__attribute__";
269+	char	maybe_attribute[sizeof attribute + 1],
270+		*anext;
271+
272+	/*
273+	 * Find the end of the assumed function declaration.
274+	 * Note that ANSI C functions can have type definitions so keep
275+	 * track of the parentheses nesting level.
276+	 */
277+	while (GETC(!=, EOF)) {
278+		switch (c) {
279+		case '\'':
280+		case '"':
281+			/* skip strings and character constants */
282+			skip_string(c);
283+			break;
284+		case '/':
285+			/* skip comments */
286+			if (GETC(==, '*'))
287+				skip_comment(c);
288+			else if (c == '/')
289+				skip_comment(c);
290+			break;
291+		case '(':
292+			level++;
293+			break;
294+		case ')':
295+			if (level == 0)
296+				goto fnd;
297+			level--;
298+			break;
299+		case '\n':
300+			SETLINE;
301+		}
302+	}
303+	return (NO);
304+fnd:
305+	/*
306+	 * we assume that the character after a function's right paren
307+	 * is a token character if it's a function and a non-token
308+	 * character if it's a declaration.  Comments don't count...
309+	 */
310+	for (anext = maybe_attribute;;) {
311+		while (GETC(!=, EOF) && iswhite(c))
312+			if (c == '\n')
313+				SETLINE;
314+		if (c == EOF)
315+			return NO;
316+		/*
317+		 * Recognize the gnu __attribute__ extension, which would
318+		 * otherwise make the heuristic test DTWT
319+		 */
320+		if (anext == maybe_attribute) {
321+			if (intoken(c)) {
322+				*anext++ = c;
323+				continue;
324+			}
325+		} else {
326+			if (intoken(c)) {
327+				if (anext - maybe_attribute 
328+				 < (ptrdiff_t)(sizeof attribute - 1))
329+					*anext++ = c;
330+				else	break;
331+				continue;
332+			} else {
333+				*anext++ = '\0';
334+				if (strcmp(maybe_attribute, attribute) == 0) {
335+					(void)ungetc(c, inf);
336+					return NO;
337+				}
338+				break;
339+			}
340+		}
341+		if (intoken(c) || c == '{')
342+			break;
343+		if (c == '/' && GETC(==, '*'))
344+			skip_comment(c);
345+		else if (c == '/')
346+			skip_comment(c);
347+		else {				/* don't ever "read" '/' */
348+			(void)ungetc(c, inf);
349+			return (NO);
350+		}
351+	}
352+	if (c != '{')
353+		(void)skip_key('{');
354+	return (YES);
355+}
356+
357+/*
358+ * hash_entry --
359+ *	handle a line starting with a '#'
360+ */
361+static void
362+hash_entry(void)
363+{
364+	int	c;			/* character read */
365+	int	curline;		/* line started on */
366+	char	*sp;			/* buffer pointer */
367+	char	tok[MAXTOKEN];		/* storage buffer */
368+
369+	curline = lineno;
370+	do if (GETC(==, EOF))
371+		return;
372+	while(c != '\n' && iswhite(c));
373+	ungetc(c, inf);
374+	for (sp = tok;;) {		/* get next token */
375+		if (GETC(==, EOF))
376+			return;
377+		if (iswhite(c))
378+			break;
379+		if (sp < tok + sizeof tok)
380+			*sp++ = c;
381+	}
382+	if(sp >= tok + sizeof tok)
383+		--sp;
384+	*sp = EOS;
385+	if (memcmp(tok, "define", 6))	/* only interested in #define's */
386+		goto skip;
387+	for (;;) {			/* this doesn't handle "#define \n" */
388+		if (GETC(==, EOF))
389+			return;
390+		if (!iswhite(c))
391+			break;
392+	}
393+	for (sp = tok;;) {		/* get next token */
394+		if(sp < tok + sizeof tok)
395+			*sp++ = c;
396+		if (GETC(==, EOF))
397+			return;
398+		/*
399+		 * this is where it DOESN'T handle
400+		 * "#define \n"
401+		 */
402+		if (!intoken(c))
403+			break;
404+	}
405+	if(sp >= tok + sizeof tok)
406+		--sp;
407+	*sp = EOS;
408+	if (dflag || c == '(') {	/* only want macros */
409+		get_line();
410+		pfnote(tok, curline);
411+	}
412+skip:	if (c == '\n') {		/* get rid of rest of define */
413+		SETLINE
414+		if (*(sp - 1) != '\\')
415+			return;
416+	}
417+	(void)skip_key('\n');
418+}
419+
420+/*
421+ * str_entry --
422+ *	handle a struct, union or enum entry
423+ */
424+static int
425+str_entry(int c /* current character */)
426+{
427+	int	curline;		/* line started on */
428+	char	*sp;			/* buffer pointer */
429+	char	tok[LINE_MAX];		/* storage buffer */
430+
431+	curline = lineno;
432+	while (iswhite(c))
433+		if (GETC(==, EOF))
434+			return (NO);
435+	if (c == '{')		/* it was "struct {" */
436+		return (YES);
437+	for (sp = tok;;) {		/* get next token */
438+		*sp++ = c;
439+		if (GETC(==, EOF))
440+			return (NO);
441+		if (!intoken(c))
442+			break;
443+	}
444+	switch (c) {
445+		case '{':		/* it was "struct foo{" */
446+			--sp;
447+			break;
448+		case '\n':		/* it was "struct foo\n" */
449+			SETLINE;
450+			/*FALLTHROUGH*/
451+		default:		/* probably "struct foo " */
452+			while (GETC(!=, EOF))
453+				if (!iswhite(c))
454+					break;
455+			if (c != '{') {
456+				(void)ungetc(c, inf);
457+				return (NO);
458+			}
459+	}
460+	*sp = EOS;
461+	pfnote(tok, curline);
462+	return (YES);
463+}
464+
465+/*
466+ * skip_comment --
467+ *	skip over comment
468+ */
469+void
470+skip_comment(int commenttype)
471+{
472+	int	c;			/* character read */
473+	int	star;			/* '*' flag */
474+
475+	for (star = 0; GETC(!=, EOF);)
476+		switch(c) {
477+		/* comments don't nest, nor can they be escaped. */
478+		case '*':
479+			star = YES;
480+			break;
481+		case '/':
482+			if (commenttype == '*' && star)
483+				return;
484+			break;
485+		case '\n':
486+			if (commenttype == '/') {
487+				/*
488+				 * we don't really parse C, so sometimes it
489+				 * is necessary to see the newline
490+				 */
491+				ungetc(c, inf);
492+				return;
493+			}
494+			SETLINE;
495+			/*FALLTHROUGH*/
496+		default:
497+			star = NO;
498+			break;
499+		}
500+}
501+
502+/*
503+ * skip_string --
504+ *	skip to the end of a string or character constant.
505+ */
506+void
507+skip_string(int key)
508+{
509+	int	c,
510+		skip;
511+
512+	for (skip = NO; GETC(!=, EOF); )
513+		switch (c) {
514+		case '\\':		/* a backslash escapes anything */
515+			skip = !skip;	/* we toggle in case it's "\\" */
516+			break;
517+		case '\n':
518+			SETLINE;
519+			/*FALLTHROUGH*/
520+		default:
521+			if (c == key && !skip)
522+				return;
523+			skip = NO;
524+		}
525+}
526+
527+/*
528+ * skip_key --
529+ *	skip to next char "key"
530+ */
531+int
532+skip_key(int key)
533+{
534+	int	c,
535+		skip,
536+		retval;
537+
538+	for (skip = retval = NO; GETC(!=, EOF);)
539+		switch(c) {
540+		case '\\':		/* a backslash escapes anything */
541+			skip = !skip;	/* we toggle in case it's "\\" */
542+			break;
543+		case ';':		/* special case for yacc; if one */
544+		case '|':		/* of these chars occurs, we may */
545+			retval = YES;	/* have moved out of the rule */
546+			break;		/* not used by C */
547+		case '\'':
548+		case '"':
549+			/* skip strings and character constants */
550+			skip_string(c);
551+			break;
552+		case '/':
553+			/* skip comments */
554+			if (GETC(==, '*')) {
555+				skip_comment(c);
556+				break;
557+			} else if (c == '/') {
558+				skip_comment(c);
559+				break;
560+			}
561+			(void)ungetc(c, inf);
562+			c = '/';
563+			goto norm;
564+		case '\n':
565+			SETLINE;
566+			/*FALLTHROUGH*/
567+		default:
568+		norm:
569+			if (c == key && !skip)
570+				return (retval);
571+			skip = NO;
572+		}
573+	return (retval);
574+}
+28, -0
 1@@ -0,0 +1,28 @@
 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+MAN = ctags.1
 8+
 9+CC = cc
10+CFLAGS = -O2
11+CPPFLAGS = -I. -I../compat -include ../compat/netcompat.h
12+DESTDIR =
13+BINDIR = /usr/local/bin
14+MANDIR = /usr/local/share/man
15+
16+all: $(PROG)
17+
18+$(PROG): $(SRCS)
19+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
20+
21+install: $(PROG)
22+	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
23+	cp $(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
24+	chmod 755 $(DESTDIR)$(BINDIR)/$(PROG)
25+	cp $(MAN) $(DESTDIR)$(MANDIR)/man1/$(MAN)
26+	chmod 644 $(DESTDIR)$(MANDIR)/man1/$(MAN)
27+
28+clean:
29+	rm -f $(PROG) *.o
+225, -0
  1@@ -0,0 +1,225 @@
  2+.\"	$NetBSD: ctags.1,v 1.18 2017/07/03 21:34:19 wiz Exp $
  3+.\"
  4+.\" Copyright (c) 1987, 1990, 1993
  5+.\"	The Regents of the University of California.  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. Neither the name of the University nor the names of its contributors
 16+.\"    may be used to endorse or promote products derived from this software
 17+.\"    without specific prior written permission.
 18+.\"
 19+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 20+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 21+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 22+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 23+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 24+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 25+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 26+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 27+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 28+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 29+.\" SUCH DAMAGE.
 30+.\"
 31+.\"     @(#)ctags.1	8.1 (Berkeley) 6/6/93
 32+.\"
 33+.Dd June 6, 1993
 34+.Dt CTAGS 1
 35+.Os
 36+.Sh NAME
 37+.Nm ctags
 38+.Nd create a tags file
 39+.Sh SYNOPSIS
 40+.Nm
 41+.Op Fl BFadtuwvx
 42+.Op Fl f Ar tagsfile
 43+.Ar name ...
 44+.Sh DESCRIPTION
 45+.Nm
 46+makes a tags file for
 47+.Xr ex 1
 48+from the specified C,
 49+Pascal, Fortran,
 50+.Tn YACC ,
 51+lex, and lisp sources.
 52+A tags file gives the locations of specified objects in a group of files.
 53+Each line of the tags file contains the object name, the file in which it
 54+is defined, and a search pattern for the object definition, separated by
 55+white-space.
 56+Using the
 57+.Ar tags
 58+file,
 59+.Xr ex 1
 60+can quickly locate these object definitions.
 61+Depending upon the options provided to
 62+.Nm ,
 63+objects will consist of subroutines, typedefs, defines, structs,
 64+enums and unions.
 65+.Bl -tag -width Ds
 66+.It Fl B
 67+use backward searching patterns
 68+.Pq Li ?...? .
 69+.It Fl F
 70+use forward searching patterns
 71+.Pq Li /.../
 72+(the default).
 73+.It Fl a
 74+append to
 75+.Ar tags
 76+file.
 77+.It Fl d
 78+create tags for
 79+.Li #defines
 80+that don't take arguments;
 81+.Li #defines
 82+that take arguments are tagged automatically.
 83+.It Fl f
 84+Places the tag descriptions in a file called
 85+.Ar tagsfile .
 86+The default behaviour is to place them in a file called
 87+.Ar tags .
 88+.It Fl t
 89+create tags for typedefs, structs, unions, and enums.
 90+.It Fl u
 91+update the specified files in the
 92+.Ar tags
 93+file, that is, all
 94+references to them are deleted, and the new values are appended to the
 95+file.
 96+(Beware: this option is implemented in a way which is rather
 97+slow; it is usually faster to simply rebuild the
 98+.Ar tags
 99+file.)
100+.It Fl v
101+An index of the form expected by
102+.Xr vgrind 1
103+is produced on the standard output.
104+This listing
105+contains the object name, file name, and page number (assuming 64
106+line pages).
107+Since the output will be sorted into lexicographic order,
108+it may be desired to run the output through
109+.Xr sort 1 .
110+Sample use:
111+.Bd -literal -offset indent
112+ctags \-v files \&| sort \-f > index
113+vgrind \-x index
114+.Ed
115+.It Fl w
116+suppress warning diagnostics.
117+.It Fl x
118+.Nm
119+produces a list of object
120+names, the line number and file name on which each is defined, as well
121+as the text of that line and prints this on the standard output.
122+This
123+is a simple index which can be printed out as an off-line readable
124+function index.
125+.El
126+.Pp
127+Files whose names end in
128+.Sq \&.c
129+or
130+.Sq \&.h
131+are assumed to be C
132+source files and are searched for C style routine and macro definitions.
133+Files whose names end in
134+.Sq \&.y
135+are assumed to be
136+.Tn YACC
137+source files.
138+Files whose names end in
139+.Sq \&.l
140+are assumed to be lisp files if their
141+first non-blank character is
142+.Sq \&; ,
143+.Sq \&( ,
144+or
145+.Sq \&[ ,
146+otherwise, they are
147+treated as lex files.
148+Other files are first examined to see if they
149+contain any Pascal or Fortran routine definitions, and, if not, are
150+searched for C style definitions.
151+.Pp
152+The tag
153+.Li main
154+is treated specially in C programs.
155+The tag formed
156+is created by prepending
157+.Ar M
158+to the name of the file, with the
159+trailing
160+.Sq \&.c
161+and any leading pathname components removed.
162+This
163+makes use of
164+.Nm
165+practical in directories with more than one
166+program.
167+.Pp
168+Yacc and lex files each have a special tag.
169+.Ar Yyparse
170+is the start
171+of the second section of the yacc file, and
172+.Ar yylex
173+is the start of
174+the second section of the lex file.
175+.Sh FILES
176+.Bl -tag -width tags -compact
177+.It Pa tags
178+default output tags file
179+.El
180+.Sh EXIT STATUS
181+.Ex -std
182+Duplicate objects are not considered errors.
183+.Sh SEE ALSO
184+.Xr ex 1 ,
185+.Xr vi 1
186+.Sh HISTORY
187+The
188+.Nm
189+command appeared in
190+.Bx 3.0 .
191+.Sh BUGS
192+Recognition of
193+.Em functions  ,
194+.Em subroutines
195+and
196+.Em procedures
197+for
198+.Tn FORTRAN
199+and Pascal is done in a very simpleminded way.
200+No attempt
201+is made to deal with block structure; if you have two Pascal procedures
202+in different blocks with the same name you lose.
203+.Nm
204+doesn't
205+understand about Pascal types.
206+.Pp
207+The method of deciding whether to look for C, Pascal or
208+.Tn FORTRAN
209+functions is a hack.
210+.Pp
211+.Nm
212+relies on the input being well formed, and any syntactical
213+errors will completely confuse it.
214+It also finds some legal syntax
215+confusing; for example, since it doesn't understand
216+.Li #ifdef Ns 's
217+(incidentally, that's a feature, not a bug), any code with unbalanced
218+braces inside
219+.Li #ifdef Ns 's
220+will cause it to become somewhat disoriented.
221+In a similar fashion, multiple line changes within a definition will
222+cause it to enter the last line of the object, rather than the first, as
223+the searching pattern.
224+The last line of multiple line
225+.Li typedef Ns 's
226+will similarly be noted.
+335, -0
  1@@ -0,0 +1,335 @@
  2+/*	$NetBSD: ctags.c,v 1.18 2024/10/31 01:50:20 kre Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1987, 1993, 1994, 1995
  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+#endif
 36+
 37+#include <sys/cdefs.h>
 38+#if defined(__COPYRIGHT) && !defined(lint)
 39+__COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994, 1995\
 40+ The Regents of the University of California.  All rights reserved.");
 41+#endif /* not lint */
 42+
 43+#if defined(__RCSID) && !defined(lint)
 44+#if 0
 45+static char sccsid[] = "@(#)ctags.c	8.4 (Berkeley) 2/7/95";
 46+#endif
 47+__RCSID("$NetBSD: ctags.c,v 1.18 2024/10/31 01:50:20 kre Exp $");
 48+#endif /* not lint */
 49+
 50+#include <err.h>
 51+#include <limits.h>
 52+#include <stdio.h>
 53+#include <string.h>
 54+#include <stdlib.h>
 55+#include <unistd.h>
 56+
 57+#include "ctags.h"
 58+
 59+/*
 60+ * ctags: create a tags file
 61+ */
 62+
 63+NODE	*head;			/* head of the sorted binary tree */
 64+
 65+				/* boolean "func" (see init()) */
 66+bool	_wht[256], _etk[256], _itk[256], _btk[256], _gd[256];
 67+
 68+FILE	*inf;			/* ioptr for current input file */
 69+FILE	*outf;			/* ioptr for tags file */
 70+
 71+long	lineftell;		/* ftell after getc( inf ) == '\n' */
 72+
 73+int	lineno;			/* line number of current line */
 74+int	dflag;			/* -d: non-macro defines */
 75+int	tflag;			/* -t: create tags for typedefs */
 76+int	vflag;			/* -v: vgrind style index output */
 77+int	wflag;			/* -w: suppress warnings */
 78+int	xflag;			/* -x: cxref style output */
 79+
 80+char	*curfile;		/* current input file name */
 81+char	searchar = '/';		/* use /.../ searches by default */
 82+char	lbuf[LINE_MAX];
 83+
 84+void	init(void);
 85+void	find_entries(char *);
 86+
 87+int
 88+main(int argc, char **argv)
 89+{
 90+	static const char	*outfile = "tags";	/* output file */
 91+	int	aflag;				/* -a: append to tags */
 92+	int	uflag;				/* -u: update tags */
 93+	int	exit_val;			/* exit value */
 94+	int	step;				/* step through args */
 95+	int	ch;				/* getopts char */
 96+	char	*cmd;				/* not nice */
 97+	char	tname[128];			/* disgusting */
 98+	size_t	sz;
 99+
100+	aflag = uflag = NO;
101+	while ((ch = getopt(argc, argv, "BFadf:tuwvx")) != -1)
102+		switch(ch) {
103+		case 'B':
104+			searchar = '?';
105+			break;
106+		case 'F':
107+			searchar = '/';
108+			break;
109+		case 'a':
110+			aflag++;
111+			break;
112+		case 'd':
113+			dflag++;
114+			break;
115+		case 'f':
116+			outfile = optarg;
117+			break;
118+		case 't':
119+			tflag++;
120+			break;
121+		case 'u':
122+			uflag++;
123+			break;
124+		case 'w':
125+			wflag++;
126+			break;
127+		case 'v':
128+			vflag++;
129+			/* FALLTHROUGH */
130+		case 'x':
131+			xflag++;
132+			break;
133+		case '?':
134+		default:
135+			goto usage;
136+		}
137+	argv += optind;
138+	argc -= optind;
139+	if (!argc) {
140+ usage:;	(void)fprintf(stderr,
141+			"usage: ctags [-BFadtuwvx] [-f tagsfile] file ...\n");
142+		exit(EXIT_FAILURE);
143+	}
144+
145+	if ((sz = shquote(outfile, tname, sizeof tname)) >= sizeof tname) {
146+		/* nb: (size_t)-1 > sizeof tname */
147+		if (sz == (size_t)-1)
148+			err(EXIT_FAILURE, "Output file name '%s' too long",
149+			    outfile);
150+		else
151+			errx(EXIT_FAILURE, "Output file name '%s' too long",
152+			    outfile);
153+	}
154+
155+	init();
156+
157+	exit_val = EXIT_SUCCESS;
158+	for (step = 0; step < argc; ++step)
159+		if (!(inf = fopen(argv[step], "r"))) {
160+			warn("%s", argv[step]);
161+			exit_val = EXIT_FAILURE;
162+		}
163+		else {
164+			curfile = argv[step];
165+			find_entries(argv[step]);
166+			(void)fclose(inf);
167+		}
168+
169+	if (head) {
170+		if (xflag)
171+			put_entries(head);
172+		else {
173+			if (uflag) {
174+				for (step = 0; step < argc; step++) {
175+					char pattern[140];
176+
177+					if (asprintf(&cmd, "\t%s\t",
178+					    argv[step]) == -1)
179+						err(EXIT_FAILURE,
180+						   "Cannot generate '\\t%s\\t'",
181+						   argv[step]);
182+
183+					if ((sz = shquote(cmd, pattern,
184+					    sizeof pattern)) >= sizeof pattern)
185+					{
186+						if (sz == (size_t)-1)
187+						    err(EXIT_FAILURE, "'%s': "
188+							"quoting pattern", cmd);
189+						else
190+						    errx(EXIT_FAILURE, "'%s': "
191+							"failed to quote", cmd);
192+					}
193+					(void)free(cmd);
194+
195+					if (asprintf(&cmd,
196+				    	 "OTAGS=$(mktemp -t tags.$$) || exit\n"
197+					 "\ttest -n \"${OTAGS}\" || exit\n"
198+					 "\ttrap 'rm -f \"${OTAGS}\"' EXIT\n"
199+					 "\tmv %s \"${OTAGS}\" || exit\n"
200+					 "\tfgrep -v %s \"${OTAGS}\" >%s\n"
201+					 "\tX=$? ; "
202+					 "test \"$X\" -le 1 || exit $X",
203+					    tname, pattern, tname) == -1)
204+						err(EXIT_FAILURE,
205+						  "Command to update %s for -u"
206+						     " %s",
207+						  argv[step], outfile);
208+
209+					if (system(cmd) != 0)
210+						errx(EXIT_FAILURE,
211+						  "Update (-u) of %s failed.   "
212+						  "Cmd:\n    %s", outfile, cmd);
213+
214+					(void)free(cmd);
215+				}
216+				++aflag;
217+			}
218+			if (!(outf = fopen(outfile, aflag ? "a" : "w")))
219+				err(EXIT_FAILURE, "%s", outfile);
220+			put_entries(head);
221+			(void)fflush(outf);
222+			if (ferror(outf))
223+				err(EXIT_FAILURE, "output error (%s)", outfile);
224+			(void)fclose(outf);
225+			if (uflag) {
226+				if (asprintf(&cmd, "sort -o %s %s",
227+				    tname, tname) == -1)
228+					err(EXIT_FAILURE,
229+					    "sort command (-u) for %s",
230+					    outfile);
231+				if (system(cmd) != 0)
232+					errx(EXIT_FAILURE, "-u: sort %s failed"
233+					    "\t[ %s ]", outfile, cmd);
234+				(void)free(cmd);
235+			}
236+		}
237+	}
238+	if ((vflag || xflag) && (fflush(stdout) != 0 || ferror(stdout) != 0))
239+		errx(EXIT_FAILURE, "write error (stdout)");
240+	exit(exit_val);
241+}
242+
243+/*
244+ * init --
245+ *	this routine sets up the boolean pseudo-functions which work by
246+ *	setting boolean flags dependent upon the corresponding character.
247+ *	Every char which is NOT in that string is false with respect to
248+ *	the pseudo-function.  Therefore, all of the array "_wht" is NO
249+ *	by default and then the elements subscripted by the chars in
250+ *	CWHITE are set to YES.  Thus, "_wht" of a char is YES if it is in
251+ *	the string CWHITE, else NO.
252+ */
253+void
254+init(void)
255+{
256+	int		i;
257+	const char	*sp;
258+
259+	for (i = 0; i < 256; i++) {
260+		_wht[i] = _etk[i] = _itk[i] = _btk[i] = NO;
261+		_gd[i] = YES;
262+	}
263+#define	CWHITE	" \f\t\n"
264+	for (sp = CWHITE; *sp; sp++)	/* white space chars */
265+		_wht[(unsigned)*sp] = YES;
266+#define	CTOKEN	" \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?"
267+	for (sp = CTOKEN; *sp; sp++)	/* token ending chars */
268+		_etk[(unsigned)*sp] = YES;
269+#define	CINTOK	"ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789"
270+	for (sp = CINTOK; *sp; sp++)	/* valid in-token chars */
271+		_itk[(unsigned)*sp] = YES;
272+#define	CBEGIN	"ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
273+	for (sp = CBEGIN; *sp; sp++)	/* token starting chars */
274+		_btk[(unsigned)*sp] = YES;
275+#define	CNOTGD	",;"
276+	for (sp = CNOTGD; *sp; sp++)	/* invalid after-function chars */
277+		_gd[(unsigned)*sp] = NO;
278+}
279+
280+/*
281+ * find_entries --
282+ *	this routine opens the specified file and calls the function
283+ *	which searches the file.
284+ */
285+void
286+find_entries(char *file)
287+{
288+	char	*cp;
289+
290+	lineno = 0;				/* should be 1 ?? KB */
291+	if ((cp = strrchr(file, '.')) != NULL) {
292+		if (cp[1] == 'l' && !cp[2]) {
293+			int	c;
294+
295+			for (;;) {
296+				if (GETC(==, EOF))
297+					return;
298+				if (!iswhite(c)) {
299+					rewind(inf);
300+					break;
301+				}
302+			}
303+#define	LISPCHR	";(["
304+/* lisp */		if (strchr(LISPCHR, c)) {
305+				l_entries();
306+				return;
307+			}
308+/* lex */		else {
309+				/*
310+				 * we search all 3 parts of a lex file
311+				 * for C references.  This may be wrong.
312+				 */
313+				toss_yysec();
314+				(void)strlcpy(lbuf, "%%$", sizeof(lbuf));
315+				pfnote("yylex", lineno);
316+				rewind(inf);
317+			}
318+		}
319+/* yacc */	else if (cp[1] == 'y' && !cp[2]) {
320+			/*
321+			 * we search only the 3rd part of a yacc file
322+			 * for C references.  This may be wrong.
323+			 */
324+			toss_yysec();
325+			(void)strlcpy(lbuf, "%%$", sizeof(lbuf));
326+			pfnote("yyparse", lineno);
327+			y_entries();
328+		}
329+/* fortran */	else if ((cp[1] != 'c' && cp[1] != 'h') && !cp[2]) {
330+			if (PF_funcs())
331+				return;
332+			rewind(inf);
333+		}
334+	}
335+/* C */	c_entries();
336+}
+92, -0
 1@@ -0,0 +1,92 @@
 2+/*	$NetBSD: ctags.h,v 1.9 2009/07/13 19:05:40 roy Exp $	*/
 3+
 4+/*
 5+ * Copyright (c) 1987, 1993, 1994
 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+ *	@(#)ctags.h	8.3 (Berkeley) 4/2/94
33+ */
34+
35+#if HAVE_NBTOOL_CONFIG_H
36+#include "nbtool_config.h"
37+#endif
38+
39+#define	bool	char
40+
41+#define	YES		1
42+#define	NO		0
43+#define	EOS		'\0'
44+
45+#define	ENDLINE		50		/* max length of pattern */
46+#define	MAXTOKEN	250		/* max size of single token */
47+
48+#define	SETLINE		{++lineno;lineftell = ftell(inf);}
49+#define	GETC(op,exp)	((c = getc(inf)) op (int)exp)
50+
51+#define	iswhite(arg)	(_wht[(unsigned)arg])	/* T if char is white */
52+#define	begtoken(arg)	(_btk[(unsigned)arg])	/* T if char can start token */
53+#define	intoken(arg)	(_itk[(unsigned)arg])	/* T if char can be in token */
54+#define	endtoken(arg)	(_etk[(unsigned)arg])	/* T if char ends tokens */
55+#define	isgood(arg)	(_gd[(unsigned)arg])	/* T if char can be after ')' */
56+
57+typedef struct nd_st {			/* sorting structure */
58+	struct nd_st	*left,
59+			*right;		/* left and right sons */
60+	char	*entry,			/* function or type name */
61+		*file,			/* file name */
62+		*pat;			/* search pattern */
63+	int	lno;			/* for -x option */
64+	bool	been_warned;		/* set if noticed dup */
65+} NODE;
66+
67+extern char	*curfile;		/* current input file name */
68+extern NODE	*head;			/* head of the sorted binary tree */
69+extern FILE    *inf;			/* ioptr for current input file */
70+extern FILE    *outf;			/* ioptr for current output file */
71+extern long	lineftell;		/* ftell after getc( inf ) == '\n' */
72+extern int	lineno;			/* line number of current line */
73+extern int	dflag;			/* -d: non-macro defines */
74+extern int	tflag;			/* -t: create tags for typedefs */
75+extern int	vflag;			/* -v: vgrind style index output */
76+extern int	wflag;			/* -w: suppress warnings */
77+extern int	xflag;			/* -x: cxref style output */
78+extern bool	_wht[], _etk[], _itk[], _btk[], _gd[];
79+extern char	lbuf[LINE_MAX];
80+extern char    *lbp;
81+extern char	searchar;		/* ex search character */
82+
83+extern int	cicmp(const char *);
84+extern void	get_line(void);
85+extern void	pfnote(const char *, int);
86+extern int	skip_key(int);
87+extern void	put_entries(NODE *);
88+extern void	toss_yysec(void);
89+extern void	l_entries(void);
90+extern void	y_entries(void);
91+extern int	PF_funcs(void);
92+extern void	c_entries(void);
93+extern void	skip_comment(int);
+175, -0
  1@@ -0,0 +1,175 @@
  2+/*	$NetBSD: fortran.c,v 1.12 2019/02/03 03:19:29 mrg Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1987, 1993, 1994
  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+#endif
 36+
 37+#include <sys/cdefs.h>
 38+#if defined(__RCSID) && !defined(lint)
 39+#if 0
 40+static char sccsid[] = "@(#)fortran.c	8.3 (Berkeley) 4/2/94";
 41+#else
 42+__RCSID("$NetBSD: fortran.c,v 1.12 2019/02/03 03:19:29 mrg Exp $");
 43+#endif
 44+#endif /* not lint */
 45+
 46+#include <ctype.h>
 47+#include <limits.h>
 48+#include <stdio.h>
 49+#include <string.h>
 50+
 51+#include "ctags.h"
 52+
 53+static void takeprec(void);
 54+
 55+char *lbp;				/* line buffer pointer */
 56+
 57+int
 58+PF_funcs(void)
 59+{
 60+	bool	pfcnt;			/* pascal/fortran functions found */
 61+	char	*cp;
 62+	char	tok[MAXTOKEN];
 63+
 64+	for (pfcnt = NO;;) {
 65+		lineftell = ftell(inf);
 66+		if (!fgets(lbuf, sizeof(lbuf), inf))
 67+			return (pfcnt);
 68+		++lineno;
 69+		lbp = lbuf;
 70+		if (*lbp == '%')	/* Ratfor escape to fortran */
 71+			++lbp;
 72+		for (; isspace((unsigned char)*lbp); ++lbp)
 73+			continue;
 74+		if (!*lbp)
 75+			continue;
 76+		switch (*lbp | ' ') {	/* convert to lower-case */
 77+		case 'c':
 78+			if (cicmp("complex") || cicmp("character"))
 79+				takeprec();
 80+			break;
 81+		case 'd':
 82+			if (cicmp("double")) {
 83+				for (; isspace((unsigned char)*lbp); ++lbp)
 84+					continue;
 85+				if (!*lbp)
 86+					continue;
 87+				if (cicmp("precision"))
 88+					break;
 89+				continue;
 90+			}
 91+			break;
 92+		case 'i':
 93+			if (cicmp("integer"))
 94+				takeprec();
 95+			break;
 96+		case 'l':
 97+			if (cicmp("logical"))
 98+				takeprec();
 99+			break;
100+		case 'r':
101+			if (cicmp("real"))
102+				takeprec();
103+			break;
104+		}
105+		for (; isspace((unsigned char)*lbp); ++lbp)
106+			continue;
107+		if (!*lbp)
108+			continue;
109+		switch (*lbp | ' ') {
110+		case 'f':
111+			if (cicmp("function"))
112+				break;
113+			continue;
114+		case 'p':
115+			if (cicmp("program") || cicmp("procedure"))
116+				break;
117+			continue;
118+		case 's':
119+			if (cicmp("subroutine"))
120+				break;
121+			/* FALLTHROUGH */
122+		default:
123+			continue;
124+		}
125+		for (; isspace((unsigned char)*lbp); ++lbp)
126+			continue;
127+		if (!*lbp)
128+			continue;
129+		for (cp = lbp + 1; *cp && intoken(*cp); ++cp)
130+			continue;
131+		if ((cp = lbp + 1) != NULL)
132+			continue;
133+		*cp = EOS;
134+		(void)strlcpy(tok, lbp, sizeof(tok));
135+		get_line();			/* process line for ex(1) */
136+		pfnote(tok, lineno);
137+		pfcnt = YES;
138+	}
139+	/*NOTREACHED*/
140+}
141+
142+/*
143+ * cicmp --
144+ *	do case-independent strcmp
145+ */
146+int
147+cicmp(const char *cp)
148+{
149+	int	len;
150+	char	*bp;
151+
152+	for (len = 0, bp = lbp; *cp && (*cp &~ ' ') == (*bp++ &~ ' ');
153+	    ++cp, ++len)
154+		continue;
155+	if (!*cp) {
156+		lbp += len;
157+		return (YES);
158+	}
159+	return (NO);
160+}
161+
162+static void
163+takeprec(void)
164+{
165+	for (; isspace((unsigned char)*lbp); ++lbp)
166+		continue;
167+	if (*lbp == '*') {
168+		for (++lbp; isspace((unsigned char)*lbp); ++lbp)
169+			continue;
170+		if (!isdigit((unsigned char)*lbp))
171+			--lbp;			/* force failure */
172+		else
173+			while (isdigit((unsigned char)*++lbp))
174+				continue;
175+	}
176+}
+112, -0
  1@@ -0,0 +1,112 @@
  2+/*	$NetBSD: lisp.c,v 1.11 2009/07/13 19:05:40 roy Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1987, 1993, 1994
  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+#endif
 36+
 37+#include <sys/cdefs.h>
 38+#if defined(__RCSID) && !defined(lint)
 39+#if 0
 40+static char sccsid[] = "@(#)lisp.c	8.3 (Berkeley) 4/2/94";
 41+#else
 42+__RCSID("$NetBSD: lisp.c,v 1.11 2009/07/13 19:05:40 roy Exp $");
 43+#endif
 44+#endif /* not lint */
 45+
 46+#include <ctype.h>
 47+#include <limits.h>
 48+#include <stdio.h>
 49+#include <string.h>
 50+
 51+#include "ctags.h"
 52+
 53+/*
 54+ * lisp tag functions
 55+ * just look for (def or (DEF
 56+ */
 57+void
 58+l_entries(void)
 59+{
 60+	int	special;
 61+	char	*cp;
 62+	char	savedc;
 63+	char	tok[MAXTOKEN];
 64+
 65+	for (;;) {
 66+		lineftell = ftell(inf);
 67+		if (!fgets(lbuf, sizeof(lbuf), inf))
 68+			return;
 69+		++lineno;
 70+		lbp = lbuf;
 71+		if (!cicmp("(def"))
 72+			continue;
 73+		special = NO;
 74+		switch(*lbp | ' ') {
 75+		case 'm':
 76+			if (cicmp("method"))
 77+				special = YES;
 78+			break;
 79+		case 'w':
 80+			if (cicmp("wrapper") || cicmp("whopper"))
 81+				special = YES;
 82+		}
 83+		for (; !isspace((unsigned char)*lbp); ++lbp)
 84+			continue;
 85+		for (; isspace((unsigned char)*lbp); ++lbp)
 86+			continue;
 87+		for (cp = lbp; *cp && *cp != '\n'; ++cp)
 88+			continue;
 89+		*cp = EOS;
 90+		if (special) {
 91+			if (!(cp = strchr(lbp, ')')))
 92+				continue;
 93+			for (; cp >= lbp && *cp != ':'; --cp)
 94+				continue;
 95+			if (cp < lbp)
 96+				continue;
 97+			lbp = cp;
 98+			for (; *cp && *cp != ')' && *cp != ' '; ++cp)
 99+				continue;
100+		}
101+		else
102+			for (cp = lbp + 1;
103+			    *cp && *cp != '(' && *cp != ' '; ++cp)
104+				continue;
105+		savedc = *cp;
106+		*cp = EOS;
107+		(void)strlcpy(tok, lbp, sizeof(tok));
108+		*cp = savedc;
109+		get_line();
110+		pfnote(tok, lineno);
111+	}
112+	/*NOTREACHED*/
113+}
+121, -0
  1@@ -0,0 +1,121 @@
  2+/*	$NetBSD: print.c,v 1.11 2025/08/02 05:58:42 kre Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1987, 1993, 1994
  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+#endif
 36+
 37+#include <sys/cdefs.h>
 38+#if defined(__RCSID) && !defined(lint)
 39+#if 0
 40+static char sccsid[] = "@(#)print.c	8.3 (Berkeley) 4/2/94";
 41+#else 
 42+__RCSID("$NetBSD: print.c,v 1.11 2025/08/02 05:58:42 kre Exp $");
 43+#endif
 44+#endif /* not lint */
 45+
 46+#include <limits.h>
 47+#include <stdio.h>
 48+#include <stdlib.h>
 49+#include <string.h>
 50+#include <unistd.h>
 51+
 52+#include "ctags.h"
 53+
 54+/*
 55+ * get_line --
 56+ *	get the line the token of interest occurred on,
 57+ *	prepare it for printing.
 58+ */
 59+void
 60+get_line(void)
 61+{
 62+	long	saveftell;
 63+	int	c;
 64+	int	cnt;
 65+	char	*cp;
 66+
 67+	saveftell = ftell(inf);
 68+	(void)fseek(inf, lineftell, SEEK_SET);
 69+	if (xflag)
 70+		for (cp = lbuf; GETC(!=, '\n'); *cp++ = c)
 71+			continue;
 72+	/*
 73+	 * do all processing here, so we don't step through the
 74+	 * line more than once; means you don't call this routine
 75+	 * unless you're sure you've got a keeper.
 76+	 */
 77+	else for (cnt = 0, cp = lbuf; GETC(!=, EOF) && cnt < ENDLINE; ++cnt) {
 78+		if (c == '\\') {		/* backslashes */
 79+			if (cnt > ENDLINE - 2)
 80+				break;
 81+			*cp++ = '\\'; *cp++ = '\\';
 82+			++cnt;
 83+		}
 84+		else if (c == (int)searchar) {	/* search character */
 85+			if (cnt > ENDLINE - 2)
 86+				break;
 87+			*cp++ = '\\'; *cp++ = c;
 88+			++cnt;
 89+		}
 90+		else if (c == '\n') {	/* end of keep */
 91+			*cp++ = '$';		/* can find whole line */
 92+			break;
 93+		}
 94+		else
 95+			*cp++ = c;
 96+	}
 97+	*cp = EOS;
 98+	(void)fseek(inf, saveftell, SEEK_SET);
 99+}
100+
101+/*
102+ * put_entries --
103+ *	write out the tags
104+ */
105+void
106+put_entries(NODE *node)
107+{
108+
109+	if (node->left)
110+		put_entries(node->left);
111+	if (vflag)
112+		printf("%s %s %d\n",
113+		    node->entry, node->file, (node->lno + 63) / 64);
114+	else if (xflag)
115+		printf("%-16s %4d %-16s %s\n",
116+		    node->entry, node->lno, node->file, node->pat);
117+	else
118+		fprintf(outf, "%s\t%s\t%c^%s%c\n",
119+		    node->entry, node->file, searchar, node->pat, searchar);
120+	if (node->right)
121+		put_entries(node->right);
122+}
+69, -0
 1@@ -0,0 +1,69 @@
 2+/*	$NetBSD: ctags.test,v 1.2 1995/03/26 20:14:14 glass Exp $	*/
 3+
 4+int	bar = (1 + 5);
 5+
 6+FOO("here is a #define test: ) {");
 7+char sysent[20];
 8+int	nsysent = sizeof (sysent) / sizeof (sysent[0]);
 9+/*
10+ * now is the time for a comment.
11+ * four lines in length...
12+ */struct struct_xtra{int list;};r4(x,y){};typedef struct{int bar;}struct_xxe;
13+#define FOO	BAR
14+struct struct_three {
15+	int	list;
16+};
17+#define SINGLE
18+int	BAD();
19+enum color {red, green, gold, brown};
20+char qq[] = "  quote(one,two) {int bar;}  ";
21+typedef struct {
22+	int	bar;
23+	struct struct_two {
24+		int foo;
25+		union union_3 {
26+			struct struct_three entry;
27+			char size[25];
28+		};
29+		struct last {
30+			struct struct_three xentry;
31+			char list[34];
32+		};
33+	};
34+} struct_one;
35+#define TWOLINE	((MAXLIST + FUTURE + 15) \
36+	/ (time_to_live ? 3 : 4))
37+#if (defined(BAR))
38+int	bar;
39+#endif
40+#define MULTIPLE {\
41+	multiple(one,two); \
42+	lineno++; \
43+	callroute(one,two); \
44+}
45+#if defined(BAR)
46+int	bar;
47+#endif
48+union union_one {
49+	struct struct_three	s3;
50+	char	foo[25];
51+};
52+#define XYZ(A,B)	(A + B / 2) * (3 - 26 + l_lineno)
53+routine1(one,two)	/* comments here are fun... */
54+	struct {
55+		int entry;
56+		char bar[34];
57+	} *one;
58+	char two[10];
59+{
60+typedef unsigned char	u_char;
61+	register struct buf *bp;
62+	five(one,two);
63+}
64+ routine2 (one,two) { puts("hello\n"); }
65+ routine3
66+(one,
67+two) { puts("world\n"); }
68+routine4(int one, char (*two)(void)) /* test ANSI arguments */
69+{
70+}
+145, -0
  1@@ -0,0 +1,145 @@
  2+/*	$NetBSD: tree.c,v 1.12 2006/04/05 19:38:47 dsl Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1987, 1993, 1994
  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+#endif
 36+
 37+#include <sys/cdefs.h>
 38+#if defined(__RCSID) && !defined(lint)
 39+#if 0
 40+static char sccsid[] = "@(#)tree.c	8.3 (Berkeley) 4/2/94";
 41+#else
 42+__RCSID("$NetBSD: tree.c,v 1.12 2006/04/05 19:38:47 dsl Exp $");
 43+#endif
 44+#endif /* not lint */
 45+
 46+#include <err.h>
 47+#include <limits.h>
 48+#include <stdio.h>
 49+#include <stdlib.h>
 50+#include <string.h>
 51+
 52+#include "ctags.h"
 53+
 54+static void	add_node(NODE *, NODE *);
 55+static void	free_tree(NODE *);
 56+
 57+/*
 58+ * pfnote --
 59+ *	enter a new node in the tree
 60+ */
 61+void
 62+pfnote(const char *name, int ln)
 63+{
 64+	NODE	*np;
 65+	char	*fp;
 66+	char	nbuf[MAXTOKEN];
 67+
 68+	/*NOSTRICT*/
 69+	if (!(np = (NODE *)malloc(sizeof(NODE)))) {
 70+		warnx("too many entries to sort");
 71+		put_entries(head);
 72+		free_tree(head);
 73+		/*NOSTRICT*/
 74+		if (!(head = np = (NODE *)malloc(sizeof(NODE))))
 75+			err(1, "out of space");
 76+	}
 77+	if (!xflag && !strcmp(name, "main")) {
 78+		if (!(fp = strrchr(curfile, '/')))
 79+			fp = curfile;
 80+		else
 81+			++fp;
 82+		(void)snprintf(nbuf, sizeof(nbuf), "M%s", fp);
 83+		fp = strrchr(nbuf, '.');
 84+		if (fp && !fp[2])
 85+			*fp = EOS;
 86+		name = nbuf;
 87+	}
 88+	if (!(np->entry = strdup(name)))
 89+		err(1, "strdup");
 90+	np->file = curfile;
 91+	np->lno = ln;
 92+	np->left = np->right = 0;
 93+	if (!(np->pat = strdup(lbuf)))
 94+		err(1, "strdup");
 95+	if (!head)
 96+		head = np;
 97+	else
 98+		add_node(np, head);
 99+}
100+
101+static void
102+add_node(NODE *node, NODE *cur_node)
103+{
104+	int	dif;
105+
106+	dif = strcmp(node->entry, cur_node->entry);
107+	if (!dif) {
108+		if (node->file == cur_node->file) {
109+			if (!wflag)
110+				fprintf(stderr, "Duplicate entry in file %s, line %d: %s\nSecond entry ignored\n", node->file, lineno, node->entry);
111+			return;
112+		}
113+		if (!cur_node->been_warned)
114+			if (!wflag)
115+				fprintf(stderr, "Duplicate entry in files %s and %s: %s (Warning only)\n", node->file, cur_node->file, node->entry);
116+		cur_node->been_warned = YES;
117+	}
118+	else if (dif < 0) {
119+		if (cur_node->left)
120+			add_node(node, cur_node->left);
121+		else
122+			cur_node->left = node;
123+	} else {
124+		if (cur_node->right)
125+			add_node(node, cur_node->right);
126+		else
127+			cur_node->right = node;
128+	}
129+}
130+
131+static void
132+free_tree(NODE *node)
133+{
134+	NODE *nnode;
135+
136+	for (; node != NULL; node = nnode) {
137+		nnode = node->left;
138+		if (node->right) {
139+			if (nnode == NULL)
140+				nnode = node->right;
141+			else
142+				free_tree(node->right);
143+		}
144+		free(node);
145+	}
146+}
+160, -0
  1@@ -0,0 +1,160 @@
  2+/*	$NetBSD: yacc.c,v 1.12 2009/07/13 19:05:40 roy Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1987, 1993, 1994
  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+#endif
 36+
 37+#include <sys/cdefs.h>
 38+#if defined(__RCSID) && !defined(lint)
 39+#if 0
 40+static char sccsid[] = "@(#)yacc.c	8.3 (Berkeley) 4/2/94";
 41+#else
 42+__RCSID("$NetBSD: yacc.c,v 1.12 2009/07/13 19:05:40 roy Exp $");
 43+#endif
 44+#endif /* not lint */
 45+
 46+#include <ctype.h>
 47+#include <limits.h>
 48+#include <stdio.h>
 49+#include <string.h>
 50+
 51+#include "ctags.h"
 52+
 53+/*
 54+ * y_entries:
 55+ *	find the yacc tags and put them in.
 56+ */
 57+void
 58+y_entries(void)
 59+{
 60+	int	c;
 61+	char	*sp;
 62+	bool	in_rule;
 63+	char	tok[MAXTOKEN];
 64+
 65+	in_rule = NO;
 66+
 67+	while (GETC(!=, EOF))
 68+		switch (c) {
 69+		case '\n':
 70+			SETLINE;
 71+			/* FALLTHROUGH */
 72+		case ' ':
 73+		case '\f':
 74+		case '\r':
 75+		case '\t':
 76+			break;
 77+		case '{':
 78+			if (skip_key('}'))
 79+				in_rule = NO;
 80+			break;
 81+		case '\'':
 82+		case '"':
 83+			if (skip_key(c))
 84+				in_rule = NO;
 85+			break;
 86+		case '%':
 87+			if (GETC(==, '%'))
 88+				return;
 89+			(void)ungetc(c, inf);
 90+			break;
 91+		case '/':
 92+			if (GETC(==, '*'))
 93+				skip_comment('*');
 94+			else
 95+				(void)ungetc(c, inf);
 96+			break;
 97+		case '|':
 98+		case ';':
 99+			in_rule = NO;
100+			break;
101+		default:
102+			if (in_rule || (!isalpha(c) && c != '.' && c != '_'))
103+				break;
104+			sp = tok;
105+			*sp++ = c;
106+			while (GETC(!=, EOF) && (intoken(c) || c == '.'))
107+				*sp++ = c;
108+			*sp = EOS;
109+			get_line();		/* may change before ':' */
110+			if (c == EOF)
111+				return;
112+			while (iswhite(c)) {
113+				if (c == '\n')
114+					SETLINE;
115+				if (GETC(==, EOF))
116+					return;
117+			}
118+			if (c == ':') {
119+				pfnote(tok, lineno);
120+				in_rule = YES;
121+			}
122+			else
123+				(void)ungetc(c, inf);
124+		}
125+}
126+
127+/*
128+ * toss_yysec --
129+ *	throw away lines up to the next "\n%%\n"
130+ */
131+void
132+toss_yysec(void)
133+{
134+	int	c;			/* read character */
135+	int	state;
136+
137+	/*
138+	 * state == 0 : waiting
139+	 * state == 1 : received a newline
140+	 * state == 2 : received first %
141+	 * state == 3 : received second %
142+	 */
143+	lineftell = ftell(inf);
144+	for (state = 0; GETC(!=, EOF);)
145+		switch (c) {
146+		case '\n':
147+			++lineno;
148+			lineftell = ftell(inf);
149+			if (state == 3)		/* done! */
150+				return;
151+			state = 1;		/* start over */
152+			break;
153+		case '%':
154+			if (state)		/* if 1 or 2 */
155+				++state;	/* goto 3 */
156+			break;
157+		default:
158+			state = 0;		/* reset */
159+			break;
160+		}
161+}
+30, -0
 1@@ -0,0 +1,30 @@
 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+MAN = finger.1
 9+
10+CC = cc
11+CFLAGS = -O2
12+CPPFLAGS = -I. -I../compat -include ../compat/netcompat.h \
13+	-DSUPPORT_UTMP -DINET6
14+DESTDIR =
15+BINDIR = /usr/local/bin
16+MANDIR = /usr/local/share/man
17+
18+all: $(PROG)
19+
20+$(PROG): $(SRCS)
21+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
22+
23+install: $(PROG)
24+	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
25+	cp $(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
26+	chmod 755 $(DESTDIR)$(BINDIR)/$(PROG)
27+	cp $(MAN) $(DESTDIR)$(MANDIR)/man1/$(MAN)
28+	chmod 644 $(DESTDIR)$(MANDIR)/man1/$(MAN)
29+
30+clean:
31+	rm -f $(PROG) *.o
+57, -0
 1@@ -0,0 +1,57 @@
 2+/*	$NetBSD: extern.h,v 1.10 2006/01/04 01:17:54 perry 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.2 (Berkeley) 4/28/95
33+ */
34+
35+extern time_t now;			/* Current time. */
36+extern char tbuf[1024];			/* Temp buffer for anybody. */
37+extern int entries;			/* Number of people. */
38+extern DB *db;				/* Database. */
39+extern int lflag;
40+extern int oflag;
41+extern int gflag;
42+extern int eightflag;
43+extern int pplan;
44+
45+void	 enter_lastlog(PERSON *);
46+PERSON	*enter_person(struct passwd *);
47+void	 enter_where(struct utmpentry *, PERSON *);
48+void	 expandusername(const char *, const char *, char *, int);
49+PERSON	*find_person(char *);
50+int	 hash(char *);
51+void	 lflag_print(void);
52+int	 match(struct passwd *, char *);
53+void	 netfinger(char *);
54+PERSON	*palloc(void);
55+char	*prphone(char *);
56+int	 psort(const void *, const void *);
57+void	 sflag_print(void);
58+PERSON **sort(void);
+229, -0
  1@@ -0,0 +1,229 @@
  2+.\"	$NetBSD: finger.1,v 1.22 2020/05/07 13:40:20 kim Exp $
  3+.\"
  4+.\" Copyright (c) 1989, 1990, 1993, 1994
  5+.\"	The Regents of the University of California.  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. Neither the name of the University nor the names of its contributors
 16+.\"    may be used to endorse or promote products derived from this software
 17+.\"    without specific prior written permission.
 18+.\"
 19+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 20+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 21+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 22+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 23+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 24+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 25+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 26+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 27+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 28+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 29+.\" SUCH DAMAGE.
 30+.\"
 31+.\"	from: @(#)finger.1	8.3 (Berkeley) 5/5/94
 32+.\"
 33+.Dd May 7, 2020
 34+.Dt FINGER 1
 35+.Os
 36+.Sh NAME
 37+.Nm finger
 38+.Nd user information lookup program
 39+.Sh SYNOPSIS
 40+.Nm
 41+.Op Fl 8ghlmops
 42+.Op Ar user ...
 43+.Op Ar user@host ...
 44+.Sh DESCRIPTION
 45+The
 46+.Nm
 47+utility displays information about the system users.
 48+.Pp
 49+Options are:
 50+.Bl -tag -width flag
 51+.It Fl 8
 52+Pass through 8-bit data.
 53+This option is intended for enabling 8-bit
 54+data output in the
 55+.Xr fingerd 8
 56+service.
 57+Using this from the command line is
 58+.Em dangerous ,
 59+as the output data may include control characters for your terminal.
 60+.It Fl g
 61+This option restricts the gecos output to only the users' real names.
 62+.It Fl h
 63+When used in conjunction with the
 64+.Fl s
 65+option, the name of the remote host is displayed instead of the office
 66+location and office phone.
 67+.It Fl l
 68+Produces a multi-line format displaying all of the information
 69+described for the
 70+.Fl s
 71+option as well as the user's home directory, home phone number, login
 72+shell, mail status, and the contents of the files
 73+.Dq Pa .forward ,
 74+.Dq Pa .plan
 75+and
 76+.Dq Pa .project
 77+from the user's home directory.
 78+.Pp
 79+If idle time is at least a minute and less than a day, it is
 80+presented in the form
 81+.Dq hh:mm .
 82+Idle times greater than a day are presented as
 83+.Dq d day[s]hh:mm .
 84+.Pp
 85+Phone numbers specified as eleven digits are printed as
 86+.Dq +N-NNN-NNN-NNNN .
 87+Numbers specified as ten or seven digits are printed as the appropriate
 88+subset of that string.
 89+Numbers specified as five digits are printed as
 90+.Dq xN-NNNN .
 91+Numbers specified as four digits are printed as
 92+.Dq xNNNN .
 93+.Pp
 94+If write permission is denied to the device, the phrase
 95+.Dq (messages off)
 96+is appended to the line containing the device name.
 97+One entry per user is displayed with the
 98+.Fl l
 99+option; if a user is logged on multiple times, terminal information
100+is repeated once per login.
101+.Pp
102+Mail status is shown as
103+.Dq \&No Mail.
104+if there is no mail at all, ``Mail last read DDD MMM ## HH:MM YYYY (TZ)''
105+if the person has looked at their
106+mailbox since new mail arriving, or
107+.Dq New mail received ... ,
108+.Dq Unread since \&...
109+if they have new mail.
110+.It Fl m
111+Prevent matching of
112+.Ar user
113+names.
114+.Ar User
115+is usually a login name; however, matching will also be done on the
116+users' real names, unless the
117+.Fl m
118+option is supplied.
119+All name matching performed by
120+.Nm
121+is case insensitive.
122+.It Fl o
123+When used in conjunction with the
124+.Fl s
125+option, the office location and office phone information is displayed.
126+This is the default.
127+.It Fl p
128+Prevents
129+the
130+.Fl l
131+option of
132+.Nm
133+from displaying the contents of the
134+.Dq Pa .forward ,
135+.Dq Pa .plan
136+and
137+.Dq Pa .project
138+files.
139+.It Fl s
140+.Nm
141+displays the user's login name, real name, terminal name and write
142+status (as a
143+.Dq *
144+after the terminal name if write permission is denied), idle time,
145+login time, and either office location and office phone number,
146+or the remote host.
147+If
148+.Fl h
149+is given, the remote is printed.
150+If
151+.Fl o
152+is given, the office location and phone number is printed instead
153+(the default).
154+.Pp
155+Idle time is in minutes if it is a single integer, hours and minutes
156+if a
157+.Dq \&:
158+is present, or days if a
159+.Dq d
160+is present.
161+Login time is displayed as the dayname if less than six days,
162+else month, day, hours and minutes, unless
163+more than six months ago, in which case the year is displayed rather
164+than the hours and minutes.
165+.Pp
166+Unknown devices as well as nonexistent idle and login times are
167+displayed as single asterisks.
168+.El
169+.Pp
170+If no options are specified,
171+.Nm
172+defaults to the
173+.Fl l
174+style output if operands are provided, otherwise to the
175+.Fl s
176+style.
177+Note that some fields may be missing, in either format, if information
178+is not available for them.
179+.Pp
180+If no arguments are specified,
181+.Nm
182+will print an entry for each user currently logged into the system.
183+.Pp
184+The
185+.Nm
186+utility may be used to look up users on a remote machine.
187+The format is to specify a
188+.Ar user
189+as
190+.Dq Li user@host ,
191+or
192+.Dq Li @host ,
193+where the default output
194+format for the former is the
195+.Fl l
196+style, and the default output format for the latter is the
197+.Fl s
198+style.
199+The
200+.Fl l
201+option is the only option that may be passed to a remote machine.
202+.Sh FILES
203+.Bl -tag -width /var/log/lastlogx -compact
204+.It Pa /var/run/utmpx
205+The
206+.Nm utmpx
207+file.
208+.It Pa /var/log/lastlogx
209+The
210+.Nm lastlogx
211+file.
212+.It Pa /var/run/utmp
213+The
214+.Nm utmp
215+file.
216+.It Pa /var/log/lastlog
217+The
218+.Nm lastlog
219+file.
220+.El
221+.Sh SEE ALSO
222+.Xr chpass 1 ,
223+.Xr w 1 ,
224+.Xr who 1 ,
225+.Xr fingerd 8
226+.Sh HISTORY
227+The
228+.Nm
229+command appeared in
230+.Bx 2.0 .
+302, -0
  1@@ -0,0 +1,302 @@
  2+/*	$NetBSD: finger.c,v 1.31 2021/10/30 09:12:09 nia Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1989, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to Berkeley by
  9+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
 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. Neither the name of the University nor the names of its contributors
 20+ *    may be used to endorse or promote products derived from this software
 21+ *    without specific prior written permission.
 22+ *
 23+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 24+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 25+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 26+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 27+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 29+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 30+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 31+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 33+ * SUCH DAMAGE.
 34+ */
 35+
 36+/*
 37+ * Luke Mewburn <lukem@NetBSD.org> added the following on 961121:
 38+ *    - mail status ("No Mail", "Mail read:...", or "New Mail ...,
 39+ *	Unread since ...".)
 40+ *    - 4 digit phone extensions (3210 is printed as x3210.)
 41+ *    - host/office toggling in short format with -h & -o.
 42+ *    - short day names (`Tue' printed instead of `Jun 21' if the
 43+ *	login time is < 6 days.
 44+ */
 45+
 46+#include <sys/cdefs.h>
 47+#ifndef lint
 48+__COPYRIGHT("@(#) Copyright (c) 1989, 1993\
 49+ The Regents of the University of California.  All rights reserved.");
 50+#endif /* not lint */
 51+
 52+#ifndef lint
 53+#if 0
 54+static char sccsid[] = "@(#)finger.c	8.5 (Berkeley) 5/4/95";
 55+#else
 56+__RCSID("$NetBSD: finger.c,v 1.31 2021/10/30 09:12:09 nia Exp $");
 57+#endif
 58+#endif /* not lint */
 59+
 60+/*
 61+ * Finger prints out information about users.  It is not portable since
 62+ * certain fields (e.g. the full user name, office, and phone numbers) are
 63+ * extracted from the gecos field of the passwd file which other UNIXes
 64+ * may not have or may use for other things.
 65+ *
 66+ * There are currently two output formats; the short format is one line
 67+ * per user and displays login name, tty, login time, real name, idle time,
 68+ * and either remote host information (default) or office location/phone
 69+ * number, depending on if -h or -o is used respectively.
 70+ * The long format gives the same information (in a more legible format) as
 71+ * well as home directory, shell, mail info, and .plan/.project files.
 72+ */
 73+
 74+#include <sys/param.h>
 75+
 76+#include <db.h>
 77+#include <err.h>
 78+#include <errno.h>
 79+#include <fcntl.h>
 80+#include <pwd.h>
 81+#include <stdio.h>
 82+#include <stdlib.h>
 83+#include <string.h>
 84+#include <time.h>
 85+#include <unistd.h>
 86+
 87+#include <locale.h>
 88+#include <langinfo.h>
 89+
 90+#include "utmpentry.h"
 91+
 92+#include "finger.h"
 93+#include "extern.h"
 94+
 95+DB *db;
 96+time_t now;
 97+int entries, gflag, lflag, mflag, oflag, sflag, eightflag, pplan;
 98+char tbuf[1024];
 99+struct utmpentry *ehead;
100+
101+static void loginlist(void);
102+static void userlist(int, char **);
103+
104+int
105+main(int argc, char **argv)
106+{
107+	int ch;
108+
109+	/* Allow user's locale settings to affect character output. */
110+	setlocale(LC_CTYPE, "");
111+
112+	/*
113+	 * Reset back to the C locale, unless we are using a known
114+	 * single-byte 8-bit locale.
115+	 */
116+	if (strncmp(nl_langinfo(CODESET), "ISO8859-", 8))
117+		setlocale(LC_CTYPE, "C");
118+
119+	oflag = 1;		/* default to old "office" behavior */
120+
121+	while ((ch = getopt(argc, argv, "lmpshog8")) != -1)
122+		switch(ch) {
123+		case 'l':
124+			lflag = 1;		/* long format */
125+			break;
126+		case 'm':
127+			mflag = 1;		/* force exact match of names */
128+			break;
129+		case 'p':
130+			pplan = 1;		/* don't show .plan/.project */
131+			break;
132+		case 's':
133+			sflag = 1;		/* short format */
134+			break;
135+		case 'h':
136+			oflag = 0;		/* remote host info */
137+			break;
138+		case 'o':
139+			oflag = 1;		/* office info */
140+			break;
141+		case 'g':
142+			gflag = 1;		/* no gecos info, besides name */
143+			break;
144+		case '8':
145+			eightflag = 1;		/* 8-bit pass-through */
146+			break;
147+		case '?':
148+		default:
149+			(void)fprintf(stderr,
150+			    "usage: finger [-lmpshog8] [login ...]\n");
151+			exit(1);
152+		}
153+	argc -= optind;
154+	argv += optind;
155+
156+	(void)time(&now);
157+	setpassent(1);
158+	entries = getutentries(NULL, &ehead);
159+	if (argc == 0) {
160+		/*
161+		 * Assign explicit "small" format if no names given and -l
162+		 * not selected.  Force the -s BEFORE we get names so proper
163+		 * screening will be done.
164+		 */
165+		if (!lflag)
166+			sflag = 1;	/* if -l not explicit, force -s */
167+		loginlist();
168+		if (entries == 0)
169+			(void)printf("No one logged on.\n");
170+	} else {
171+		userlist(argc, argv);
172+		/*
173+		 * Assign explicit "large" format if names given and -s not
174+		 * explicitly stated.  Force the -l AFTER we get names so any
175+		 * remote finger attempts specified won't be mishandled.
176+		 */
177+		if (!sflag)
178+			lflag = 1;	/* if -s not explicit, force -l */
179+	}
180+	if (entries) {
181+		if (lflag)
182+			lflag_print();
183+		else
184+			sflag_print();
185+	}
186+	return (0);
187+}
188+
189+static void
190+loginlist(void)
191+{
192+	PERSON *pn;
193+	DBT data, key;
194+	struct passwd *pw;
195+	int r, seqflag;
196+	struct utmpentry *ep;
197+
198+	for (ep = ehead; ep; ep = ep->next) {
199+		if ((pn = find_person(ep->name)) == NULL) {
200+			if ((pw = getpwnam(ep->name)) == NULL)
201+				continue;
202+			pn = enter_person(pw);
203+		}
204+		enter_where(ep, pn);
205+	}
206+	if (db && lflag)
207+		for (seqflag = R_FIRST;; seqflag = R_NEXT) {
208+			PERSON *tmp;
209+
210+			r = (*db->seq)(db, &key, &data, seqflag);
211+			if (r == -1)
212+				err(1, "db seq");
213+			if (r == 1)
214+				break;
215+			memmove(&tmp, data.data, sizeof tmp);
216+			enter_lastlog(tmp);
217+		}
218+}
219+
220+static void
221+userlist(int argc, char **argv)
222+{
223+	PERSON *pn;
224+	DBT data, key;
225+	struct passwd *pw;
226+	int r, seqflag, *used, *ip;
227+	char **ap, **nargv, **np, **p;
228+	struct utmpentry *ep;
229+
230+	nargv = NULL;
231+	if (reallocarr(&nargv, argc + 1, sizeof(char *)) != 0)
232+		err(1, NULL);
233+	if ((used = calloc(argc, sizeof(int))) == NULL)
234+		err(1, NULL);
235+
236+	/* Pull out all network requests. */
237+	for (ap = p = argv, np = nargv; *p; ++p)
238+		if (strchr(*p, '@'))
239+			*np++ = *p;
240+		else
241+			*ap++ = *p;
242+
243+	*np++ = NULL;
244+	*ap++ = NULL;
245+
246+	if (!*argv)
247+		goto net;
248+
249+	/*
250+	 * Traverse the list of possible login names and check the login name
251+	 * and real name against the name specified by the user.
252+	 */
253+	if (mflag) {
254+		for (p = argv; *p; ++p)
255+			if ((pw = getpwnam(*p)) != NULL)
256+				enter_person(pw);
257+			else
258+				warnx("%s: no such user", *p);
259+	} else {
260+		while ((pw = getpwent()) != NULL)
261+			for (p = argv, ip = used; *p; ++p, ++ip)
262+				if (match(pw, *p)) {
263+					enter_person(pw);
264+					*ip = 1;
265+				}
266+		for (p = argv, ip = used; *p; ++p, ++ip)
267+			if (!*ip)
268+				warnx("%s: no such user", *p);
269+	}
270+
271+	/* Handle network requests. */
272+net:
273+	for (p = nargv; *p;)
274+		netfinger(*p++);
275+
276+	if (entries == 0)
277+		goto done;
278+
279+	/*
280+	 * Scan thru the list of users currently logged in, saving
281+	 * appropriate data whenever a match occurs.
282+	 */
283+	for (ep = ehead; ep; ep = ep->next) {
284+		if ((pn = find_person(ep->name)) == NULL)
285+			continue;
286+		enter_where(ep, pn);
287+	}
288+	if (db != NULL)
289+		for (seqflag = R_FIRST;; seqflag = R_NEXT) {
290+			PERSON *tmp;
291+
292+			r = (*db->seq)(db, &key, &data, seqflag);
293+			if (r == -1)
294+				err(1, "db seq");
295+			if (r == 1)
296+				break;
297+			memmove(&tmp, data.data, sizeof tmp);
298+			enter_lastlog(tmp);
299+		}
300+done:
301+	free(nargv);
302+	free(used);
303+}
+67, -0
 1@@ -0,0 +1,67 @@
 2+/*	$NetBSD: finger.h,v 1.10 2007/05/05 16:55:17 christos Exp $	*/
 3+
 4+/*
 5+ * Copyright (c) 1989, 1993
 6+ *	The Regents of the University of California.  All rights reserved.
 7+ *
 8+ * This code is derived from software contributed to Berkeley by
 9+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
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. Neither the name of the University nor the names of its contributors
20+ *    may be used to endorse or promote products derived from this software
21+ *    without specific prior written permission.
22+ *
23+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33+ * SUCH DAMAGE.
34+ *
35+ *	from: @(#)finger.h	8.1 (Berkeley) 6/6/93
36+ */
37+
38+
39+/*
40+ * All unique persons are linked in a list headed by "head" and linkd
41+ * by the "next" field, as well as kept in a hash table.
42+ */
43+
44+typedef struct person {
45+	uid_t uid;			/* user id */
46+	char *dir;			/* user's home directory */
47+	char *homephone;		/* pointer to home phone no. */
48+	char *name;			/* login name */
49+	char *office;			/* pointer to office name */
50+	char *officephone;		/* pointer to office phone no. */
51+	char *realname;			/* pointer to full name */
52+	char *shell;			/* user's shell */
53+	time_t mailread;		/* last time mail was read */
54+	time_t mailrecv;		/* last time mail was read */
55+	struct where *whead, *wtail;	/* list of where user is or has been */
56+} PERSON;
57+
58+enum status { LASTLOG, LOGGEDIN };
59+
60+typedef struct where {
61+	struct where *next;		/* next place user is or has been */
62+	enum status info;		/* type/status of request */
63+	short writable;			/* tty is writable */
64+	time_t loginat;			/* time of (last) login */
65+	time_t idletime;		/* how long idle (if logged in) */
66+	char *tty;			/* tty line */
67+	char *host;			/* remote host name */
68+} WHERE;
+389, -0
  1@@ -0,0 +1,389 @@
  2+/*	$NetBSD: lprint.c,v 1.23 2013/01/18 22:10:31 christos Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1989, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to Berkeley by
  9+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
 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. Neither the name of the University nor the names of its contributors
 20+ *    may be used to endorse or promote products derived from this software
 21+ *    without specific prior written permission.
 22+ *
 23+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 24+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 25+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 26+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 27+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 29+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 30+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 31+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 33+ * SUCH DAMAGE.
 34+ */
 35+
 36+#include <sys/cdefs.h>
 37+#ifndef lint
 38+#if 0
 39+static char sccsid[] = "@(#)lprint.c	8.3 (Berkeley) 4/28/95";
 40+#else
 41+__RCSID( "$NetBSD: lprint.c,v 1.23 2013/01/18 22:10:31 christos Exp $");
 42+#endif
 43+#endif /* not lint */
 44+
 45+#include <sys/types.h>
 46+#include <sys/stat.h>
 47+#include <sys/time.h>
 48+#include <fcntl.h>
 49+#include <tzfile.h>
 50+#include <db.h>
 51+#include <err.h>
 52+#include <pwd.h>
 53+#include <errno.h>
 54+#include <unistd.h>
 55+#include <stdio.h>
 56+#include <string.h>
 57+#include <time.h>
 58+#include <ctype.h>
 59+#include <paths.h>
 60+#include <vis.h>
 61+
 62+#include "utmpentry.h"
 63+#include "finger.h"
 64+#include "extern.h"
 65+
 66+#define	LINE_LEN	80
 67+#define	TAB_LEN		8		/* 8 spaces between tabs */
 68+#define	_PATH_FORWARD	".forward"
 69+#define	_PATH_PLAN	".plan"
 70+#define	_PATH_PROJECT	".project"
 71+
 72+static int	demi_print(const char *, int);
 73+static void	lprint(PERSON *);
 74+static int	show_text(const char *, const char *, const char *);
 75+static void	vputc(int);
 76+
 77+#ifdef __SVR4
 78+#define TIMEZONE(a)	tzname[0]
 79+#else
 80+#define TIMEZONE(a)	(a)->tm_zone
 81+#endif
 82+
 83+void
 84+lflag_print(void)
 85+{
 86+	PERSON *pn;
 87+	int sflag, r;
 88+	PERSON *tmp;
 89+	DBT data, key;
 90+
 91+	if (db == NULL)
 92+		return;
 93+
 94+	for (sflag = R_FIRST;; sflag = R_NEXT) {
 95+		r = (*db->seq)(db, &key, &data, sflag);
 96+		if (r == -1)
 97+			err(1, "db seq");
 98+		if (r == 1)
 99+			break;
100+		memmove(&tmp, data.data, sizeof tmp);
101+		pn = tmp;
102+		if (sflag != R_FIRST)
103+			putchar('\n');
104+		lprint(pn);
105+		if (!pplan) {
106+			(void)show_text(pn->dir,
107+			    _PATH_FORWARD, "Mail forwarded to");
108+			(void)show_text(pn->dir, _PATH_PROJECT, "Project");
109+			if (!show_text(pn->dir, _PATH_PLAN, "Plan"))
110+				(void)printf("No Plan.\n");
111+		}
112+	}
113+}
114+
115+static size_t
116+visify(char *buf, size_t blen, const char *str)
117+{
118+	int len = strnvisx(buf, blen, str, strlen(str), VIS_WHITE|VIS_CSTYLE);
119+	if (len == -1) {
120+		buf[0] = '\0';
121+		return 0;
122+	}
123+	return len;
124+}
125+
126+static void
127+fmt_time(char *buf, size_t blen, time_t ti, time_t n)
128+{
129+	struct tm *tp = localtime(&ti);
130+	if (tp != NULL) {
131+		char *t = asctime(tp);
132+		char *tzn = TIMEZONE(tp);
133+		if (n == (time_t)-1 ||
134+		    n - ti > SECSPERDAY * DAYSPERNYEAR / 2)
135+			snprintf(buf, blen, "%.16s %.4s (%s)", t, t + 20, tzn);
136+		else
137+			snprintf(buf, blen, "%.16s (%s)", t, tzn);
138+	} else
139+		snprintf(buf, blen, "[*bad time 0x%llx*]", (long long)ti);
140+}
141+
142+/*
143+ * idle time is tough; if have one, print a comma,
144+ * then spaces to pad out the device name, then the
145+ * idle time.  Follow with a comma if a remote login.
146+ */
147+static int
148+print_idle(time_t t, int maxlen, size_t hostlen, size_t ttylen) {
149+
150+	int cpr;
151+	struct tm *delta = gmtime(&t);
152+
153+	if (delta == NULL)
154+		return printf("Bad idle 0x%llx", (long long)t);
155+
156+	if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0)
157+		return 0;
158+
159+	cpr = printf("%-*s idle ", (int)(maxlen - ttylen + 1), ",");
160+	if (delta->tm_yday > 0) {
161+		cpr += printf("%d day%s ", delta->tm_yday,
162+		   delta->tm_yday == 1 ? "" : "s");
163+	}
164+	cpr += printf("%d:%02d", delta->tm_hour, delta->tm_min);
165+	if (hostlen) {
166+		putchar(',');
167+		++cpr;
168+	}
169+	return cpr;
170+}
171+
172+static void
173+lprint(PERSON *pn)
174+{
175+	WHERE *w;
176+	int cpr, len, maxlen;
177+	int oddfield;
178+	char timebuf[128], ttybuf[64], hostbuf[512];
179+	size_t ttylen, hostlen;
180+
181+	cpr = 0;
182+	/*
183+	 * long format --
184+	 *	login name
185+	 *	real name
186+	 *	home directory
187+	 *	shell
188+	 *	office, office phone, home phone if available
189+	 *	mail status
190+	 */
191+	(void)printf("Login: %-15s\t\t\tName: %s\nDirectory: %-25s",
192+	    pn->name, pn->realname, pn->dir);
193+	(void)printf("\tShell: %-s\n", *pn->shell ? pn->shell : _PATH_BSHELL);
194+
195+	if (gflag)
196+		goto no_gecos;
197+	/*
198+	 * try and print office, office phone, and home phone on one line;
199+	 * if that fails, do line filling so it looks nice.
200+	 */
201+#define	OFFICE_TAG		"Office"
202+#define	OFFICE_PHONE_TAG	"Office Phone"
203+	oddfield = 0;
204+	if (pn->office && pn->officephone &&
205+	    strlen(pn->office) + strlen(pn->officephone) +
206+	    sizeof(OFFICE_TAG) + 2 <= 5 * TAB_LEN) {
207+		(void)snprintf(timebuf, sizeof(timebuf), "%s: %s, %s",
208+		    OFFICE_TAG, pn->office, prphone(pn->officephone));
209+		oddfield = demi_print(timebuf, oddfield);
210+	} else {
211+		if (pn->office) {
212+			(void)snprintf(timebuf, sizeof(timebuf), "%s: %s",
213+			    OFFICE_TAG, pn->office);
214+			oddfield = demi_print(timebuf, oddfield);
215+		}
216+		if (pn->officephone) {
217+			(void)snprintf(timebuf, sizeof(timebuf), "%s: %s",
218+			    OFFICE_PHONE_TAG, prphone(pn->officephone));
219+			oddfield = demi_print(timebuf, oddfield);
220+		}
221+	}
222+	if (pn->homephone) {
223+		(void)snprintf(timebuf, sizeof(timebuf), "%s: %s", "Home Phone",
224+		    prphone(pn->homephone));
225+		oddfield = demi_print(timebuf, oddfield);
226+	}
227+	if (oddfield)
228+		putchar('\n');
229+
230+no_gecos:
231+	/*
232+	 * long format con't:
233+	 * if logged in
234+	 *	terminal
235+	 *	idle time
236+	 *	if messages allowed
237+	 *	where logged in from
238+	 * if not logged in
239+	 *	when last logged in
240+	 */
241+	/* find out longest device name for this user for formatting */
242+	for (w = pn->whead, maxlen = -1; w != NULL; w = w->next) {
243+		visify(ttybuf, sizeof(ttybuf), w->tty);
244+		if ((len = strlen(ttybuf)) > maxlen)
245+			maxlen = len;
246+	}
247+	/* find rest of entries for user */
248+	for (w = pn->whead; w != NULL; w = w->next) {
249+		ttylen = visify(ttybuf, sizeof(ttybuf), w->tty);
250+		hostlen = visify(hostbuf, sizeof(hostbuf), w->host);
251+		switch (w->info) {
252+		case LOGGEDIN:
253+			fmt_time(timebuf, sizeof(timebuf), w->loginat, -1);
254+			cpr = printf("On since %s on %s", timebuf, ttybuf);
255+
256+			cpr += print_idle(w->idletime, maxlen, hostlen,
257+			    ttylen);
258+
259+			if (!w->writable)
260+				cpr += printf(" (messages off)");
261+			break;
262+		case LASTLOG:
263+			if (w->loginat == 0) {
264+				(void)printf("Never logged in.");
265+				break;
266+			}
267+			fmt_time(timebuf, sizeof(timebuf), w->loginat, now);
268+			cpr = printf("Last login %s on %s", timebuf, ttybuf);
269+			break;
270+		}
271+		if (hostlen) {
272+			if (LINE_LEN < (cpr + 6 + hostlen))
273+				(void)printf("\n   ");
274+			(void)printf(" from %s", hostbuf);
275+		}
276+		putchar('\n');
277+	}
278+	if (pn->mailrecv == -1)
279+		printf("No Mail.\n");
280+	else if (pn->mailrecv > pn->mailread) {
281+		fmt_time(timebuf, sizeof(timebuf), pn->mailrecv, -1);
282+		printf("New mail received %s\n", timebuf);
283+		fmt_time(timebuf, sizeof(timebuf), pn->mailread, -1);
284+		printf("     Unread since %s\n", timebuf);
285+	} else {
286+		fmt_time(timebuf, sizeof(timebuf), pn->mailread, -1);
287+		printf("Mail last read %s\n", timebuf);
288+	}
289+}
290+
291+static int
292+demi_print(const char *str, int oddfield)
293+{
294+	static int lenlast;
295+	int lenthis, maxlen;
296+
297+	lenthis = strlen(str);
298+	if (oddfield) {
299+		/*
300+		 * We left off on an odd number of fields.  If we haven't
301+		 * crossed the midpoint of the screen, and we have room for
302+		 * the next field, print it on the same line; otherwise,
303+		 * print it on a new line.
304+		 *
305+		 * Note: we insist on having the right hand fields start
306+		 * no less than 5 tabs out.
307+		 */
308+		maxlen = 5 * TAB_LEN;
309+		if (maxlen < lenlast)
310+			maxlen = lenlast;
311+		if (((((maxlen / TAB_LEN) + 1) * TAB_LEN) +
312+		    lenthis) <= LINE_LEN) {
313+			while(lenlast < (4 * TAB_LEN)) {
314+				putchar('\t');
315+				lenlast += TAB_LEN;
316+			}
317+			(void)printf("\t%s\n", str);	/* force one tab */
318+		} else {
319+			(void)printf("\n%s", str);	/* go to next line */
320+			oddfield = !oddfield;	/* this'll be undone below */
321+		}
322+	} else
323+		(void)printf("%s", str);
324+	oddfield = !oddfield;			/* toggle odd/even marker */
325+	lenlast = lenthis;
326+	return(oddfield);
327+}
328+
329+static int
330+show_text(const char *directory, const char *file_name, const char *header)	
331+{
332+	struct stat sb;
333+	FILE *fp;
334+	int ch, cnt, lastc;
335+	char *p;
336+	int fd, nr;
337+
338+	lastc = 0;
339+	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", directory, file_name);
340+	if ((fd = open(tbuf, O_RDONLY)) < 0 || fstat(fd, &sb) ||
341+	    sb.st_size == 0)
342+		return(0);
343+
344+	/* If short enough, and no newlines, show it on a single line.*/
345+	if (sb.st_size <= (off_t)(LINE_LEN - strlen(header) - 5)) {
346+		nr = read(fd, tbuf, sizeof(tbuf));
347+		if (nr <= 0) {
348+			(void)close(fd);
349+			return(0);
350+		}
351+		for (p = tbuf, cnt = nr; cnt--; ++p)
352+			if (*p == '\n')
353+				break;
354+		if (cnt <= 1) {
355+			(void)printf("%s: ", header);
356+			for (p = tbuf, cnt = nr; cnt--; ++p)
357+				vputc(lastc = (unsigned char)*p);
358+			if (lastc != '\n')
359+				(void)putchar('\n');
360+			(void)close(fd);
361+			return(1);
362+		}
363+		else
364+			(void)lseek(fd, 0L, SEEK_SET);
365+	}
366+	if ((fp = fdopen(fd, "r")) == NULL)
367+		return(0);
368+	(void)printf("%s:\n", header);
369+	while ((ch = getc(fp)) != EOF)
370+		vputc(lastc = ch);
371+	if (lastc != '\n')
372+		(void)putchar('\n');
373+	(void)fclose(fp);
374+	return(1);
375+}
376+
377+static void
378+vputc(int ch)
379+{
380+	char visout[5], *s2;
381+
382+	if (eightflag || isprint(ch) || isspace(ch)) {
383+	    (void)putchar(ch);
384+	    return;
385+	}
386+	ch = toascii(ch);
387+	vis(visout, ch, VIS_SAFE|VIS_NOSLASH, 0);
388+	for (s2 = visout; *s2; s2++)
389+		(void)putchar(*s2);
390+}
+159, -0
  1@@ -0,0 +1,159 @@
  2+/*	$NetBSD: net.c,v 1.23 2009/04/12 06:18:54 lukem Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1989, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to Berkeley by
  9+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
 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. Neither the name of the University nor the names of its contributors
 20+ *    may be used to endorse or promote products derived from this software
 21+ *    without specific prior written permission.
 22+ *
 23+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 24+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 25+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 26+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 27+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 29+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 30+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 31+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 33+ * SUCH DAMAGE.
 34+ */
 35+
 36+#include <sys/cdefs.h>
 37+#ifndef lint
 38+#if 0
 39+static char sccsid[] = "@(#)net.c	8.4 (Berkeley) 4/28/95";
 40+#else
 41+__RCSID("$NetBSD: net.c,v 1.23 2009/04/12 06:18:54 lukem Exp $");
 42+#endif
 43+#endif /* not lint */
 44+
 45+#include <sys/types.h>
 46+#include <sys/socket.h>
 47+
 48+#include <netinet/in.h>
 49+
 50+#include <arpa/inet.h>
 51+
 52+#include <netdb.h>
 53+#include <time.h>
 54+#include <db.h>
 55+#include <unistd.h>
 56+#include <pwd.h>
 57+#include <stdio.h>
 58+#include <string.h>
 59+#include <ctype.h>
 60+#include <err.h>
 61+
 62+#include "utmpentry.h"
 63+
 64+#include "finger.h"
 65+#include "extern.h"
 66+
 67+void
 68+netfinger(char *name)
 69+{
 70+	FILE *fp;
 71+	int c, lastc;
 72+	int s;
 73+	char *host;
 74+	struct addrinfo hints, *res, *res0;
 75+	int error;
 76+	const char *emsg = NULL;
 77+
 78+	lastc = 0;
 79+	if (!(host = strrchr(name, '@')))
 80+		return;
 81+	*host++ = '\0';
 82+	memset(&hints, 0, sizeof(hints));
 83+	hints.ai_family = PF_UNSPEC;
 84+	hints.ai_socktype = SOCK_STREAM;
 85+	hints.ai_flags = AI_CANONNAME;
 86+	error = getaddrinfo(host, "finger", &hints, &res0);
 87+	if (error) {
 88+		warnx("%s: %s", gai_strerror(error), host);
 89+		return;
 90+	}
 91+
 92+	s = -1;
 93+	for (res = res0; res; res = res->ai_next) {
 94+		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
 95+		if (s < 0) {
 96+			emsg = "socket";
 97+			continue;
 98+		}
 99+
100+		if (connect(s, res->ai_addr, res->ai_addrlen)) {
101+			close(s);
102+			s = -1;
103+			emsg = "connect";
104+			continue;
105+		}
106+
107+		break;
108+	}
109+	if (s < 0) {
110+		if (emsg != NULL)
111+			warn("%s", emsg);
112+		return;
113+	}
114+
115+	/* have network connection; identify the host connected with */
116+	(void)printf("[%s]\n", res0->ai_canonname ? res0->ai_canonname : host);
117+
118+	/* -l flag for remote fingerd  */
119+	if (lflag)
120+		write(s, "/W ", 3);
121+	/* send the name followed by <CR><LF> */
122+	(void)write(s, name, strlen(name));
123+	(void)write(s, "\r\n", 2);
124+
125+	/*
126+	 * Read from the remote system; once we're connected, we assume some
127+	 * data.  If none arrives, we hang until the user interrupts.
128+	 *
129+	 * If we see a <CR> followed by a newline character, only output
130+	 * one newline.
131+	 *
132+	 * If a character isn't printable and it isn't a space, we strip the
133+	 * 8th bit and set the 7th bit.  Every ASCII character with bit 7 set
134+	 * is printable.
135+	 */
136+	if ((fp = fdopen(s, "r")) != NULL)
137+		while ((c = getc(fp)) != EOF) {
138+			if (c == '\r') {
139+				if (lastc == '\r')	/* ^M^M - skip dupes */
140+					continue;
141+				c = '\n';
142+				lastc = '\r';
143+			} else {
144+				if (!(eightflag || isprint(c) || isspace(c))) {
145+					c &= 0x7f;
146+					c |= 0x40;
147+				}
148+				if (lastc != '\r' || c != '\n')
149+					lastc = c;
150+				else {
151+					lastc = '\n';
152+					continue;
153+				}
154+			}
155+			putchar(c);
156+		}
157+	if (lastc != '\n')
158+		putchar('\n');
159+	(void)fclose(fp);
160+}
+173, -0
  1@@ -0,0 +1,173 @@
  2+/*	$NetBSD: sprint.c,v 1.18 2015/11/21 15:22:17 christos Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1989, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to Berkeley by
  9+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
 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. Neither the name of the University nor the names of its contributors
 20+ *    may be used to endorse or promote products derived from this software
 21+ *    without specific prior written permission.
 22+ *
 23+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 24+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 25+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 26+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 27+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 29+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 30+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 31+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 33+ * SUCH DAMAGE.
 34+ */
 35+
 36+#include <sys/cdefs.h>
 37+#ifndef lint
 38+#if 0
 39+static char sccsid[] = "@(#)sprint.c	8.3 (Berkeley) 4/28/95";
 40+#else
 41+__RCSID("$NetBSD: sprint.c,v 1.18 2015/11/21 15:22:17 christos Exp $");
 42+#endif
 43+#endif /* not lint */
 44+
 45+#include <sys/param.h>
 46+#include <sys/time.h>
 47+
 48+#include <time.h>
 49+#include <tzfile.h>
 50+#include <db.h>
 51+#include <err.h>
 52+#include <pwd.h>
 53+#include <errno.h>
 54+#include <stdio.h>
 55+#include <stdlib.h>
 56+#include <string.h>
 57+
 58+#include "utmpentry.h"
 59+
 60+#include "finger.h"
 61+#include "extern.h"
 62+
 63+static void	  stimeprint(WHERE *);
 64+
 65+void
 66+sflag_print(void)
 67+{
 68+	PERSON *pn;
 69+	WHERE *w;
 70+	int sflag, r;
 71+	char *p;
 72+	PERSON *tmp;
 73+	DBT data, key;
 74+
 75+	if (db == NULL)
 76+		return;
 77+
 78+	/*
 79+	 * short format --
 80+	 *	login name
 81+	 *	real name
 82+	 *	terminal name
 83+	 *	if terminal writable (add an '*' to the terminal name
 84+	 *		if not)
 85+	 *	if logged in show idle time and day logged in, else
 86+	 *		show last login date and time.  If > 6 months,
 87+	 *		show year instead of time.  If < 6 days,
 88+	 *		show day name instead of month & day.
 89+	 *	if -h given
 90+	 *		remote host
 91+	 *	else if -o given (overriding -h) (default)
 92+	 *		office location
 93+	 *		office phone
 94+	 */
 95+#define	MAXREALNAME	18
 96+	(void)printf("%-*s %-*s %s %s\n", (int)maxname, "Login", MAXREALNAME,
 97+	    "Name", " Tty      Idle  Login Time  ", (gflag) ? "" :
 98+	    (oflag) ? "Office     Office Phone" : "Where");
 99+
100+	for (sflag = R_FIRST;; sflag = R_NEXT) {
101+		r = (*db->seq)(db, &key, &data, sflag);
102+		if (r == -1)
103+			err(1, "db seq");
104+		if (r == 1)
105+			break;
106+		memmove(&tmp, data.data, sizeof tmp);
107+		pn = tmp;
108+
109+		for (w = pn->whead; w != NULL; w = w->next) {
110+			(void)printf("%-*.*s %-*.*s ", (int)maxname, 
111+			    (int)maxname,
112+			    pn->name, MAXREALNAME, MAXREALNAME,
113+			    pn->realname ? pn->realname : "");
114+			if (!w->loginat) {
115+				(void)printf("  *     *  No logins   ");
116+				goto office;
117+			}
118+			(void)putchar(w->info == LOGGEDIN && !w->writable ?
119+			    '*' : ' ');
120+			if (*w->tty)
121+				(void)printf("%-7.7s ", w->tty);
122+			else
123+				(void)printf("        ");
124+			if (w->info == LOGGEDIN) {
125+				stimeprint(w);
126+				(void)printf("  ");
127+			} else
128+				(void)printf("    *  ");
129+			p = ctime(&w->loginat);
130+			if (now - w->loginat < SECSPERDAY * (DAYSPERWEEK - 1))
131+				(void)printf("%.3s %-8.5s", p, p + 11);
132+			else if (now - w->loginat
133+			      < SECSPERDAY * DAYSPERNYEAR / 2)
134+				(void)printf("%.6s %-5.5s", p + 4, p + 11);
135+			else
136+				(void)printf("%.6s %-5.4s", p + 4, p + 20);
137+office:
138+			if (gflag)
139+				goto no_gecos;
140+			putchar(' ');
141+			if (oflag) {
142+				if (pn->office)
143+					(void)printf("%-10.10s", pn->office);
144+				else if (pn->officephone)
145+					(void)printf("%-10.10s", " ");
146+				if (pn->officephone)
147+					(void)printf(" %-.15s",
148+						    prphone(pn->officephone));
149+			} else
150+				(void)printf("%.*s", MAXHOSTNAMELEN, w->host);
151+no_gecos:
152+			putchar('\n');
153+		}
154+	}
155+}
156+
157+static void
158+stimeprint(WHERE *w)
159+{
160+	struct tm *delta;
161+
162+	delta = gmtime(&w->idletime);
163+	if (!delta->tm_yday) {
164+		if (!delta->tm_hour) {
165+			if (!delta->tm_min)
166+				(void)printf("    -");
167+			else
168+				(void)printf("%5d", delta->tm_min);
169+		} else
170+			(void)printf("%2d:%02d",
171+			    delta->tm_hour, delta->tm_min);
172+	} else
173+		(void)printf("%4dd", delta->tm_yday);
174+}
+436, -0
  1@@ -0,0 +1,436 @@
  2+/*	$NetBSD: util.c,v 1.31 2022/05/24 20:50:20 andvar Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1989, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * This code is derived from software contributed to Berkeley by
  9+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
 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. Neither the name of the University nor the names of its contributors
 20+ *    may be used to endorse or promote products derived from this software
 21+ *    without specific prior written permission.
 22+ *
 23+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 24+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 25+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 26+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 27+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 29+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 30+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 31+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 32+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 33+ * SUCH DAMAGE.
 34+ */
 35+
 36+/*
 37+ * Portions Copyright (c) 1983, 1995, 1996 Eric P. Allman
 38+ *
 39+ * This code is derived from software contributed to Berkeley by
 40+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
 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+ * 3. All advertising materials mentioning features or use of this software
 51+ *    must display the following acknowledgement:
 52+ *	This product includes software developed by the University of
 53+ *	California, Berkeley and its contributors.
 54+ * 4. 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+#include <sys/cdefs.h>
 72+#ifndef lint
 73+#if 0
 74+static char sccsid[] = "@(#)util.c	8.3 (Berkeley) 4/28/95";
 75+#else
 76+__RCSID("$NetBSD: util.c,v 1.31 2022/05/24 20:50:20 andvar Exp $");
 77+#endif
 78+#endif /* not lint */
 79+
 80+#include <sys/param.h>
 81+#include <sys/stat.h>
 82+
 83+#include <db.h>
 84+#include <ctype.h>
 85+#include <err.h>
 86+#include <errno.h>
 87+#include <fcntl.h>
 88+#include <paths.h>
 89+#include <pwd.h>
 90+#include <stdio.h>
 91+#include <stdlib.h>
 92+#include <string.h>
 93+#include <unistd.h>
 94+
 95+#include "utmpentry.h"
 96+
 97+#include "finger.h"
 98+#include "extern.h"
 99+
100+static void	 find_idle_and_ttywrite(WHERE *);
101+static void	 userinfo(PERSON *, struct passwd *);
102+static WHERE	*walloc(PERSON *);
103+
104+int
105+match(struct passwd *pw, char *user)
106+{
107+	char *p;
108+	char *bp, name[1024];
109+
110+	if (!strcasecmp(pw->pw_name, user))
111+		return(1);
112+
113+	(void)strlcpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf));
114+
115+	/* Ampersands get replaced by the login name. */
116+	if (!(p = strsep(&bp, ",")))
117+		return(0);
118+
119+	expandusername(p, pw->pw_name, name, sizeof(name));
120+	bp = name;
121+	while ((p = strsep(&bp, "\t ")))
122+		if (!strcasecmp(p, user))
123+			return(1);
124+	return(0);
125+}
126+
127+/* inspired by usr.sbin/sendmail/util.c::buildfname */
128+void
129+expandusername(const char *gecos, const char *login, char *buf, int buflen)
130+{
131+	const char *p;
132+	char *bp;
133+
134+	/* why do we skip asterisks!?!? */
135+	if (*gecos == '*')
136+		gecos++;
137+	bp = buf;
138+
139+	/* copy gecos, interpolating & to be full name */
140+	for (p = gecos; *p != '\0'; p++) {
141+		if (bp >= &buf[buflen - 1]) {
142+			/* buffer overflow - just use login name */
143+			snprintf(buf, buflen, "%s", login);
144+			buf[buflen - 1] = '\0';
145+			return;
146+		}
147+		if (*p == '&') {
148+			/* interpolate full name */
149+			snprintf(bp, buflen - (bp - buf), "%s", login);
150+			*bp = toupper((unsigned char)*bp);
151+			bp += strlen(bp);
152+		}
153+		else
154+			*bp++ = *p;
155+	}
156+	*bp = '\0';
157+}
158+
159+void
160+enter_lastlog(PERSON *pn)
161+{
162+	WHERE *w;
163+	static int opened;
164+#ifdef SUPPORT_UTMPX
165+# define ll_time	ll_tv.tv_sec
166+# define UT_LINESIZE	_UTX_LINESIZE
167+# define UT_HOSTSIZE	_UTX_HOSTSIZE
168+	static DB *lldb = NULL;
169+	DBT key, data;
170+	struct lastlogx ll;
171+#else
172+	static int fd;
173+	struct lastlog ll;
174+#endif
175+	char doit = 0;
176+
177+	(void)memset(&ll, 0, sizeof(ll));
178+
179+	/* some systems may not maintain lastlog, don't report errors. */
180+	if (!opened) {
181+#ifdef SUPPORT_UTMPX
182+		lldb = dbopen(_PATH_LASTLOGX, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL);
183+#else
184+		fd = open(_PATH_LASTLOG, O_RDONLY, 0);
185+#endif
186+		opened = 1;
187+	}
188+#ifdef SUPPORT_UTMPX
189+	if (lldb != NULL) {
190+		key.data = &pn->uid;
191+		key.size = sizeof(pn->uid);
192+		if ((*lldb->get)(lldb, &key, &data, 0) == 0 &&
193+		    data.size == sizeof(ll))
194+			(void)memcpy(&ll, data.data, sizeof(ll));
195+	}
196+#else
197+	if (fd == -1 ||
198+	    lseek(fd, (off_t)pn->uid * sizeof(ll), SEEK_SET) !=
199+	    (off_t)pn->uid * (off_t)sizeof(ll) ||
200+	    read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) {
201+			/* as if never logged in */
202+			ll.ll_line[0] = ll.ll_host[0] = '\0';
203+			ll.ll_time = 0;
204+		}
205+#endif
206+	if ((w = pn->whead) == NULL)
207+		doit = 1;
208+	else if (ll.ll_time != 0) {
209+		/* if last login is earlier than some current login */
210+		for (; !doit && w != NULL; w = w->next)
211+			if (w->info == LOGGEDIN && w->loginat < ll.ll_time)
212+				doit = 1;
213+		/*
214+		 * and if it's not any of the current logins
215+		 * can't use time comparison because there may be a small
216+		 * discrepancy since login calls time() twice
217+		 */
218+		for (w = pn->whead; doit && w != NULL; w = w->next)
219+			if (w->info == LOGGEDIN &&
220+			    strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0)
221+				doit = 0;
222+	}
223+	if (doit) {
224+		w = walloc(pn);
225+		w->info = LASTLOG;
226+		if ((w->tty = malloc(UT_LINESIZE + 1)) == NULL)
227+			err(1, NULL);
228+		memcpy(w->tty, ll.ll_line, UT_LINESIZE);
229+		w->tty[UT_LINESIZE] = '\0';
230+		if ((w->host = malloc(UT_HOSTSIZE + 1)) == NULL)
231+			err(1, NULL);
232+		memcpy(w->host, ll.ll_host, UT_HOSTSIZE);
233+		w->host[UT_HOSTSIZE] = '\0';
234+		w->loginat = ll.ll_time;
235+	}
236+}
237+
238+void
239+enter_where(struct utmpentry *ep, PERSON *pn)
240+{
241+	WHERE *w = walloc(pn);
242+
243+	w->info = LOGGEDIN;
244+	w->tty = ep->line;
245+	w->host = ep->host;
246+	w->loginat = (time_t)ep->tv.tv_sec;
247+	find_idle_and_ttywrite(w);
248+}
249+
250+PERSON *
251+enter_person(struct passwd *pw)
252+{
253+	DBT data, key;
254+	PERSON *pn;
255+
256+	if (db == NULL &&
257+	    (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL)
258+		err(1, NULL);
259+
260+	key.data = (char *)pw->pw_name;
261+	key.size = strlen(pw->pw_name);
262+
263+	switch ((*db->get)(db, &key, &data, 0)) {
264+	case 0:
265+		memmove(&pn, data.data, sizeof pn);
266+		return (pn);
267+	default:
268+	case -1:
269+		err(1, "db get");
270+		/* NOTREACHED */
271+	case 1:
272+		++entries;
273+		pn = palloc();
274+		userinfo(pn, pw);
275+		pn->whead = NULL;
276+
277+		data.size = sizeof(PERSON *);
278+		data.data = &pn;
279+		if ((*db->put)(db, &key, &data, 0))
280+			err(1, "db put");
281+		return (pn);
282+	}
283+}
284+
285+PERSON *
286+find_person(char *name)
287+{
288+	DBT data, key;
289+	PERSON *p;
290+
291+	if (!db)
292+		return(NULL);
293+
294+	key.data = name;
295+	key.size = strlen(name);
296+
297+	if ((*db->get)(db, &key, &data, 0))
298+		return (NULL);
299+	memmove(&p, data.data, sizeof p);
300+	return (p);
301+}
302+
303+PERSON *
304+palloc(void)
305+{
306+	PERSON *p;
307+
308+	if ((p = malloc(sizeof(PERSON))) == NULL)
309+		err(1, NULL);
310+	return(p);
311+}
312+
313+static WHERE *
314+walloc(PERSON *pn)
315+{
316+	WHERE *w;
317+
318+	if ((w = malloc(sizeof(WHERE))) == NULL)
319+		err(1, NULL);
320+	if (pn->whead == NULL)
321+		pn->whead = pn->wtail = w;
322+	else {
323+		pn->wtail->next = w;
324+		pn->wtail = w;
325+	}
326+	w->next = NULL;
327+	return(w);
328+}
329+
330+char *
331+prphone(char *num)
332+{
333+	char *p;
334+	int len;
335+	static char pbuf[15];
336+
337+	/* don't touch anything if the user has their own formatting */
338+	for (p = num; *p; ++p)
339+		if (!isdigit((unsigned char)*p))
340+			return(num);
341+	len = p - num;
342+	p = pbuf;
343+	switch(len) {
344+	case 11:			/* +0-123-456-7890 */
345+		*p++ = '+';
346+		*p++ = *num++;
347+		*p++ = '-';
348+		/* FALLTHROUGH */
349+	case 10:			/* 012-345-6789 */
350+		*p++ = *num++;
351+		*p++ = *num++;
352+		*p++ = *num++;
353+		*p++ = '-';
354+		/* FALLTHROUGH */
355+	case 7:				/* 012-3456 */
356+		*p++ = *num++;
357+		*p++ = *num++;
358+		*p++ = *num++;
359+		break;
360+	case 5:				/* x0-1234 */
361+	case 4:				/* x1234 */
362+		*p++ = 'x';
363+		*p++ = *num++;
364+		break;
365+	default:
366+		return(num);
367+	}
368+	if (len != 4) {
369+		*p++ = '-';
370+		*p++ = *num++;
371+	}
372+	*p++ = *num++;
373+	*p++ = *num++;
374+	*p++ = *num++;
375+	*p = '\0';
376+	return(pbuf);
377+}
378+
379+static void
380+find_idle_and_ttywrite(WHERE *w)
381+{
382+	struct stat sb;
383+
384+	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty);
385+	if (stat(tbuf, &sb) < 0) {
386+		warn("%s", tbuf);
387+		w->idletime = 0;
388+		w->writable = 0;
389+		return;
390+	}
391+	w->idletime = now < sb.st_atime ? 0 : now - sb.st_atime;
392+
393+#define	TALKABLE	0220		/* tty is writable if 220 mode */
394+	w->writable = ((sb.st_mode & TALKABLE) == TALKABLE);
395+}
396+
397+static void
398+userinfo(PERSON *pn, struct passwd *pw)
399+{
400+	char *p;
401+	char *bp, name[1024];
402+	struct stat sb;
403+
404+	pn->realname = pn->office = pn->officephone = pn->homephone = NULL;
405+
406+	pn->uid = pw->pw_uid;
407+	pn->name = strdup(pw->pw_name);
408+	pn->dir = strdup(pw->pw_dir);
409+	pn->shell = strdup(pw->pw_shell);
410+
411+	(void)strlcpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf));
412+
413+	/* ampersands get replaced by the login name */
414+	if (!(p = strsep(&bp, ",")))
415+		return;
416+	expandusername(p, pw->pw_name, name, sizeof(name));
417+	pn->realname = strdup(name);
418+	pn->office = ((p = strsep(&bp, ",")) && *p) ?
419+	    strdup(p) : NULL;
420+	pn->officephone = ((p = strsep(&bp, ",")) && *p) ?
421+	    strdup(p) : NULL;
422+	pn->homephone = ((p = strsep(&bp, ",")) && *p) ?
423+	    strdup(p) : NULL;
424+	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR,
425+	    pw->pw_name);
426+	pn->mailrecv = -1;		/* -1 == not_valid */
427+	if (stat(tbuf, &sb) < 0) {
428+		if (errno != ENOENT) {
429+			(void)fprintf(stderr,
430+			    "finger: %s: %s\n", tbuf, strerror(errno));
431+			return;
432+		}
433+	} else if (sb.st_size != 0) {
434+		pn->mailrecv = sb.st_mtime;
435+		pn->mailread = sb.st_atime;
436+	}
437+}
+320, -0
  1@@ -0,0 +1,320 @@
  2+/*	$NetBSD: utmpentry.c,v 1.22 2021/02/26 02:45:43 christos Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 2002 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+#include <sys/cdefs.h>
 34+#ifndef lint
 35+__RCSID("$NetBSD: utmpentry.c,v 1.22 2021/02/26 02:45:43 christos Exp $");
 36+#endif
 37+
 38+#include <sys/stat.h>
 39+
 40+#include <time.h>
 41+#include <string.h>
 42+#include <err.h>
 43+#include <stdlib.h>
 44+
 45+#ifdef SUPPORT_UTMP
 46+#include <utmp.h>
 47+#endif
 48+#ifdef SUPPORT_UTMPX
 49+#include <utmpx.h>
 50+#endif
 51+
 52+#include "utmpentry.h"
 53+
 54+
 55+#define COMPILE_ASSERT(x) ((void)0)
 56+
 57+
 58+#ifdef SUPPORT_UTMP
 59+static void getentry(struct utmpentry *, struct utmp *);
 60+static struct timespec utmptime = {0, 0};
 61+#endif
 62+#ifdef SUPPORT_UTMPX
 63+static void getentryx(struct utmpentry *, struct utmpx *);
 64+static struct timespec utmpxtime = {0, 0};
 65+#endif
 66+#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
 67+static int setup(const char *);
 68+static void adjust_size(struct utmpentry *e);
 69+#endif
 70+
 71+size_t maxname = 8, maxline = 8, maxhost = 16;
 72+int etype = 1 << USER_PROCESS;
 73+static size_t numutmp = 0;
 74+static struct utmpentry *ehead;
 75+
 76+#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
 77+static void
 78+adjust_size(struct utmpentry *e)
 79+{
 80+	size_t max;
 81+
 82+	if ((max = strlen(e->name)) > maxname)
 83+		maxname = max;
 84+	if ((max = strlen(e->line)) > maxline)
 85+		maxline = max;
 86+	if ((max = strlen(e->host)) > maxhost)
 87+		maxhost = max;
 88+}
 89+
 90+static int
 91+setup(const char *fname)
 92+{
 93+	int what = 3;
 94+	struct stat st;
 95+	const char *sfname;
 96+
 97+	if (fname != NULL) {
 98+		size_t len = strlen(fname);
 99+		if (len == 0)
100+			errx(1, "Filename cannot be 0 length.");
101+		what = fname[len - 1] == 'x' ? 1 : 2;
102+		if (what == 1) {
103+#ifdef SUPPORT_UTMPX
104+			if (utmpxname(fname) == 0)
105+				warnx("Cannot set utmpx file to `%s'",
106+				    fname);
107+#else
108+			warnx("utmpx support not compiled in");
109+#endif
110+		} else {
111+#ifdef SUPPORT_UTMP
112+			if (utmpname(fname) == 0)
113+				warnx("Cannot set utmp file to `%s'",
114+				    fname);
115+#else
116+			warnx("utmp support not compiled in");
117+#endif
118+		}
119+	}
120+#ifdef SUPPORT_UTMPX
121+	if (what & 1) {
122+		sfname = fname ? fname : _PATH_UTMPX;
123+		if (stat(sfname, &st) == -1) {
124+			warn("Cannot stat `%s'", sfname);
125+			what &= ~1;
126+		} else {
127+			if (timespeccmp(&st.st_mtim, &utmpxtime, >))
128+				utmpxtime = st.st_mtim;
129+			else
130+				what &= ~1;
131+		}
132+	}
133+#endif
134+#ifdef SUPPORT_UTMP
135+	if (what & 2) {
136+		sfname = fname ? fname : _PATH_UTMP;
137+		if (stat(sfname, &st) == -1) {
138+			warn("Cannot stat `%s'", sfname);
139+			what &= ~2;
140+		} else {
141+			if (timespeccmp(&st.st_mtim, &utmptime, >))
142+				utmptime = st.st_mtim;
143+			else
144+				what &= ~2;
145+		}
146+	}
147+#endif
148+	return what;
149+}
150+#endif
151+
152+void
153+endutentries(void)
154+{
155+	struct utmpentry *ep;
156+
157+#ifdef SUPPORT_UTMP
158+	timespecclear(&utmptime);
159+#endif
160+#ifdef SUPPORT_UTMPX
161+	timespecclear(&utmpxtime);
162+#endif
163+	ep = ehead;
164+	while (ep) {
165+		struct utmpentry *sep = ep;
166+		ep = ep->next;
167+		free(sep);
168+	}
169+	ehead = NULL;
170+	numutmp = 0;
171+}
172+
173+size_t
174+getutentries(const char *fname, struct utmpentry **epp)
175+{
176+#ifdef SUPPORT_UTMPX
177+	struct utmpx *utx;
178+#endif
179+#ifdef SUPPORT_UTMP
180+	struct utmp *ut;
181+#endif
182+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
183+	struct utmpentry *ep;
184+	int what = setup(fname);
185+	struct utmpentry **nextp = &ehead;
186+	switch (what) {
187+	case 0:
188+		/* No updates */
189+		*epp = ehead;
190+		return numutmp;
191+	default:
192+		/* Need to re-scan */
193+		ehead = NULL;
194+		numutmp = 0;
195+	}
196+#endif
197+
198+#ifdef SUPPORT_UTMPX
199+	setutxent();
200+	while ((what & 1) && (utx = getutxent()) != NULL) {
201+		if (fname == NULL && ((1 << utx->ut_type) & etype) == 0)
202+			continue;
203+		if ((ep = calloc(1, sizeof(*ep))) == NULL) {
204+			warn(NULL);
205+			return 0;
206+		}
207+		getentryx(ep, utx);
208+		*nextp = ep;
209+		nextp = &(ep->next);
210+	}
211+#endif
212+
213+#ifdef SUPPORT_UTMP
214+	setutent();
215+	if ((etype & (1 << USER_PROCESS)) != 0) {
216+		while ((what & 2) && (ut = getutent()) != NULL) {
217+			if (fname == NULL && (*ut->ut_name == '\0' ||
218+			    *ut->ut_line == '\0'))
219+				continue;
220+			/* Don't process entries that we have utmpx for */
221+			for (ep = ehead; ep != NULL; ep = ep->next) {
222+				if (strncmp(ep->line, ut->ut_line,
223+				    sizeof(ut->ut_line)) == 0)
224+					break;
225+			}
226+			if (ep != NULL)
227+				continue;
228+			if ((ep = calloc(1, sizeof(*ep))) == NULL) {
229+				warn(NULL);
230+				return 0;
231+			}
232+			getentry(ep, ut);
233+			*nextp = ep;
234+			nextp = &(ep->next);
235+		}
236+	}
237+#endif
238+	numutmp = 0;
239+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
240+	if (ehead != NULL) {
241+		struct utmpentry *from = ehead, *save;
242+
243+		ehead = NULL;
244+		while (from != NULL) {
245+			for (nextp = &ehead;
246+			    (*nextp) && strcmp(from->line, (*nextp)->line) > 0;
247+			    nextp = &(*nextp)->next)
248+				continue;
249+			save = from;
250+			from = from->next;
251+			save->next = *nextp;
252+			*nextp = save;
253+			numutmp++;
254+		}
255+	}
256+	*epp = ehead;
257+	return numutmp;
258+#else
259+	*epp = NULL;
260+	return 0;
261+#endif
262+}
263+
264+#ifdef SUPPORT_UTMP
265+static void
266+getentry(struct utmpentry *e, struct utmp *up)
267+{
268+	COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
269+	COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
270+	COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
271+
272+	/*
273+	 * e has just been calloc'd. We don't need to clear it or
274+	 * append null-terminators, because its length is strictly
275+	 * greater than the source string. Use memcpy to read up->ut_*
276+	 * because they may not be terminated. For this reason we use
277+	 * the size of the source as the length argument.
278+	 */
279+	memcpy(e->name, up->ut_name, sizeof(up->ut_name));
280+	memcpy(e->line, up->ut_line, sizeof(up->ut_line));
281+	memcpy(e->host, up->ut_host, sizeof(up->ut_host));
282+
283+	e->tv.tv_sec = up->ut_time;
284+	e->tv.tv_usec = 0;
285+	e->pid = 0;
286+	e->term = 0;
287+	e->exit = 0;
288+	e->sess = 0;
289+	e->type = USER_PROCESS;
290+	adjust_size(e);
291+}
292+#endif
293+
294+#ifdef SUPPORT_UTMPX
295+static void
296+getentryx(struct utmpentry *e, struct utmpx *up)
297+{
298+	COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
299+	COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
300+	COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
301+
302+	/*
303+	 * e has just been calloc'd. We don't need to clear it or
304+	 * append null-terminators, because its length is strictly
305+	 * greater than the source string. Use memcpy to read up->ut_*
306+	 * because they may not be terminated. For this reason we use
307+	 * the size of the source as the length argument.
308+	 */
309+	memcpy(e->name, up->ut_name, sizeof(up->ut_name));
310+	memcpy(e->line, up->ut_line, sizeof(up->ut_line));
311+	memcpy(e->host, up->ut_host, sizeof(up->ut_host));
312+
313+	e->tv = up->ut_tv;
314+	e->pid = up->ut_pid;
315+	e->term = up->ut_exit.e_termination;
316+	e->exit = up->ut_exit.e_exit;
317+	e->sess = up->ut_session;
318+	e->type = up->ut_type;
319+	adjust_size(e);
320+}
321+#endif
+76, -0
 1@@ -0,0 +1,76 @@
 2+/*	$NetBSD: utmpentry.h,v 1.8 2015/11/21 15:01:43 christos Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 2002 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 defined(SUPPORT_UTMPX)
34+# include <utmpx.h>
35+# define WHO_NAME_LEN		_UTX_USERSIZE
36+# define WHO_LINE_LEN		_UTX_LINESIZE
37+# define WHO_HOST_LEN		_UTX_HOSTSIZE
38+#elif defined(SUPPORT_UTMP)
39+# include <utmp.h>
40+# define WHO_NAME_LEN		UT_NAMESIZE
41+# define WHO_LINE_LEN		UT_LINESIZE
42+# define WHO_HOST_LEN		UT_HOSTSIZE
43+#else
44+# error Either SUPPORT_UTMPX or SUPPORT_UTMP must be defined!
45+#endif
46+
47+
48+struct utmpentry {
49+	char name[WHO_NAME_LEN + 1];
50+	char line[WHO_LINE_LEN + 1];
51+	char host[WHO_HOST_LEN + 1];
52+	struct timeval tv;
53+	pid_t pid;
54+	uint16_t term;
55+	uint16_t exit;
56+	uint16_t sess;
57+	uint16_t type;
58+	struct utmpentry *next;
59+};
60+
61+extern size_t maxname, maxline, maxhost;
62+extern int etype;
63+
64+/*
65+ * getutentries provides a linked list of struct utmpentry and returns
66+ * the number of entries. The first argument, if not null, names an
67+ * alternate utmp(x) file to look in.
68+ *
69+ * The memory returned by getutentries belongs to getutentries. The
70+ * list returned (or elements of it) may be returned again later if
71+ * utmp hasn't changed in the meantime.
72+ *
73+ * endutentries clears and frees the cached data.
74+ */
75+
76+size_t getutentries(const char *, struct utmpentry **);
77+void endutentries(void);
+28, -0
 1@@ -0,0 +1,28 @@
 2+.POSIX:
 3+
 4+PROG = jot
 5+SRCS = jot.c ../compat/getprogname.c ../compat/strlcpy.c
 6+MAN = jot.1
 7+
 8+CC = cc
 9+CFLAGS = -O2
10+CPPFLAGS = -I../compat -include ../compat/netcompat.h
11+LDLIBS = -lm
12+DESTDIR =
13+BINDIR = /usr/local/bin
14+MANDIR = /usr/local/share/man
15+
16+all: $(PROG)
17+
18+$(PROG): $(SRCS)
19+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
20+
21+install: $(PROG)
22+	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
23+	cp $(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
24+	chmod 755 $(DESTDIR)$(BINDIR)/$(PROG)
25+	cp $(MAN) $(DESTDIR)$(MANDIR)/man1/$(MAN)
26+	chmod 644 $(DESTDIR)$(MANDIR)/man1/$(MAN)
27+
28+clean:
29+	rm -f $(PROG) *.o
+212, -0
  1@@ -0,0 +1,212 @@
  2+.\"	$NetBSD: jot.1,v 1.17 2020/04/25 11:11:33 simonb Exp $
  3+.\"
  4+.\" Copyright (c) 1993
  5+.\"	The Regents of the University of California.  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. Neither the name of the University nor the names of its contributors
 16+.\"    may be used to endorse or promote products derived from this software
 17+.\"    without specific prior written permission.
 18+.\"
 19+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 20+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 21+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 22+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 23+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 24+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 25+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 26+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 27+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 28+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 29+.\" SUCH DAMAGE.
 30+.\"
 31+.\"	@(#)jot.1	8.1 (Berkeley) 6/6/93
 32+.\"
 33+.Dd April 25, 2020
 34+.Dt JOT 1
 35+.Os
 36+.Sh NAME
 37+.Nm jot
 38+.Nd print sequential or random data
 39+.Sh SYNOPSIS
 40+.Nm
 41+.Op Fl cnr
 42+.Op Fl b Ar word
 43+.Op Fl p Ar precision
 44+.Op Fl s Ar string
 45+.Op Fl w Ar word
 46+.Oo Ar reps
 47+.Oo Ar begin
 48+.Oo Ar end
 49+.Op Ar s
 50+.Oc
 51+.Oc
 52+.Oc
 53+.Sh DESCRIPTION
 54+The
 55+.Nm jot
 56+utility is used to print out increasing, decreasing, random,
 57+or redundant data (usually numbers) one per line.
 58+The default is to produce sequential data.
 59+.Pp
 60+The following options are available:
 61+.Bl -tag -width indent
 62+.It Fl b Ar word
 63+Just print
 64+.Ar word
 65+repetitively.
 66+.It Fl c
 67+This is an abbreviation for
 68+.Fl w Ar %c .
 69+.It Fl n
 70+Do not print the final newline normally appended to the output.
 71+.It Fl p Ar precision
 72+Print only as many digits or characters of the data
 73+as indicated by the integer
 74+.Ar precision .
 75+In the absence of
 76+.Fl p ,
 77+the precision is the greater of the precisions of
 78+.Ar begin
 79+and
 80+.Ar end .
 81+The
 82+.Fl p
 83+option is overridden by whatever appears in a
 84+.Xr printf 3
 85+conversion following
 86+.Fl w .
 87+.It Fl r
 88+Generate random data.
 89+.It Fl s Ar string
 90+Print data separated by
 91+.Ar string .
 92+Normally, newlines separate data.
 93+.It Fl w Ar word
 94+Print
 95+.Ar word
 96+with the generated data appended to it.
 97+Octal, hexadecimal, exponential, ASCII, zero padded,
 98+and right-adjusted representations
 99+are possible by using the appropriate
100+.Xr printf 3
101+conversion specification inside
102+.Ar word ,
103+in which case the data are inserted rather than appended.
104+.El
105+.Pp
106+The last four arguments indicate, respectively,
107+the number of data, the lower bound, the upper bound,
108+and the step size or, for random data, the seed.
109+Any argument may be omitted, and
110+will be considered as such if given as
111+.Dq - .
112+Any three of these arguments determines the fourth.
113+If four are specified and the given and computed values of
114+.Ar reps
115+conflict, the lower value is used.
116+If fewer than three are specified, defaults are assigned
117+left to right, except for
118+.Ar s ,
119+which assumes its default unless both
120+.Ar begin
121+and
122+.Ar end
123+are given.
124+.Pp
125+When sequential data are requested the defaults for the four arguments
126+are 100 data, a lower bound of 1, an upper bound of 100 and a step size
127+of 1.
128+When random data are requested
129+.Ar s
130+defaults to a seed depending upon the time of day.
131+.Ar reps
132+is expected to be an unsigned integer,
133+and if given as zero is taken to be infinite.
134+.Ar begin
135+and
136+.Ar end
137+may be given as real numbers or as characters
138+representing the corresponding value in ASCII.
139+The last argument must be a real number.
140+.Pp
141+Random numbers are obtained through
142+.Xr random 3 .
143+The name
144+.Nm jot
145+derives in part from
146+.Nm iota ,
147+a function in APL.
148+.Sh EXAMPLES
149+The command:
150+.Dl "jot - 42 87 1"
151+prints the integers from 42 to 87, inclusive.
152+.Pp
153+The command:
154+.Dl "jot 21 \-1 1.00"
155+prints 21 evenly spaced numbers increasing from \-1 to 1.
156+.Pp
157+The command:
158+.Dl "jot \-c 128 0"
159+prints the ASCII character set.
160+.Pp
161+The command:
162+.Dl "jot \-w xa%c 26 a"
163+prints the strings
164+.Dq xaa
165+through
166+.Dq xaz .
167+.Pp
168+The command:
169+.Dl "jot \-r \-c 160 a z | rs \-g 0 8"
170+prints 20 random 8-letter strings.
171+.Pp
172+The command:
173+.Dl "jot \-b y 0"
174+is equivalent to
175+.Xr yes 1 .
176+.Pp
177+The command:
178+.Dl "jot \-w %ds/old/new/ 30 2 \- 5"
179+prints thirty
180+.Xr ed 1
181+substitution commands applying to lines 2, 7, 12, etc.
182+.Pp
183+The command:
184+.Dl "jot 0 9 \- \-.5"
185+prints the stuttering sequence 9, 8, 8, 7, etc.
186+.Pp
187+The command:
188+.Dl "jot \-b x 512 > block"
189+creates a file containing exactly 1024 bytes.
190+.Pp
191+The command:
192+.Dl "expand \-\`jot \-s, \- 10 132 4\`"
193+sets tabs four spaces apart starting
194+from column 10 and ending in column 132.
195+.Pp
196+The command:
197+.Dl "grep \`jot \-s """" \-b . 80\`"
198+prints all lines 80 characters or longer.
199+.Sh SEE ALSO
200+.Xr ed 1 ,
201+.Xr expand 1 ,
202+.Xr rs 1 ,
203+.Xr seq 1 ,
204+.Xr yes 1 ,
205+.Xr printf 3 ,
206+.Xr random 3
207+.Sh HISTORY
208+The
209+.Nm
210+utility first appeared in
211+.Bx 4.2 .
212+.Sh AUTHORS
213+.An John A. Kunze
+402, -0
  1@@ -0,0 +1,402 @@
  2+/*	$NetBSD: jot.c,v 1.28 2020/06/14 01:26:46 kamil 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+#include <sys/cdefs.h>
 34+#ifndef lint
 35+__COPYRIGHT("@(#) Copyright (c) 1993\
 36+ The Regents of the University of California.  All rights reserved.");
 37+#endif /* not lint */
 38+
 39+#ifndef lint
 40+#if 0
 41+static char sccsid[] = "@(#)jot.c	8.1 (Berkeley) 6/6/93";
 42+#endif
 43+__RCSID("$NetBSD: jot.c,v 1.28 2020/06/14 01:26:46 kamil Exp $");
 44+#endif /* not lint */
 45+
 46+/*
 47+ * jot - print sequential or random data
 48+ *
 49+ * Author:  John Kunze, Office of Comp. Affairs, UCB
 50+ */
 51+
 52+#include <ctype.h>
 53+#include <err.h>
 54+#include <limits.h>
 55+#include <math.h>
 56+#include <stdio.h>
 57+#include <stdlib.h>
 58+#include <string.h>
 59+#include <time.h>
 60+#include <unistd.h>
 61+
 62+#define	REPS_DEF	100
 63+#define	BEGIN_DEF	1
 64+#define	ENDER_DEF	100
 65+#define	STEP_DEF	1
 66+
 67+#define	is_default(s)	(strcmp((s), "-") == 0)
 68+
 69+static double	begin = BEGIN_DEF;
 70+static double	ender = ENDER_DEF;
 71+static double	step = STEP_DEF;
 72+static long	reps = REPS_DEF;
 73+static int	randomize;
 74+static int	boring;
 75+static int	prec = -1;
 76+static int	dox;
 77+static int	chardata;
 78+static int	nofinalnl;
 79+static const char *sepstring = "\n";
 80+static char	format[BUFSIZ];
 81+
 82+static void	getargs(int, char *[]);
 83+static void	getformat(void);
 84+static int	getprec(char *);
 85+static void	putdata(double, long);
 86+static void	usage(void) __dead;
 87+
 88+int
 89+main(int argc, char *argv[])
 90+{
 91+	double	x;
 92+	long	i;
 93+
 94+	getargs(argc, argv);
 95+	if (randomize) {
 96+		x = ender - begin;
 97+		if (x < 0) {
 98+			x = -x;
 99+			begin = ender;
100+		}
101+		if (dox == 0)
102+			/*
103+			 * We are printing floating point, generate random
104+			 * number that include both supplied limits.
105+			 * Due to FP routing for display the low and high
106+			 * values are likely to occur half as often as all
107+			 * the others.
108+			 */
109+			x /= (1u << 31) - 1.0;
110+		else {
111+			/*
112+			 * We are printing integers increase the range by
113+			 * one but ensure we never generate it.
114+			 * This makes all the integer values equally likely.
115+			 */
116+			x += 1.0;
117+			x /= (1u << 31);
118+		}
119+		srandom((unsigned long) step);
120+		for (i = 1; i <= reps || reps == 0; i++)
121+			putdata(random() * x + begin, reps - i);
122+	} else {
123+		/*
124+		 * If we are going to display as integer, add 0.5 here
125+		 * and use floor(x) later to get sane rounding.
126+		 */
127+		x = begin;
128+		if (dox)
129+			x += 0.5;
130+		for (i = 1; i <= reps || reps == 0; i++, x += step)
131+			putdata(x, reps - i);
132+	}
133+	if (!nofinalnl)
134+		putchar('\n');
135+	exit(0);
136+}
137+
138+static void
139+getargs(int argc, char *argv[])
140+{
141+	unsigned int have = 0;
142+#define BEGIN	1
143+#define	STEP	2	/* seed if -r */
144+#define REPS	4
145+#define	ENDER	8
146+	int n = 0;
147+	long t;
148+	char *ep;
149+
150+	for (;;) {
151+		switch (getopt(argc, argv, "b:cnp:rs:w:")) {
152+		default:
153+			usage();
154+		case -1:
155+			break;
156+		case 'c':
157+			chardata = 1;
158+			continue;
159+		case 'n':
160+			nofinalnl = 1;
161+			continue;
162+		case 'p':
163+			prec = strtol(optarg, &ep, 0);
164+			if (*ep != 0 || prec < 0)
165+				errx(EXIT_FAILURE, "Bad precision value");
166+			continue;
167+		case 'r':
168+			randomize = 1;
169+			continue;
170+		case 's':
171+			sepstring = optarg;
172+			continue;
173+		case 'b':
174+			boring = 1;
175+			/* FALLTHROUGH */
176+		case 'w':
177+			strlcpy(format, optarg, sizeof(format));
178+			continue;
179+		}
180+		break;
181+	}
182+	argc -= optind;
183+	argv += optind;
184+
185+	switch (argc) {	/* examine args right to left, falling thru cases */
186+	case 4:
187+		if (!is_default(argv[3])) {
188+			step = strtod(argv[3], &ep);
189+			if (*ep != 0)
190+				errx(EXIT_FAILURE, "Bad step value:  %s",
191+				    argv[3]);
192+			have |= STEP;
193+		}
194+		/* FALLTHROUGH */
195+	case 3:
196+		if (!is_default(argv[2])) {
197+			if (!sscanf(argv[2], "%lf", &ender))
198+				ender = argv[2][strlen(argv[2])-1];
199+			have |= ENDER;
200+			if (prec < 0)
201+				n = getprec(argv[2]);
202+		}
203+		/* FALLTHROUGH */
204+	case 2:
205+		if (!is_default(argv[1])) {
206+			if (!sscanf(argv[1], "%lf", &begin))
207+				begin = argv[1][strlen(argv[1])-1];
208+			have |= BEGIN;
209+			if (prec < 0)
210+				prec = getprec(argv[1]);
211+			if (n > prec)		/* maximum precision */
212+				prec = n;
213+		}
214+		/* FALLTHROUGH */
215+	case 1:
216+		if (!is_default(argv[0])) {
217+			reps = strtoul(argv[0], &ep, 0);
218+			if (*ep != 0 || reps < 0)
219+				errx(EXIT_FAILURE, "Bad reps value:  %s",
220+				    argv[0]);
221+			have |= REPS;
222+		}
223+		/* FALLTHROUGH */
224+	case 0:
225+		break;
226+	default:
227+		errx(EXIT_FAILURE,
228+		    "Too many arguments.  What do you mean by %s?", argv[4]);
229+	}
230+
231+	if (prec == -1)
232+		prec = 0;
233+
234+	getformat();
235+
236+	if (randomize) {
237+		/* 'step' is the seed here, use pseudo-random default */
238+		if (!(have & STEP))
239+			step = time(NULL) * getpid();
240+		/* Take the default values for everything else */
241+		return;
242+	}
243+
244+	/*
245+	 * The loop we run uses begin/step/reps, so if we have been
246+	 * given an end value (ender) we must use it to replace the
247+	 * default values of the others.
248+	 * We will assume a begin of 0 and step of 1 if necessary.
249+	 */
250+
251+	switch (have) {
252+
253+	case ENDER | STEP:
254+	case ENDER | STEP | BEGIN:
255+		/* Calculate reps */
256+		if (step == 0.0)
257+			reps = 0;	/* ie infinite */
258+		else {
259+			reps = (ender - begin + step) / step;
260+			if (reps <= 0)
261+				errx(EXIT_FAILURE, "Impossible stepsize");
262+		}
263+		break;
264+
265+	case REPS | ENDER:
266+	case REPS | ENDER | STEP:
267+		/* Calculate begin */
268+		if (reps == 0)
269+			errx(EXIT_FAILURE,
270+			    "Must specify begin if reps == 0");
271+		begin = ender - reps * step + step;
272+		break;
273+
274+	case REPS | BEGIN | ENDER:
275+		/* Calculate step */
276+		if (reps == 0)
277+			errx(EXIT_FAILURE,
278+			    "Infinite sequences cannot be bounded");
279+		if (reps == 1)
280+			step = 0.0;
281+		else
282+			step = (ender - begin) / (reps - 1);
283+		break;
284+
285+	case REPS | BEGIN | ENDER | STEP:
286+		/* reps given and implied - take smaller */
287+		if (step == 0.0)
288+			break;
289+		t = (ender - begin + step) / step;
290+		if (t <= 0)
291+			errx(EXIT_FAILURE,
292+			    "Impossible stepsize");
293+		if (t < reps)
294+			reps = t;
295+		break;
296+
297+	default:
298+		/* No values can be calculated, use defaults */
299+		break;
300+	}
301+}
302+
303+static void
304+putdata(double x, long notlast)
305+{
306+
307+	if (boring)				/* repeated word */
308+		printf("%s", format);
309+	else if (dox)				/* scalar */
310+		printf(format, (long)floor(x));
311+	else					/* real */
312+		printf(format, x);
313+	if (notlast != 0)
314+		fputs(sepstring, stdout);
315+}
316+
317+__dead static void
318+usage(void)
319+{
320+	(void)fprintf(stderr, "usage: %s [-cnr] [-b word] [-p precision] "
321+	    "[-s string] [-w word] [reps [begin [end [step | seed]]]]\n",
322+	    getprogname());
323+	exit(1);
324+}
325+
326+static int
327+getprec(char *num_str)
328+{
329+
330+	num_str = strchr(num_str, '.');
331+	if (num_str == NULL)
332+		return 0;
333+	return strspn(num_str + 1, "0123456789");
334+}
335+
336+static void
337+getformat(void)
338+{
339+	char	*p;
340+	size_t	sz;
341+
342+	if (boring)				/* no need to bother */
343+		return;
344+	for (p = format; *p; p++) {		/* look for '%' */
345+		if (*p == '%') {
346+			if (*(p+1) != '%')
347+				break;
348+			p++;		/* leave %% alone */
349+		}
350+	}
351+	sz = sizeof(format) - strlen(format) - 1;
352+	if (!*p) {
353+		if (chardata || prec == 0) {
354+			if ((size_t)snprintf(p, sz, "%%%s", chardata ? "c" : "ld") >= sz)
355+				errx(EXIT_FAILURE, "-w word too long");
356+			dox = 1;
357+		} else {
358+			if (snprintf(p, sz, "%%.%df", prec) >= (int)sz)
359+				errx(EXIT_FAILURE, "-w word too long");
360+		}
361+	} else if (!*(p+1)) {
362+		if (sz <= 0)
363+			errx(EXIT_FAILURE, "-w word too long");
364+		strcat(format, "%");		/* cannot end in single '%' */
365+	} else {
366+		p++;				/* skip leading % */
367+		for(; *p && !isalpha((unsigned char)*p); p++) {
368+			/* allow all valid printf(3) flags, but deny '*' */
369+			if (!strchr("0123456789#-+. ", *p))
370+				break;
371+		}
372+		/* Allow 'l' prefix, but no other. */
373+		if (*p == 'l')
374+			p++;
375+		switch (*p) {
376+		case 'f': case 'e': case 'g': case '%':
377+		case 'E': case 'G':
378+			break;
379+		case 's':
380+			errx(EXIT_FAILURE,
381+			    "cannot convert numeric data to strings");
382+			break;
383+		case 'd': case 'o': case 'x': case 'u':
384+		case 'D': case 'O': case 'X': case 'U':
385+		case 'c': case 'i':
386+			dox = 1;
387+			break;
388+		default:
389+			errx(EXIT_FAILURE, "unknown or invalid format `%s'",
390+			    format);
391+		}
392+		/* Need to check for trailing stuff to print */
393+		for (; *p; p++)		/* look for '%' */
394+			if (*p == '%') {
395+				if (*(p+1) != '%')
396+					break;
397+				p++;		/* leave %% alone */
398+			}
399+		if (*p)
400+			errx(EXIT_FAILURE, "unknown or invalid format `%s'",
401+			    format);
402+	}
403+}
+27, -0
 1@@ -0,0 +1,27 @@
 2+.POSIX:
 3+
 4+PROG = pr
 5+SRCS = pr.c egetopt.c ../compat/raise_default_signal.c
 6+MAN = pr.1
 7+
 8+CC = cc
 9+CFLAGS = -O2
10+CPPFLAGS = -I../compat -include ../compat/netcompat.h
11+DESTDIR =
12+BINDIR = /usr/local/bin
13+MANDIR = /usr/local/share/man
14+
15+all: $(PROG)
16+
17+$(PROG): $(SRCS)
18+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
19+
20+install: $(PROG)
21+	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
22+	cp $(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
23+	chmod 755 $(DESTDIR)$(BINDIR)/$(PROG)
24+	cp $(MAN) $(DESTDIR)$(MANDIR)/man1/$(MAN)
25+	chmod 644 $(DESTDIR)$(MANDIR)/man1/$(MAN)
26+
27+clean:
28+	rm -f $(PROG) *.o
+215, -0
  1@@ -0,0 +1,215 @@
  2+/*	$NetBSD: egetopt.c,v 1.9 2011/09/06 18:26:06 joerg Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1991 Keith Muller.
  6+ * Copyright (c) 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+#include <sys/cdefs.h>
 38+#ifndef lint
 39+#if 0
 40+from: static char sccsid[] = "@(#)egetopt.c	8.1 (Berkeley) 6/6/93";
 41+#else
 42+__RCSID("$NetBSD: egetopt.c,v 1.9 2011/09/06 18:26:06 joerg Exp $");
 43+#endif
 44+#endif /* not lint */
 45+
 46+#include <ctype.h>
 47+#include <stdio.h>
 48+#include <stdlib.h>
 49+#include <string.h>
 50+
 51+#include "extern.h"
 52+
 53+/*
 54+ * egetopt:	get option letter from argument vector (an extended
 55+ *		version of getopt).
 56+ *
 57+ * Non standard additions to the ostr specs are:
 58+ * 1) '?': immediate value following arg is optional (no white space
 59+ *    between the arg and the value)
 60+ * 2) '#': +/- followed by a number (with an optional sign but
 61+ *    no white space between the arg and the number). The - may be
 62+ *    combined with other options, but the + cannot.
 63+ */
 64+
 65+int	eopterr = 1;		/* if error message should be printed */
 66+int	eoptind = 1;		/* index into parent argv vector */
 67+int	eoptopt;		/* character checked for validity */
 68+char	*eoptarg;		/* argument associated with option */
 69+
 70+#define	BADCH	(int)'?'
 71+static char EMSG[1] = { '\0' };
 72+
 73+int
 74+egetopt(int nargc, char * const *nargv, const char *ostr)
 75+{
 76+	static char *place = EMSG;	/* option letter processing */
 77+	char *oli;			/* option letter list index */
 78+	static int delim;		/* which option delimiter */
 79+	char *p;
 80+	static char savec = '\0';
 81+
 82+	if (savec != '\0') {
 83+		*place = savec;
 84+		savec = '\0';
 85+	}
 86+
 87+	if (!*place) {
 88+		/*
 89+		 * update scanning pointer
 90+		 */
 91+		if ((eoptind >= nargc) ||
 92+		    ((*(place = nargv[eoptind]) != '-') && (*place != '+'))) {
 93+			place = EMSG;
 94+			return (-1);
 95+		}
 96+
 97+		delim = (int)*place;
 98+		if (place[1] && *++place == '-' && !place[1]) {
 99+			/*
100+			 * found "--"
101+			 */
102+			++eoptind;
103+			place = EMSG;
104+			return (-1);
105+		}
106+	}
107+
108+	/*
109+	 * check option letter
110+	 */
111+	if ((eoptopt = (int)*place++) == (int)':' || (eoptopt == (int)'?') ||
112+	    !(oli = strchr(ostr, eoptopt))) {
113+		/*
114+		 * if the user didn't specify '-' as an option,
115+		 * assume it means -1 when by itself.
116+		 */
117+		if ((eoptopt == (int)'-') && !*place)
118+			return (-1);
119+		if (strchr(ostr, '#') && (isdigit(eoptopt) ||
120+		    (((eoptopt == (int)'-') || (eoptopt == (int)'+')) &&
121+		      isdigit((unsigned char)*place)))) {
122+			/*
123+			 * # option: +/- with a number is ok
124+			 */
125+			for (p = place; *p != '\0'; ++p) {
126+				if (!isdigit((unsigned char)*p))
127+					break;
128+			}
129+			eoptarg = place-1;
130+
131+			if (*p == '\0') {
132+				place = EMSG;
133+				++eoptind;
134+			} else {
135+				place = p;
136+				savec = *p;
137+				*place = '\0';
138+			}
139+			return (delim);
140+		}
141+
142+		if (!*place)
143+			++eoptind;
144+		if (eopterr) {
145+			if (!(p = strrchr(*nargv, '/')))
146+				p = *nargv;
147+			else
148+				++p;
149+			(void)fprintf(stderr, "%s: illegal option -- %c\n",
150+			    p, eoptopt);
151+		}
152+		return (BADCH);
153+	}
154+	if (delim == (int)'+') {
155+		/*
156+		 * '+' is only allowed with numbers
157+		 */
158+		if (!*place)
159+			++eoptind;
160+		if (eopterr) {
161+			if (!(p = strrchr(*nargv, '/')))
162+				p = *nargv;
163+			else
164+				++p;
165+			(void)fprintf(stderr,
166+				"%s: illegal '+' delimiter with option -- %c\n",
167+				p, eoptopt);
168+		}
169+		return (BADCH);
170+	}
171+	++oli;
172+	if ((*oli != ':') && (*oli != '?')) {
173+		/*
174+		 * don't need argument
175+		 */
176+		eoptarg = NULL;
177+		if (!*place)
178+			++eoptind;
179+		return (eoptopt);
180+	}
181+
182+	if (*place) {
183+		/*
184+		 * no white space
185+		 */
186+		eoptarg = place;
187+	} else if (*oli == '?') {
188+		/*
189+		 * no arg, but NOT required
190+		 */
191+		eoptarg = NULL;
192+	} else if (nargc <= ++eoptind) {
193+		/*
194+		 * no arg, but IS required
195+		 */
196+		place = EMSG;
197+		if (eopterr) {
198+			if (!(p = strrchr(*nargv, '/')))
199+				p = *nargv;
200+			else
201+				++p;
202+			(void)fprintf(stderr,
203+			    "%s: option requires an argument -- %c\n", p,
204+			    eoptopt);
205+		}
206+		return (BADCH);
207+	} else {
208+		/*
209+		 * arg has white space
210+		 */
211+		eoptarg = nargv[eoptind];
212+	}
213+	place = EMSG;
214+	++eoptind;
215+	return (eoptopt);
216+}
+42, -0
 1@@ -0,0 +1,42 @@
 2+/*	$NetBSD: extern.h,v 1.6 2011/09/06 18:26:06 joerg Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 1991 Keith Muller.
 6+ * Copyright (c) 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+ *      from: @(#)extern.h	8.1 (Berkeley) 6/6/93
37+ *	$NetBSD: extern.h,v 1.6 2011/09/06 18:26:06 joerg Exp $
38+ */
39+
40+extern int eoptind;
41+extern char *eoptarg;
42+
43+int	 egetopt(int, char * const *, const char *);
+366, -0
  1@@ -0,0 +1,366 @@
  2+.\"	$NetBSD: pr.1,v 1.24 2019/09/01 18:31:37 sevan Exp $
  3+.\"
  4+.\" Copyright (c) 1991 Keith Muller.
  5+.\" Copyright (c) 1993
  6+.\"	The Regents of the University of California.  All rights reserved.
  7+.\" Copyright (c) 1994-1995, 1997, 1999-2003, 2009, 2012
  8+.\"	The NetBSD Foundation, Inc.
  9+.\"
 10+.\" This code is derived from software contributed to Berkeley by
 11+.\" Keith Muller of the University of California, San Diego.
 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+.\"     from: @(#)pr.1	8.1 (Berkeley) 6/6/93
 38+.\"
 39+.Dd September 1, 2019
 40+.Dt PR 1
 41+.Os
 42+.Sh NAME
 43+.Nm pr
 44+.Nd print files
 45+.Sh SYNOPSIS
 46+.Nm
 47+.Op Ar \&+page
 48+.Op Fl Ar column
 49+.Op Fl adFfmprt
 50+.Oo
 51+.Fl e Ns Oo Ar char Oc Ns Op Ar gap
 52+.Oc
 53+.Op Fl h Ar header
 54+.Oo
 55+.Fl i Ns Oo Ar char Oc Ns Op Ar gap
 56+.Oc
 57+.Op Fl l Ar lines
 58+.Oo
 59+.Fl n Ns Oo Ar char Oc Ns Op Ar width
 60+.Oc
 61+.Op Fl o Ar offset
 62+.Oo
 63+.Fl s Ns Op Ar char
 64+.Oc
 65+.Op Fl T Ar timefmt
 66+.Op Fl w Ar width
 67+.Op -
 68+.Op Ar file ...
 69+.Sh DESCRIPTION
 70+The
 71+.Nm
 72+utility is a printing and pagination filter for text files.
 73+When multiple input files are specified, each is read, formatted,
 74+and written to standard output.
 75+By default, the input is separated into 66-line pages, each with
 76+.Bl -bullet
 77+.It
 78+A 5-line header with the page number, date, time, and
 79+the pathname of the file.
 80+.It
 81+A 5-line trailer consisting of blank lines.
 82+.El
 83+.Pp
 84+If standard output is associated with a terminal,
 85+diagnostic messages are suppressed until the
 86+.Nm
 87+utility has completed processing.
 88+.Pp
 89+When multiple column output is specified,
 90+text columns are of equal width.
 91+By default text columns are separated by at least one
 92+.Aq Em blank .
 93+Input lines that do not fit into a text column are truncated.
 94+Lines are not truncated under single column output.
 95+.Sh OPTIONS
 96+In the following option descriptions, column, lines, offset, page, and
 97+width are positive decimal integers and gap is a nonnegative decimal integer.
 98+.Bl -tag -width 4n
 99+.It Ar \&+page
100+Begin output at page number
101+.Ar page
102+of the formatted input.
103+.It Fl Ar column
104+Produce output that is
105+.Ar columns
106+wide (default is 1) that is written vertically
107+down each column in the order in which the text
108+is received from the input file.
109+The options
110+.Fl e
111+and
112+.Fl i
113+are assumed.
114+This option should not be used with
115+.Fl m .
116+When used with
117+.Fl t ,
118+the minimum number of lines is used to display the output.
119+.It Fl a
120+Modify the effect of the
121+.Fl column
122+option so that the columns are filled across the page in a round-robin order
123+(e.g., when column is 2, the first input line heads column
124+1, the second heads column 2, the third is the second line
125+in column 1, etc.).
126+This option requires the use of the
127+.Fl column
128+option.
129+.It Fl d
130+Produce output that is double spaced.
131+An extra
132+.Aq Em newline
133+character is output following every
134+.Aq newline
135+found in the input.
136+.It Fl e Ns Oo Ar char Oc Ns Op Ar gap
137+Expand each input
138+.Aq tab
139+to the next greater column
140+position specified by the formula
141+.Ar n*gap+1 ,
142+where
143+.Em n
144+is an integer > 0.
145+If
146+.Ar gap
147+is zero or is omitted the default is 8.
148+All
149+.Aq Em tab
150+characters in the input are expanded into the appropriate
151+number of
152+.Ao Em space Ac Ns s .
153+If any nondigit character,
154+.Ar char ,
155+is specified, it is used as the input tab character.
156+If the first character of
157+.Ar char
158+is a digit then
159+.Ar char
160+is treated as
161+.Ar gap .
162+.It Fl F
163+Use a
164+.Aq Em form-feed
165+character for new pages,
166+instead of the default behavior that uses a
167+sequence of
168+.Aq Em newline
169+characters.
170+.It Fl f
171+Same as
172+.Fl F .
173+Additionally pause before beginning the first page
174+if the standard output is associated with a terminal.
175+.It Fl h Ar header
176+Use the string
177+.Ar header
178+to replace the
179+.Ar file name
180+in the header line.
181+.It Fl i Ns Oo Ar char Oc Ns Op Ar gap
182+In output, replace multiple
183+.Ao space Ac Ns s
184+with
185+.Ao tab Ac Ns s
186+whenever two or more
187+adjacent
188+.Ao space Ac Ns s
189+reach column positions
190+.Ar gap+1 ,
191+.Ar 2*gap+1 ,
192+etc.
193+If
194+.Ar gap
195+is zero or omitted, default
196+.Aq Em tab
197+settings at every eighth column position
198+is used.
199+If any nondigit character,
200+.Ar char ,
201+is specified, it is used as the output
202+.Aq Em tab
203+character.
204+If the first character of
205+.Ar char
206+is a digit then
207+.Ar char
208+is treated as
209+.Ar gap .
210+.It Fl l Ar lines
211+Override the 66 line default and reset the page length to
212+.Ar lines .
213+If
214+.Ar lines
215+is not greater than the sum of both the header and trailer
216+depths (in lines), the
217+.Nm
218+utility suppresses output of both the header and trailer, as if the
219+.Fl t
220+option were in effect.
221+.It Fl m
222+Merge the contents of multiple files.
223+One line from each file specified by a file operand is
224+written side by side into text columns of equal fixed widths, in
225+terms of the number of column positions.
226+The number of text columns depends on the number of
227+file operands successfully opened.
228+The maximum number of files merged depends on page width and the
229+per process open file limit.
230+The options
231+.Fl e
232+and
233+.Fl i
234+are assumed.
235+.It Fl n Ns Oo Ar char Oc Ns Op Ar width
236+Provide
237+.Ar width
238+digit line numbering.
239+The default for
240+.Ar width ,
241+if not specified, is 5.
242+The number occupies the first
243+.Ar width
244+column positions of each text column or each line of
245+.Fl m
246+output.
247+If
248+.Ar char
249+(any nondigit character) is given, it is appended to the line number to
250+separate it from whatever follows.
251+The default for
252+.Ar char
253+is a
254+.Aq Em tab .
255+Line numbers longer than
256+.Ar width
257+columns are truncated.
258+.It Fl o Ar offset
259+Each line of output is preceded by
260+.Ar offset
261+.Ao Em space Ac Ns s .
262+If the
263+.Fl o
264+option is not specified, the default is zero.
265+The space taken is in addition to the output line width.
266+.It Fl p
267+Pause before beginning each page if the
268+standard output is associated with a terminal.
269+.Nm
270+will write an
271+.Aq Em alert
272+to standard error and wait for a
273+.Aq Em carriage-return
274+to be read on
275+.Pa /dev/tty .
276+.It Fl r
277+Write no diagnostic reports on failure to open a file.
278+.It Fl s Ns Op Ar char
279+Separate text columns by the single character
280+.Ar char
281+instead of by the appropriate number of
282+.Ao Em space Ac Ns s
283+(default for
284+.Ar char
285+is the
286+.Aq Em tab
287+character).
288+.It Fl T
289+Specify an
290+.Xr strftime 3
291+format string to be used to format the date and time information in the page
292+header.
293+.It Fl t
294+Print neither the five-line identifying
295+header nor the five-line trailer usually supplied for each page.
296+Quit printing after the last line of each file without spacing to the
297+end of the page.
298+.It Fl w Ar width
299+Set the width of the line to
300+.Ar width
301+column positions for multiple text-column output only.
302+If the
303+.Fl w
304+option is not specified and the
305+.Fl s
306+option is not specified, the default width is 72.
307+If the
308+.Fl w
309+option is not specified and the
310+.Fl s
311+option is specified, the default width is 512.
312+.It Ar file
313+A pathname of a file to be printed.
314+If no
315+.Ar file
316+operands are specified, or if a
317+.Ar file
318+operand is
319+.Sq Fl ,
320+the standard input is used.
321+The standard input is used only if no
322+.Ar file
323+operands are specified, or if a
324+.Ar file
325+operand is
326+.Sq Fl .
327+.El
328+.Pp
329+The
330+.Fl s
331+option does not allow the option letter to be separated from its
332+argument, and the options
333+.Fl e ,
334+.Fl i ,
335+and
336+.Fl n
337+require that both arguments, if present, not be separated from the option
338+letter.
339+.Sh ERRORS
340+If
341+.Nm
342+receives an interrupt while printing to a terminal, it
343+flushes all accumulated error messages to the screen before
344+terminating.
345+.Pp
346+The
347+.Nm
348+utility exits 0 on success, and 1 if an error occurs.
349+.Pp
350+Error messages are written to standard error during the printing
351+process (if output is redirected) or after all successful
352+file printing is complete (when printing to a terminal).
353+.Sh SEE ALSO
354+.Xr cat 1 ,
355+.Xr more 1 ,
356+.Xr strftime 3
357+.Sh STANDARDS
358+The
359+.Nm
360+utility is
361+.St -p1003.1-2008
362+compatible.
363+.Sh HISTORY
364+A
365+.Nm
366+utility appeared in
367+.At v1 .
+1900, -0
   1@@ -0,0 +1,1900 @@
   2+/*	$NetBSD: pr.c,v 1.27 2022/05/23 19:52:35 andvar Exp $	*/
   3+
   4+/*-
   5+ * Copyright (c) 1991 Keith Muller.
   6+ * Copyright (c) 1993
   7+ *	The Regents of the University of California.  All rights reserved.
   8+ * Copyright (c) 2012
   9+ *	The NetBSD Foundation, Inc.
  10+ *
  11+ * This code is derived from software contributed to Berkeley by
  12+ * Keith Muller of the University of California, San Diego.
  13+ *
  14+ * Redistribution and use in source and binary forms, with or without
  15+ * modification, are permitted provided that the following conditions
  16+ * are met:
  17+ * 1. Redistributions of source code must retain the above copyright
  18+ *    notice, this list of conditions and the following disclaimer.
  19+ * 2. Redistributions in binary form must reproduce the above copyright
  20+ *    notice, this list of conditions and the following disclaimer in the
  21+ *    documentation and/or other materials provided with the distribution.
  22+ * 3. Neither the name of the University nor the names of its contributors
  23+ *    may be used to endorse or promote products derived from this software
  24+ *    without specific prior written permission.
  25+ *
  26+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36+ * SUCH DAMAGE.
  37+ */
  38+
  39+#include <sys/cdefs.h>
  40+#ifndef lint
  41+__COPYRIGHT("@(#) Copyright (c) 1993\
  42+ The Regents of the University of California.  All rights reserved.");
  43+#endif /* not lint */
  44+
  45+#ifndef lint
  46+#if 0
  47+from: static char sccsid[] = "@(#)pr.c	8.1 (Berkeley) 6/6/93";
  48+#else
  49+__RCSID("$NetBSD: pr.c,v 1.27 2022/05/23 19:52:35 andvar Exp $");
  50+#endif
  51+#endif /* not lint */
  52+
  53+#include <sys/types.h>
  54+#include <sys/time.h>
  55+#include <sys/stat.h>
  56+
  57+#include <ctype.h>
  58+#include <errno.h>
  59+#include <signal.h>
  60+#include <stdio.h>
  61+#include <stdlib.h>
  62+#include <string.h>
  63+#include <time.h>
  64+#include <unistd.h>
  65+
  66+#include "pr.h"
  67+#include "extern.h"
  68+
  69+/*
  70+ * pr:	a printing and pagination filter. If multiple input files
  71+ *	are specified, each is read, formatted, and written to standard
  72+ *	output. By default, input is separated into 66-line pages, each
  73+ *	with a header that includes the page number, date, time and the
  74+ *	files pathname.
  75+ *
  76+ *	Complies with posix P1003.2/D11
  77+ */
  78+
  79+/*
  80+ * parameter variables
  81+ */
  82+static int	pgnm;			/* starting page number */
  83+static int	clcnt;			/* number of columns */
  84+static int	colwd;			/* column data width - multiple columns */
  85+static int	across;			/* mult col flag; write across page */
  86+static int	dspace;			/* double space flag */
  87+static char	inchar;			/* expand input char */
  88+static int	ingap;			/* expand input gap */
  89+static int	formfeed;		/* use formfeed as trailer */
  90+static char	*header;		/* header name instead of file name */
  91+static char	ochar;			/* contract output char */
  92+static int	ogap;			/* contract output gap */
  93+static int	lines;			/* number of lines per page */
  94+static int	merge;			/* merge multiple files in output */
  95+static char	nmchar;			/* line numbering append char */
  96+static int	nmwd;			/* width of line number field */
  97+static int	offst;			/* number of page offset spaces */
  98+static int	nodiag;			/* do not report file open errors */
  99+static char	schar;			/* text column separation character */
 100+static int	sflag;			/* -s option for multiple columns */
 101+static int	ttyout;			/* output is a tty */
 102+static int	nohead;			/* do not write head and trailer */
 103+static int	pgpause;		/* pause before each page */
 104+static int	pgwd;			/* page width with multiple col output */
 105+static const char *timefrmt = TIMEFMT;	/* time conversion string */
 106+static FILE	*ttyinf;		/* input terminal for page pauses */
 107+
 108+/*
 109+ * misc globals
 110+ */
 111+static FILE	*errf;			/* error message file pointer */
 112+static int	addone;			/* page length is odd with double space */
 113+static int	errcnt;			/* error count on file processing */
 114+static const char	digs[] = "0123456789";	/* page number translation map */
 115+
 116+static void	 addnum(char *, int, int);
 117+static void	 flsh_errs(void);
 118+static int	 horzcol(int, char **);
 119+static int	 inln(FILE *, char *, int, int *, int, int *);
 120+static int	 inskip(FILE *, int, int);
 121+static void	 mfail(void);
 122+static int	 mulfile(int, char **);
 123+static FILE	*nxtfile(int, char **, const char **, char *, int);
 124+static int	 onecol(int, char **);
 125+static int	 otln(char *, int, int *, int *, int);
 126+static void	 pfail(void);
 127+static int	 prhead(char *, const char *, int);
 128+static void	 prpause(int);
 129+static int	 prtail(int, int);
 130+static int	 setup(int, char **);
 131+__dead static void	 terminate(int);
 132+static void	 usage(void);
 133+static int	 vertcol(int, char **);
 134+
 135+int
 136+main(int argc, char *argv[])
 137+{
 138+	int ret_val;
 139+
 140+	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
 141+		(void)signal(SIGINT, terminate);
 142+	ret_val = setup(argc, argv);
 143+	if (!ret_val) {
 144+		/*
 145+		 * select the output format based on options
 146+		 */
 147+		if (merge)
 148+			ret_val = mulfile(argc, argv);
 149+		else if (clcnt == 1)
 150+			ret_val = onecol(argc, argv);
 151+		else if (across)
 152+			ret_val = horzcol(argc, argv);
 153+		else
 154+			ret_val = vertcol(argc, argv);
 155+	} else
 156+		usage();
 157+	flsh_errs();
 158+	if (errcnt || ret_val)
 159+		exit(1);
 160+	return(0);
 161+}
 162+
 163+/*
 164+ * onecol:	print files with only one column of output.
 165+ *		Line length is unlimited.
 166+ */
 167+static int
 168+onecol(int argc, char *argv[])
 169+{
 170+	int cnt = -1;
 171+	int off;
 172+	int lrgln;
 173+	int linecnt;
 174+	int num;
 175+	int lncnt;
 176+	int pagecnt;
 177+	int ips;
 178+	int ops;
 179+	int cps;
 180+	char *obuf = NULL;
 181+	char *lbuf;
 182+	char *nbuf;
 183+	char *hbuf = NULL;
 184+	char *ohbuf;
 185+	FILE *inf = NULL;
 186+	const char *fname;
 187+	int mor;
 188+	int error = 1;
 189+
 190+	if (nmwd)
 191+		num = nmwd + 1;
 192+	else
 193+		num = 0;
 194+	off = num + offst;
 195+
 196+	/*
 197+	 * allocate line buffer
 198+	 */
 199+	if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL)
 200+		goto oomem;
 201+	/*
 202+	 * allocate header buffer
 203+	 */
 204+	if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
 205+		goto oomem;
 206+
 207+	ohbuf = hbuf + offst;
 208+	nbuf = obuf + offst;
 209+	lbuf = nbuf + num;
 210+	if (num)
 211+		nbuf[--num] = nmchar;
 212+	if (offst) {
 213+		(void)memset(obuf, (int)' ', offst);
 214+		(void)memset(hbuf, (int)' ', offst);
 215+	}
 216+
 217+	/*
 218+	 * loop by file
 219+	 */
 220+	while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
 221+		if (pgnm) {
 222+			/*
 223+			 * skip to specified page
 224+			 */
 225+			if (inskip(inf, pgnm, lines))
 226+				continue;
 227+			pagecnt = pgnm;
 228+		} else
 229+			pagecnt = 1;
 230+		lncnt = 0;
 231+
 232+		/*
 233+		 * loop by page
 234+		 */
 235+		for(;;) {
 236+			linecnt = 0;
 237+			lrgln = 0;
 238+			ops = 0;
 239+			ips = 0;
 240+			cps = 0;
 241+
 242+			/*
 243+			 * loop by line
 244+			 */
 245+			while (linecnt < lines) {
 246+				/*
 247+				 * input next line
 248+				 */
 249+				if ((cnt = inln(inf,lbuf,LBUF,&cps,0,&mor)) < 0)
 250+					break;
 251+				if (!linecnt) {
 252+					if (pgpause)
 253+						prpause(pagecnt);
 254+
 255+				        if (!nohead &&
 256+					    prhead(hbuf, fname, pagecnt))
 257+						goto out;
 258+				}
 259+
 260+				/*
 261+				 * start of new line.
 262+				 */
 263+				if (!lrgln) {
 264+					if (num)
 265+						addnum(nbuf, num, ++lncnt);
 266+					if (otln(obuf,cnt+off, &ips, &ops, mor))
 267+						goto out;
 268+				} else if (otln(lbuf, cnt, &ips, &ops, mor))
 269+					goto out;
 270+
 271+				/*
 272+				 * if line bigger than buffer, get more
 273+				 */
 274+				if (mor) {
 275+					lrgln = 1;
 276+					continue;
 277+				}
 278+
 279+				/*
 280+				 * whole line rcvd. reset tab proc. state
 281+				 */
 282+				++linecnt;
 283+				lrgln = 0;
 284+				ops = 0;
 285+				ips = 0;
 286+			}
 287+
 288+			/*
 289+			 * fill to end of page
 290+			 */
 291+			if (linecnt && prtail(lines-linecnt-lrgln, lrgln))
 292+				goto out;
 293+
 294+			/*
 295+			 * On EOF go to next file
 296+			 */
 297+			if (cnt < 0)
 298+				break;
 299+			++pagecnt;
 300+		}
 301+		if (inf != stdin)
 302+			(void)fclose(inf);
 303+	}
 304+	if (eoptind < argc)
 305+		goto out;
 306+	error = 0;
 307+	goto out;
 308+oomem:
 309+	mfail();
 310+out:
 311+	free(obuf);
 312+	free(hbuf);
 313+	if (inf != NULL && inf != stdin)
 314+		(void)fclose(inf);
 315+	return error;
 316+}
 317+
 318+/*
 319+ * vertcol:	print files with more than one column of output down a page
 320+ */
 321+static int
 322+vertcol(int argc, char *argv[])
 323+{
 324+	char *ptbf;
 325+	char **lstdat = NULL;
 326+	int i;
 327+	int j;
 328+	int cnt = -1;
 329+	int pln;
 330+	int *indy = NULL;
 331+	int cvc;
 332+	int *lindy = NULL;
 333+	int lncnt;
 334+	int stp;
 335+	int pagecnt;
 336+	int col = colwd + 1;
 337+	int mxlen = pgwd + offst + 1;
 338+	int mclcnt = clcnt - 1;
 339+	struct vcol *vc = NULL;
 340+	int mvc;
 341+	int tvc;
 342+	int cw = nmwd + 1;
 343+	int fullcol;
 344+	char *buf = NULL;
 345+	char *hbuf = NULL;
 346+	char *ohbuf;
 347+	const char *fname;
 348+	FILE *inf = NULL;
 349+	int ips = 0;
 350+	int cps = 0;
 351+	int ops = 0;
 352+	int mor = 0;
 353+	int error = 1;
 354+
 355+	/*
 356+	 * allocate page buffer
 357+	 */
 358+	if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL)
 359+		goto oomem;
 360+
 361+	/*
 362+	 * allocate page header
 363+	 */
 364+	if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
 365+		goto oomem;
 366+	ohbuf = hbuf + offst;
 367+	if (offst)
 368+		(void)memset(hbuf, (int)' ', offst);
 369+
 370+	/*
 371+	 * col pointers when no headers
 372+	 */
 373+	mvc = lines * clcnt;
 374+	if ((vc = malloc((unsigned)mvc*sizeof(struct vcol))) == NULL)
 375+		goto oomem;
 376+
 377+	/*
 378+	 * pointer into page where last data per line is located
 379+	 */
 380+	if ((lstdat = malloc((unsigned)lines*sizeof(char *))) == NULL)
 381+		goto oomem;
 382+
 383+	/*
 384+	 * fast index lookups to locate start of lines
 385+	 */
 386+	if ((indy = malloc((unsigned)lines*sizeof(int))) == NULL)
 387+		goto oomem;
 388+	if ((lindy = malloc((unsigned)lines*sizeof(int))) == NULL)
 389+		goto oomem;
 390+
 391+	if (nmwd)
 392+		fullcol = col + cw;
 393+	else
 394+		fullcol = col;
 395+
 396+	/*
 397+	 * initialize buffer lookup indexes and offset area
 398+	 */
 399+	for (j = 0; j < lines; ++j) {
 400+		lindy[j] = j * mxlen;
 401+		indy[j] = lindy[j] + offst;
 402+		if (offst) {
 403+			ptbf = buf + lindy[j];
 404+			(void)memset(ptbf, (int)' ', offst);
 405+			ptbf += offst;
 406+		} else
 407+			ptbf = buf + indy[j];
 408+		lstdat[j] = ptbf;
 409+	}
 410+
 411+	/*
 412+	 * loop by file
 413+	 */
 414+	while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
 415+		if (pgnm) {
 416+			/*
 417+			 * skip to requested page
 418+			 */
 419+			if (inskip(inf, pgnm, lines))
 420+				continue;
 421+			pagecnt = pgnm;
 422+		} else
 423+			pagecnt = 1;
 424+		lncnt = 0;
 425+
 426+		/*
 427+		 * loop by page
 428+		 */
 429+		for(;;) {
 430+			/*
 431+			 * loop by column
 432+			 */
 433+			cvc = 0;
 434+			for (i = 0; i < clcnt; ++i) {
 435+				j = 0;
 436+				/*
 437+				 * if last column, do not pad
 438+				 */
 439+				if (i == mclcnt)
 440+					stp = 1;
 441+				else
 442+					stp = 0;
 443+				/*
 444+				 * loop by line
 445+				 */
 446+				for(;;) {
 447+					/*
 448+					 * is this first column
 449+					 */
 450+					if (!i) {
 451+						ptbf = buf + indy[j];
 452+						lstdat[j] = ptbf;
 453+					} else 
 454+						ptbf = lstdat[j];
 455+					vc[cvc].pt = ptbf;
 456+
 457+					/*
 458+					 * add number
 459+					 */
 460+					if (nmwd) {
 461+						addnum(ptbf, nmwd, ++lncnt);
 462+						ptbf += nmwd;
 463+						*ptbf++ = nmchar;
 464+					}
 465+
 466+					/*
 467+					 * input next line
 468+					 */
 469+					cnt = inln(inf,ptbf,colwd,&cps,1,&mor);
 470+					vc[cvc++].cnt = cnt;
 471+					if (cnt < 0)
 472+						break;
 473+					ptbf += cnt;
 474+
 475+					/*
 476+					 * pad all but last column on page
 477+					 */
 478+					if (!stp) {
 479+						/*
 480+						 * pad to end of column
 481+						 */
 482+						if (sflag)
 483+							*ptbf++ = schar;
 484+						else if ((pln = col-cnt) > 0) {
 485+							(void)memset(ptbf,
 486+								(int)' ',pln);
 487+							ptbf += pln;
 488+						}
 489+					}
 490+					/*
 491+					 * remember last char in line
 492+					 */
 493+					lstdat[j] = ptbf;
 494+					if (++j >= lines)
 495+						break;
 496+				}
 497+				if (cnt < 0)
 498+					break;
 499+			}
 500+
 501+			/*
 502+			 * when -t (no header) is specified the spec requires
 503+			 * the min number of lines. The last page may not have
 504+			 * balanced length columns. To fix this we must reorder
 505+			 * the columns. This is a very slow technique so it is
 506+			 * only used under limited conditions. Without -t, the
 507+			 * balancing of text columns is unspecified. To NOT
 508+			 * balance the last page, add the global variable
 509+			 * nohead to the if statement below e.g.
 510+			 *
 511+			 * if ((cnt < 0) && nohead && cvc ......
 512+			 */
 513+			--cvc;
 514+
 515+			/*
 516+			 * check to see if last page needs to be reordered
 517+			 */
 518+			if ((cnt < 0) && cvc && ((mvc-cvc) >= clcnt)){
 519+				pln = cvc/clcnt;
 520+				if (cvc % clcnt)
 521+					++pln;
 522+
 523+				if (pgpause)
 524+					prpause(pagecnt);
 525+
 526+				/*
 527+				 * print header
 528+				 */
 529+				if (!nohead && prhead(hbuf, fname, pagecnt))
 530+					goto out;
 531+				for (i = 0; i < pln; ++i) {
 532+					ips = 0;
 533+					ops = 0;
 534+					if (offst&& otln(buf,offst,&ips,&ops,1)) {
 535+						error = 1;
 536+						goto out;
 537+					}
 538+					tvc = i;
 539+
 540+					for (j = 0; j < clcnt; ++j) {
 541+						/*
 542+						 * determine column length
 543+						 */
 544+						if (j == mclcnt) {
 545+							/*
 546+							 * last column
 547+							 */
 548+							cnt = vc[tvc].cnt;
 549+							if (nmwd)
 550+								cnt += cw;
 551+						} else if (sflag) {
 552+							/*
 553+							 * single ch between
 554+							 */
 555+							cnt = vc[tvc].cnt + 1;
 556+							if (nmwd)
 557+								cnt += cw;
 558+						} else
 559+							cnt = fullcol;
 560+						if (otln(vc[tvc].pt, cnt, &ips,
 561+								&ops, 1))
 562+							goto out;
 563+						tvc += pln;
 564+						if (tvc >= cvc)
 565+							break;
 566+					}
 567+					/*
 568+					 * terminate line
 569+					 */
 570+					if (otln(buf, 0, &ips, &ops, 0))
 571+						goto out;
 572+				}
 573+				/*
 574+				 * pad to end of page
 575+				 */
 576+				if (prtail((lines - pln), 0))
 577+					goto out;
 578+				/*
 579+				 * done with output, go to next file
 580+				 */
 581+				break;
 582+			}
 583+
 584+			/*
 585+			 * determine how many lines to output
 586+			 */
 587+			if (i > 0)
 588+				pln = lines;
 589+			else
 590+				pln = j;
 591+
 592+			/*
 593+			 * print header
 594+			 */
 595+			if (pln) {
 596+				if (pgpause)
 597+					prpause(pagecnt);
 598+
 599+			        if (!nohead && prhead(hbuf, fname, pagecnt))
 600+					goto out;
 601+			}
 602+
 603+			/*
 604+			 * output each line
 605+			 */
 606+			for (i = 0; i < pln; ++i) {
 607+				ptbf = buf + lindy[i];
 608+				if ((j = lstdat[i] - ptbf) <= offst)
 609+					break;
 610+				if (otln(ptbf, j, &ips, &ops, 0))
 611+					goto out;
 612+			}
 613+
 614+			/*
 615+			 * pad to end of page
 616+			 */
 617+			if (pln && prtail((lines - pln), 0))
 618+				goto out;
 619+
 620+			/*
 621+			 * if EOF go to next file
 622+			 */
 623+			if (cnt < 0)
 624+				break;
 625+			++pagecnt;
 626+		}
 627+		if (inf != stdin)
 628+			(void)fclose(inf);
 629+	}
 630+	if (eoptind < argc)
 631+		goto out;
 632+	error = 0;
 633+	goto out;
 634+oomem:
 635+	mfail();
 636+out:
 637+	free(buf);
 638+	free(hbuf);
 639+	free(vc);
 640+	free(lstdat);
 641+	free(lindy);
 642+	if (inf != NULL && inf != stdin)
 643+		(void)fclose(inf);
 644+	return error;
 645+}
 646+
 647+/*
 648+ * horzcol:	print files with more than one column of output across a page
 649+ */
 650+static int
 651+horzcol(int argc, char *argv[])
 652+{
 653+	char *ptbf;
 654+	int pln;
 655+	int cnt = -1;
 656+	char *lstdat;
 657+	int col = colwd + 1;
 658+	int j;
 659+	int i;
 660+	int lncnt;
 661+	int pagecnt;
 662+	char *buf = NULL;
 663+	char *hbuf = NULL;
 664+	char *ohbuf;
 665+	const char *fname;
 666+	FILE *inf = NULL;
 667+	int ips = 0;
 668+	int cps = 0;
 669+	int ops = 0;
 670+	int mor = 0;
 671+	int error = 1;
 672+
 673+	if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
 674+		goto oomem;
 675+
 676+	/*
 677+	 * page header
 678+	 */
 679+	if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
 680+		goto oomem;
 681+	ohbuf = hbuf + offst;
 682+	if (offst) {
 683+		(void)memset(buf, (int)' ', offst);
 684+		(void)memset(hbuf, (int)' ', offst);
 685+	}
 686+
 687+	/*
 688+	 * loop by file
 689+	 */
 690+	while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
 691+		if (pgnm) {
 692+			if (inskip(inf, pgnm, lines))
 693+				continue;
 694+			pagecnt = pgnm;
 695+		} else
 696+			pagecnt = 1;
 697+		lncnt = 0;
 698+
 699+		/*
 700+		 * loop by page
 701+		 */
 702+		for(;;) {
 703+			/*
 704+			 * loop by line
 705+			 */
 706+			for (i = 0; i < lines; ++i) {
 707+				ptbf = buf + offst;
 708+				lstdat = ptbf;
 709+				j = 0;
 710+				/*
 711+				 * loop by col
 712+				 */
 713+				for(;;) {
 714+					if (nmwd) {
 715+						/*
 716+						 * add number to column
 717+						 */
 718+						addnum(ptbf, nmwd, ++lncnt);
 719+						ptbf += nmwd;
 720+						*ptbf++ = nmchar;
 721+					}
 722+					/*
 723+					 * input line
 724+					 */
 725+					if ((cnt = inln(inf,ptbf,colwd,&cps,1,
 726+							&mor)) < 0)
 727+						break;
 728+					ptbf += cnt;
 729+					lstdat = ptbf;
 730+
 731+					/*
 732+					 * if last line skip padding
 733+					 */
 734+					if (++j >= clcnt)
 735+						break;
 736+
 737+					/*
 738+					 * pad to end of column
 739+					 */
 740+					if (sflag)
 741+						*ptbf++ = schar;
 742+					else if ((pln = col - cnt) > 0) {
 743+						(void)memset(ptbf,(int)' ',pln);
 744+						ptbf += pln;
 745+					}
 746+				}
 747+
 748+				/*
 749+				 * determine line length
 750+				 */
 751+				if ((j = lstdat - buf) <= offst)
 752+					break;
 753+				if (!i) {
 754+					if (pgpause)
 755+						prpause(pagecnt);
 756+
 757+				        if (!nohead &&
 758+					    prhead(hbuf, fname, pagecnt))
 759+						goto out;
 760+				}
 761+				/*
 762+				 * output line
 763+				 */
 764+				if (otln(buf, j, &ips, &ops, 0))
 765+					goto out;
 766+			}
 767+
 768+			/*
 769+			 * pad to end of page
 770+			 */
 771+			if (i && prtail(lines-i, 0))
 772+				goto out;
 773+
 774+			/*
 775+			 * if EOF go to next file
 776+			 */
 777+			if (cnt < 0)
 778+				break;
 779+			++pagecnt;
 780+		}
 781+		if (inf != stdin)
 782+			(void)fclose(inf);
 783+	}
 784+	if (eoptind < argc)
 785+		goto out;
 786+	error = 0;
 787+	goto out;
 788+oomem:
 789+	mfail();
 790+out:
 791+	free(buf);
 792+	free(hbuf);
 793+	if (inf != NULL && inf != stdin)
 794+		(void)fclose(inf);
 795+	return error;
 796+}
 797+
 798+/*
 799+ * mulfile:	print files with more than one column of output and
 800+ *		more than one file concurrently
 801+ */
 802+static int
 803+mulfile(int argc, char *argv[])
 804+{
 805+	char *ptbf;
 806+	int j;
 807+	int pln;
 808+	int cnt;
 809+	char *lstdat;
 810+	int i;
 811+	FILE **fbuf = NULL;
 812+	int actf;
 813+	int lncnt;
 814+	int col;
 815+	int pagecnt;
 816+	int fproc;
 817+	char *buf = NULL;
 818+	char *hbuf = NULL;
 819+	char *ohbuf;
 820+	const char *fname;
 821+	int ips = 0;
 822+	int cps = 0;
 823+	int ops = 0;
 824+	int mor = 0;
 825+	int error = 1;
 826+
 827+	/*
 828+	 * array of FILE *, one for each operand
 829+	 */
 830+	if ((fbuf = calloc(clcnt, sizeof(FILE *))) == NULL)
 831+		goto oomem;
 832+
 833+	/*
 834+	 * page header
 835+	 */
 836+	if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
 837+		goto oomem;
 838+	ohbuf = hbuf + offst;
 839+
 840+	/*
 841+	 * do not know how many columns yet. The number of operands provide an
 842+	 * upper bound on the number of columns. We use the number of files
 843+	 * we can open successfully to set the number of columns. The operation
 844+	 * of the merge operation (-m) in relation to unsuccessful file opens
 845+	 * is unspecified by posix.
 846+	 */
 847+	j = 0;
 848+	while (j < clcnt) {
 849+		if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL)
 850+			break;
 851+		if (pgnm && (inskip(fbuf[j], pgnm, lines)))
 852+			fbuf[j] = NULL;
 853+		++j;
 854+	}
 855+
 856+	/*
 857+	 * if no files, exit
 858+	 */
 859+	if (!j)
 860+		goto out;
 861+
 862+	/*
 863+	 * calculate page boundaries based on open file count
 864+	 */
 865+	clcnt = j;
 866+	if (nmwd) {
 867+		colwd = (pgwd - clcnt - nmwd)/clcnt;
 868+		pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
 869+	} else {
 870+		colwd = (pgwd + 1 - clcnt)/clcnt;
 871+		pgwd = ((colwd + 1) * clcnt) - 1;
 872+	}
 873+	if (colwd < 1) {
 874+		(void)fprintf(errf,
 875+		  "pr: page width too small for %d columns\n", clcnt);
 876+		goto out;
 877+	}
 878+	actf = clcnt;
 879+	col = colwd + 1;
 880+
 881+	/*
 882+	 * line buffer
 883+	 */
 884+	if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
 885+		goto out;
 886+	if (offst) {
 887+		(void)memset(buf, (int)' ', offst);
 888+		(void)memset(hbuf, (int)' ', offst);
 889+	}
 890+	if (pgnm)
 891+		pagecnt = pgnm;
 892+	else
 893+		pagecnt = 1;
 894+	lncnt = 0;
 895+
 896+	/*
 897+	 * continue to loop while any file still has data
 898+	 */
 899+	while (actf > 0) {
 900+		/*
 901+		 * loop by line
 902+		 */
 903+		for (i = 0; i < lines; ++i) {
 904+			ptbf = buf + offst;
 905+			lstdat = ptbf;
 906+			if (nmwd) {
 907+				/*
 908+				 * add line number to line
 909+				 */
 910+				addnum(ptbf, nmwd, ++lncnt);
 911+				ptbf += nmwd;
 912+				*ptbf++ = nmchar;
 913+			}
 914+			j = 0;
 915+			fproc = 0;
 916+
 917+			/*
 918+			 * loop by column
 919+			 */
 920+			for (j = 0; j < clcnt; ++j) {
 921+				if (fbuf[j] == NULL) {
 922+					/*
 923+					 * empty column; EOF
 924+					 */
 925+					cnt = 0;
 926+				} else if ((cnt = inln(fbuf[j], ptbf, colwd,
 927+							&cps, 1, &mor)) < 0) {
 928+					/*
 929+					 * EOF hit; no data
 930+					 */
 931+					if (fbuf[j] != stdin)
 932+						(void)fclose(fbuf[j]);
 933+					fbuf[j] = NULL;
 934+					--actf;
 935+					cnt = 0;
 936+				} else {
 937+					/*
 938+					 * process file data
 939+					 */
 940+					ptbf += cnt;
 941+					lstdat = ptbf;
 942+					fproc++;
 943+				}
 944+
 945+				/*
 946+				 * if last ACTIVE column, done with line
 947+				 */
 948+				if (fproc >= actf)
 949+					break;
 950+
 951+				/*
 952+				 * pad to end of column
 953+				 */
 954+				if (sflag) {
 955+					*ptbf++ = schar;
 956+				} else if ((pln = col - cnt) > 0) {
 957+					(void)memset(ptbf, (int)' ', pln);
 958+					ptbf += pln;
 959+				}
 960+			}
 961+
 962+			/*
 963+			 * calculate data in line
 964+			 */
 965+			if ((j = lstdat - buf) <= offst)
 966+				break;
 967+
 968+			if (!i) {
 969+				if (pgpause)
 970+					prpause(pagecnt);
 971+
 972+			        if (!nohead && prhead(hbuf, fname, pagecnt))
 973+					goto out;
 974+			}
 975+
 976+			/*
 977+			 * output line
 978+			 */
 979+			if (otln(buf, j, &ips, &ops, 0))
 980+				goto out;
 981+
 982+			/*
 983+			 * if no more active files, done
 984+			 */
 985+			if (actf <= 0) {
 986+				++i;
 987+				break;
 988+			}
 989+		}
 990+
 991+		/*
 992+		 * pad to end of page
 993+		 */
 994+		if (i && prtail(lines-i, 0))
 995+			goto out;
 996+		++pagecnt;
 997+	}
 998+	if (eoptind < argc)
 999+		goto out;
1000+	error = 0;
1001+	goto out;
1002+oomem:
1003+	mfail();
1004+out:
1005+	if (fbuf) {
1006+		for (j = 0; j < clcnt; j++)
1007+			if (fbuf[j] && fbuf[j] != stdin)
1008+				(void)fclose(fbuf[j]);
1009+		free(fbuf);
1010+	}
1011+	free(hbuf);
1012+	free(buf);
1013+	return error;
1014+}
1015+
1016+/*
1017+ * inln():	input a line of data (unlimited length lines supported)
1018+ *		Input is optionally expanded to spaces
1019+ *
1020+ *	inf:	file
1021+ *	buf:	buffer
1022+ *	lim:	buffer length
1023+ *	cps:	column position 1st char in buffer (large line support)
1024+ *	trnc:	throw away data more than lim up to \n 
1025+ *	mor:	set if more data in line (not truncated)
1026+ */
1027+static int
1028+inln(FILE *inf, char *buf, int lim, int *cps, int trnc, int *mor)
1029+{
1030+	int col;
1031+	int gap = ingap;
1032+	int ch = EOF;
1033+	char *ptbuf;
1034+	int chk = (int)inchar;
1035+
1036+	ptbuf = buf;
1037+
1038+	if (gap) {
1039+		/*
1040+		 * expanding input option
1041+		 */
1042+		while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1043+			/*
1044+			 * is this the input "tab" char
1045+			 */
1046+			if (ch == chk) {
1047+				/*
1048+				 * expand to number of spaces
1049+				 */
1050+				col = (ptbuf - buf) + *cps;
1051+				col = gap - (col % gap);
1052+
1053+				/*
1054+				 * if more than this line, push back
1055+				 */
1056+				if ((col > lim) && (ungetc(ch, inf) == EOF))
1057+					return(1);
1058+
1059+				/*
1060+				 * expand to spaces
1061+				 */
1062+				while ((--col >= 0) && (--lim >= 0))
1063+					*ptbuf++ = ' ';
1064+				continue;
1065+			}
1066+			if (ch == '\n')
1067+				break;
1068+			*ptbuf++ = ch;
1069+		}
1070+	} else {
1071+		/*
1072+		 * no expansion
1073+		 */
1074+		while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1075+			if (ch == '\n')
1076+				break;
1077+			*ptbuf++ = ch;
1078+		}
1079+	}
1080+	col = ptbuf - buf;
1081+	if (ch == EOF) {
1082+		*mor = 0;
1083+		*cps = 0;
1084+		if (!col)
1085+			return(-1);
1086+		return(col);
1087+	}
1088+	if (ch == '\n') {
1089+		/*
1090+		 * entire line processed
1091+		 */
1092+		*mor = 0;
1093+		*cps = 0;
1094+		return(col);
1095+	}
1096+
1097+	/*
1098+	 * line was larger than limit
1099+	 */
1100+	if (trnc) {
1101+		/*
1102+		 * throw away rest of line
1103+		 */
1104+		while ((ch = getc(inf)) != EOF) {
1105+			if (ch == '\n')
1106+				break;
1107+		}
1108+		*cps = 0;
1109+		*mor = 0;
1110+	} else {
1111+		/*
1112+		 * save column offset if not truncated
1113+		 */
1114+		*cps += col;
1115+		*mor = 1;
1116+	}
1117+
1118+	return(col);
1119+}
1120+
1121+/*
1122+ * otln():	output a line of data. (Supports unlimited length lines)
1123+ *		output is optionally contracted to tabs
1124+ *
1125+ *	buf:	output buffer with data
1126+ *	cnt:	number of chars of valid data in buf
1127+ *	svips:	buffer input column position (for large lines)
1128+ *	svops:	buffer output column position (for large lines)
1129+ *	mor:	output line not complete in this buf; more data to come.	
1130+ *		1 is more, 0 is complete, -1 is no \n's
1131+ */
1132+static int
1133+otln(char *buf, int cnt, int *svips, int *svops, int mor)
1134+{
1135+	int ops;		/* last col output */
1136+	int ips;		/* last col in buf examined */
1137+	int gap = ogap;
1138+	int tbps;
1139+	char *endbuf;
1140+
1141+	if (ogap) {
1142+		/*
1143+		 * contracting on output
1144+		 */
1145+		endbuf = buf + cnt;
1146+		ops = *svops;
1147+		ips = *svips;
1148+		while (buf < endbuf) {
1149+			/*
1150+			 * count number of spaces and ochar in buffer
1151+			 */
1152+			if (*buf == ' ') {
1153+				++ips;
1154+				++buf;
1155+				continue;
1156+			}
1157+
1158+			/*
1159+			 * simulate ochar processing
1160+			 */
1161+			if (*buf == ochar) {
1162+				ips += gap - (ips % gap);
1163+				++buf;
1164+				continue;
1165+			}
1166+
1167+			/*
1168+			 * got a non space char; contract out spaces
1169+			 */
1170+			while (ips - ops > 1) {
1171+				/*
1172+				 * use as many ochar as will fit
1173+				 */
1174+				if ((tbps = ops + gap - (ops % gap)) > ips)
1175+					break;
1176+				if (putchar(ochar) == EOF) {
1177+					pfail();
1178+					return(1);
1179+				}
1180+				ops = tbps;
1181+			}
1182+
1183+			while (ops < ips) {
1184+				/*
1185+				 * finish off with spaces
1186+				 */
1187+				if (putchar(' ') == EOF) {
1188+					pfail();
1189+					return(1);
1190+				}
1191+				++ops;
1192+			}
1193+
1194+			/*
1195+			 * output non space char
1196+			 */
1197+			if (putchar(*buf++) == EOF) {
1198+				pfail();
1199+				return(1);
1200+			}
1201+			++ips;
1202+			++ops;
1203+		}
1204+
1205+		if (mor > 0) {
1206+			/*
1207+			 * if incomplete line, save position counts
1208+			 */
1209+			*svops = ops;
1210+			*svips = ips;
1211+			return(0);
1212+		}
1213+
1214+		if (mor < 0) {
1215+			while (ips - ops > 1) {
1216+				/*
1217+				 * use as many ochar as will fit
1218+				 */
1219+				if ((tbps = ops + gap - (ops % gap)) > ips)
1220+					break;
1221+				if (putchar(ochar) == EOF) {
1222+					pfail();
1223+					return(1);
1224+				}
1225+				ops = tbps;
1226+			}
1227+			while (ops < ips) {
1228+				/*
1229+				 * finish off with spaces
1230+				 */
1231+				if (putchar(' ') == EOF) {
1232+					pfail();
1233+					return(1);
1234+				}
1235+				++ops;
1236+			}
1237+			return(0);
1238+		}
1239+	} else {
1240+		/*
1241+		 * output is not contracted
1242+		 */
1243+		if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
1244+			pfail();
1245+			return(1);
1246+		}
1247+		if (mor != 0)
1248+			return(0);
1249+	}
1250+
1251+	/*
1252+	 * process line end and double space as required
1253+	 */
1254+	if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
1255+		pfail();
1256+		return(1);
1257+	}
1258+	return(0);
1259+}
1260+
1261+/*
1262+ * inskip():	skip over pgcnt pages with lncnt lines per page
1263+ *		file is closed at EOF (if not stdin).
1264+ *
1265+ *	inf	FILE * to read from
1266+ *	pgcnt	number of pages to skip
1267+ *	lncnt	number of lines per page
1268+ */
1269+static int
1270+inskip(FILE *inf, int pgcnt, int lncnt)
1271+{
1272+	int c;
1273+	int cnt;
1274+
1275+	while(--pgcnt > 0) {
1276+		cnt = lncnt;
1277+		while ((c = getc(inf)) != EOF) {
1278+			if ((c == '\n') && (--cnt == 0))
1279+				break;
1280+		}
1281+		if (c == EOF) {
1282+			if (inf != stdin)
1283+				(void)fclose(inf);
1284+			return(1);
1285+		}
1286+	}
1287+	return(0);
1288+}
1289+
1290+/*
1291+ * nxtfile:	returns a FILE * to next file in arg list and sets the
1292+ *		time field for this file (or current date).
1293+ *
1294+ *	buf	array to store proper date for the header.
1295+ *	dt	if set skips the date processing (used with -m)
1296+ */
1297+static FILE *
1298+nxtfile(int argc, char **argv, const char **fname, char *buf, int dt)
1299+{
1300+	FILE *inf = NULL;
1301+	struct timeval tv;
1302+	struct timezone tz;
1303+	struct tm *timeptr = NULL;
1304+	struct stat statbuf;
1305+	time_t curtime;
1306+	static int twice = -1;
1307+
1308+	++twice;
1309+	if (eoptind >= argc) {
1310+		/*
1311+		 * no file listed; default, use standard input
1312+		 */
1313+		if (twice)
1314+			return(NULL);
1315+		clearerr(stdin);
1316+		inf = stdin;
1317+		if (header != NULL)
1318+			*fname = header;
1319+		else
1320+			*fname = FNAME;
1321+		if (nohead)
1322+			return(inf);
1323+		if (gettimeofday(&tv, &tz) < 0) {
1324+			++errcnt;
1325+			(void)fprintf(errf, "pr: cannot get time of day, %s\n",
1326+				strerror(errno));
1327+			eoptind = argc - 1;
1328+			return(NULL);
1329+		}
1330+		curtime = tv.tv_sec;
1331+		timeptr = localtime(&curtime);
1332+	}
1333+	for (; eoptind < argc; ++eoptind) {
1334+		if (strcmp(argv[eoptind], "-") == 0) {
1335+			/*
1336+			 * process a "-" for filename
1337+			 */
1338+			clearerr(stdin);
1339+			inf = stdin;
1340+			if (header != NULL)
1341+				*fname = header;
1342+			else
1343+				*fname = FNAME;
1344+			++eoptind;
1345+			if (nohead || (dt && twice))
1346+				return(inf);
1347+			if (gettimeofday(&tv, &tz) < 0) {
1348+				++errcnt;
1349+				(void)fprintf(errf,
1350+					"pr: cannot get time of day, %s\n",
1351+					strerror(errno));
1352+				return(NULL);
1353+			}
1354+			curtime = tv.tv_sec;
1355+			timeptr = localtime(&curtime);
1356+		} else {
1357+			/*
1358+			 * normal file processing
1359+			 */
1360+			if ((inf = fopen(argv[eoptind], "r")) == NULL) {
1361+				++errcnt;
1362+				if (nodiag)
1363+					continue;
1364+				(void)fprintf(errf, "pr: Cannot open %s, %s\n",
1365+					argv[eoptind], strerror(errno));
1366+				continue;
1367+			}
1368+			if (header != NULL)
1369+				*fname = header;
1370+			else if (dt)
1371+				*fname = FNAME;
1372+			else
1373+				*fname = argv[eoptind];
1374+			++eoptind;
1375+			if (nohead || (dt && twice))
1376+				return(inf);
1377+
1378+			if (dt) {
1379+				if (gettimeofday(&tv, &tz) < 0) {
1380+					++errcnt;
1381+					(void)fprintf(errf,
1382+					     "pr: cannot get time of day, %s\n",
1383+					     strerror(errno));
1384+					return(NULL);
1385+				}
1386+				curtime = tv.tv_sec;
1387+				timeptr = localtime(&curtime);
1388+			} else {
1389+				if (fstat(fileno(inf), &statbuf) < 0) {
1390+					++errcnt;
1391+					(void)fclose(inf);
1392+					(void)fprintf(errf, 
1393+						"pr: Cannot stat %s, %s\n",
1394+						argv[eoptind], strerror(errno));
1395+					return(NULL);
1396+				}
1397+				timeptr = localtime(&(statbuf.st_mtime));
1398+			}
1399+		}
1400+		break;
1401+	}
1402+	if (inf == NULL)
1403+		return(NULL);
1404+
1405+	/*
1406+	 * set up time field used in header
1407+	 */
1408+	if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
1409+		++errcnt;
1410+		if (inf != stdin)
1411+			(void)fclose(inf);
1412+		(void)fputs("pr: time conversion failed\n", errf);
1413+		return(NULL);
1414+	}
1415+	return(inf);
1416+}
1417+
1418+/*
1419+ * addnum():	adds the line number to the column
1420+ *		Truncates from the front or pads with spaces as required.
1421+ *		Numbers are right justified.
1422+ *
1423+ *	buf	buffer to store the number
1424+ *	wdth	width of buffer to fill
1425+ *	line	line number
1426+ *
1427+ *		NOTE: numbers occupy part of the column. The posix
1428+ *		spec does not specify if -i processing should or should not
1429+ *		occur on number padding. The spec does say it occupies
1430+ *		part of the column. The usage of addnum	currently treats
1431+ *		numbers as part of the column so spaces may be replaced.
1432+ */
1433+void
1434+addnum(char *buf, int wdth, int line)
1435+{
1436+	char *pt = buf + wdth;
1437+
1438+	do {
1439+		*--pt = digs[line % 10];
1440+		line /= 10;
1441+	} while (line && (pt > buf));
1442+
1443+	/*
1444+	 * pad with space as required
1445+	 */
1446+	while (pt > buf)
1447+		*--pt = ' ';
1448+}
1449+
1450+/*
1451+ * prpause():	pause before printing each page
1452+ *
1453+ *	pagcnt	page number
1454+ */
1455+static void
1456+prpause(int pagcnt)
1457+{
1458+
1459+	if (ttyout) {
1460+		int c;
1461+
1462+		(void)putc('\a', stderr);
1463+		(void)fflush(stderr);
1464+
1465+		while ((c = getc(ttyinf)) != '\n' && c != EOF)
1466+			;
1467+
1468+		/*
1469+		 * pause ONLY before first page of first file
1470+		 */
1471+		if (pgpause == FIRSTPAGE && pagcnt == 1)
1472+			pgpause = NO_PAUSE;
1473+	}
1474+}
1475+
1476+/*
1477+ * prhead():	prints the top of page header
1478+ *
1479+ *	buf	buffer with time field (and offset)
1480+ *	cnt	number of chars in buf
1481+ *	fname	fname field for header
1482+ *	pagcnt	page number
1483+ */
1484+static int
1485+prhead(char *buf, const char *fname, int pagcnt)
1486+{
1487+	int ips = 0;
1488+	int ops = 0;
1489+
1490+	if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
1491+		pfail();
1492+		return(1);
1493+	}
1494+	/*
1495+	 * posix is not clear if the header is subject to line length
1496+	 * restrictions. The specification for header line format
1497+	 * in the spec clearly does not limit length. No pr currently
1498+	 * restricts header length. However if we need to truncate in
1499+	 * a reasonable way, adjust the length of the printf by
1500+	 * changing HDFMT to allow a length max as an argument printf.
1501+	 * buf (which contains the offset spaces and time field could
1502+	 * also be trimmed
1503+	 *
1504+	 * note only the offset (if any) is processed for tab expansion
1505+	 */
1506+	if (offst && otln(buf, offst, &ips, &ops, -1))
1507+		return(1);
1508+	(void)printf(HDFMT,buf+offst, fname, pagcnt);
1509+	return(0);
1510+}
1511+
1512+/*
1513+ * prtail():	pad page with empty lines (if required) and print page trailer
1514+ *		if requested
1515+ *
1516+ *	cnt	number of lines of padding needed
1517+ *	incomp	was a '\n' missing from last line output
1518+ */
1519+static int
1520+prtail(int cnt, int incomp)
1521+{
1522+	if (nohead) {
1523+		/*
1524+		 * only pad with no headers when incomplete last line
1525+		 */
1526+		if (!incomp)
1527+			return(0);
1528+		if ((dspace && (putchar('\n') == EOF)) ||
1529+		    (putchar('\n') == EOF)) {
1530+			pfail();
1531+			return(1);
1532+		}
1533+		return(0);
1534+	}
1535+
1536+	/*
1537+	 * if double space output two \n
1538+	 */
1539+	if (dspace)
1540+		cnt *= 2;
1541+
1542+	/*
1543+	 * if an odd number of lines per page, add an extra \n
1544+	 */
1545+	if (addone)
1546+		++cnt;
1547+
1548+	/*
1549+	 * pad page
1550+	 */
1551+	if (formfeed) {
1552+		if ((incomp && (putchar('\n') == EOF)) || 
1553+		    (putchar('\f') == EOF)) {
1554+			pfail();
1555+			return(1);
1556+		}
1557+		return(0);
1558+	} 
1559+	cnt += TAILLEN;
1560+	while (--cnt >= 0) {
1561+		if (putchar('\n') == EOF) {
1562+			pfail();
1563+			return(1);
1564+		}
1565+	}
1566+	return(0);
1567+}
1568+
1569+/*
1570+ * terminate():	when a SIGINT is recvd
1571+ */
1572+static void
1573+terminate(int which_sig)
1574+{
1575+	flsh_errs();
1576+	(void)raise_default_signal(which_sig);
1577+	exit(1);
1578+}
1579+
1580+
1581+/*
1582+ * flsh_errs():	output saved up diagnostic messages after all normal
1583+ *		processing has completed
1584+ */
1585+static void
1586+flsh_errs(void)
1587+{
1588+	char buf[BUFSIZ];
1589+
1590+	(void)fflush(stdout);
1591+	(void)fflush(errf);
1592+	if (errf == stderr)
1593+		return;
1594+	rewind(errf);
1595+	while (fgets(buf, BUFSIZ, errf) != NULL)
1596+		(void)fputs(buf, stderr);
1597+}
1598+
1599+static void
1600+mfail(void)
1601+{
1602+	(void)fputs("pr: memory allocation failed\n", errf);
1603+}
1604+
1605+static void
1606+pfail(void)
1607+{
1608+	(void)fprintf(errf, "pr: write failure, %s\n", strerror(errno));
1609+}
1610+
1611+static void
1612+usage(void)
1613+{
1614+	(void)fputs(
1615+	 "usage: pr [+page] [-col] [-adFfmprt] [-e[ch][gap]] [-h header]\n",
1616+		    errf);
1617+	(void)fputs(
1618+	 "          [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",
1619+		    errf);
1620+	(void)fputs(
1621+	 "          [-s[ch]] [-T timefmt] [-w width] [-] [file ...]\n",
1622+		    errf);
1623+}
1624+
1625+/*
1626+ * setup:	Validate command args, initialize and perform sanity 
1627+ *		checks on options
1628+ */
1629+static int
1630+setup(int argc, char **argv)
1631+{
1632+	int c;
1633+	int eflag = 0;
1634+	int iflag = 0;
1635+	int wflag = 0;
1636+	int cflag = 0;
1637+
1638+	ttyinf = stdin;
1639+
1640+	if (isatty(fileno(stdout))) {
1641+		/*
1642+		 * defer diagnostics until processing is done
1643+		 */
1644+		if ((errf = tmpfile()) == NULL) {
1645+		       (void)fputs("Cannot defer diagnostic messages\n",stderr);
1646+		       return(1);
1647+		}
1648+		ttyout = 1;
1649+	} else
1650+		errf = stderr;
1651+	while ((c = egetopt(argc, argv, "#adFfmrte?h:i?l:n?o:ps?T:w:")) != -1) {
1652+		switch (c) {
1653+		case '+':
1654+			if ((pgnm = atoi(eoptarg)) < 1) {
1655+			    (void)fputs("pr: +page number must be 1 or more\n",
1656+				errf);
1657+			    return(1);
1658+			}
1659+			break;
1660+		case '-':
1661+			if ((clcnt = atoi(eoptarg)) < 1) {
1662+			    (void)fputs("pr: -columns must be 1 or more\n",errf);
1663+			    return(1);
1664+			}
1665+			if (clcnt > 1)
1666+				++cflag;
1667+			break;
1668+		case 'a':
1669+			++across;
1670+			break;
1671+		case 'd':
1672+			++dspace;
1673+			break;
1674+		case 'e':
1675+			++eflag;
1676+			if ((eoptarg != NULL) &&
1677+			    !isdigit((unsigned char)*eoptarg))
1678+				inchar = *eoptarg++;
1679+			else
1680+				inchar = INCHAR;
1681+			if ((eoptarg != NULL) &&
1682+			    isdigit((unsigned char)*eoptarg)) {
1683+				if ((ingap = atoi(eoptarg)) < 0) {
1684+					(void)fputs(
1685+					"pr: -e gap must be 0 or more\n", errf);
1686+					return(1);
1687+				}
1688+				if (ingap == 0)
1689+					ingap = INGAP;
1690+			} else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1691+				(void)fprintf(errf,
1692+				      "pr: invalid value for -e %s\n", eoptarg);
1693+				return(1);
1694+			} else
1695+				ingap = INGAP;
1696+			break;
1697+		case 'f':
1698+			pgpause |= FIRSTPAGE;
1699+			/*FALLTHROUGH*/
1700+		case 'F':
1701+			++formfeed;
1702+			break;
1703+		case 'h':
1704+			header = eoptarg;
1705+			break;
1706+		case 'i':
1707+			++iflag;
1708+			if ((eoptarg != NULL) &&
1709+			    !isdigit((unsigned char)*eoptarg))
1710+				ochar = *eoptarg++;
1711+			else
1712+				ochar = OCHAR;
1713+			if ((eoptarg != NULL) &&
1714+			    isdigit((unsigned char)*eoptarg)) {
1715+				if ((ogap = atoi(eoptarg)) < 0) {
1716+					(void)fputs(
1717+					"pr: -i gap must be 0 or more\n", errf);
1718+					return(1);
1719+				}
1720+				if (ogap == 0)
1721+					ogap = OGAP;
1722+			} else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1723+				(void)fprintf(errf,
1724+				      "pr: invalid value for -i %s\n", eoptarg);
1725+				return(1);
1726+			} else
1727+				ogap = OGAP;
1728+			break;
1729+		case 'l':
1730+			if (!isdigit((unsigned char)*eoptarg) ||
1731+			    ((lines=atoi(eoptarg)) < 1)) {
1732+				(void)fputs(
1733+				 "pr: Number of lines must be 1 or more\n",errf);
1734+				return(1);
1735+			}
1736+			break;
1737+		case 'm':
1738+			++merge;
1739+			break;
1740+		case 'n':
1741+			if ((eoptarg != NULL) &&
1742+			    !isdigit((unsigned char)*eoptarg))
1743+				nmchar = *eoptarg++;
1744+			else
1745+				nmchar = NMCHAR;
1746+			if ((eoptarg != NULL) &&
1747+			    isdigit((unsigned char)*eoptarg)) {
1748+				if ((nmwd = atoi(eoptarg)) < 1) {
1749+					(void)fputs(
1750+					"pr: -n width must be 1 or more\n",errf);
1751+					return(1);
1752+				}
1753+			} else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1754+				(void)fprintf(errf,
1755+				      "pr: invalid value for -n %s\n", eoptarg);
1756+				return(1);
1757+			} else
1758+				nmwd = NMWD;
1759+			break;
1760+		case 'o':
1761+			if (!isdigit((unsigned char)*eoptarg) ||
1762+			    ((offst = atoi(eoptarg))< 1)){
1763+				(void)fputs("pr: -o offset must be 1 or more\n",
1764+					errf);
1765+				return(1);
1766+			}
1767+			break;
1768+		case 'p':
1769+			pgpause |= EACHPAGE;
1770+			break;
1771+		case 'r':
1772+			++nodiag;
1773+			break;
1774+		case 's':
1775+			++sflag;
1776+			if (eoptarg == NULL)
1777+				schar = SCHAR;
1778+			else
1779+				schar = *eoptarg++;
1780+			if (eoptarg && *eoptarg != '\0') {
1781+				(void)fprintf(errf,
1782+				      "pr: invalid value for -s %s\n", eoptarg);
1783+				return(1);
1784+			}
1785+			break;
1786+		case 'T':
1787+			timefrmt = eoptarg;
1788+			break;
1789+		case 't':
1790+			++nohead;
1791+			break;
1792+		case 'w':
1793+			++wflag;
1794+			if (!isdigit((unsigned char)*eoptarg) ||
1795+			    ((pgwd = atoi(eoptarg)) < 1)){
1796+				(void)fputs(
1797+				   "pr: -w width must be 1 or more \n",errf);
1798+				return(1);
1799+			}
1800+			break;
1801+		case '?':
1802+		default:
1803+			return(1);
1804+		}
1805+	}
1806+
1807+	/*
1808+	 * default and sanity checks
1809+	 */
1810+	if (!clcnt) {
1811+		if (merge) {
1812+			if ((clcnt = argc - eoptind) <= 1) {
1813+				clcnt = CLCNT;
1814+				merge = 0;
1815+			}
1816+		} else
1817+			clcnt = CLCNT;
1818+	}
1819+	if (across) {
1820+		if (clcnt == 1) {
1821+			(void)fputs("pr: -a flag requires multiple columns\n",
1822+				errf);
1823+			return(1);
1824+		}
1825+		if (merge) {
1826+			(void)fputs("pr: -m cannot be used with -a\n", errf);
1827+			return(1);
1828+		}
1829+	}
1830+	if (!wflag) {
1831+		if (sflag)
1832+			pgwd = SPGWD;
1833+		else
1834+			pgwd = PGWD;
1835+	}
1836+	if (cflag || merge) {
1837+		if (!eflag) {
1838+			inchar = INCHAR;
1839+			ingap = INGAP;
1840+		}
1841+		if (!iflag) {
1842+			ochar = OCHAR;
1843+			ogap = OGAP;
1844+		}
1845+	}
1846+	if (cflag) {
1847+		if (merge) {
1848+			(void)fputs(
1849+			  "pr: -m cannot be used with multiple columns\n", errf);
1850+			return(1);
1851+		}
1852+		if (nmwd) {
1853+			colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1854+			pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1855+		} else {
1856+			colwd = (pgwd + 1 - clcnt)/clcnt;
1857+			pgwd = ((colwd + 1) * clcnt) - 1;
1858+		}
1859+		if (colwd < 1) {
1860+			(void)fprintf(errf,
1861+			  "pr: page width is too small for %d columns\n",clcnt);
1862+			return(1);
1863+		}
1864+	}
1865+	if (!lines)
1866+		lines = LINES;
1867+
1868+	/*
1869+	 * make sure long enough for headers. if not disable
1870+	 */
1871+	if (lines <= HEADLEN + TAILLEN)
1872+		++nohead;	
1873+	else if (!nohead)
1874+		lines -= HEADLEN + TAILLEN;
1875+
1876+	/*
1877+	 * adjust for double space on odd length pages
1878+	 */
1879+	if (dspace) {
1880+		if (lines == 1)
1881+			dspace = 0;
1882+		else {
1883+			if (lines & 1)
1884+				++addone;
1885+			lines /= 2;
1886+		}
1887+	}
1888+
1889+	/*
1890+	 * open /dev/tty if we are to pause before each page
1891+	 * but only if stdout is a terminal and stdin is not a terminal
1892+	 */
1893+	if (ttyout && pgpause && !isatty(fileno(stdin))) {
1894+		if ((ttyinf = fopen("/dev/tty", "r")) == NULL) {
1895+			(void)fprintf(errf, "pr: cannot open terminal\n");
1896+			return(1);
1897+		}
1898+	}
1899+
1900+	return(0);
1901+}
+79, -0
 1@@ -0,0 +1,79 @@
 2+/*	$NetBSD: pr.h,v 1.5 2012/07/24 02:13:04 ginsbach Exp $	*/
 3+
 4+/*-
 5+ * Copyright (c) 1991 Keith Muller.
 6+ * Copyright (c) 1993
 7+ *	The Regents of the University of California.  All rights reserved.
 8+ * Copyright (c) 2012
 9+ *	The NetBSD Foundation, Inc.
10+ *
11+ * This code is derived from software contributed to Berkeley by
12+ * Keith Muller of the University of California, San Diego.
13+ *
14+ * Redistribution and use in source and binary forms, with or without
15+ * modification, are permitted provided that the following conditions
16+ * are met:
17+ * 1. Redistributions of source code must retain the above copyright
18+ *    notice, this list of conditions and the following disclaimer.
19+ * 2. Redistributions in binary form must reproduce the above copyright
20+ *    notice, this list of conditions and the following disclaimer in the
21+ *    documentation and/or other materials provided with the distribution.
22+ * 3. Neither the name of the University nor the names of its contributors
23+ *    may be used to endorse or promote products derived from this software
24+ *    without specific prior written permission.
25+ *
26+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36+ * SUCH DAMAGE.
37+ *
38+ *      from: @(#)pr.h	8.1 (Berkeley) 6/6/93
39+ *	$NetBSD: pr.h,v 1.5 2012/07/24 02:13:04 ginsbach Exp $
40+ */
41+
42+/*
43+ * parameter defaults
44+ */
45+#define	CLCNT		1
46+#define	INCHAR		'\t'
47+#define	INGAP		8
48+#define	OCHAR		'\t'
49+#define OGAP		8
50+#define	LINES		66
51+#define	NMWD		5
52+#define	NMCHAR		'\t'
53+#define	SCHAR		'\t'
54+#define	PGWD		72
55+#define SPGWD		512
56+
57+/*
58+ * misc default values
59+ */
60+#define	HDFMT		"%s %s Page %d\n\n\n"
61+#define	HEADLEN		5
62+#define	TAILLEN		5
63+#define	TIMEFMT		"%b %e %H:%M %Y"
64+#define	FNAME		""
65+#define	LBUF		8192
66+#define	HDBUF		512
67+
68+/* when to pause before (for -f and -p options) */
69+#define NO_PAUSE	0
70+#define FIRSTPAGE	1
71+#define ENSUINGPAGES	2
72+#define EACHPAGE	(FIRSTPAGE | ENSUINGPAGES)
73+
74+/*
75+ * structure for vertical columns. Used to balance cols on last page
76+ */
77+struct vcol {
78+	char *pt;		/* ptr to col */
79+	int cnt;		/* char count */
80+};
+27, -0
 1@@ -0,0 +1,27 @@
 2+.POSIX:
 3+
 4+PROG = rs
 5+SRCS = rs.c
 6+MAN = rs.1
 7+
 8+CC = cc
 9+CFLAGS = -O2
10+CPPFLAGS = -I../compat -include ../compat/netcompat.h
11+DESTDIR =
12+BINDIR = /usr/local/bin
13+MANDIR = /usr/local/share/man
14+
15+all: $(PROG)
16+
17+$(PROG): $(SRCS)
18+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
19+
20+install: $(PROG)
21+	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
22+	cp $(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
23+	chmod 755 $(DESTDIR)$(BINDIR)/$(PROG)
24+	cp $(MAN) $(DESTDIR)$(MANDIR)/man1/$(MAN)
25+	chmod 644 $(DESTDIR)$(MANDIR)/man1/$(MAN)
26+
27+clean:
28+	rm -f $(PROG) *.o
+227, -0
  1@@ -0,0 +1,227 @@
  2+.\"	$NetBSD: rs.1,v 1.11 2017/07/03 21:34:21 wiz Exp $
  3+.\"
  4+.\" Copyright (c) 1993
  5+.\"	The Regents of the University of California.  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. Neither the name of the University nor the names of its contributors
 16+.\"    may be used to endorse or promote products derived from this software
 17+.\"    without specific prior written permission.
 18+.\"
 19+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 20+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 21+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 22+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 23+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 24+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 25+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 26+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 27+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 28+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 29+.\" SUCH DAMAGE.
 30+.\"
 31+.\"	@(#)rs.1	8.2 (Berkeley) 12/30/93
 32+.\"
 33+.Dd January 1, 2016
 34+.Dt RS 1
 35+.Os
 36+.Sh NAME
 37+.Nm rs
 38+.Nd reshape a data array
 39+.Sh SYNOPSIS
 40+.Nm
 41+.Op Fl CcSs Op Ar x
 42+.Op Fl GgKkw Ar N
 43+.Op Fl EeHhjmnTty
 44+.Op Ar rows Op Ar cols
 45+.Sh DESCRIPTION
 46+.Nm
 47+reads the standard input, interpreting each line as a row
 48+of blank-separated entries in an array,
 49+transforms the array according to the options,
 50+and writes it on the standard output.
 51+With no arguments it transforms stream input into a columnar
 52+format convenient for terminal viewing.
 53+.Pp
 54+The shape of the input array is deduced from the number of lines
 55+and the number of columns on the first line.
 56+If that shape is inconvenient, a more useful one might be
 57+obtained by skipping some of the input with the
 58+.Fl k
 59+option.
 60+Other options control interpretation of the input columns.
 61+.Pp
 62+The shape of the output array is influenced by the
 63+.Ar rows
 64+and
 65+.Ar cols
 66+specifications, which should be positive integers.
 67+If only one of them is a positive integer,
 68+.Nm
 69+computes a value for the other which will accommodate
 70+all of the data.
 71+When necessary, missing data are supplied in a manner
 72+specified by the options and surplus data are deleted.
 73+There are options to control presentation of the output columns,
 74+including transposition of the rows and columns.
 75+.Pp
 76+The options are described below.
 77+.Bl -tag -width xxxxxx -offset indent
 78+.It Fl C Op Ar x
 79+Output columns are delimited by the single character
 80+.Ar x .
 81+A missing
 82+.Ar x
 83+is taken to be
 84+.Sq \&^I .
 85+.It Fl c Op Ar x
 86+Input columns are delimited by the single character
 87+.Ar x .
 88+A missing
 89+.Ar x
 90+is taken to be
 91+.Sq \&^I .
 92+.It Fl e
 93+Consider each line of input as an array entry.
 94+.It Fl G Ar N
 95+The gutter width (inter-column space) has
 96+.Ar N
 97+percent of the maximum column width added to it.
 98+.It Fl g Ar N
 99+The gutter width (inter-column space), normally 2, is taken to be
100+.Ar N .
101+.It Fl H
102+Like
103+.Fl h ,
104+but also print the length of each line.
105+.It Fl h
106+Print the shape of the input array and do nothing else.
107+The shape is just the number of lines and the number of
108+entries on the first line.
109+.It Fl j
110+Right adjust entries within columns.
111+.It Fl K Ar N
112+Like
113+.Fl k ,
114+but print the ignored lines.
115+.It Fl k Ar N
116+Ignore the first
117+.Ar N
118+lines of input.
119+.It Fl m
120+Do not trim excess delimiters from the ends of the output array.
121+.It Fl n
122+On lines having fewer entries than the first line,
123+use null entries to pad out the line.
124+Normally, missing entries are taken from the next line of input.
125+.It Fl S Op Ar x
126+Like
127+.Fl C ,
128+but padded strings of
129+.Ar x
130+are delimiters.
131+.It Fl s Op Ar x
132+Like
133+.Fl c ,
134+but maximal strings of
135+.Ar x
136+are delimiters.
137+.It Fl T
138+Print the pure transpose of the input, ignoring any
139+.Ar rows
140+or
141+.Ar cols
142+specification.
143+.It Fl t
144+Fill in the rows of the output array using the columns of the
145+input array, that is, transpose the input while honoring any
146+.Ar rows
147+and
148+.Ar cols
149+specifications.
150+.It Fl w Ar N
151+The width of the display, normally 80, is taken to be the positive
152+integer
153+.Ar N .
154+.It Fl y
155+If there are too few entries to make up the output dimensions,
156+pad the output by recycling the input from the beginning.
157+Normally, the output is padded with blanks.
158+.It Fl z
159+Adapt column widths to fit the largest entries appearing in them.
160+.El
161+.Pp
162+With no arguments,
163+.Nm
164+transposes its input, and assumes one array entry per input line
165+unless the first non-ignored line is longer than the display width.
166+Option letters which take numerical arguments interpret a missing
167+number as zero unless otherwise indicated.
168+.Sh EXAMPLES
169+.Nm
170+can be used as a filter to convert the stream output
171+of certain programs (e.g.,
172+.Xr spell 1 ,
173+.Xr du 1 ,
174+.Xr file 1 ,
175+.Xr look 1 ,
176+.Xr nm 1 ,
177+.Xr who 1 ,
178+and
179+.Xr wc 1 )
180+into a convenient
181+.Dq window
182+format, as in
183+.Dl who | rs
184+.Pp
185+This function has been incorporated into the
186+.Xr ls 1
187+program, though for most programs with similar output
188+.Nm
189+suffices.
190+.Pp
191+To convert stream input into vector output and back again, use
192+.Dl rs 1 0 | rs 0 1
193+A 10 by 10 array of random numbers from 1 to 100 and
194+its transpose can be generated with
195+.Dl "jot \-r 100 | rs 10 10 | tee array | rs \-T > tarray"
196+.Pp
197+In the editor
198+.Xr vi 1 ,
199+a file consisting of a multi-line vector with 9 elements per line
200+can undergo insertions and deletions,
201+and then be neatly reshaped into 9 columns with
202+.Dl :1,$!rs 0 9
203+.Pp
204+Finally, to sort a database by the first line of each 4-line field, try
205+.Dl "rs \-eC 0 4 | sort | rs \-c 0 1"
206+.Sh SEE ALSO
207+.Xr jot 1 ,
208+.Xr pr 1 ,
209+.Xr sort 1 ,
210+.Xr vi 1
211+.Sh HISTORY
212+The
213+.Nm
214+utility first appeared in
215+.Bx 4.2 .
216+.Sh AUTHORS
217+.An John A. Kunze
218+.Sh BUGS
219+Handles only two dimensional arrays.
220+.Pp
221+The algorithm currently reads the whole file into memory,
222+so files that do not fit in memory will not be reshaped.
223+.Pp
224+Fields cannot be defined yet on character positions.
225+.Pp
226+Re-ordering of columns is not yet possible.
227+.Pp
228+There are too many options.
+560, -0
  1@@ -0,0 +1,560 @@
  2+/*	$NetBSD: rs.c,v 1.18 2026/01/10 08:09:03 mlelstv 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+#include <sys/cdefs.h>
 34+#ifndef lint
 35+__COPYRIGHT("@(#) Copyright (c) 1993\
 36+ The Regents of the University of California.  All rights reserved.");
 37+#endif /* not lint */
 38+
 39+#ifndef lint
 40+#if 0
 41+static char sccsid[] = "@(#)rs.c	8.1 (Berkeley) 6/6/93";
 42+#else
 43+__RCSID("$NetBSD: rs.c,v 1.18 2026/01/10 08:09:03 mlelstv Exp $");
 44+#endif
 45+#endif /* not lint */
 46+
 47+/*
 48+ *	rs - reshape a data array
 49+ *	Author:  John Kunze, Office of Comp. Affairs, UCB
 50+ *		BEWARE: lots of unfinished edges
 51+ */
 52+
 53+#include <ctype.h>
 54+#include <err.h>
 55+#include <stdio.h>
 56+#include <stdlib.h>
 57+#include <string.h>
 58+#include <stdarg.h>
 59+
 60+static long	flags;
 61+#define	TRANSPOSE	000001
 62+#define	MTRANSPOSE	000002
 63+#define	ONEPERLINE	000004
 64+#define	ONEISEPONLY	000010
 65+#define	ONEOSEPONLY	000020
 66+#define	NOTRIMENDCOL	000040
 67+#define	SQUEEZE		000100
 68+#define	SHAPEONLY	000200
 69+#define	DETAILSHAPE	000400
 70+#define	RIGHTADJUST	001000
 71+#define	NULLPAD		002000
 72+#define	RECYCLE		004000
 73+#define	SKIPPRINT	010000
 74+#define	ICOLBOUNDS	020000
 75+#define	OCOLBOUNDS	040000
 76+#define ONEPERCHAR	0100000
 77+#define NOARGS		0200000
 78+
 79+static short	*colwidths;
 80+static short	*cord;
 81+static short	*icbd;
 82+static short	*ocbd;
 83+static int	nelem;
 84+static char	**elem;
 85+static char	**endelem;
 86+static char	*curline;
 87+static int	allocsize = BUFSIZ;
 88+static int	curlen;
 89+static int	irows, icols;
 90+static int	orows, ocols;
 91+static int	maxlen;
 92+static int	skip;
 93+static int	propgutter;
 94+static char	isep = ' ', osep = ' ';
 95+static int	owidth = 80, gutter = 2;
 96+
 97+static void	  usage(const char *, ...) __dead __printflike(1, 2);
 98+static void	  getargs(int, char *[]);
 99+static void	  getfile(void);
100+static int	  get_line(void);
101+static char	 *getlist(short **, char *);
102+static char	 *getnum(int *, char *, int);
103+static char	**getptrs(char **);
104+static void	  prepfile(void);
105+static void	  prints(char *, int);
106+static void	  putfile(void);
107+
108+#define INCR(ep) do {			\
109+	if (++ep >= endelem)		\
110+		ep = getptrs(ep);	\
111+} while(0)
112+
113+int
114+main(int argc, char *argv[])
115+{
116+	getargs(argc, argv);
117+	getfile();
118+	if (flags & SHAPEONLY) {
119+		printf("%d %d\n", irows, icols);
120+		exit(0);
121+	}
122+	prepfile();
123+	putfile();
124+	exit(0);
125+}
126+
127+static void
128+getfile(void)
129+{
130+	char empty[1] = { '\0' };
131+	char *p;
132+	char *endp;
133+	char **ep = 0;
134+	int multisep = (flags & ONEISEPONLY ? 0 : 1);
135+	int nullpad = flags & NULLPAD;
136+	char **padto;
137+
138+	while (skip--) {
139+		get_line();
140+		if (flags & SKIPPRINT)
141+			puts(curline);
142+	}
143+	get_line();
144+	if (flags & NOARGS && curlen < owidth)
145+		flags |= ONEPERLINE;
146+	if (flags & ONEPERLINE)
147+		icols = 1;
148+	else				/* count cols on first line */
149+		for (p = curline, endp = curline + curlen; p < endp; p++) {
150+			if (*p == isep && multisep)
151+				continue;
152+			icols++;
153+			while (*p && *p != isep)
154+				p++;
155+		}
156+	ep = getptrs(elem);
157+	p = curline;
158+	do {
159+		if (flags & ONEPERLINE) {
160+			*ep = curline;
161+			INCR(ep);		/* prepare for next entry */
162+			if (maxlen < curlen)
163+				maxlen = curlen;
164+			irows++;
165+			continue;
166+		}
167+		for (p = curline, endp = curline + curlen; p < endp; p++) {
168+			if (*p == isep && multisep)
169+				continue;	/* eat up column separators */
170+			if (*p == isep)		/* must be an empty column */
171+				*ep = empty;
172+			else			/* store column entry */
173+				*ep = p;
174+			while (p < endp && *p != isep)
175+				p++;		/* find end of entry */
176+			*p = '\0';		/* mark end of entry */
177+			if (maxlen < p - *ep)	/* update maxlen */
178+				maxlen = p - *ep;
179+			INCR(ep);		/* prepare for next entry */
180+		}
181+		irows++;			/* update row count */
182+		if (nullpad) {			/* pad missing entries */
183+			padto = elem + irows * icols;
184+			while (ep < padto) {
185+				*ep = empty;
186+				INCR(ep);
187+			}
188+		}
189+	} while (get_line() != EOF);
190+	*ep = 0;				/* mark end of pointers */
191+	nelem = ep - elem;
192+}
193+
194+static void
195+putfile(void)
196+{
197+	char **ep;
198+	int i, j, n;
199+
200+	ep = elem;
201+	if (flags & TRANSPOSE) {
202+		for (i = 0; i < orows; i++) {
203+			for (j = i; j < nelem; j += orows)
204+				prints(ep[j], (j - i) / orows);
205+			putchar('\n');
206+		}
207+	} else {
208+		for (n = 0, i = 0; i < orows && n < nelem; i++) {
209+			for (j = 0; j < ocols; j++) {
210+				if (n++ >= nelem)
211+					break;
212+				prints(*ep++, j);
213+			}
214+			putchar('\n');
215+		}
216+	}
217+}
218+
219+static void
220+prints(char *s, int col)
221+{
222+	int n;
223+	char *p = s;
224+
225+	while (*p)
226+		p++;
227+	n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s));
228+	if (flags & RIGHTADJUST)
229+		while (n-- > 0)
230+			putchar(osep);
231+	for (p = s; *p; p++)
232+		putchar(*p);
233+	while (n-- > 0)
234+		putchar(osep);
235+}
236+
237+static void
238+usage(const char *msg, ...)
239+{
240+	va_list ap;
241+
242+	va_start(ap, msg);
243+	vwarnx(msg, ap);
244+	va_end(ap);
245+	fprintf(stderr,
246+"usage:  rs [ -[csCS][x][kKgGw][N]tTeEnyjhHm ] [ rows [ cols ] ]\n");
247+	exit(1);
248+}
249+
250+static void
251+prepfile(void)
252+{
253+	char **ep;
254+	int  i;
255+	int  j;
256+	char **lp;
257+	int colw;
258+	int max = 0;
259+	int n;
260+
261+	ep = NULL;
262+	if (!nelem)
263+		exit(0);
264+	gutter += maxlen * propgutter / 100.0;
265+	colw = maxlen + gutter;
266+	if (flags & MTRANSPOSE) {
267+		orows = icols;
268+		ocols = irows;
269+	}
270+	else if (orows == 0 && ocols == 0) {	/* decide rows and cols */
271+		ocols = owidth / colw;
272+		if (ocols == 0) {
273+			warnx("Display width %d is less than column width %d", owidth, colw);
274+			ocols = 1;
275+		}
276+		if (ocols > nelem)
277+			ocols = nelem;
278+		orows = nelem / ocols + (nelem % ocols ? 1 : 0);
279+	}
280+	else if (orows == 0)			/* decide on rows */
281+		orows = nelem / ocols + (nelem % ocols ? 1 : 0);
282+	else if (ocols == 0)			/* decide on cols */
283+		ocols = nelem / orows + (nelem % orows ? 1 : 0);
284+	lp = elem + orows * ocols;
285+	while (lp > endelem) {
286+		getptrs(elem + nelem);
287+		lp = elem + orows * ocols;
288+	}
289+	if (flags & RECYCLE) {
290+		for (ep = elem + nelem; ep < lp; ep++)
291+			*ep = *(ep - nelem);
292+		nelem = lp - elem;
293+	}
294+	if (!(colwidths = (short *) malloc(ocols * sizeof(short))))
295+		errx(1, "malloc:  No gutter space");
296+	if (flags & SQUEEZE) {
297+		if (flags & TRANSPOSE)
298+			for (ep = elem, i = 0; i < ocols; i++) {
299+				for (j = 0; j < orows; j++) {
300+					if (ep - elem >= nelem)
301+						break;
302+					if ((n = strlen(*ep++)) > max)
303+						max = n;
304+				}
305+				colwidths[i] = max + gutter;
306+			}
307+		else
308+			for (ep = elem, i = 0; i < ocols; i++) {
309+				for (j = i; j < nelem; j += ocols)
310+					if ((n = strlen(ep[j])) > max)
311+						max = n;
312+				colwidths[i] = max + gutter;
313+			}
314+	}
315+	/*	for (i = 0; i < orows; i++) {
316+			for (j = i; j < nelem; j += orows)
317+				prints(ep[j], (j - i) / orows);
318+			putchar('\n');
319+		}
320+	else
321+		for (i = 0; i < orows; i++) {
322+			for (j = 0; j < ocols; j++)
323+				prints(*ep++, j);
324+			putchar('\n');
325+		}*/
326+	else
327+		for (i = 0; i < ocols; i++)
328+			colwidths[i] = colw;
329+	if (!(flags & NOTRIMENDCOL)) {
330+		if (flags & RIGHTADJUST)
331+			colwidths[0] -= gutter;
332+		else
333+			colwidths[ocols - 1] = 0;
334+	}
335+	n = orows * ocols;
336+	if (n > nelem && (flags & RECYCLE))
337+		nelem = n;
338+	/*for (i = 0; i < ocols; i++)
339+		fprintf(stderr, "%d ",colwidths[i]);
340+	fprintf(stderr, "is colwidths, nelem %d\n", nelem);*/
341+}
342+
343+#define	BSIZE	2048
344+char	ibuf[BSIZE];		/* two screenfuls should do */
345+
346+static int
347+get_line(void)	/* get line; maintain curline, curlen; manage storage */
348+{
349+	static	int putlength;
350+	static	char *endblock = ibuf + BSIZE;
351+	char *p;
352+	int c, i;
353+
354+	if (!irows) {
355+		curline = ibuf;
356+		putlength = flags & DETAILSHAPE;
357+	}
358+	else if (skip <= 0) {			/* don't waste storage */
359+		curline += curlen + 1;
360+		if (putlength)		/* print length, recycle storage */
361+			printf(" %d line %d\n", curlen, irows);
362+	}
363+	if (!putlength && endblock - curline < BUFSIZ) {   /* need storage */
364+		/*ww = endblock-curline; tt += ww;*/
365+		/*printf("#wasted %d total %d\n",ww,tt);*/
366+		if (!(curline = (char *) malloc(BSIZE)))
367+			errx(1, "File too large");
368+		endblock = curline + BSIZE;
369+		/*printf("#endb %d curline %d\n",endblock,curline);*/
370+	}
371+	for (p = curline, i = 1; i < BUFSIZ; *p++ = c, i++)
372+		if ((c = getchar()) == EOF || c == '\n')
373+			break;
374+	*p = '\0';
375+	curlen = i - 1;
376+	return(c);
377+}
378+
379+static char **
380+getptrs(char **sp)
381+{
382+	char **p;
383+	ptrdiff_t off;
384+
385+	allocsize += allocsize;
386+	off = sp - elem;
387+	p = (char **)realloc(elem, allocsize * sizeof(char *));
388+	if (p == (char **)0)
389+		err(1, "no memory");
390+
391+	sp = p + off;
392+	endelem = (elem = p) + allocsize;
393+	return(sp);
394+}
395+
396+static void
397+getargs(int ac, char *av[])
398+{
399+	char *p;
400+
401+	if (ac == 1) {
402+		flags |= NOARGS | TRANSPOSE;
403+	}
404+	while (--ac && **++av == '-')
405+		for (p = *av+1; *p; p++)
406+			switch (*p) {
407+			case 'T':
408+				flags |= MTRANSPOSE;
409+				/* FALLTHROUGH */
410+			case 't':
411+				flags |= TRANSPOSE;
412+				break;
413+			case 'c':		/* input col. separator */
414+				flags |= ONEISEPONLY;
415+				/* FALLTHROUGH */
416+			case 's':		/* one or more allowed */
417+				if (p[1])
418+					isep = *++p;
419+				else
420+					isep = '\t';	/* default is ^I */
421+				break;
422+			case 'C':
423+				flags |= ONEOSEPONLY;
424+				/* FALLTHROUGH */
425+			case 'S':
426+				if (p[1])
427+					osep = *++p;
428+				else
429+					osep = '\t';	/* default is ^I */
430+				break;
431+			case 'w':		/* window width, default 80 */
432+				p = getnum(&owidth, p, 0);
433+				if (owidth <= 0)
434+				usage("Width must be a positive integer");
435+				break;
436+			case 'K':			/* skip N lines */
437+				flags |= SKIPPRINT;
438+				/* FALLTHROUGH */
439+			case 'k':			/* skip, do not print */
440+				p = getnum(&skip, p, 0);
441+				if (!skip)
442+					skip = 1;
443+				break;
444+			case 'm':
445+				flags |= NOTRIMENDCOL;
446+				break;
447+			case 'g':		/* gutter space */
448+				p = getnum(&gutter, p, 0);
449+				break;
450+			case 'G':
451+				p = getnum(&propgutter, p, 0);
452+				break;
453+			case 'e':		/* each line is an entry */
454+				flags |= ONEPERLINE;
455+				break;
456+			case 'E':
457+				flags |= ONEPERCHAR;
458+				break;
459+			case 'j':			/* right adjust */
460+				flags |= RIGHTADJUST;
461+				break;
462+			case 'n':	/* null padding for missing values */
463+				flags |= NULLPAD;
464+				break;
465+			case 'y':
466+				flags |= RECYCLE;
467+				break;
468+			case 'H':			/* print shape only */
469+				flags |= DETAILSHAPE;
470+				/* FALLTHROUGH */
471+			case 'h':
472+				flags |= SHAPEONLY;
473+				break;
474+			case 'z':			/* squeeze col width */
475+				flags |= SQUEEZE;
476+				break;
477+			/*case 'p':
478+				ipagespace = atoi(++p);	(default is 1)
479+				break;*/
480+			case 'o':			/* col order */
481+				p = getlist(&cord, p);
482+				break;
483+			case 'b':
484+				flags |= ICOLBOUNDS;
485+				p = getlist(&icbd, p);
486+				break;
487+			case 'B':
488+				flags |= OCOLBOUNDS;
489+				p = getlist(&ocbd, p);
490+				break;
491+			default:
492+				usage("Bad flag:  %.1s", p);
493+			}
494+	/*if (!osep)
495+		osep = isep;*/
496+	switch (ac) {
497+	/*case 3:
498+		opages = atoi(av[2]);*/
499+		/* FALLTHROUGH */
500+	case 2:
501+		ocols = atoi(av[1]);
502+		/* FALLTHROUGH */
503+	case 1:
504+		orows = atoi(av[0]);
505+		/* FALLTHROUGH */
506+	case 0:
507+		break;
508+	default:
509+		usage("Too many arguments.");
510+	}
511+}
512+
513+static char *
514+getlist(short **list, char *p)
515+{
516+	int count = 1;
517+	char *t;
518+
519+	for (t = p + 1; *t; t++) {
520+		if (!isdigit((unsigned char)*t))
521+			usage("Option %.1s requires a list of unsigned numbers separated by commas", t);
522+		count++;
523+		while (*t && isdigit((unsigned char)*t))
524+			t++;
525+		if (*t != ',')
526+			break;
527+	}
528+	if (!(*list = (short *) malloc(count * sizeof(short))))
529+		errx(1, "No list space");
530+	count = 0;
531+	for (t = p + 1; *t; t++) {
532+		(*list)[count++] = atoi(t);
533+		printf("++ %d ", (*list)[count-1]);
534+		fflush(stdout);
535+		while (*t && isdigit((unsigned char)*t))
536+			t++;
537+		if (*t != ',')
538+			break;
539+	}
540+	(*list)[count] = 0;
541+	return(t - 1);
542+}
543+
544+static char *
545+getnum(int *num, char *p, int strict)	/* num = number p points to; if (strict) complain */
546+					/* returns pointer to end of num */
547+{
548+	char *t = p;
549+
550+	if (!isdigit((unsigned char)*++t)) {
551+		if (strict || *t == '-' || *t == '+')
552+			usage("Option %.1s requires an unsigned integer", p);
553+		*num = 0;
554+		return(p);
555+	}
556+	*num = atoi(t);
557+	while (*++t)
558+		if (!isdigit((unsigned char)*t))
559+			break;
560+	return(--t);
561+}
+28, -0
 1@@ -0,0 +1,28 @@
 2+.POSIX:
 3+
 4+PROG = script
 5+SRCS = script.c ../compat/getprogname.c
 6+MAN = script.1
 7+
 8+CC = cc
 9+CFLAGS = -O2
10+CPPFLAGS = -I../compat -include ../compat/netcompat.h
11+LDLIBS = -lutil
12+DESTDIR =
13+BINDIR = /usr/local/bin
14+MANDIR = /usr/local/share/man
15+
16+all: $(PROG)
17+
18+$(PROG): $(SRCS)
19+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LDLIBS)
20+
21+install: $(PROG)
22+	mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
23+	cp $(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
24+	chmod 755 $(DESTDIR)$(BINDIR)/$(PROG)
25+	cp $(MAN) $(DESTDIR)$(MANDIR)/man1/$(MAN)
26+	chmod 644 $(DESTDIR)$(MANDIR)/man1/$(MAN)
27+
28+clean:
29+	rm -f $(PROG) *.o
+149, -0
  1@@ -0,0 +1,149 @@
  2+.\"	$NetBSD: script.1,v 1.14 2022/01/16 19:04:00 christos Exp $
  3+.\"
  4+.\" Copyright (c) 1980, 1990, 1993
  5+.\"	The Regents of the University of California.  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. Neither the name of the University nor the names of its contributors
 16+.\"    may be used to endorse or promote products derived from this software
 17+.\"    without specific prior written permission.
 18+.\"
 19+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 20+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 21+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 22+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 23+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 24+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 25+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 26+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 27+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 28+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 29+.\" SUCH DAMAGE.
 30+.\"
 31+.\"	@(#)script.1	8.1 (Berkeley) 6/6/93
 32+.\"
 33+.Dd January 16, 2022
 34+.Dt SCRIPT 1
 35+.Os
 36+.Sh NAME
 37+.Nm script
 38+.Nd make typescript of terminal session
 39+.Sh SYNOPSIS
 40+.Nm
 41+.Op Fl adefpqr
 42+.Op Fl c Ar command
 43+.Op Ar file
 44+.Sh DESCRIPTION
 45+.Nm
 46+makes a typescript of everything printed on your terminal.
 47+It is useful for students who need a hardcopy record of an interactive
 48+session as proof of an assignment, as the typescript file
 49+can be printed out later with
 50+.Xr lpr 1 .
 51+.Pp
 52+If the argument
 53+.Ar file
 54+is given,
 55+.Nm
 56+saves all dialogue in
 57+.Ar file .
 58+If no file name is given, the typescript is saved in the file
 59+.Pa typescript  .
 60+.Pp
 61+Option:
 62+.Bl -tag -width Ds
 63+.It Fl a
 64+Append the output to
 65+.Ar file
 66+or
 67+.Pa typescript ,
 68+retaining the prior contents.
 69+.It Fl c Ar command
 70+Run the named
 71+.Ar command
 72+instead of the shell.
 73+Useful for capturing the output of a program that behaves differently when
 74+associated with a tty.
 75+.It Fl d
 76+When playing back a session with the
 77+.Fl p
 78+flag, don't sleep between records when playing back a timestamped session.
 79+.It Fl e
 80+Exit with the shell-style exit status of the shell or
 81+.Ar command ,
 82+instead of always exiting successfully.
 83+.It Fl f
 84+Flush output after each write.
 85+This is useful for watching the script output in real time.
 86+.It Fl p
 87+Play back a session recorded with the
 88+.Fl r
 89+flag in real time.
 90+.It Fl q
 91+Be quiet, and don't output started and ended lines.
 92+.It Fl r
 93+Record a session with input, output, and timestamping.
 94+.El
 95+.Pp
 96+The script ends when the forked shell exits (a
 97+.Em control-D
 98+to exit
 99+the Bourne shell
100+.Pf ( Xr sh 1 ) ,
101+and
102+.Em exit ,
103+.Em logout
104+or
105+.Em control-d
106+(if
107+.Em ignoreeof
108+is not set) for the
109+C-shell,
110+.Xr csh 1 ) .
111+.Pp
112+Certain interactive commands, such as
113+.Xr vi 1 ,
114+create garbage in the typescript file.
115+.Nm
116+works best with commands that do not manipulate the
117+screen, the results are meant to emulate a hardcopy
118+terminal.
119+.Sh ENVIRONMENT
120+The following environment variable is used by
121+.Nm :
122+.Bl -tag -width SHELL
123+.It Ev SHELL
124+If the variable
125+.Ev SHELL
126+exists, the shell forked by
127+.Nm
128+will be that shell.
129+If
130+.Ev SHELL
131+is not set, the Bourne shell
132+is assumed.
133+(Most shells set this variable automatically).
134+.El
135+.Sh SEE ALSO
136+.Xr csh 1
137+(for the
138+.Em history
139+mechanism).
140+.Sh HISTORY
141+The
142+.Nm
143+command appeared in
144+.Bx 3.0 .
145+.Sh BUGS
146+.Nm
147+places
148+.Sy everything
149+in the log file, including linefeeds and backspaces.
150+This is not what the naive user expects.
+523, -0
  1@@ -0,0 +1,523 @@
  2+/*	$NetBSD: script.c,v 1.34 2023/05/09 15:43:39 hgutch Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 1980, 1992, 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+#ifndef lint
 35+__COPYRIGHT("@(#) Copyright (c) 1980, 1992, 1993\
 36+ The Regents of the University of California.  All rights reserved.");
 37+#endif /* not lint */
 38+
 39+#ifndef lint
 40+#if 0
 41+static char sccsid[] = "@(#)script.c	8.1 (Berkeley) 6/6/93";
 42+#endif
 43+__RCSID("$NetBSD: script.c,v 1.34 2023/05/09 15:43:39 hgutch Exp $");
 44+#endif /* not lint */
 45+
 46+#include <sys/types.h>
 47+#include <sys/wait.h>
 48+#include <sys/stat.h>
 49+#include <sys/ioctl.h>
 50+#include <sys/time.h>
 51+#include <sys/param.h>
 52+#include <sys/uio.h>
 53+
 54+#include <err.h>
 55+#include <byteswap.h>
 56+#include <errno.h>
 57+#include <fcntl.h>
 58+#include <paths.h>
 59+#include <signal.h>
 60+#include <stdio.h>
 61+#include <stdlib.h>
 62+#include <string.h>
 63+#include <termios.h>
 64+#include <time.h>
 65+#include <tzfile.h>
 66+#include <unistd.h>
 67+#include <pty.h>
 68+#include <utmp.h>
 69+
 70+#define	DEF_BUF	65536
 71+
 72+struct stamp {
 73+	uint64_t scr_len;	/* amount of data */
 74+	uint64_t scr_sec;	/* time it arrived in seconds... */
 75+	uint32_t scr_usec;	/* ...and microseconds */
 76+	uint32_t scr_direction;	/* 'i', 'o', etc (also indicates endianness) */
 77+};
 78+
 79+static FILE	*fscript;
 80+static int	master, slave;
 81+static int	child, subchild;
 82+static size_t	outcc;
 83+static int	usesleep, rawout;
 84+static int	quiet, flush;
 85+static const char *fname;
 86+
 87+static volatile	sig_atomic_t die = 0;	/* exit if 1 */
 88+static int	cstat = EXIT_SUCCESS;	/* cmd. exit status */
 89+static int	eflag;
 90+static int	isterm;
 91+static struct	termios tt;
 92+
 93+__dead static void	done(int);
 94+__dead static void	doshell(const char *);
 95+__dead static void	fail(void);
 96+static sig_t	xsignal(int, sig_t);
 97+__dead static void	dooutput(void);
 98+static void	finish(int);
 99+static void	scriptflush(int);
100+static void	record(FILE *, char *, size_t, int);
101+static void	consume(FILE *, off_t, char *, int);
102+__dead static void	playback(FILE *);
103+
104+int
105+main(int argc, char *argv[])
106+{
107+	ssize_t scc;
108+	size_t cc;
109+	struct termios rtt;
110+	struct winsize win;
111+	int aflg, pflg, ch;
112+	char ibuf[BUFSIZ];
113+	const char *command;
114+
115+	aflg = 0;
116+	pflg = 0;
117+	usesleep = 1;
118+	rawout = 0;
119+	quiet = 0;
120+	flush = 0;
121+	command = NULL;
122+	while ((ch = getopt(argc, argv, "ac:defpqr")) != -1)
123+		switch(ch) {
124+		case 'a':
125+			aflg = 1;
126+			break;
127+		case 'c':
128+			command = optarg;
129+			break;
130+		case 'd':
131+			usesleep = 0;
132+			break;
133+		case 'e':
134+			eflag = 1;
135+			break;
136+		case 'f':
137+			flush = 1;
138+			break;
139+		case 'p':
140+			pflg = 1;
141+			break;
142+		case 'q':
143+			quiet = 1;
144+			break;
145+		case 'r':
146+			rawout = 1;
147+			break;
148+		case '?':
149+		default:
150+			(void)fprintf(stderr,
151+			    "Usage: %s [-c <command>][-adefpqr] [file]\n",
152+			    getprogname());
153+			exit(EXIT_FAILURE);
154+		}
155+	argc -= optind;
156+	argv += optind;
157+
158+	if (argc > 0)
159+		fname = argv[0];
160+	else
161+		fname = "typescript";
162+
163+	if ((fscript = fopen(fname, pflg ? "r" : aflg ? "a" : "w")) == NULL)
164+		err(EXIT_FAILURE, "fopen %s", fname);
165+
166+	if (pflg)
167+		playback(fscript);
168+
169+	if (tcgetattr(STDIN_FILENO, &tt) == -1 ||
170+	    ioctl(STDIN_FILENO, TIOCGWINSZ, &win) == -1) {
171+		if (errno != ENOTTY) /* For debugger. */
172+			err(EXIT_FAILURE, "tcgetattr/ioctl");
173+		if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
174+			err(EXIT_FAILURE, "openpty");
175+	} else {
176+		if (openpty(&master, &slave, NULL, &tt, &win) == -1)
177+			err(EXIT_FAILURE, "openpty");
178+		isterm = 1;
179+	}
180+
181+	if (!quiet)
182+		(void)printf("Script started, output file is %s\n", fname);
183+
184+	if (isterm) {
185+		rtt = tt;
186+		cfmakeraw(&rtt);
187+		rtt.c_lflag &= ~ECHO;
188+		(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
189+	}
190+
191+	(void)xsignal(SIGCHLD, finish);
192+	child = fork();
193+	if (child == -1) {
194+		warn("fork");
195+		fail();
196+	}
197+	if (child == 0) {
198+		(void)xsignal(SIGCHLD, SIG_DFL);
199+		subchild = child = fork();
200+		if (child == -1) {
201+			warn("fork");
202+			fail();
203+		}
204+		if (child)
205+			dooutput();
206+		else
207+			doshell(command);
208+	}
209+
210+	if (!rawout)
211+		(void)fclose(fscript);
212+	while (!die && (scc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0) {
213+		cc = (size_t)scc;
214+		if (rawout)
215+			record(fscript, ibuf, cc, 'i');
216+		(void)write(master, ibuf, cc);
217+	}
218+	done(cstat);
219+}
220+
221+/**
222+ * wrapper around sigaction() because we want POSIX semantics:
223+ * no auto-restarting of interrupted slow syscalls.
224+ */
225+static sig_t
226+xsignal(int signo, sig_t handler)
227+{
228+	struct sigaction sa, osa;
229+
230+	sa.sa_handler = handler;
231+	sa.sa_flags = 0;
232+	sigemptyset(&sa.sa_mask);
233+	if (sigaction(signo, &sa, &osa) == -1)
234+		return SIG_ERR;
235+	return osa.sa_handler;
236+}
237+
238+static int
239+getshellstatus(int status)
240+{
241+	if (WIFEXITED(status))
242+		return WEXITSTATUS(status);
243+	if (WIFSIGNALED(status))
244+		return 128 + WTERMSIG(status);
245+	return EXIT_FAILURE;
246+}
247+
248+static void
249+finish(int signo)
250+{
251+	int pid, status;
252+
253+	die = 0;
254+	while ((pid = wait(&status)) > 0)
255+		if (pid == child) {
256+			die = 1;
257+		}
258+
259+	if (!die)
260+		return;
261+	done(eflag ? getshellstatus(status) : EXIT_SUCCESS);
262+}
263+
264+static void
265+dooutput(void)
266+{
267+	struct itimerval value;
268+	ssize_t scc;
269+	size_t cc;
270+	time_t tvec;
271+	char obuf[BUFSIZ];
272+
273+	(void)close(STDIN_FILENO);
274+	tvec = time(NULL);
275+	if (rawout)
276+		record(fscript, NULL, 0, 's');
277+	else if (!quiet)
278+		(void)fprintf(fscript, "Script started on %s", ctime(&tvec));
279+
280+	(void)signal(SIGALRM, scriptflush);
281+	value.it_interval.tv_sec = SECSPERMIN / 2;
282+	value.it_interval.tv_usec = 0;
283+	value.it_value = value.it_interval;
284+	(void)setitimer(ITIMER_REAL, &value, NULL);
285+	for (;;) {
286+		scc = read(master, obuf, sizeof(obuf));
287+		if (scc <= 0)
288+			break;
289+		cc = (size_t)scc;
290+		(void)write(STDOUT_FILENO, obuf, cc);
291+		if (rawout)
292+			record(fscript, obuf, cc, 'o');
293+		else
294+			(void)fwrite(obuf, 1, cc, fscript);
295+		outcc += cc;
296+		if (flush)
297+			(void)fflush(fscript);
298+	}
299+	done(cstat);
300+}
301+
302+static void
303+scriptflush(int signo)
304+{
305+	if (outcc) {
306+		(void)fflush(fscript);
307+		outcc = 0;
308+	}
309+}
310+
311+static void
312+doshell(const char *command)
313+{
314+	const char *shell;
315+
316+	(void)close(master);
317+	(void)fclose(fscript);
318+	login_tty(slave);
319+	if (command == NULL) {
320+		shell = getenv("SHELL");
321+		if (shell == NULL)
322+			shell = _PATH_BSHELL;
323+		execl(shell, shell, "-i", NULL);
324+		warn("execl `%s'", shell);
325+	} else {
326+		int ret = system(command);
327+		if (ret == -1)
328+			warn("system `%s'", command);
329+		else
330+			exit(eflag ? getshellstatus(ret) : EXIT_FAILURE);
331+	}
332+
333+	fail();
334+}
335+
336+static void
337+fail(void)
338+{
339+
340+	(void)kill(0, SIGTERM);
341+	done(EXIT_FAILURE);
342+}
343+
344+static void
345+done(int status)
346+{
347+	time_t tvec;
348+
349+	if (subchild) {
350+		tvec = time(NULL);
351+		if (rawout)
352+			record(fscript, NULL, 0, 'e');
353+		else if (!quiet)
354+			(void)fprintf(fscript,"\nScript done on %s",
355+			    ctime(&tvec));
356+		(void)fclose(fscript);
357+		(void)close(master);
358+	} else {
359+		if (isterm)
360+			(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
361+		if (!quiet)
362+			(void)printf("Script done, output file is %s\n", fname);
363+	}
364+	exit(status);
365+}
366+
367+static void
368+record(FILE *fp, char *buf, size_t cc, int direction)
369+{
370+	struct iovec iov[2];
371+	struct stamp stamp;
372+	struct timeval tv;
373+
374+	(void)gettimeofday(&tv, NULL);
375+	stamp.scr_len = cc;
376+	stamp.scr_sec = tv.tv_sec;
377+	stamp.scr_usec = tv.tv_usec;
378+	stamp.scr_direction = direction;
379+	iov[0].iov_len = sizeof(stamp);
380+	iov[0].iov_base = &stamp;
381+	iov[1].iov_len = cc;
382+	iov[1].iov_base = buf;
383+	if (writev(fileno(fp), &iov[0], 2) == -1)
384+		err(EXIT_FAILURE, "writev");
385+}
386+
387+static void
388+consume(FILE *fp, off_t len, char *buf, int reg)
389+{
390+	size_t l;
391+
392+	if (reg) {
393+		if (fseeko(fp, len, SEEK_CUR) == -1)
394+			err(EXIT_FAILURE, NULL);
395+	}
396+	else {
397+		while (len > 0) {
398+			l = MIN(DEF_BUF, len);
399+			if (fread(buf, sizeof(char), l, fp) != l)
400+				err(EXIT_FAILURE, "cannot read buffer");
401+			len -= l;
402+		}
403+	}
404+}
405+
406+#define swapstamp(stamp) do { \
407+	if (stamp.scr_direction > 0xff) { \
408+		stamp.scr_len = bswap64(stamp.scr_len); \
409+		stamp.scr_sec = bswap64(stamp.scr_sec); \
410+		stamp.scr_usec = bswap32(stamp.scr_usec); \
411+		stamp.scr_direction = bswap32(stamp.scr_direction); \
412+	} \
413+} while (0/*CONSTCOND*/)
414+
415+static void
416+termset(void)
417+{
418+	struct termios traw;
419+
420+	if (tcgetattr(STDOUT_FILENO, &tt) == -1) {
421+		if (errno != ENOTTY) /* For debugger. */
422+			err(EXIT_FAILURE, "tcgetattr");
423+		return;
424+	}
425+	isterm = 1;
426+	traw = tt;
427+	cfmakeraw(&traw);
428+	traw.c_lflag |= ISIG;
429+        (void)tcsetattr(STDOUT_FILENO, TCSANOW, &traw);
430+}
431+
432+static void
433+termreset(void)
434+{
435+	if (isterm)
436+		(void)tcsetattr(STDOUT_FILENO, TCSADRAIN, &tt);
437+
438+	isterm = 0;
439+}
440+
441+static void
442+playback(FILE *fp)
443+{
444+	struct timespec tsi, tso;
445+	struct stamp stamp;
446+	struct stat pst;
447+	char buf[DEF_BUF];
448+	off_t nread, save_len;
449+	size_t l;
450+	time_t tclock;
451+	int reg;
452+
453+	if (fstat(fileno(fp), &pst) == -1)
454+		err(EXIT_FAILURE, "fstat failed");
455+
456+	reg = S_ISREG(pst.st_mode);
457+
458+	for (nread = 0; !reg || nread < pst.st_size; nread += save_len) {
459+		if (fread(&stamp, sizeof(stamp), 1, fp) != 1) {
460+			if (reg)
461+				err(EXIT_FAILURE, "reading playback header");
462+			else
463+				break;
464+		}
465+		swapstamp(stamp);
466+		save_len = sizeof(stamp);
467+
468+		if (reg && stamp.scr_len >
469+		    (uint64_t)(pst.st_size - save_len) - nread)
470+			errx(EXIT_FAILURE, "invalid stamp");
471+
472+		save_len += stamp.scr_len;
473+		tclock = stamp.scr_sec;
474+		tso.tv_sec = stamp.scr_sec;
475+		tso.tv_nsec = stamp.scr_usec * 1000;
476+
477+		switch (stamp.scr_direction) {
478+		case 's':
479+			if (!quiet)
480+			    (void)printf("Script started on %s",
481+				ctime(&tclock));
482+			tsi = tso;
483+			(void)consume(fp, stamp.scr_len, buf, reg);
484+			termset();
485+			atexit(termreset);
486+			break;
487+		case 'e':
488+			termreset();
489+			if (!quiet)
490+				(void)printf("\nScript done on %s",
491+				    ctime(&tclock));
492+			(void)consume(fp, stamp.scr_len, buf, reg);
493+			break;
494+		case 'i':
495+			/* throw input away */
496+			(void)consume(fp, stamp.scr_len, buf, reg);
497+			break;
498+		case 'o':
499+			tsi.tv_sec = tso.tv_sec - tsi.tv_sec;
500+			tsi.tv_nsec = tso.tv_nsec - tsi.tv_nsec;
501+			if (tsi.tv_nsec < 0) {
502+				tsi.tv_sec -= 1;
503+				tsi.tv_nsec += 1000000000;
504+			}
505+			if (usesleep)
506+				(void)nanosleep(&tsi, NULL);
507+			tsi = tso;
508+			while (stamp.scr_len > 0) {
509+				l = MIN(DEF_BUF, stamp.scr_len);
510+				if (fread(buf, sizeof(char), l, fp) != l)
511+					err(EXIT_FAILURE, "cannot read buffer");
512+
513+				(void)write(STDOUT_FILENO, buf, l);
514+				stamp.scr_len -= l;
515+			}
516+			break;
517+		default:
518+			errx(EXIT_FAILURE, "invalid direction %u",
519+			    stamp.scr_direction);
520+		}
521+	}
522+	(void)fclose(fp);
523+	exit(EXIT_SUCCESS);
524+}