commit b63693d
shrub
·
2026-05-26 14:34:59 +0000 UTC
parent b63693d
initial commit
A
Makefile
+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+}
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+}
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
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.
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+}
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);
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+}
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+}
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+}
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+}
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+}
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 *);
A
pr/pr.1
+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 .
A
pr/pr.c
+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+}
A
pr/pr.h
+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
A
rs/rs.1
+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.
A
rs/rs.c
+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+}