summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorPeter Wemm <peter@FreeBSD.org>1997-03-11 13:08:12 +0000
committerPeter Wemm <peter@FreeBSD.org>1997-03-11 13:08:12 +0000
commit0da30e9aa76c5df66cd092f30b904824b0594ccc (patch)
treeb9c456274fa3eebeb19145d7b05cc474de4967a3 /usr.bin
parented0051823057e576a8e86f421232888b53559e82 (diff)
downloadsrc-test2-0da30e9aa76c5df66cd092f30b904824b0594ccc.tar.gz
src-test2-0da30e9aa76c5df66cd092f30b904824b0594ccc.zip
Notes
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/basename/basename.c139
-rw-r--r--usr.bin/col/col.c538
-rw-r--r--usr.bin/colrm/colrm.c168
-rw-r--r--usr.bin/column/column.c305
-rw-r--r--usr.bin/comm/comm.c187
-rw-r--r--usr.bin/cut/cut.c298
-rw-r--r--usr.bin/dirname/dirname.c145
-rw-r--r--usr.bin/du/du.c230
-rw-r--r--usr.bin/find/find.1461
-rw-r--r--usr.bin/find/find.c202
-rw-r--r--usr.bin/find/function.c1055
-rw-r--r--usr.bin/find/main.c154
-rw-r--r--usr.bin/finger/extern.h49
-rw-r--r--usr.bin/finger/finger.1166
-rw-r--r--usr.bin/finger/finger.c268
-rw-r--r--usr.bin/finger/lprint.c336
-rw-r--r--usr.bin/finger/net.c149
-rw-r--r--usr.bin/finger/sprint.c151
-rw-r--r--usr.bin/finger/util.c357
-rw-r--r--usr.bin/fstat/fstat.c748
-rw-r--r--usr.bin/head/head.c182
-rw-r--r--usr.bin/hexdump/hexsyntax.c129
-rw-r--r--usr.bin/hexdump/odsyntax.c265
-rw-r--r--usr.bin/join/join.1215
-rw-r--r--usr.bin/join/join.c587
-rw-r--r--usr.bin/look/look.c359
-rw-r--r--usr.bin/mt/mt.c276
-rw-r--r--usr.bin/printenv/printenv.c100
-rw-r--r--usr.bin/rev/rev.c110
-rw-r--r--usr.bin/tr/str.c342
-rw-r--r--usr.bin/tr/tr.c290
-rw-r--r--usr.bin/uname/uname.c163
-rw-r--r--usr.bin/uniq/uniq.c276
33 files changed, 9400 insertions, 0 deletions
diff --git a/usr.bin/basename/basename.c b/usr.bin/basename/basename.c
new file mode 100644
index 000000000000..4925b274edc0
--- /dev/null
+++ b/usr.bin/basename/basename.c
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)basename.c 8.4 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *p;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1 && argc != 2)
+ usage();
+
+ /*
+ * (1) If string is // it is implementation defined whether steps (2)
+ * through (5) are skipped or processed.
+ *
+ * (2) If string consists entirely of slash characters, string shall
+ * be set to a single slash character. In this case, skip steps
+ * (3) through (5).
+ */
+ for (p = *argv;; ++p) {
+ if (!*p) {
+ if (p > *argv)
+ (void)printf("/\n");
+ else
+ (void)printf("\n");
+ exit(0);
+ }
+ if (*p != '/')
+ break;
+ }
+
+ /*
+ * (3) If there are any trailing slash characters in string, they
+ * shall be removed.
+ */
+ for (; *p; ++p)
+ continue;
+ while (*--p == '/')
+ continue;
+ *++p = '\0';
+
+ /*
+ * (4) If there are any slash characters remaining in string, the
+ * prefix of string up to an including the last slash character
+ * in string shall be removed.
+ */
+ while (--p >= *argv)
+ if (*p == '/')
+ break;
+ ++p;
+
+ /*
+ * (5) If the suffix operand is present, is not identical to the
+ * characters remaining in string, and is identical to a suffix
+ * of the characters remaining in string, the suffix suffix
+ * shall be removed from string.
+ */
+ if (*++argv) {
+ int suffixlen, stringlen, off;
+
+ suffixlen = strlen(*argv);
+ stringlen = strlen(p);
+
+ if (suffixlen < stringlen) {
+ off = stringlen - suffixlen;
+ if (!strcmp(p + off, *argv))
+ p[off] = '\0';
+ }
+ }
+ (void)printf("%s\n", p);
+ exit(0);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: basename string [suffix]\n");
+ exit(1);
+}
diff --git a/usr.bin/col/col.c b/usr.bin/col/col.c
new file mode 100644
index 000000000000..84582819716a
--- /dev/null
+++ b/usr.bin/col/col.c
@@ -0,0 +1,538 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Rendell of the Memorial University of Newfoundland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)col.c 8.5 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define BS '\b' /* backspace */
+#define TAB '\t' /* tab */
+#define SPACE ' ' /* space */
+#define NL '\n' /* newline */
+#define CR '\r' /* carriage return */
+#define ESC '\033' /* escape */
+#define SI '\017' /* shift in to normal character set */
+#define SO '\016' /* shift out to alternate character set */
+#define VT '\013' /* vertical tab (aka reverse line feed) */
+#define RLF '\007' /* ESC-07 reverse line feed */
+#define RHLF '\010' /* ESC-010 reverse half-line feed */
+#define FHLF '\011' /* ESC-011 forward half-line feed */
+
+/* build up at least this many lines before flushing them out */
+#define BUFFER_MARGIN 32
+
+typedef char CSET;
+
+typedef struct char_str {
+#define CS_NORMAL 1
+#define CS_ALTERNATE 2
+ short c_column; /* column character is in */
+ CSET c_set; /* character set (currently only 2) */
+ char c_char; /* character in question */
+} CHAR;
+
+typedef struct line_str LINE;
+struct line_str {
+ CHAR *l_line; /* characters on the line */
+ LINE *l_prev; /* previous line */
+ LINE *l_next; /* next line */
+ int l_lsize; /* allocated sizeof l_line */
+ int l_line_len; /* strlen(l_line) */
+ int l_needs_sort; /* set if chars went in out of order */
+ int l_max_col; /* max column in the line */
+};
+
+LINE *alloc_line __P((void));
+void dowarn __P((int));
+void flush_line __P((LINE *));
+void flush_lines __P((int));
+void flush_blanks __P((void));
+void free_line __P((LINE *));
+void usage __P((void));
+void wrerr __P((void));
+void *xmalloc __P((void *, size_t));
+
+CSET last_set; /* char_set of last char printed */
+LINE *lines;
+int compress_spaces; /* if doing space -> tab conversion */
+int fine; /* if `fine' resolution (half lines) */
+int max_bufd_lines; /* max # lines to keep in memory */
+int nblank_lines; /* # blanks after last flushed line */
+int no_backspaces; /* if not to output any backspaces */
+
+#define PUTC(ch) \
+ if (putchar(ch) == EOF) \
+ wrerr();
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+ CHAR *c;
+ CSET cur_set; /* current character set */
+ LINE *l; /* current line */
+ int extra_lines; /* # of lines above first line */
+ int cur_col; /* current column */
+ int cur_line; /* line number of current position */
+ int max_line; /* max value of cur_line */
+ int this_line; /* line l points to */
+ int nflushd_lines; /* number of lines that were flushed */
+ int adjust, opt, warned;
+
+ max_bufd_lines = 128;
+ compress_spaces = 1; /* compress spaces into tabs */
+ while ((opt = getopt(argc, argv, "bfhl:x")) != EOF)
+ switch (opt) {
+ case 'b': /* do not output backspaces */
+ no_backspaces = 1;
+ break;
+ case 'f': /* allow half forward line feeds */
+ fine = 1;
+ break;
+ case 'h': /* compress spaces into tabs */
+ compress_spaces = 1;
+ break;
+ case 'l': /* buffered line count */
+ if ((max_bufd_lines = atoi(optarg)) <= 0) {
+ (void)fprintf(stderr,
+ "col: bad -l argument %s.\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'x': /* do not compress spaces into tabs */
+ compress_spaces = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (optind != argc)
+ usage();
+
+ /* this value is in half lines */
+ max_bufd_lines *= 2;
+
+ adjust = cur_col = extra_lines = warned = 0;
+ cur_line = max_line = nflushd_lines = this_line = 0;
+ cur_set = last_set = CS_NORMAL;
+ lines = l = alloc_line();
+
+ while ((ch = getchar()) != EOF) {
+ if (!isgraph(ch)) {
+ switch (ch) {
+ case BS: /* can't go back further */
+ if (cur_col == 0)
+ continue;
+ --cur_col;
+ continue;
+ case CR:
+ cur_col = 0;
+ continue;
+ case ESC: /* just ignore EOF */
+ switch(getchar()) {
+ case RLF:
+ cur_line -= 2;
+ break;
+ case RHLF:
+ cur_line--;
+ break;
+ case FHLF:
+ cur_line++;
+ if (cur_line > max_line)
+ max_line = cur_line;
+ }
+ continue;
+ case NL:
+ cur_line += 2;
+ if (cur_line > max_line)
+ max_line = cur_line;
+ cur_col = 0;
+ continue;
+ case SPACE:
+ ++cur_col;
+ continue;
+ case SI:
+ cur_set = CS_NORMAL;
+ continue;
+ case SO:
+ cur_set = CS_ALTERNATE;
+ continue;
+ case TAB: /* adjust column */
+ cur_col |= 7;
+ ++cur_col;
+ continue;
+ case VT:
+ cur_line -= 2;
+ continue;
+ }
+ continue;
+ }
+
+ /* Must stuff ch in a line - are we at the right one? */
+ if (cur_line != this_line - adjust) {
+ LINE *lnew;
+ int nmove;
+
+ adjust = 0;
+ nmove = cur_line - this_line;
+ if (!fine) {
+ /* round up to next line */
+ if (cur_line & 1) {
+ adjust = 1;
+ nmove++;
+ }
+ }
+ if (nmove < 0) {
+ for (; nmove < 0 && l->l_prev; nmove++)
+ l = l->l_prev;
+ if (nmove) {
+ if (nflushd_lines == 0) {
+ /*
+ * Allow backup past first
+ * line if nothing has been
+ * flushed yet.
+ */
+ for (; nmove < 0; nmove++) {
+ lnew = alloc_line();
+ l->l_prev = lnew;
+ lnew->l_next = l;
+ l = lines = lnew;
+ extra_lines++;
+ }
+ } else {
+ if (!warned++)
+ dowarn(cur_line);
+ cur_line -= nmove;
+ }
+ }
+ } else {
+ /* may need to allocate here */
+ for (; nmove > 0 && l->l_next; nmove--)
+ l = l->l_next;
+ for (; nmove > 0; nmove--) {
+ lnew = alloc_line();
+ lnew->l_prev = l;
+ l->l_next = lnew;
+ l = lnew;
+ }
+ }
+ this_line = cur_line + adjust;
+ nmove = this_line - nflushd_lines;
+ if (nmove >= max_bufd_lines + BUFFER_MARGIN) {
+ nflushd_lines += nmove - max_bufd_lines;
+ flush_lines(nmove - max_bufd_lines);
+ }
+ }
+ /* grow line's buffer? */
+ if (l->l_line_len + 1 >= l->l_lsize) {
+ int need;
+
+ need = l->l_lsize ? l->l_lsize * 2 : 90;
+ l->l_line = (CHAR *)xmalloc((void *) l->l_line,
+ (unsigned) need * sizeof(CHAR));
+ l->l_lsize = need;
+ }
+ c = &l->l_line[l->l_line_len++];
+ c->c_char = ch;
+ c->c_set = cur_set;
+ c->c_column = cur_col;
+ /*
+ * If things are put in out of order, they will need sorting
+ * when it is flushed.
+ */
+ if (cur_col < l->l_max_col)
+ l->l_needs_sort = 1;
+ else
+ l->l_max_col = cur_col;
+ cur_col++;
+ }
+ if (max_line == 0)
+ exit(0); /* no lines, so just exit */
+
+ /* goto the last line that had a character on it */
+ for (; l->l_next; l = l->l_next)
+ this_line++;
+ flush_lines(this_line - nflushd_lines + extra_lines + 1);
+
+ /* make sure we leave things in a sane state */
+ if (last_set != CS_NORMAL)
+ PUTC('\017');
+
+ /* flush out the last few blank lines */
+ nblank_lines = max_line - this_line;
+ if (max_line & 1)
+ nblank_lines++;
+ else if (!nblank_lines)
+ /* missing a \n on the last line? */
+ nblank_lines = 2;
+ flush_blanks();
+ exit(0);
+}
+
+void
+flush_lines(nflush)
+ int nflush;
+{
+ LINE *l;
+
+ while (--nflush >= 0) {
+ l = lines;
+ lines = l->l_next;
+ if (l->l_line) {
+ flush_blanks();
+ flush_line(l);
+ }
+ nblank_lines++;
+ if (l->l_line)
+ (void)free((void *)l->l_line);
+ free_line(l);
+ }
+ if (lines)
+ lines->l_prev = NULL;
+}
+
+/*
+ * Print a number of newline/half newlines. If fine flag is set, nblank_lines
+ * is the number of half line feeds, otherwise it is the number of whole line
+ * feeds.
+ */
+void
+flush_blanks()
+{
+ int half, i, nb;
+
+ half = 0;
+ nb = nblank_lines;
+ if (nb & 1) {
+ if (fine)
+ half = 1;
+ else
+ nb++;
+ }
+ nb /= 2;
+ for (i = nb; --i >= 0;)
+ PUTC('\n');
+ if (half) {
+ PUTC('\033');
+ PUTC('9');
+ if (!nb)
+ PUTC('\r');
+ }
+ nblank_lines = 0;
+}
+
+/*
+ * Write a line to stdout taking care of space to tab conversion (-h flag)
+ * and character set shifts.
+ */
+void
+flush_line(l)
+ LINE *l;
+{
+ CHAR *c, *endc;
+ int nchars, last_col, this_col;
+
+ last_col = 0;
+ nchars = l->l_line_len;
+
+ if (l->l_needs_sort) {
+ static CHAR *sorted;
+ static int count_size, *count, i, save, sorted_size, tot;
+
+ /*
+ * Do an O(n) sort on l->l_line by column being careful to
+ * preserve the order of characters in the same column.
+ */
+ if (l->l_lsize > sorted_size) {
+ sorted_size = l->l_lsize;
+ sorted = (CHAR *)xmalloc((void *)sorted,
+ (unsigned)sizeof(CHAR) * sorted_size);
+ }
+ if (l->l_max_col >= count_size) {
+ count_size = l->l_max_col + 1;
+ count = (int *)xmalloc((void *)count,
+ (unsigned)sizeof(int) * count_size);
+ }
+ memset((char *)count, 0, sizeof(int) * l->l_max_col + 1);
+ for (i = nchars, c = l->l_line; --i >= 0; c++)
+ count[c->c_column]++;
+
+ /*
+ * calculate running total (shifted down by 1) to use as
+ * indices into new line.
+ */
+ for (tot = 0, i = 0; i <= l->l_max_col; i++) {
+ save = count[i];
+ count[i] = tot;
+ tot += save;
+ }
+
+ for (i = nchars, c = l->l_line; --i >= 0; c++)
+ sorted[count[c->c_column]++] = *c;
+ c = sorted;
+ } else
+ c = l->l_line;
+ while (nchars > 0) {
+ this_col = c->c_column;
+ endc = c;
+ do {
+ ++endc;
+ } while (--nchars > 0 && this_col == endc->c_column);
+
+ /* if -b only print last character */
+ if (no_backspaces)
+ c = endc - 1;
+
+ if (this_col > last_col) {
+ int nspace = this_col - last_col;
+
+ if (compress_spaces && nspace > 1) {
+ int ntabs;
+
+ ntabs = this_col / 8 - last_col / 8;
+ nspace -= ntabs * 8;
+ while (--ntabs >= 0)
+ PUTC('\t');
+ }
+ while (--nspace >= 0)
+ PUTC(' ');
+ last_col = this_col;
+ }
+ last_col++;
+
+ for (;;) {
+ if (c->c_set != last_set) {
+ switch (c->c_set) {
+ case CS_NORMAL:
+ PUTC('\017');
+ break;
+ case CS_ALTERNATE:
+ PUTC('\016');
+ }
+ last_set = c->c_set;
+ }
+ PUTC(c->c_char);
+ if (++c >= endc)
+ break;
+ PUTC('\b');
+ }
+ }
+}
+
+#define NALLOC 64
+
+static LINE *line_freelist;
+
+LINE *
+alloc_line()
+{
+ LINE *l;
+ int i;
+
+ if (!line_freelist) {
+ l = (LINE *)xmalloc((void *)NULL, sizeof(LINE) * NALLOC);
+ line_freelist = l;
+ for (i = 1; i < NALLOC; i++, l++)
+ l->l_next = l + 1;
+ l->l_next = NULL;
+ }
+ l = line_freelist;
+ line_freelist = l->l_next;
+
+ memset(l, 0, sizeof(LINE));
+ return (l);
+}
+
+void
+free_line(l)
+ LINE *l;
+{
+
+ l->l_next = line_freelist;
+ line_freelist = l;
+}
+
+void *
+xmalloc(p, size)
+ void *p;
+ size_t size;
+{
+
+ if (!(p = (void *)realloc(p, size)))
+ err(1, NULL);
+ return (p);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: col [-bfx] [-l nline]\n");
+ exit(1);
+}
+
+void
+wrerr()
+{
+
+ (void)fprintf(stderr, "col: write error.\n");
+ exit(1);
+}
+
+void
+dowarn(line)
+ int line;
+{
+
+ warnx("warning: can't back up %s",
+ line < 0 ? "past first line" : "-- line already flushed");
+}
diff --git a/usr.bin/colrm/colrm.c b/usr.bin/colrm/colrm.c
new file mode 100644
index 000000000000..06f6232a3c1f
--- /dev/null
+++ b/usr.bin/colrm/colrm.c
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)colrm.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define TAB 8
+
+void err __P((const char *, ...));
+void check __P((FILE *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register u_long column, start, stop;
+ register int ch;
+ char *p;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ start = stop = 0;
+ switch(argc) {
+ case 2:
+ stop = strtol(argv[1], &p, 10);
+ if (stop <= 0 || *p)
+ err("illegal column -- %s", argv[1]);
+ /* FALLTHROUGH */
+ case 1:
+ start = strtol(argv[0], &p, 10);
+ if (start <= 0 || *p)
+ err("illegal column -- %s", argv[0]);
+ break;
+ case 0:
+ break;
+ default:
+ usage();
+ }
+
+ if (stop && start > stop)
+ err("illegal start and stop columns");
+
+ for (column = 0;;) {
+ switch (ch = getchar()) {
+ case EOF:
+ check(stdin);
+ break;
+ case '\b':
+ if (column)
+ --column;
+ break;
+ case '\n':
+ column = 0;
+ break;
+ case '\t':
+ column = (column + TAB) & ~(TAB - 1);
+ break;
+ default:
+ ++column;
+ break;
+ }
+
+ if ((!start || column < start || stop && column > stop) &&
+ putchar(ch) == EOF)
+ check(stdout);
+ }
+}
+
+void
+check(stream)
+ FILE *stream;
+{
+ if (feof(stream))
+ exit(0);
+ if (ferror(stream))
+ err("%s: %s",
+ stream == stdin ? "stdin" : "stdout", strerror(errno));
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: colrm [start [stop]]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "colrm: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/column/column.c b/usr.bin/column/column.c
new file mode 100644
index 000000000000..5dbab1cb070a
--- /dev/null
+++ b/usr.bin/column/column.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)column.c 8.4 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void c_columnate __P((void));
+void *emalloc __P((int));
+void input __P((FILE *));
+void maketbl __P((void));
+void print __P((void));
+void r_columnate __P((void));
+void usage __P((void));
+
+int termwidth = 80; /* default terminal width */
+
+int entries; /* number of records */
+int eval; /* exit value */
+int maxlength; /* longest record */
+char **list; /* array of pointers to records */
+char *separator = "\t "; /* field separator for table option */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct winsize win;
+ FILE *fp;
+ int ch, tflag, xflag;
+ char *p;
+
+ if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
+ if (p = getenv("COLUMNS"))
+ termwidth = atoi(p);
+ } else
+ termwidth = win.ws_col;
+
+ tflag = xflag = 0;
+ while ((ch = getopt(argc, argv, "c:s:tx")) != EOF)
+ switch(ch) {
+ case 'c':
+ termwidth = atoi(optarg);
+ break;
+ case 's':
+ separator = optarg;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ input(stdin);
+ else for (; *argv; ++argv)
+ if (fp = fopen(*argv, "r")) {
+ input(fp);
+ (void)fclose(fp);
+ } else {
+ warn("%s", *argv);
+ eval = 1;
+ }
+
+ if (!entries)
+ exit(eval);
+
+ if (tflag)
+ maketbl();
+ else if (maxlength >= termwidth)
+ print();
+ else if (xflag)
+ c_columnate();
+ else
+ r_columnate();
+ exit(eval);
+}
+
+#define TAB 8
+void
+c_columnate()
+{
+ int chcnt, col, cnt, endcol, numcols;
+ char **lp;
+
+ maxlength = (maxlength + TAB) & ~(TAB - 1);
+ numcols = termwidth / maxlength;
+ endcol = maxlength;
+ for (chcnt = col = 0, lp = list;; ++lp) {
+ chcnt += printf("%s", *lp);
+ if (!--entries)
+ break;
+ if (++col == numcols) {
+ chcnt = col = 0;
+ endcol = maxlength;
+ putchar('\n');
+ } else {
+ while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
+ (void)putchar('\t');
+ chcnt = cnt;
+ }
+ endcol += maxlength;
+ }
+ }
+ if (chcnt)
+ putchar('\n');
+}
+
+void
+r_columnate()
+{
+ int base, chcnt, cnt, col, endcol, numcols, numrows, row;
+
+ maxlength = (maxlength + TAB) & ~(TAB - 1);
+ numcols = termwidth / maxlength;
+ numrows = entries / numcols;
+ if (entries % numcols)
+ ++numrows;
+
+ for (row = 0; row < numrows; ++row) {
+ endcol = maxlength;
+ for (base = row, chcnt = col = 0; col < numcols; ++col) {
+ chcnt += printf("%s", list[base]);
+ if ((base += numrows) >= entries)
+ break;
+ while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
+ (void)putchar('\t');
+ chcnt = cnt;
+ }
+ endcol += maxlength;
+ }
+ putchar('\n');
+ }
+}
+
+void
+print()
+{
+ int cnt;
+ char **lp;
+
+ for (cnt = entries, lp = list; cnt--; ++lp)
+ (void)printf("%s\n", *lp);
+}
+
+typedef struct _tbl {
+ char **list;
+ int cols, *len;
+} TBL;
+#define DEFCOLS 25
+
+void
+maketbl()
+{
+ TBL *t;
+ int coloff, cnt;
+ char *p, **lp;
+ int *lens, maxcols;
+ TBL *tbl;
+ char **cols;
+
+ t = tbl = emalloc(entries * sizeof(TBL));
+ cols = emalloc((maxcols = DEFCOLS) * sizeof(char *));
+ lens = emalloc(maxcols * sizeof(int));
+ for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
+ for (coloff = 0, p = *lp; cols[coloff] = strtok(p, separator);
+ p = NULL)
+ if (++coloff == maxcols) {
+ if (!(cols = realloc(cols, (u_int)maxcols +
+ DEFCOLS * sizeof(char *))) ||
+ !(lens = realloc(lens,
+ (u_int)maxcols + DEFCOLS * sizeof(int))))
+ err(1, NULL);
+ memset((char *)lens + maxcols * sizeof(int),
+ 0, DEFCOLS * sizeof(int));
+ maxcols += DEFCOLS;
+ }
+ t->list = emalloc(coloff * sizeof(char *));
+ t->len = emalloc(coloff * sizeof(int));
+ for (t->cols = coloff; --coloff >= 0;) {
+ t->list[coloff] = cols[coloff];
+ t->len[coloff] = strlen(cols[coloff]);
+ if (t->len[coloff] > lens[coloff])
+ lens[coloff] = t->len[coloff];
+ }
+ }
+ for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
+ for (coloff = 0; coloff < t->cols - 1; ++coloff)
+ (void)printf("%s%*s", t->list[coloff],
+ lens[coloff] - t->len[coloff] + 2, " ");
+ (void)printf("%s\n", t->list[coloff]);
+ }
+}
+
+#define DEFNUM 1000
+#define MAXLINELEN (LINE_MAX + 1)
+
+void
+input(fp)
+ FILE *fp;
+{
+ static int maxentry;
+ int len;
+ char *p, buf[MAXLINELEN];
+
+ if (!list)
+ list = emalloc((maxentry = DEFNUM) * sizeof(char *));
+ while (fgets(buf, MAXLINELEN, fp)) {
+ for (p = buf; *p && isspace(*p); ++p);
+ if (!*p)
+ continue;
+ if (!(p = strchr(p, '\n'))) {
+ warnx("line too long");
+ eval = 1;
+ continue;
+ }
+ *p = '\0';
+ len = p - buf;
+ if (maxlength < len)
+ maxlength = len;
+ if (entries == maxentry) {
+ maxentry += DEFNUM;
+ if (!(list = realloc(list,
+ (u_int)maxentry * sizeof(char *))))
+ err(1, NULL);
+ }
+ list[entries++] = strdup(buf);
+ }
+}
+
+void *
+emalloc(size)
+ int size;
+{
+ char *p;
+
+ if (!(p = malloc(size)))
+ err(1, NULL);
+ memset(p, 0, size);
+ return (p);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: column [-tx] [-c columns] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/comm/comm.c b/usr.bin/comm/comm.c
new file mode 100644
index 000000000000..82f995fc76e2
--- /dev/null
+++ b/usr.bin/comm/comm.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Case Larsen.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)comm.c 8.4 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXLINELEN (LINE_MAX + 1)
+
+char *tabs[] = { "", "\t", "\t\t" };
+
+FILE *file __P((char *));
+void show __P((FILE *, char *, char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int comp, file1done, file2done, read1, read2;
+ int ch, flag1, flag2, flag3;
+ FILE *fp1, *fp2;
+ char *col1, *col2, *col3;
+ char **p, line1[MAXLINELEN], line2[MAXLINELEN];
+
+ flag1 = flag2 = flag3 = 1;
+ while ((ch = getopt(argc, argv, "-123")) != EOF)
+ switch(ch) {
+ case '-':
+ --optind;
+ goto done;
+ case '1':
+ flag1 = 0;
+ break;
+ case '2':
+ flag2 = 0;
+ break;
+ case '3':
+ flag3 = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+done: argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ fp1 = file(argv[0]);
+ fp2 = file(argv[1]);
+
+ /* for each column printed, add another tab offset */
+ p = tabs;
+ col1 = col2 = col3 = NULL;
+ if (flag1)
+ col1 = *p++;
+ if (flag2)
+ col2 = *p++;
+ if (flag3)
+ col3 = *p;
+
+ for (read1 = read2 = 1;;) {
+ /* read next line, check for EOF */
+ if (read1)
+ file1done = !fgets(line1, MAXLINELEN, fp1);
+ if (read2)
+ file2done = !fgets(line2, MAXLINELEN, fp2);
+
+ /* if one file done, display the rest of the other file */
+ if (file1done) {
+ if (!file2done && col2)
+ show(fp2, col2, line2);
+ break;
+ }
+ if (file2done) {
+ if (!file1done && col1)
+ show(fp1, col1, line1);
+ break;
+ }
+
+ /* lines are the same */
+ if (!(comp = strcmp(line1, line2))) {
+ read1 = read2 = 1;
+ if (col3)
+ (void)printf("%s%s", col3, line1);
+ continue;
+ }
+
+ /* lines are different */
+ if (comp < 0) {
+ read1 = 1;
+ read2 = 0;
+ if (col1)
+ (void)printf("%s%s", col1, line1);
+ } else {
+ read1 = 0;
+ read2 = 1;
+ if (col2)
+ (void)printf("%s%s", col2, line2);
+ }
+ }
+ exit(0);
+}
+
+void
+show(fp, offset, buf)
+ FILE *fp;
+ char *offset, *buf;
+{
+
+ do {
+ (void)printf("%s%s", offset, buf);
+ } while (fgets(buf, MAXLINELEN, fp));
+}
+
+FILE *
+file(name)
+ char *name;
+{
+ FILE *fp;
+
+ if (!strcmp(name, "-"))
+ return (stdin);
+ if ((fp = fopen(name, "r")) == NULL) {
+ (void)fprintf(stderr, "comm: %s: %s\n", name, strerror(errno));
+ exit(1);
+ }
+ return (fp);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: comm [-123] file1 file2\n");
+ exit(1);
+}
diff --git a/usr.bin/cut/cut.c b/usr.bin/cut/cut.c
new file mode 100644
index 000000000000..15e1524e2e6e
--- /dev/null
+++ b/usr.bin/cut/cut.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam S. Moskowitz of Menlo Consulting and Marciano Pitargue.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int cflag;
+char dchar;
+int dflag;
+int fflag;
+int sflag;
+
+void c_cut __P((FILE *, char *));
+void err __P((const char *, ...));
+void f_cut __P((FILE *, char *));
+void get_list __P((char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FILE *fp;
+ void (*fcn) __P((FILE *, char *));
+ int ch;
+
+ dchar = '\t'; /* default delimiter is \t */
+
+ while ((ch = getopt(argc, argv, "c:d:f:s")) != EOF)
+ switch(ch) {
+ case 'c':
+ fcn = c_cut;
+ get_list(optarg);
+ cflag = 1;
+ break;
+ case 'd':
+ dchar = *optarg;
+ dflag = 1;
+ break;
+ case 'f':
+ get_list(optarg);
+ fcn = f_cut;
+ fflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (fflag) {
+ if (cflag)
+ usage();
+ } else if (!cflag || dflag || sflag)
+ usage();
+
+ if (*argv)
+ for (; *argv; ++argv) {
+ if (!(fp = fopen(*argv, "r")))
+ err("%s: %s\n", *argv, strerror(errno));
+ fcn(fp, *argv);
+ (void)fclose(fp);
+ }
+ else
+ fcn(stdin, "stdin");
+ exit(0);
+}
+
+int autostart, autostop, maxval;
+
+char positions[_POSIX2_LINE_MAX + 1];
+
+void
+get_list(list)
+ char *list;
+{
+ register int setautostart, start, stop;
+ register char *pos;
+ char *p;
+
+ /*
+ * set a byte in the positions array to indicate if a field or
+ * column is to be selected; use +1, it's 1-based, not 0-based.
+ * This parser is less restrictive than the Draft 9 POSIX spec.
+ * POSIX doesn't allow lists that aren't in increasing order or
+ * overlapping lists. We also handle "-3-5" although there's no
+ * real reason too.
+ */
+ for (; p = strtok(list, ", \t"); list = NULL) {
+ setautostart = start = stop = 0;
+ if (*p == '-') {
+ ++p;
+ setautostart = 1;
+ }
+ if (isdigit(*p)) {
+ start = stop = strtol(p, &p, 10);
+ if (setautostart && start > autostart)
+ autostart = start;
+ }
+ if (*p == '-') {
+ if (isdigit(p[1]))
+ stop = strtol(p + 1, &p, 10);
+ if (*p == '-') {
+ ++p;
+ if (!autostop || autostop > stop)
+ autostop = stop;
+ }
+ }
+ if (*p)
+ err("[-cf] list: illegal list value\n");
+ if (!stop || !start)
+ err("[-cf] list: values may not include zero\n");
+ if (stop > _POSIX2_LINE_MAX)
+ err("[-cf] list: %d too large (max %d)\n",
+ stop, _POSIX2_LINE_MAX);
+ if (maxval < stop)
+ maxval = stop;
+ for (pos = positions + start; start++ <= stop; *pos++ = 1);
+ }
+
+ /* overlapping ranges */
+ if (autostop && maxval > autostop)
+ maxval = autostop;
+
+ /* set autostart */
+ if (autostart)
+ memset(positions + 1, '1', autostart);
+}
+
+/* ARGSUSED */
+void
+c_cut(fp, fname)
+ FILE *fp;
+ char *fname;
+{
+ register int ch, col;
+ register char *pos;
+
+ for (;;) {
+ pos = positions + 1;
+ for (col = maxval; col; --col) {
+ if ((ch = getc(fp)) == EOF)
+ return;
+ if (ch == '\n')
+ break;
+ if (*pos++)
+ (void)putchar(ch);
+ }
+ if (ch != '\n')
+ if (autostop)
+ while ((ch = getc(fp)) != EOF && ch != '\n')
+ (void)putchar(ch);
+ else
+ while ((ch = getc(fp)) != EOF && ch != '\n');
+ (void)putchar('\n');
+ }
+}
+
+void
+f_cut(fp, fname)
+ FILE *fp;
+ char *fname;
+{
+ register int ch, field, isdelim;
+ register char *pos, *p, sep;
+ int output;
+ char lbuf[_POSIX2_LINE_MAX + 1];
+
+ for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) {
+ output = 0;
+ for (isdelim = 0, p = lbuf;; ++p) {
+ if (!(ch = *p))
+ err("%s: line too long.\n", fname);
+ /* this should work if newline is delimiter */
+ if (ch == sep)
+ isdelim = 1;
+ if (ch == '\n') {
+ if (!isdelim && !sflag)
+ (void)printf("%s", lbuf);
+ break;
+ }
+ }
+ if (!isdelim)
+ continue;
+
+ pos = positions + 1;
+ for (field = maxval, p = lbuf; field; --field, ++pos) {
+ if (*pos) {
+ if (output++)
+ (void)putchar(sep);
+ while ((ch = *p++) != '\n' && ch != sep)
+ (void)putchar(ch);
+ } else
+ while ((ch = *p++) != '\n' && ch != sep);
+ if (ch == '\n')
+ break;
+ }
+ if (ch != '\n')
+ if (autostop) {
+ if (output)
+ (void)putchar(sep);
+ for (; (ch = *p) != '\n'; ++p)
+ (void)putchar(ch);
+ } else
+ for (; (ch = *p) != '\n'; ++p);
+ (void)putchar('\n');
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage:\tcut -c list [file1 ...]\n\tcut -f list [-s] [-d delim] [file ...]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "cut: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/dirname/dirname.c b/usr.bin/dirname/dirname.c
new file mode 100644
index 000000000000..ca0caf0bcd40
--- /dev/null
+++ b/usr.bin/dirname/dirname.c
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)dirname.c 8.4 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *p;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ /*
+ * (1) If string is //, skip steps (2) through (5).
+ * (2) If string consists entirely of slash characters, string
+ * shall be set to a single slash character. In this case,
+ * skip steps (3) through (8).
+ */
+ for (p = *argv;; ++p) {
+ if (!*p) {
+ if (p > *argv)
+ (void)printf("/\n");
+ else
+ (void)printf(".\n");
+ exit(0);
+ }
+ if (*p != '/')
+ break;
+ }
+
+ /*
+ * (3) If there are any trailing slash characters in string, they
+ * shall be removed.
+ */
+ for (; *p; ++p);
+ while (*--p == '/')
+ continue;
+ *++p = '\0';
+
+ /*
+ * (4) If there are no slash characters remaining in string,
+ * string shall be set to a single period character. In this
+ * case skip steps (5) through (8).
+ *
+ * (5) If there are any trailing nonslash characters in string,
+ * they shall be removed.
+ */
+ while (--p >= *argv)
+ if (*p == '/')
+ break;
+ ++p;
+ if (p == *argv) {
+ (void)printf(".\n");
+ exit(0);
+ }
+
+ /*
+ * (6) If the remaining string is //, it is implementation defined
+ * whether steps (7) and (8) are skipped or processed.
+ *
+ * This case has already been handled, as part of steps (1) and (2).
+ */
+
+ /*
+ * (7) If there are any trailing slash characters in string, they
+ * shall be removed.
+ */
+ while (--p >= *argv)
+ if (*p != '/')
+ break;
+ ++p;
+
+ /*
+ * (8) If the remaining string is empty, string shall be set to
+ * a single slash character.
+ */
+ *p = '\0';
+ (void)printf("%s\n", p == *argv ? "/" : *argv);
+ exit(0);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: dirname path\n");
+ exit(1);
+}
diff --git a/usr.bin/du/du.c b/usr.bin/du/du.c
new file mode 100644
index 000000000000..173ea63b7628
--- /dev/null
+++ b/usr.bin/du/du.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Newcomb.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int linkchk __P((FTSENT *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FTS *fts;
+ FTSENT *p;
+ long blocksize;
+ int ftsoptions, listdirs, listfiles;
+ int Hflag, Lflag, Pflag, aflag, ch, notused, rval, sflag;
+ char **save;
+
+ save = argv;
+ Hflag = Lflag = Pflag = aflag = sflag = 0;
+ ftsoptions = FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "HLPasx")) != EOF)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = Pflag = 0;
+ break;
+ case 'P':
+ Pflag = 1;
+ Hflag = Lflag = 0;
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * XXX
+ * Because of the way that fts(3) works, logical walks will not count
+ * the blocks actually used by symbolic links. We rationalize this by
+ * noting that users computing logical sizes are likely to do logical
+ * copies, so not counting the links is correct. The real reason is
+ * that we'd have to re-implement the kernel's symbolic link traversing
+ * algorithm to get this right. If, for example, you have relative
+ * symbolic links referencing other relative symbolic links, it gets
+ * very nasty, very fast. The bottom line is that it's documented in
+ * the man page, so it's a feature.
+ */
+ if (Hflag)
+ ftsoptions |= FTS_COMFOLLOW;
+ if (Lflag) {
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ }
+
+ if (aflag) {
+ if (sflag)
+ usage();
+ listdirs = listfiles = 1;
+ } else if (sflag)
+ listdirs = listfiles = 0;
+ else {
+ listfiles = 0;
+ listdirs = 1;
+ }
+
+ if (!*argv) {
+ argv = save;
+ argv[0] = ".";
+ argv[1] = NULL;
+ }
+
+ (void)getbsize(&notused, &blocksize);
+ blocksize /= 512;
+
+ if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
+ err(1, NULL);
+
+ for (rval = 0; (p = fts_read(fts)) != NULL;)
+ switch (p->fts_info) {
+ case FTS_D: /* Ignore. */
+ break;
+ case FTS_DP:
+ p->fts_parent->fts_number +=
+ p->fts_number += p->fts_statp->st_blocks;
+ /*
+ * If listing each directory, or not listing files
+ * or directories and this is post-order of the
+ * root of a traversal, display the total.
+ */
+ if (listdirs || !listfiles && !p->fts_level)
+ (void)printf("%ld\t%s\n",
+ howmany(p->fts_number, blocksize),
+ p->fts_path);
+ break;
+ case FTS_DC: /* Ignore. */
+ break;
+ case FTS_DNR: /* Warn, continue. */
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ default:
+ if (p->fts_statp->st_nlink > 1 && linkchk(p))
+ break;
+ /*
+ * If listing each file, or a non-directory file was
+ * the root of a traversal, display the total.
+ */
+ if (listfiles || !p->fts_level)
+ (void)printf("%qd\t%s\n",
+ howmany(p->fts_statp->st_blocks, blocksize),
+ p->fts_path);
+ p->fts_parent->fts_number += p->fts_statp->st_blocks;
+ }
+ if (errno)
+ err(1, "fts_read");
+ exit(0);
+}
+
+typedef struct _ID {
+ dev_t dev;
+ ino_t inode;
+} ID;
+
+int
+linkchk(p)
+ FTSENT *p;
+{
+ static ID *files;
+ static int maxfiles, nfiles;
+ ID *fp, *start;
+ ino_t ino;
+ dev_t dev;
+
+ ino = p->fts_statp->st_ino;
+ dev = p->fts_statp->st_dev;
+ if ((start = files) != NULL)
+ for (fp = start + nfiles - 1; fp >= start; --fp)
+ if (ino == fp->inode && dev == fp->dev)
+ return (1);
+
+ if (nfiles == maxfiles && (files = realloc((char *)files,
+ (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL)
+ err(1, "");
+ files[nfiles].inode = ino;
+ files[nfiles].dev = dev;
+ ++nfiles;
+ return (0);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: du [-H | -L | -P] [-a | -s] [-x] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/find/find.1 b/usr.bin/find/find.1
new file mode 100644
index 000000000000..a5fc1738b6fb
--- /dev/null
+++ b/usr.bin/find/find.1
@@ -0,0 +1,461 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)find.1 8.7 (Berkeley) 5/9/95
+.\"
+.Dd May 9, 1995
+.Dt FIND 1
+.Os
+.Sh NAME
+.Nm find
+.Nd walk a file hierarchy
+.Sh SYNOPSIS
+.Nm find
+.Op Fl H | Fl L | Fl P
+.Op Fl Xdx
+.Op Fl f Ar file
+.Op Ar file ...
+.Ar expression
+.Sh DESCRIPTION
+.Nm Find
+recursively descends the directory tree for each
+.Ar file
+listed, evaluating an
+.Ar expression
+(composed of the ``primaries'' and ``operands'' listed below) in terms
+of each file in the tree.
+.Pp
+The options are as follows:
+.Pp
+.Bl -tag -width Ds
+.It Fl H
+The
+.Fl H
+option causes the file information and file type (see
+.Xr stat 2)
+returned for each symbolic link specified on the command line to be
+those of the file referenced by the link, not the link itself.
+If the referenced file does not exist, the file information and type will
+be for the link itself. File information of all symbolic links not on
+the command line is that of the link itself.
+.It Fl L
+The
+.Fl L
+option causes the file information and file type (see
+.Xr stat 2)
+returned for each symbolic link to be those of the file referenced by the
+link, not the link itself.
+If the referenced file does not exist, the file information and type will
+be for the link itself.
+.It Fl P
+The
+.Fl P
+option causes the file information and file type (see
+.Xr stat 2)
+returned for each symbolic link to be those of the link itself.
+.It Fl X
+The
+.Fl X
+option is a modification to permit
+.Nm
+to be safely used in conjunction with
+.Xr xargs 1 .
+If a file name contains any of the delimiting characters used by
+.Xr xargs ,
+a diagnostic message is displayed on standard error, and the file
+is skipped.
+The delimiting characters include single (`` ' '') and double (`` " '')
+quotes, backslash (``\e''), space, tab and newline characters.
+.It Fl d
+The
+.Fl d
+option causes
+.Nm find
+to perform a depth\-first traversal, i.e. directories
+are visited in post\-order and all entries in a directory will be acted
+on before the directory itself.
+By default,
+.Nm find
+visits directories in pre\-order, i.e. before their contents.
+Note, the default is
+.Ar not
+a breadth\-first traversal.
+.It Fl f
+The
+.Fl f
+option specifies a file hierarchy for
+.Nm find
+to traverse.
+File hierarchies may also be specified as the operands immediately
+following the options.
+.It Fl x
+The
+.Fl x
+option prevents
+.Nm find
+from descending into directories that have a device number different
+than that of the file from which the descent began.
+.El
+.Sh PRIMARIES
+.Bl -tag -width Ds
+.It Ic -atime Ar n
+True if the difference between the file last access time and the time
+.Nm find
+was started, rounded up to the next full 24\-hour period, is
+.Ar n
+24\-hour periods.
+.It Ic -ctime Ar n
+True if the difference between the time of last change of file status
+information and the time
+.Nm find
+was started, rounded up to the next full 24\-hour period, is
+.Ar n
+24\-hour periods.
+.It Ic -exec Ar utility Op argument ... ;
+True if the program named
+.Ar utility
+returns a zero value as its exit status.
+Optional arguments may be passed to the utility.
+The expression must be terminated by a semicolon (``;'').
+If the string ``{}'' appears anywhere in the utility name or the
+arguments it is replaced by the pathname of the current file.
+.Ar Utility
+will be executed from the directory from which
+.Nm find
+was executed.
+.It Ic -fstype Ar type
+True if the file is contained in a file system of type
+.Ar type .
+The
+.Xr sysctl 8
+command can be used to find out the types of filesystems
+that are available on the system:
+.Bd -literal -offset indent
+sysctl vfs
+.Ed
+In addition, there are two pseudo-types, ``local'' and ``rdonly''.
+The former matches any file system physically mounted on the system where
+the
+.Nm find
+is being executed and the latter matches any file system which is
+mounted read-only.
+.It Ic -group Ar gname
+True if the file belongs to the group
+.Ar gname .
+If
+.Ar gname
+is numeric and there is no such group name, then
+.Ar gname
+is treated as a group id.
+.It Ic -inum Ar n
+True if the file has inode number
+.Ar n .
+.It Ic -links Ar n
+True if the file has
+.Ar n
+links.
+.It Ic -ls
+This primary always evaluates to true.
+The following information for the current file is written to standard output:
+its inode number, size in 512\-byte blocks, file permissions, number of hard
+links, owner, group, size in bytes, last modification time, and pathname.
+If the file is a block or character special file, the major and minor numbers
+will be displayed instead of the size in bytes.
+If the file is a symbolic link, the pathname of the linked\-to file will be
+displayed preceded by ``\->''.
+The format is identical to that produced by ``ls \-dgils''.
+.It Ic -mtime Ar n
+True if the difference between the file last modification time and the time
+.Nm find
+was started, rounded up to the next full 24\-hour period, is
+.Ar n
+24\-hour periods.
+.It Ic \&-ok Ar utility Op argument ... ;
+The
+.Ic \&-ok
+primary is identical to the
+.Ic -exec
+primary with the exception that
+.Nm find
+requests user affirmation for the execution of the utility by printing
+a message to the terminal and reading a response.
+If the response is other than ``y'' the command is not executed and the
+value of the
+.Ar \&ok
+expression is false.
+.It Ic -name Ar pattern
+True if the last component of the pathname being examined matches
+.Ar pattern .
+Special shell pattern matching characters (``['', ``]'', ``*'', and ``?'')
+may be used as part of
+.Ar pattern .
+These characters may be matched explicitly by escaping them with a
+backslash (``\e'').
+.It Ic -newer Ar file
+True if the current file has a more recent last modification time than
+.Ar file .
+.It Ic -nouser
+True if the file belongs to an unknown user.
+.It Ic -nogroup
+True if the file belongs to an unknown group.
+.It Ic -path Ar pattern
+True if the pathname being examined matches
+.Ar pattern .
+Special shell pattern matching characters (``['', ``]'', ``*'', and ``?'')
+may be used as part of
+.Ar pattern .
+These characters may be matched explicitly by escaping them with a
+backslash (``\e'').
+Slashes (``/'') are treated as normal characters and do not have to be
+matched explicitly.
+.It Ic -perm Op Fl Ns Ar mode
+The
+.Ar mode
+may be either symbolic (see
+.Xr chmod 1 )
+or an octal number.
+If the mode is symbolic, a starting value of zero is assumed and the
+mode sets or clears permissions without regard to the process' file mode
+creation mask.
+If the mode is octal, only bits 07777
+.Pf ( Dv S_ISUID
+|
+.Dv S_ISGID
+|
+.Dv S_ISTXT
+|
+.Dv S_IRWXU
+|
+.Dv S_IRWXG
+|
+.Dv S_IRWXO )
+of the file's mode bits participate
+in the comparison.
+If the mode is preceded by a dash (``\-''), this primary evaluates to true
+if at least all of the bits in the mode are set in the file's mode bits.
+If the mode is not preceded by a dash, this primary evaluates to true if
+the bits in the mode exactly match the file's mode bits.
+Note, the first character of a symbolic mode may not be a dash (``\-'').
+.It Ic -print
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output.
+If none of
+.Ic -exec ,
+.Ic -ls ,
+or
+.Ic \&-ok
+is specified, the given expression shall be effectively replaced by
+.Cm \&( Ns Ar given\& expression Ns Cm \&)
+.Ic -print .
+.It Ic -prune
+This primary always evaluates to true.
+It causes
+.Nm find
+to not descend into the current file.
+Note, the
+.Ic -prune
+primary has no effect if the
+.Fl d
+option was specified.
+.It Ic -size Ar n Ns Op Cm c
+True if the file's size, rounded up, in 512\-byte blocks is
+.Ar n .
+If
+.Ar n
+is followed by a ``c'', then the primary is true if the
+file's size is
+.Ar n
+bytes.
+.It Ic -type Ar t
+True if the file is of the specified type.
+Possible file types are as follows:
+.Pp
+.Bl -tag -width flag -offset indent -compact
+.It Cm b
+block special
+.It Cm c
+character special
+.It Cm d
+directory
+.It Cm f
+regular file
+.It Cm l
+symbolic link
+.It Cm p
+FIFO
+.It Cm s
+socket
+.El
+.Pp
+.It Ic -user Ar uname
+True if the file belongs to the user
+.Ar uname .
+If
+.Ar uname
+is numeric and there is no such user name, then
+.Ar uname
+is treated as a user id.
+.El
+.Pp
+All primaries which take a numeric argument allow the number to be
+preceded by a plus sign (``+'') or a minus sign (``\-'').
+A preceding plus sign means ``more than n'', a preceding minus sign means
+``less than n'' and neither means ``exactly n'' .
+.Sh OPERATORS
+The primaries may be combined using the following operators.
+The operators are listed in order of decreasing precedence.
+.Bl -tag -width (expression)
+.It Cm \&( Ns Ar expression Ns Cm \&)
+This evaluates to true if the parenthesized expression evaluates to
+true.
+.Pp
+.It Cm \&! Ns Ar expression
+This is the unary
+.Tn NOT
+operator.
+It evaluates to true if the expression is false.
+.Pp
+.It Ar expression Cm -and Ar expression
+.It Ar expression expression
+The
+.Cm -and
+operator is the logical
+.Tn AND
+operator.
+As it is implied by the juxtaposition of two expressions it does not
+have to be specified.
+The expression evaluates to true if both expressions are true.
+The second expression is not evaluated if the first expression is false.
+.Pp
+.It Ar expression Cm -or Ar expression
+The
+.Cm -or
+operator is the logical
+.Tn OR
+operator.
+The expression evaluates to true if either the first or the second expression
+is true.
+The second expression is not evaluated if the first expression is true.
+.El
+.Pp
+All operands and primaries must be separate arguments to
+.Nm find .
+Primaries which themselves take arguments expect each argument
+to be a separate argument to
+.Nm find .
+.Sh EXAMPLES
+.Pp
+The following examples are shown as given to the shell:
+.Bl -tag -width findx
+.It Li "find / \e! -name \*q*.c\*q -print"
+Print out a list of all the files whose names do not end in ``.c''.
+.It Li "find / -newer ttt -user wnj -print"
+Print out a list of all the files owned by user ``wnj'' that are newer
+than the file ``ttt''.
+.It Li "find / \e! \e( -newer ttt -user wnj \e) -print"
+Print out a list of all the files which are not both newer than ``ttt''
+and owned by ``wnj''.
+.It Li "find / \e( -newer ttt -or -user wnj \e) -print"
+Print out a list of all the files that are either owned by ``wnj'' or
+that are newer than ``ttt''.
+.El
+.Sh SEE ALSO
+.Xr chmod 1 ,
+.Xr locate 1 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr getgrent 3 ,
+.Xr getpwent 3 ,
+.Xr strmode 3 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm find
+utility syntax is a superset of the syntax specified by the
+.St -p1003.2
+standard.
+.Pp
+The
+.Fl s
+and
+.Fl X
+options and the
+.Ic -inum
+and
+.Ic -ls
+primaries are extensions to
+.St -p1003.2 .
+.Pp
+Historically, the
+.Fl d ,
+.Fl h
+and
+.Fl x
+options were implemented using the primaries ``\-depth'', ``\-follow'',
+and ``\-xdev''.
+These primaries always evaluated to true.
+As they were really global variables that took effect before the traversal
+began, some legal expressions could have unexpected results.
+An example is the expression ``\-print \-o \-depth''.
+As \-print always evaluates to true, the standard order of evaluation
+implies that \-depth would never be evaluated.
+This is not the case.
+.Pp
+The operator ``-or'' was implemented as ``\-o'', and the operator ``-and''
+was implemented as ``\-a''.
+.Pp
+Historic implementations of the
+.Ic exec
+and
+.Ic ok
+primaries did not replace the string ``{}'' in the utility name or the
+utility arguments if it had preceding or following non-whitespace characters.
+This version replaces it no matter where in the utility name or arguments
+it appears.
+.Sh BUGS
+The special characters used by
+.Nm find
+are also special characters to many shell programs.
+In particular, the characters ``*'', ``['', ``]'', ``?'', ``('', ``)'',
+``!'', ``\e'' and ``;'' may have to be escaped from the shell.
+.Pp
+As there is no delimiter separating options and file names or file
+names and the
+.Ar expression ,
+it is difficult to specify files named ``-xdev'' or ``!''.
+These problems are handled by the
+.Fl f
+option and the
+.Xr getopt 3
+``--'' construct.
diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c
new file mode 100644
index 000000000000..dbc6c689a437
--- /dev/null
+++ b/usr.bin/find/find.c
@@ -0,0 +1,202 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)find.c 8.5 (Berkeley) 8/5/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "find.h"
+
+/*
+ * find_formplan --
+ * process the command line and create a "plan" corresponding to the
+ * command arguments.
+ */
+PLAN *
+find_formplan(argv)
+ char **argv;
+{
+ PLAN *plan, *tail, *new;
+
+ /*
+ * for each argument in the command line, determine what kind of node
+ * it is, create the appropriate node type and add the new plan node
+ * to the end of the existing plan. The resulting plan is a linked
+ * list of plan nodes. For example, the string:
+ *
+ * % find . -name foo -newer bar -print
+ *
+ * results in the plan:
+ *
+ * [-name foo]--> [-newer bar]--> [-print]
+ *
+ * in this diagram, `[-name foo]' represents the plan node generated
+ * by c_name() with an argument of foo and `-->' represents the
+ * plan->next pointer.
+ */
+ for (plan = tail = NULL; *argv;) {
+ if (!(new = find_create(&argv)))
+ continue;
+ if (plan == NULL)
+ tail = plan = new;
+ else {
+ tail->next = new;
+ tail = new;
+ }
+ }
+
+ /*
+ * if the user didn't specify one of -print, -ok or -exec, then -print
+ * is assumed so we bracket the current expression with parens, if
+ * necessary, and add a -print node on the end.
+ */
+ if (!isoutput) {
+ if (plan == NULL) {
+ new = c_print();
+ tail = plan = new;
+ } else {
+ new = c_openparen();
+ new->next = plan;
+ plan = new;
+ new = c_closeparen();
+ tail->next = new;
+ tail = new;
+ new = c_print();
+ tail->next = new;
+ tail = new;
+ }
+ }
+
+ /*
+ * the command line has been completely processed into a search plan
+ * except for the (, ), !, and -o operators. Rearrange the plan so
+ * that the portions of the plan which are affected by the operators
+ * are moved into operator nodes themselves. For example:
+ *
+ * [!]--> [-name foo]--> [-print]
+ *
+ * becomes
+ *
+ * [! [-name foo] ]--> [-print]
+ *
+ * and
+ *
+ * [(]--> [-depth]--> [-name foo]--> [)]--> [-print]
+ *
+ * becomes
+ *
+ * [expr [-depth]-->[-name foo] ]--> [-print]
+ *
+ * operators are handled in order of precedence.
+ */
+
+ plan = paren_squish(plan); /* ()'s */
+ plan = not_squish(plan); /* !'s */
+ plan = or_squish(plan); /* -o's */
+ return (plan);
+}
+
+FTS *tree; /* pointer to top of FTS hierarchy */
+
+/*
+ * find_execute --
+ * take a search plan and an array of search paths and executes the plan
+ * over all FTSENT's returned for the given search paths.
+ */
+int
+find_execute(plan, paths)
+ PLAN *plan; /* search plan */
+ char **paths; /* array of pathnames to traverse */
+{
+ register FTSENT *entry;
+ PLAN *p;
+ int rval;
+
+ if ((tree = fts_open(paths, ftsoptions, (int (*)())NULL)) == NULL)
+ err(1, "ftsopen");
+
+ for (rval = 0; (entry = fts_read(tree)) != NULL;) {
+ switch (entry->fts_info) {
+ case FTS_D:
+ if (isdepth)
+ continue;
+ break;
+ case FTS_DP:
+ if (!isdepth)
+ continue;
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ (void)fflush(stdout);
+ warnx("%s: %s",
+ entry->fts_path, strerror(entry->fts_errno));
+ rval = 1;
+ continue;
+#ifdef FTS_W
+ case FTS_W:
+ continue;
+#endif /* FTS_W */
+ }
+#define BADCH " \t\n\\'\""
+ if (isxargs && strpbrk(entry->fts_path, BADCH)) {
+ (void)fflush(stdout);
+ warnx("%s: illegal path", entry->fts_path);
+ rval = 1;
+ continue;
+ }
+
+ /*
+ * Call all the functions in the execution plan until one is
+ * false or all have been executed. This is where we do all
+ * the work specified by the user on the command line.
+ */
+ for (p = plan; p && (p->eval)(p, entry); p = p->next);
+ }
+ if (errno)
+ err(1, "fts_read");
+ return (rval);
+}
diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c
new file mode 100644
index 000000000000..02ff6f640597
--- /dev/null
+++ b/usr.bin/find/function.c
@@ -0,0 +1,1055 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#include "find.h"
+
+#define COMPARE(a, b) { \
+ switch (plan->flags) { \
+ case F_EQUAL: \
+ return (a == b); \
+ case F_LESSTHAN: \
+ return (a < b); \
+ case F_GREATER: \
+ return (a > b); \
+ default: \
+ abort(); \
+ } \
+}
+
+static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
+
+/*
+ * find_parsenum --
+ * Parse a string of the form [+-]# and return the value.
+ */
+static long
+find_parsenum(plan, option, vp, endch)
+ PLAN *plan;
+ char *option, *vp, *endch;
+{
+ long value;
+ char *endchar, *str; /* Pointer to character ending conversion. */
+
+ /* Determine comparison from leading + or -. */
+ str = vp;
+ switch (*str) {
+ case '+':
+ ++str;
+ plan->flags = F_GREATER;
+ break;
+ case '-':
+ ++str;
+ plan->flags = F_LESSTHAN;
+ break;
+ default:
+ plan->flags = F_EQUAL;
+ break;
+ }
+
+ /*
+ * Convert the string with strtol(). Note, if strtol() returns zero
+ * and endchar points to the beginning of the string we know we have
+ * a syntax error.
+ */
+ value = strtol(str, &endchar, 10);
+ if (value == 0 && endchar == str)
+ errx(1, "%s: %s: illegal numeric value", option, vp);
+ if (endchar[0] && (endch == NULL || endchar[0] != *endch))
+ errx(1, "%s: %s: illegal trailing character", option, vp);
+ if (endch)
+ *endch = endchar[0];
+ return (value);
+}
+
+/*
+ * The value of n for the inode times (atime, ctime, and mtime) is a range,
+ * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
+ * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
+ * user wanted. Correct so that -1 is "less than 1".
+ */
+#define TIME_CORRECT(p, ttype) \
+ if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \
+ ++((p)->t_data);
+
+/*
+ * -atime n functions --
+ *
+ * True if the difference between the file access time and the
+ * current time is n 24 hour periods.
+ */
+int
+f_atime(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ extern time_t now;
+
+ COMPARE((now - entry->fts_statp->st_atime +
+ SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
+}
+
+PLAN *
+c_atime(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_ATIME, f_atime);
+ new->t_data = find_parsenum(new, "-atime", arg, NULL);
+ TIME_CORRECT(new, N_ATIME);
+ return (new);
+}
+/*
+ * -ctime n functions --
+ *
+ * True if the difference between the last change of file
+ * status information and the current time is n 24 hour periods.
+ */
+int
+f_ctime(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ extern time_t now;
+
+ COMPARE((now - entry->fts_statp->st_ctime +
+ SECSPERDAY - 1) / SECSPERDAY, plan->t_data);
+}
+
+PLAN *
+c_ctime(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_CTIME, f_ctime);
+ new->t_data = find_parsenum(new, "-ctime", arg, NULL);
+ TIME_CORRECT(new, N_CTIME);
+ return (new);
+}
+
+/*
+ * -depth functions --
+ *
+ * Always true, causes descent of the directory hierarchy to be done
+ * so that all entries in a directory are acted on before the directory
+ * itself.
+ */
+int
+f_always_true(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (1);
+}
+
+PLAN *
+c_depth()
+{
+ isdepth = 1;
+
+ return (palloc(N_DEPTH, f_always_true));
+}
+
+/*
+ * [-exec | -ok] utility [arg ... ] ; functions --
+ *
+ * True if the executed utility returns a zero value as exit status.
+ * The end of the primary expression is delimited by a semicolon. If
+ * "{}" occurs anywhere, it gets replaced by the current pathname.
+ * The current directory for the execution of utility is the same as
+ * the current directory when the find utility was started.
+ *
+ * The primary -ok is different in that it requests affirmation of the
+ * user before executing the utility.
+ */
+int
+f_exec(plan, entry)
+ register PLAN *plan;
+ FTSENT *entry;
+{
+ extern int dotfd;
+ register int cnt;
+ pid_t pid;
+ int status;
+
+ for (cnt = 0; plan->e_argv[cnt]; ++cnt)
+ if (plan->e_len[cnt])
+ brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
+ entry->fts_path, plan->e_len[cnt]);
+
+ if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
+ return (0);
+
+ switch (pid = vfork()) {
+ case -1:
+ err(1, "fork");
+ /* NOTREACHED */
+ case 0:
+ if (fchdir(dotfd)) {
+ warn("chdir");
+ _exit(1);
+ }
+ execvp(plan->e_argv[0], plan->e_argv);
+ warn("%s", plan->e_argv[0]);
+ _exit(1);
+ }
+ pid = waitpid(pid, &status, 0);
+ return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
+}
+
+/*
+ * c_exec --
+ * build three parallel arrays, one with pointers to the strings passed
+ * on the command line, one with (possibly duplicated) pointers to the
+ * argv array, and one with integer values that are lengths of the
+ * strings, but also flags meaning that the string has to be massaged.
+ */
+PLAN *
+c_exec(argvp, isok)
+ char ***argvp;
+ int isok;
+{
+ PLAN *new; /* node returned */
+ register int cnt;
+ register char **argv, **ap, *p;
+
+ isoutput = 1;
+
+ new = palloc(N_EXEC, f_exec);
+ if (isok)
+ new->flags = F_NEEDOK;
+
+ for (ap = argv = *argvp;; ++ap) {
+ if (!*ap)
+ errx(1,
+ "%s: no terminating \";\"", isok ? "-ok" : "-exec");
+ if (**ap == ';')
+ break;
+ }
+
+ cnt = ap - *argvp + 1;
+ new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
+ new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
+ new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
+
+ for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
+ new->e_orig[cnt] = *argv;
+ for (p = *argv; *p; ++p)
+ if (p[0] == '{' && p[1] == '}') {
+ new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
+ new->e_len[cnt] = MAXPATHLEN;
+ break;
+ }
+ if (!*p) {
+ new->e_argv[cnt] = *argv;
+ new->e_len[cnt] = 0;
+ }
+ }
+ new->e_argv[cnt] = new->e_orig[cnt] = NULL;
+
+ *argvp = argv + 1;
+ return (new);
+}
+
+/*
+ * -follow functions --
+ *
+ * Always true, causes symbolic links to be followed on a global
+ * basis.
+ */
+PLAN *
+c_follow()
+{
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+
+ return (palloc(N_FOLLOW, f_always_true));
+}
+
+/*
+ * -fstype functions --
+ *
+ * True if the file is of a certain type.
+ */
+int
+f_fstype(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ static dev_t curdev; /* need a guaranteed illegal dev value */
+ static int first = 1;
+ struct statfs sb;
+ static short val;
+ char *p, save[2];
+
+ /* Only check when we cross mount point. */
+ if (first || curdev != entry->fts_statp->st_dev) {
+ curdev = entry->fts_statp->st_dev;
+
+ /*
+ * Statfs follows symlinks; find wants the link's file system,
+ * not where it points.
+ */
+ if (entry->fts_info == FTS_SL ||
+ entry->fts_info == FTS_SLNONE) {
+ if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
+ ++p;
+ else
+ p = entry->fts_accpath;
+ save[0] = p[0];
+ p[0] = '.';
+ save[1] = p[1];
+ p[1] = '\0';
+
+ } else
+ p = NULL;
+
+ if (statfs(entry->fts_accpath, &sb))
+ err(1, "%s", entry->fts_accpath);
+
+ if (p) {
+ p[0] = save[0];
+ p[1] = save[1];
+ }
+
+ first = 0;
+
+ /*
+ * Further tests may need both of these values, so
+ * always copy both of them.
+ */
+ val = sb.f_flags;
+ val = sb.f_type;
+ }
+ switch (plan->flags) {
+ case F_MTFLAG:
+ return (val & plan->mt_data);
+ case F_MTTYPE:
+ return (val == plan->mt_data);
+ default:
+ abort();
+ }
+}
+
+PLAN *
+c_fstype(arg)
+ char *arg;
+{
+ register PLAN *new;
+ struct vfsconf vfc;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_FSTYPE, f_fstype);
+
+ /*
+ * Check first for a filesystem name.
+ */
+ if (getvfsbyname(arg, &vfc) == 0) {
+ new->flags = F_MTTYPE;
+ new->mt_data = vfc.vfc_typenum;
+ return (new);
+ }
+
+ switch (*arg) {
+ case 'l':
+ if (!strcmp(arg, "local")) {
+ new->flags = F_MTFLAG;
+ new->mt_data = MNT_LOCAL;
+ return (new);
+ }
+ break;
+ case 'r':
+ if (!strcmp(arg, "rdonly")) {
+ new->flags = F_MTFLAG;
+ new->mt_data = MNT_RDONLY;
+ return (new);
+ }
+ break;
+ }
+ errx(1, "%s: unknown file type", arg);
+ /* NOTREACHED */
+}
+
+/*
+ * -group gname functions --
+ *
+ * True if the file belongs to the group gname. If gname is numeric and
+ * an equivalent of the getgrnam() function does not return a valid group
+ * name, gname is taken as a group ID.
+ */
+int
+f_group(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (entry->fts_statp->st_gid == plan->g_data);
+}
+
+PLAN *
+c_group(gname)
+ char *gname;
+{
+ PLAN *new;
+ struct group *g;
+ gid_t gid;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ g = getgrnam(gname);
+ if (g == NULL) {
+ gid = atoi(gname);
+ if (gid == 0 && gname[0] != '0')
+ errx(1, "-group: %s: no such group", gname);
+ } else
+ gid = g->gr_gid;
+
+ new = palloc(N_GROUP, f_group);
+ new->g_data = gid;
+ return (new);
+}
+
+/*
+ * -inum n functions --
+ *
+ * True if the file has inode # n.
+ */
+int
+f_inum(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ COMPARE(entry->fts_statp->st_ino, plan->i_data);
+}
+
+PLAN *
+c_inum(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_INUM, f_inum);
+ new->i_data = find_parsenum(new, "-inum", arg, NULL);
+ return (new);
+}
+
+/*
+ * -links n functions --
+ *
+ * True if the file has n links.
+ */
+int
+f_links(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ COMPARE(entry->fts_statp->st_nlink, plan->l_data);
+}
+
+PLAN *
+c_links(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_LINKS, f_links);
+ new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
+ return (new);
+}
+
+/*
+ * -ls functions --
+ *
+ * Always true - prints the current entry to stdout in "ls" format.
+ */
+int
+f_ls(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
+ return (1);
+}
+
+PLAN *
+c_ls()
+{
+ ftsoptions &= ~FTS_NOSTAT;
+ isoutput = 1;
+
+ return (palloc(N_LS, f_ls));
+}
+
+/*
+ * -mtime n functions --
+ *
+ * True if the difference between the file modification time and the
+ * current time is n 24 hour periods.
+ */
+int
+f_mtime(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ extern time_t now;
+
+ COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
+ SECSPERDAY, plan->t_data);
+}
+
+PLAN *
+c_mtime(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_MTIME, f_mtime);
+ new->t_data = find_parsenum(new, "-mtime", arg, NULL);
+ TIME_CORRECT(new, N_MTIME);
+ return (new);
+}
+
+/*
+ * -name functions --
+ *
+ * True if the basename of the filename being examined
+ * matches pattern using Pattern Matching Notation S3.14
+ */
+int
+f_name(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (!fnmatch(plan->c_data, entry->fts_name, 0));
+}
+
+PLAN *
+c_name(pattern)
+ char *pattern;
+{
+ PLAN *new;
+
+ new = palloc(N_NAME, f_name);
+ new->c_data = pattern;
+ return (new);
+}
+
+/*
+ * -newer file functions --
+ *
+ * True if the current file has been modified more recently
+ * then the modification time of the file named by the pathname
+ * file.
+ */
+int
+f_newer(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (entry->fts_statp->st_mtime > plan->t_data);
+}
+
+PLAN *
+c_newer(filename)
+ char *filename;
+{
+ PLAN *new;
+ struct stat sb;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ if (stat(filename, &sb))
+ err(1, "%s", filename);
+ new = palloc(N_NEWER, f_newer);
+ new->t_data = sb.st_mtime;
+ return (new);
+}
+
+/*
+ * -nogroup functions --
+ *
+ * True if file belongs to a user ID for which the equivalent
+ * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
+ */
+int
+f_nogroup(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ char *group_from_gid();
+
+ return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
+}
+
+PLAN *
+c_nogroup()
+{
+ ftsoptions &= ~FTS_NOSTAT;
+
+ return (palloc(N_NOGROUP, f_nogroup));
+}
+
+/*
+ * -nouser functions --
+ *
+ * True if file belongs to a user ID for which the equivalent
+ * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
+ */
+int
+f_nouser(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ char *user_from_uid();
+
+ return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
+}
+
+PLAN *
+c_nouser()
+{
+ ftsoptions &= ~FTS_NOSTAT;
+
+ return (palloc(N_NOUSER, f_nouser));
+}
+
+/*
+ * -path functions --
+ *
+ * True if the path of the filename being examined
+ * matches pattern using Pattern Matching Notation S3.14
+ */
+int
+f_path(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (!fnmatch(plan->c_data, entry->fts_path, 0));
+}
+
+PLAN *
+c_path(pattern)
+ char *pattern;
+{
+ PLAN *new;
+
+ new = palloc(N_NAME, f_path);
+ new->c_data = pattern;
+ return (new);
+}
+
+/*
+ * -perm functions --
+ *
+ * The mode argument is used to represent file mode bits. If it starts
+ * with a leading digit, it's treated as an octal mode, otherwise as a
+ * symbolic mode.
+ */
+int
+f_perm(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ mode_t mode;
+
+ mode = entry->fts_statp->st_mode &
+ (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
+ if (plan->flags == F_ATLEAST)
+ return ((plan->m_data | mode) == mode);
+ else
+ return (mode == plan->m_data);
+ /* NOTREACHED */
+}
+
+PLAN *
+c_perm(perm)
+ char *perm;
+{
+ PLAN *new;
+ mode_t *set;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_PERM, f_perm);
+
+ if (*perm == '-') {
+ new->flags = F_ATLEAST;
+ ++perm;
+ }
+
+ if ((set = setmode(perm)) == NULL)
+ err(1, "-perm: %s: illegal mode string", perm);
+
+ new->m_data = getmode(set, 0);
+ return (new);
+}
+
+/*
+ * -print functions --
+ *
+ * Always true, causes the current pathame to be written to
+ * standard output.
+ */
+int
+f_print(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ (void)printf("%s\n", entry->fts_path);
+ return (1);
+}
+
+PLAN *
+c_print()
+{
+ isoutput = 1;
+
+ return (palloc(N_PRINT, f_print));
+}
+
+/*
+ * -prune functions --
+ *
+ * Prune a portion of the hierarchy.
+ */
+int
+f_prune(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ extern FTS *tree;
+
+ if (fts_set(tree, entry, FTS_SKIP))
+ err(1, "%s", entry->fts_path);
+ return (1);
+}
+
+PLAN *
+c_prune()
+{
+ return (palloc(N_PRUNE, f_prune));
+}
+
+/*
+ * -size n[c] functions --
+ *
+ * True if the file size in bytes, divided by an implementation defined
+ * value and rounded up to the next integer, is n. If n is followed by
+ * a c, the size is in bytes.
+ */
+#define FIND_SIZE 512
+static int divsize = 1;
+
+int
+f_size(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ off_t size;
+
+ size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
+ FIND_SIZE : entry->fts_statp->st_size;
+ COMPARE(size, plan->o_data);
+}
+
+PLAN *
+c_size(arg)
+ char *arg;
+{
+ PLAN *new;
+ char endch;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_SIZE, f_size);
+ endch = 'c';
+ new->o_data = find_parsenum(new, "-size", arg, &endch);
+ if (endch == 'c')
+ divsize = 0;
+ return (new);
+}
+
+/*
+ * -type c functions --
+ *
+ * True if the type of the file is c, where c is b, c, d, p, f or w
+ * for block special file, character special file, directory, FIFO,
+ * regular file or whiteout respectively.
+ */
+int
+f_type(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
+}
+
+PLAN *
+c_type(typestring)
+ char *typestring;
+{
+ PLAN *new;
+ mode_t mask;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ switch (typestring[0]) {
+ case 'b':
+ mask = S_IFBLK;
+ break;
+ case 'c':
+ mask = S_IFCHR;
+ break;
+ case 'd':
+ mask = S_IFDIR;
+ break;
+ case 'f':
+ mask = S_IFREG;
+ break;
+ case 'l':
+ mask = S_IFLNK;
+ break;
+ case 'p':
+ mask = S_IFIFO;
+ break;
+ case 's':
+ mask = S_IFSOCK;
+ break;
+#ifdef FTS_WHITEOUT
+ case 'w':
+ mask = S_IFWHT;
+ ftsoptions |= FTS_WHITEOUT;
+ break;
+#endif /* FTS_WHITEOUT */
+ default:
+ errx(1, "-type: %s: unknown type", typestring);
+ }
+
+ new = palloc(N_TYPE, f_type);
+ new->m_data = mask;
+ return (new);
+}
+
+/*
+ * -user uname functions --
+ *
+ * True if the file belongs to the user uname. If uname is numeric and
+ * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
+ * return a valid user name, uname is taken as a user ID.
+ */
+int
+f_user(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (entry->fts_statp->st_uid == plan->u_data);
+}
+
+PLAN *
+c_user(username)
+ char *username;
+{
+ PLAN *new;
+ struct passwd *p;
+ uid_t uid;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ p = getpwnam(username);
+ if (p == NULL) {
+ uid = atoi(username);
+ if (uid == 0 && username[0] != '0')
+ errx(1, "-user: %s: no such user", username);
+ } else
+ uid = p->pw_uid;
+
+ new = palloc(N_USER, f_user);
+ new->u_data = uid;
+ return (new);
+}
+
+/*
+ * -xdev functions --
+ *
+ * Always true, causes find not to decend past directories that have a
+ * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
+ */
+PLAN *
+c_xdev()
+{
+ ftsoptions |= FTS_XDEV;
+
+ return (palloc(N_XDEV, f_always_true));
+}
+
+/*
+ * ( expression ) functions --
+ *
+ * True if expression is true.
+ */
+int
+f_expr(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ register PLAN *p;
+ register int state;
+
+ for (p = plan->p_data[0];
+ p && (state = (p->eval)(p, entry)); p = p->next);
+ return (state);
+}
+
+/*
+ * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
+ * eliminated during phase 2 of find_formplan() --- the '(' node is converted
+ * to a N_EXPR node containing the expression and the ')' node is discarded.
+ */
+PLAN *
+c_openparen()
+{
+ return (palloc(N_OPENPAREN, (int (*)())-1));
+}
+
+PLAN *
+c_closeparen()
+{
+ return (palloc(N_CLOSEPAREN, (int (*)())-1));
+}
+
+/*
+ * ! expression functions --
+ *
+ * Negation of a primary; the unary NOT operator.
+ */
+int
+f_not(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ register PLAN *p;
+ register int state;
+
+ for (p = plan->p_data[0];
+ p && (state = (p->eval)(p, entry)); p = p->next);
+ return (!state);
+}
+
+PLAN *
+c_not()
+{
+ return (palloc(N_NOT, f_not));
+}
+
+/*
+ * expression -o expression functions --
+ *
+ * Alternation of primaries; the OR operator. The second expression is
+ * not evaluated if the first expression is true.
+ */
+int
+f_or(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ register PLAN *p;
+ register int state;
+
+ for (p = plan->p_data[0];
+ p && (state = (p->eval)(p, entry)); p = p->next);
+
+ if (state)
+ return (1);
+
+ for (p = plan->p_data[1];
+ p && (state = (p->eval)(p, entry)); p = p->next);
+ return (state);
+}
+
+PLAN *
+c_or()
+{
+ return (palloc(N_OR, f_or));
+}
+
+static PLAN *
+palloc(t, f)
+ enum ntype t;
+ int (*f) __P((PLAN *, FTSENT *));
+{
+ PLAN *new;
+
+ if ((new = malloc(sizeof(PLAN))) == NULL)
+ err(1, NULL);
+ new->type = t;
+ new->eval = f;
+ new->flags = 0;
+ new->next = NULL;
+ return (new);
+}
diff --git a/usr.bin/find/main.c b/usr.bin/find/main.c
new file mode 100644
index 000000000000..20985c88e57f
--- /dev/null
+++ b/usr.bin/find/main.c
@@ -0,0 +1,154 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "find.h"
+
+time_t now; /* time find was run */
+int dotfd; /* starting directory */
+int ftsoptions; /* options for the ftsopen(3) call */
+int isdeprecated; /* using deprecated syntax */
+int isdepth; /* do directories on post-order visit */
+int isoutput; /* user specified output operator */
+int isxargs; /* don't permit xargs delimiting chars */
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char **p, **start;
+ int Hflag, Lflag, Pflag, ch;
+
+ (void)time(&now); /* initialize the time-of-day */
+
+ p = start = argv;
+ Hflag = Lflag = Pflag = 0;
+ ftsoptions = FTS_NOSTAT | FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "HLPXdf:x")) != EOF)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = Pflag = 0;
+ break;
+ case 'P':
+ Pflag = 1;
+ Hflag = Lflag = 0;
+ break;
+ case 'X':
+ isxargs = 1;
+ break;
+ case 'd':
+ isdepth = 1;
+ break;
+ case 'f':
+ *p++ = optarg;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case '?':
+ default:
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (Hflag)
+ ftsoptions |= FTS_COMFOLLOW;
+ if (Lflag) {
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ }
+
+ /*
+ * Find first option to delimit the file list. The first argument
+ * that starts with a -, or is a ! or a ( must be interpreted as a
+ * part of the find expression, according to POSIX .2.
+ */
+ for (; *argv != NULL; *p++ = *argv++) {
+ if (argv[0][0] == '-')
+ break;
+ if ((argv[0][0] == '!' || argv[0][0] == '(') &&
+ argv[0][1] == '\0')
+ break;
+ }
+
+ if (p == start)
+ usage();
+ *p = NULL;
+
+ if ((dotfd = open(".", O_RDONLY, 0)) < 0)
+ err(1, ".");
+
+ exit(find_execute(find_formplan(argv), start));
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: find [-H | -L | -P] [-Xdx] [-f file] [file ...] [expression]\n");
+ exit(1);
+}
diff --git a/usr.bin/finger/extern.h b/usr.bin/finger/extern.h
new file mode 100644
index 000000000000..b213fe4a7f12
--- /dev/null
+++ b/usr.bin/finger/extern.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.2 (Berkeley) 4/28/95
+ */
+
+extern char tbuf[1024]; /* Temp buffer for anybody. */
+extern int entries; /* Number of people. */
+extern DB *db; /* Database. */
+
+void enter_lastlog __P((PERSON *));
+PERSON *enter_person __P((struct passwd *));
+void enter_where __P((struct utmp *, PERSON *));
+PERSON *find_person __P((char *));
+void lflag_print __P((void));
+int match __P((struct passwd *, char *));
+void netfinger __P((char *));
+PERSON *palloc __P((void));
+char *prphone __P((char *));
+void sflag_print __P((void));
diff --git a/usr.bin/finger/finger.1 b/usr.bin/finger/finger.1
new file mode 100644
index 000000000000..045084a23c77
--- /dev/null
+++ b/usr.bin/finger/finger.1
@@ -0,0 +1,166 @@
+.\" Copyright (c) 1989, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)finger.1 8.3 (Berkeley) 5/5/94
+.\"
+.Dd May 5, 1994
+.Dt FINGER 1
+.Os BSD 4
+.Sh NAME
+.Nm finger
+.Nd user information lookup program
+.Sh SYNOPSIS
+.Nm finger
+.Op Fl lmsp
+.Op Ar user ...
+.Op Ar user@host ...
+.Sh DESCRIPTION
+The
+.Nm finger
+displays information about the system users.
+.Pp
+Options are:
+.Bl -tag -width flag
+.It Fl s
+.Nm Finger
+displays the user's login name, real name, terminal name and write
+status (as a ``*'' before the terminal name if write permission is
+denied), idle time, login time, office location and office phone
+number.
+.Pp
+Idle time is in minutes if it is a single integer, hours and minutes
+if a ``:'' is present, or days if a ``d'' is present.
+Login time is displayed as month, day, hours and minutes, unless
+more than six months ago, in which case the year is displayed rather
+than the hours and minutes.
+.Pp
+Unknown devices as well as nonexistent idle and login times are
+displayed as single asterisks.
+.Pp
+.It Fl l
+Produces a multi-line format displaying all of the information
+described for the
+.Fl s
+option as well as the user's home directory, home phone number, login
+shell, and the contents of the files
+.Dq Pa .forward ,
+.Dq Pa .plan
+and
+.Dq Pa .project
+from the user's home directory.
+.Pp
+If idle time is at least a minute and less than a day, it is
+presented in the form ``hh:mm''.
+Idle times greater than a day are presented as ``d day[s]hh:mm''.
+.Pp
+Phone numbers specified as eleven digits are printed as ``+N-NNN-NNN-NNNN''.
+Numbers specified as ten or seven digits are printed as the appropriate
+subset of that string.
+Numbers specified as five digits are printed as ``xN-NNNN''.
+.Pp
+If write permission is denied to the device, the phrase ``(messages off)''
+is appended to the line containing the device name.
+One entry per user is displayed with the
+.Fl l
+option; if a user is logged on multiple times, terminal information
+is repeated once per login.
+.Pp
+.It Fl p
+Prevents
+the
+.Fl l
+option of
+.Nm finger
+from displaying the contents of the
+.Dq Pa .forward ,
+.Dq Pa .plan
+and
+.Dq Pa .project
+files.
+.It Fl m
+Prevent matching of
+.Ar user
+names.
+.Ar User
+is usually a login name; however, matching will also be done on the
+users' real names, unless the
+.Fl m
+option is supplied.
+All name matching performed by
+.Nm finger
+is case insensitive.
+.El
+.Pp
+If no options are specified,
+.Nm finger
+defaults to the
+.Fl l
+style output if operands are provided, otherwise to the
+.Fl s
+style.
+Note that some fields may be missing, in either format, if information
+is not available for them.
+.Pp
+If no arguments are specified,
+.Nm finger
+will print an entry for each user currently logged into the system.
+.Pp
+.Nm Finger
+may be used to look up users on a remote machine.
+The format is to specify a
+.Ar user
+as
+.Dq Li user@host ,
+or
+.Dq Li @host ,
+where the default output
+format for the former is the
+.Fl l
+style, and the default output format for the latter is the
+.Fl s
+style.
+The
+.Fl l
+option is the only option that may be passed to a remote machine.
+.Sh FILES
+.Bl -tag -width /var/log/lastlog -compact
+.It Pa /var/log/lastlog
+last login data base
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr w 1 ,
+.Xr who 1 ,
+.Sh HISTORY
+The
+.Nm finger
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/finger/finger.c b/usr.bin/finger/finger.c
new file mode 100644
index 000000000000..f4ece3a39435
--- /dev/null
+++ b/usr.bin/finger/finger.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)finger.c 8.5 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+/*
+ * Finger prints out information about users. It is not portable since
+ * certain fields (e.g. the full user name, office, and phone numbers) are
+ * extracted from the gecos field of the passwd file which other UNIXes
+ * may not have or may use for other things.
+ *
+ * There are currently two output formats; the short format is one line
+ * per user and displays login name, tty, login time, real name, idle time,
+ * and office location/phone number. The long format gives the same
+ * information (in a more legible format) as well as home directory, shell,
+ * mail info, and .plan/.project files.
+ */
+
+#include <sys/param.h>
+
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "finger.h"
+
+DB *db;
+time_t now;
+int entries, lflag, mflag, pplan, sflag;
+char tbuf[1024];
+
+static void loginlist __P((void));
+static void userlist __P((int, char **));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "lmps")) != EOF)
+ switch(ch) {
+ case 'l':
+ lflag = 1; /* long format */
+ break;
+ case 'm':
+ mflag = 1; /* force exact match of names */
+ break;
+ case 'p':
+ pplan = 1; /* don't show .plan/.project */
+ break;
+ case 's':
+ sflag = 1; /* short format */
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr,
+ "usage: finger [-lmps] [login ...]\n");
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ (void)time(&now);
+ setpassent(1);
+ if (!*argv) {
+ /*
+ * Assign explicit "small" format if no names given and -l
+ * not selected. Force the -s BEFORE we get names so proper
+ * screening will be done.
+ */
+ if (!lflag)
+ sflag = 1; /* if -l not explicit, force -s */
+ loginlist();
+ if (entries == 0)
+ (void)printf("No one logged on.\n");
+ } else {
+ userlist(argc, argv);
+ /*
+ * Assign explicit "large" format if names given and -s not
+ * explicitly stated. Force the -l AFTER we get names so any
+ * remote finger attempts specified won't be mishandled.
+ */
+ if (!sflag)
+ lflag = 1; /* if -s not explicit, force -l */
+ }
+ if (entries)
+ if (lflag)
+ lflag_print();
+ else
+ sflag_print();
+ return (0);
+}
+
+static void
+loginlist()
+{
+ register PERSON *pn;
+ DBT data, key;
+ struct passwd *pw;
+ struct utmp user;
+ int r, sflag;
+ char name[UT_NAMESIZE + 1];
+
+ if (!freopen(_PATH_UTMP, "r", stdin))
+ err(1, "%s", _PATH_UTMP);
+ name[UT_NAMESIZE] = NULL;
+ while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
+ if (!user.ut_name[0])
+ continue;
+ if ((pn = find_person(user.ut_name)) == NULL) {
+ bcopy(user.ut_name, name, UT_NAMESIZE);
+ if ((pw = getpwnam(name)) == NULL)
+ continue;
+ pn = enter_person(pw);
+ }
+ enter_where(&user, pn);
+ }
+ if (db && lflag)
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ PERSON *tmp;
+
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err(1, "db seq");
+ if (r == 1)
+ break;
+ memmove(&tmp, data.data, sizeof tmp);
+ enter_lastlog(tmp);
+ }
+}
+
+static void
+userlist(argc, argv)
+ register int argc;
+ register char **argv;
+{
+ register PERSON *pn;
+ DBT data, key;
+ struct utmp user;
+ struct passwd *pw;
+ int r, sflag, *used, *ip;
+ char **ap, **nargv, **np, **p;
+
+ if ((nargv = malloc((argc+1) * sizeof(char *))) == NULL ||
+ (used = calloc(argc, sizeof(int))) == NULL)
+ err(1, NULL);
+
+ /* Pull out all network requests. */
+ for (ap = p = argv, np = nargv; *p; ++p)
+ if (index(*p, '@'))
+ *np++ = *p;
+ else
+ *ap++ = *p;
+
+ *np++ = NULL;
+ *ap++ = NULL;
+
+ if (!*argv)
+ goto net;
+
+ /*
+ * Traverse the list of possible login names and check the login name
+ * and real name against the name specified by the user.
+ */
+ if (mflag)
+ for (p = argv; *p; ++p)
+ if ((pw = getpwnam(*p)) != NULL)
+ enter_person(pw);
+ else
+ (void)fprintf(stderr,
+ "finger: %s: no such user\n", *p);
+ else {
+ while ((pw = getpwent()) != NULL)
+ for (p = argv, ip = used; *p; ++p, ++ip)
+ if (match(pw, *p)) {
+ enter_person(pw);
+ *ip = 1;
+ }
+ for (p = argv, ip = used; *p; ++p, ++ip)
+ if (!*ip)
+ (void)fprintf(stderr,
+ "finger: %s: no such user\n", *p);
+ }
+
+ /* Handle network requests. */
+net: for (p = nargv; *p;)
+ netfinger(*p++);
+
+ if (entries == 0)
+ return;
+
+ /*
+ * Scan thru the list of users currently logged in, saving
+ * appropriate data whenever a match occurs.
+ */
+ if (!freopen(_PATH_UTMP, "r", stdin))
+ err(1, "%s", _PATH_UTMP);
+ while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
+ if (!user.ut_name[0])
+ continue;
+ if ((pn = find_person(user.ut_name)) == NULL)
+ continue;
+ enter_where(&user, pn);
+ }
+ if (db)
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ PERSON *tmp;
+
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err(1, "db seq");
+ if (r == 1)
+ break;
+ memmove(&tmp, data.data, sizeof tmp);
+ enter_lastlog(tmp);
+ }
+}
diff --git a/usr.bin/finger/lprint.c b/usr.bin/finger/lprint.c
new file mode 100644
index 000000000000..96edb1ba264b
--- /dev/null
+++ b/usr.bin/finger/lprint.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lprint.c 8.3 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <time.h>
+#include <tzfile.h>
+#include <db.h>
+#include <err.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <paths.h>
+#include "finger.h"
+
+#define LINE_LEN 80
+#define TAB_LEN 8 /* 8 spaces between tabs */
+#define _PATH_FORWARD ".forward"
+#define _PATH_PLAN ".plan"
+#define _PATH_PROJECT ".project"
+
+static int demi_print __P((char *, int));
+static void lprint __P((PERSON *));
+static int show_text __P((char *, char *, char *));
+static void vputc __P((int));
+
+void
+lflag_print()
+{
+ extern int pplan;
+ register PERSON *pn;
+ register int sflag, r;
+ PERSON *tmp;
+ DBT data, key;
+
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err(1, "db seq");
+ if (r == 1)
+ break;
+ memmove(&tmp, data.data, sizeof tmp);
+ pn = tmp;
+ if (sflag != R_FIRST)
+ putchar('\n');
+ lprint(pn);
+ if (!pplan) {
+ (void)show_text(pn->dir,
+ _PATH_FORWARD, "Mail forwarded to");
+ (void)show_text(pn->dir, _PATH_PROJECT, "Project");
+ if (!show_text(pn->dir, _PATH_PLAN, "Plan"))
+ (void)printf("No Plan.\n");
+ }
+ }
+}
+
+static void
+lprint(pn)
+ register PERSON *pn;
+{
+ extern time_t now;
+ register struct tm *delta;
+ register WHERE *w;
+ register int cpr, len, maxlen;
+ struct tm *tp;
+ int oddfield;
+ char *t, *tzn;
+
+ /*
+ * long format --
+ * login name
+ * real name
+ * home directory
+ * shell
+ * office, office phone, home phone if available
+ */
+ (void)printf("Login: %-15s\t\t\tName: %s\nDirectory: %-25s",
+ pn->name, pn->realname, pn->dir);
+ (void)printf("\tShell: %-s\n", *pn->shell ? pn->shell : _PATH_BSHELL);
+
+ /*
+ * try and print office, office phone, and home phone on one line;
+ * if that fails, do line filling so it looks nice.
+ */
+#define OFFICE_TAG "Office"
+#define OFFICE_PHONE_TAG "Office Phone"
+ oddfield = 0;
+ if (pn->office && pn->officephone &&
+ strlen(pn->office) + strlen(pn->officephone) +
+ sizeof(OFFICE_TAG) + 2 <= 5 * TAB_LEN) {
+ (void)snprintf(tbuf, sizeof(tbuf), "%s: %s, %s",
+ OFFICE_TAG, pn->office, prphone(pn->officephone));
+ oddfield = demi_print(tbuf, oddfield);
+ } else {
+ if (pn->office) {
+ (void)snprintf(tbuf, sizeof(tbuf), "%s: %s",
+ OFFICE_TAG, pn->office);
+ oddfield = demi_print(tbuf, oddfield);
+ }
+ if (pn->officephone) {
+ (void)snprintf(tbuf, sizeof(tbuf), "%s: %s",
+ OFFICE_PHONE_TAG, prphone(pn->officephone));
+ oddfield = demi_print(tbuf, oddfield);
+ }
+ }
+ if (pn->homephone) {
+ (void)snprintf(tbuf, sizeof(tbuf), "%s: %s", "Home Phone",
+ prphone(pn->homephone));
+ oddfield = demi_print(tbuf, oddfield);
+ }
+ if (oddfield)
+ putchar('\n');
+
+ /*
+ * long format con't: * if logged in
+ * terminal
+ * idle time
+ * if messages allowed
+ * where logged in from
+ * if not logged in
+ * when last logged in
+ */
+ /* find out longest device name for this user for formatting */
+ for (w = pn->whead, maxlen = -1; w != NULL; w = w->next)
+ if ((len = strlen(w->tty)) > maxlen)
+ maxlen = len;
+ /* find rest of entries for user */
+ for (w = pn->whead; w != NULL; w = w->next) {
+ switch (w->info) {
+ case LOGGEDIN:
+ tp = localtime(&w->loginat);
+ t = asctime(tp);
+ tzn = tp->tm_zone;
+ cpr = printf("On since %.16s (%s) on %s",
+ t, tzn, w->tty);
+ /*
+ * idle time is tough; if have one, print a comma,
+ * then spaces to pad out the device name, then the
+ * idle time. Follow with a comma if a remote login.
+ */
+ delta = gmtime(&w->idletime);
+ if (delta->tm_yday || delta->tm_hour || delta->tm_min) {
+ cpr += printf("%-*s idle ",
+ maxlen - strlen(w->tty) + 1, ",");
+ if (delta->tm_yday > 0) {
+ cpr += printf("%d day%s ",
+ delta->tm_yday,
+ delta->tm_yday == 1 ? "" : "s");
+ }
+ cpr += printf("%d:%02d",
+ delta->tm_hour, delta->tm_min);
+ if (*w->host) {
+ putchar(',');
+ ++cpr;
+ }
+ }
+ if (!w->writable)
+ cpr += printf(" (messages off)");
+ break;
+ case LASTLOG:
+ if (w->loginat == 0) {
+ (void)printf("Never logged in.");
+ break;
+ }
+ tp = localtime(&w->loginat);
+ t = asctime(tp);
+ tzn = tp->tm_zone;
+ if (now - w->loginat > SECSPERDAY * DAYSPERNYEAR / 2)
+ cpr =
+ printf("Last login %.16s %.4s (%s) on %s",
+ t, t + 20, tzn, w->tty);
+ else
+ cpr = printf("Last login %.16s (%s) on %s",
+ t, tzn, w->tty);
+ break;
+ }
+ if (*w->host) {
+ if (LINE_LEN < (cpr + 6 + strlen(w->host)))
+ (void)printf("\n ");
+ (void)printf(" from %s", w->host);
+ }
+ putchar('\n');
+ }
+}
+
+static int
+demi_print(str, oddfield)
+ char *str;
+ int oddfield;
+{
+ static int lenlast;
+ int lenthis, maxlen;
+
+ lenthis = strlen(str);
+ if (oddfield) {
+ /*
+ * We left off on an odd number of fields. If we haven't
+ * crossed the midpoint of the screen, and we have room for
+ * the next field, print it on the same line; otherwise,
+ * print it on a new line.
+ *
+ * Note: we insist on having the right hand fields start
+ * no less than 5 tabs out.
+ */
+ maxlen = 5 * TAB_LEN;
+ if (maxlen < lenlast)
+ maxlen = lenlast;
+ if (((((maxlen / TAB_LEN) + 1) * TAB_LEN) +
+ lenthis) <= LINE_LEN) {
+ while(lenlast < (4 * TAB_LEN)) {
+ putchar('\t');
+ lenlast += TAB_LEN;
+ }
+ (void)printf("\t%s\n", str); /* force one tab */
+ } else {
+ (void)printf("\n%s", str); /* go to next line */
+ oddfield = !oddfield; /* this'll be undone below */
+ }
+ } else
+ (void)printf("%s", str);
+ oddfield = !oddfield; /* toggle odd/even marker */
+ lenlast = lenthis;
+ return(oddfield);
+}
+
+static int
+show_text(directory, file_name, header)
+ char *directory, *file_name, *header;
+{
+ struct stat sb;
+ register FILE *fp;
+ register int ch, cnt, lastc;
+ register char *p;
+ int fd, nr;
+
+ (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", directory, file_name);
+ if ((fd = open(tbuf, O_RDONLY)) < 0 || fstat(fd, &sb) ||
+ sb.st_size == 0)
+ return(0);
+
+ /* If short enough, and no newlines, show it on a single line.*/
+ if (sb.st_size <= LINE_LEN - strlen(header) - 5) {
+ nr = read(fd, tbuf, sizeof(tbuf));
+ if (nr <= 0) {
+ (void)close(fd);
+ return(0);
+ }
+ for (p = tbuf, cnt = nr; cnt--; ++p)
+ if (*p == '\n')
+ break;
+ if (cnt <= 1) {
+ (void)printf("%s: ", header);
+ for (p = tbuf, cnt = nr; cnt--; ++p)
+ vputc(lastc = *p);
+ if (lastc != '\n')
+ (void)putchar('\n');
+ (void)close(fd);
+ return(1);
+ }
+ else
+ (void)lseek(fd, 0L, SEEK_SET);
+ }
+ if ((fp = fdopen(fd, "r")) == NULL)
+ return(0);
+ (void)printf("%s:\n", header);
+ while ((ch = getc(fp)) != EOF)
+ vputc(lastc = ch);
+ if (lastc != '\n')
+ (void)putchar('\n');
+ (void)fclose(fp);
+ return(1);
+}
+
+static void
+vputc(ch)
+ register int ch;
+{
+ int meta;
+
+ if (!isascii(ch)) {
+ (void)putchar('M');
+ (void)putchar('-');
+ ch = toascii(ch);
+ meta = 1;
+ } else
+ meta = 0;
+ if (isprint(ch) || !meta && (ch == ' ' || ch == '\t' || ch == '\n'))
+ (void)putchar(ch);
+ else {
+ (void)putchar('^');
+ (void)putchar(ch == '\177' ? '?' : ch | 0100);
+ }
+}
diff --git a/usr.bin/finger/net.c b/usr.bin/finger/net.c
new file mode 100644
index 000000000000..3eb7518b52c2
--- /dev/null
+++ b/usr.bin/finger/net.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)net.c 8.4 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <db.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "finger.h"
+
+void
+netfinger(name)
+ char *name;
+{
+ extern int lflag;
+ register FILE *fp;
+ register int c, lastc;
+ struct in_addr defaddr;
+ struct hostent *hp, def;
+ struct servent *sp;
+ struct sockaddr_in sin;
+ int s;
+ char *alist[1], *host;
+
+ if (!(host = rindex(name, '@')))
+ return;
+ *host++ = NULL;
+ if (isdigit(*host) && (defaddr.s_addr = inet_addr(host)) != -1) {
+ def.h_name = host;
+ def.h_addr_list = alist;
+ def.h_addr = (char *)&defaddr;
+ def.h_length = sizeof(struct in_addr);
+ def.h_addrtype = AF_INET;
+ def.h_aliases = 0;
+ hp = &def;
+ } else if (!(hp = gethostbyname(host))) {
+ (void)fprintf(stderr,
+ "finger: unknown host: %s\n", host);
+ return;
+ }
+ if (!(sp = getservbyname("finger", "tcp"))) {
+ (void)fprintf(stderr, "finger: tcp/finger: unknown service\n");
+ return;
+ }
+ sin.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
+ sin.sin_port = sp->s_port;
+ if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) {
+ perror("finger: socket");
+ return;
+ }
+
+ /* have network connection; identify the host connected with */
+ (void)printf("[%s]\n", hp->h_name);
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ perror("finger: connect");
+ (void)close(s);
+ return;
+ }
+
+ /* -l flag for remote fingerd */
+ if (lflag)
+ write(s, "/W ", 3);
+ /* send the name followed by <CR><LF> */
+ (void)write(s, name, strlen(name));
+ (void)write(s, "\r\n", 2);
+
+ /*
+ * Read from the remote system; once we're connected, we assume some
+ * data. If none arrives, we hang until the user interrupts.
+ *
+ * If we see a <CR> or a <CR> with the high bit set, treat it as
+ * a newline; if followed by a newline character, only output one
+ * newline.
+ *
+ * Otherwise, all high bits are stripped; if it isn't printable and
+ * it isn't a space, we can simply set the 7th bit. Every ASCII
+ * character with bit 7 set is printable.
+ */
+ lastc = 0;
+ if ((fp = fdopen(s, "r")) != NULL)
+ while ((c = getc(fp)) != EOF) {
+ c &= 0x7f;
+ if (c == 0x0d) {
+ if (lastc == '\r') /* ^M^M - skip dupes */
+ continue;
+ c = '\n';
+ lastc = '\r';
+ } else {
+ if (!isprint(c) && !isspace(c))
+ c |= 0x40;
+ if (lastc != '\r' || c != '\n')
+ lastc = c;
+ else {
+ lastc = '\n';
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ if (lastc != '\n')
+ putchar('\n');
+ putchar('\n');
+ (void)fclose(fp);
+}
diff --git a/usr.bin/finger/sprint.c b/usr.bin/finger/sprint.c
new file mode 100644
index 000000000000..54c7eea1c660
--- /dev/null
+++ b/usr.bin/finger/sprint.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sprint.c 8.3 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+#include <tzfile.h>
+#include <db.h>
+#include <err.h>
+#include <pwd.h>
+#include <errno.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "finger.h"
+
+static void stimeprint __P((WHERE *));
+
+void
+sflag_print()
+{
+ extern time_t now;
+ register PERSON *pn;
+ register WHERE *w;
+ register int sflag, r;
+ register char *p;
+ PERSON *tmp;
+ DBT data, key;
+
+ /*
+ * short format --
+ * login name
+ * real name
+ * terminal name (the XX of ttyXX)
+ * if terminal writeable (add an '*' to the terminal name
+ * if not)
+ * if logged in show idle time and day logged in, else
+ * show last login date and time. If > 6 moths,
+ * show year instead of time.
+ * office location
+ * office phone
+ */
+#define MAXREALNAME 20
+ (void)printf("%-*s %-*s %s\n", UT_NAMESIZE, "Login", MAXREALNAME,
+ "Name", "Tty Idle Login Time Office Office Phone");
+
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err(1, "db seq");
+ if (r == 1)
+ break;
+ memmove(&tmp, data.data, sizeof tmp);
+ pn = tmp;
+
+ for (w = pn->whead; w != NULL; w = w->next) {
+ (void)printf("%-*.*s %-*.*s ", UT_NAMESIZE, UT_NAMESIZE,
+ pn->name, MAXREALNAME, MAXREALNAME,
+ pn->realname ? pn->realname : "");
+ if (!w->loginat) {
+ (void)printf(" * * No logins ");
+ goto office;
+ }
+ (void)putchar(w->info == LOGGEDIN && !w->writable ?
+ '*' : ' ');
+ if (*w->tty)
+ (void)printf("%-2.2s ",
+ w->tty[0] != 't' || w->tty[1] != 't' ||
+ w->tty[2] != 'y' ? w->tty : w->tty + 3);
+ else
+ (void)printf(" ");
+ if (w->info == LOGGEDIN) {
+ stimeprint(w);
+ (void)printf(" ");
+ } else
+ (void)printf(" * ");
+ p = ctime(&w->loginat);
+ (void)printf("%.6s", p + 4);
+ if (now - w->loginat >= SECSPERDAY * DAYSPERNYEAR / 2)
+ (void)printf(" %.4s", p + 20);
+ else
+ (void)printf(" %.5s", p + 11);
+office: if (pn->office)
+ (void)printf(" %-10.10s", pn->office);
+ else if (pn->officephone)
+ (void)printf(" %-10.10s", " ");
+ if (pn->officephone)
+ (void)printf(" %-.15s",
+ prphone(pn->officephone));
+ putchar('\n');
+ }
+ }
+}
+
+static void
+stimeprint(w)
+ WHERE *w;
+{
+ register struct tm *delta;
+
+ delta = gmtime(&w->idletime);
+ if (!delta->tm_yday)
+ if (!delta->tm_hour)
+ if (!delta->tm_min)
+ (void)printf(" ");
+ else
+ (void)printf("%5d", delta->tm_min);
+ else
+ (void)printf("%2d:%02d",
+ delta->tm_hour, delta->tm_min);
+ else
+ (void)printf("%4dd", delta->tm_yday);
+}
diff --git a/usr.bin/finger/util.c b/usr.bin/finger/util.c
new file mode 100644
index 000000000000..b51b7ab6794f
--- /dev/null
+++ b/usr.bin/finger/util.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <db.h>
+#include <err.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <paths.h>
+#include "finger.h"
+
+static void find_idle_and_ttywrite __P((WHERE *));
+static void userinfo __P((PERSON *, struct passwd *));
+static WHERE *walloc __P((PERSON *));
+
+int
+match(pw, user)
+ struct passwd *pw;
+ char *user;
+{
+ register char *p, *t;
+ char name[1024];
+
+ if (!strcasecmp(pw->pw_name, user))
+ return(1);
+
+ /*
+ * XXX
+ * Why do we skip asterisks!?!?
+ */
+ (void)strcpy(p = tbuf, pw->pw_gecos);
+ if (*p == '*')
+ ++p;
+
+ /* Ampersands get replaced by the login name. */
+ if ((p = strtok(p, ",")) == NULL)
+ return(0);
+
+ for (t = name; (*t = *p) != '\0'; ++p)
+ if (*t == '&') {
+ (void)strcpy(t, pw->pw_name);
+ while (*++t);
+ }
+ else
+ ++t;
+ for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL)
+ if (!strcasecmp(p, user))
+ return(1);
+ return(0);
+}
+
+void
+enter_lastlog(pn)
+ register PERSON *pn;
+{
+ register WHERE *w;
+ static int opened, fd;
+ struct lastlog ll;
+ char doit = 0;
+
+ /* some systems may not maintain lastlog, don't report errors. */
+ if (!opened) {
+ fd = open(_PATH_LASTLOG, O_RDONLY, 0);
+ opened = 1;
+ }
+ if (fd == -1 ||
+ lseek(fd, (long)pn->uid * sizeof(ll), SEEK_SET) !=
+ (long)pn->uid * sizeof(ll) ||
+ read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) {
+ /* as if never logged in */
+ ll.ll_line[0] = ll.ll_host[0] = NULL;
+ ll.ll_time = 0;
+ }
+ if ((w = pn->whead) == NULL)
+ doit = 1;
+ else if (ll.ll_time != 0) {
+ /* if last login is earlier than some current login */
+ for (; !doit && w != NULL; w = w->next)
+ if (w->info == LOGGEDIN && w->loginat < ll.ll_time)
+ doit = 1;
+ /*
+ * and if it's not any of the current logins
+ * can't use time comparison because there may be a small
+ * discrepency since login calls time() twice
+ */
+ for (w = pn->whead; doit && w != NULL; w = w->next)
+ if (w->info == LOGGEDIN &&
+ strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0)
+ doit = 0;
+ }
+ if (doit) {
+ w = walloc(pn);
+ w->info = LASTLOG;
+ bcopy(ll.ll_line, w->tty, UT_LINESIZE);
+ w->tty[UT_LINESIZE] = 0;
+ bcopy(ll.ll_host, w->host, UT_HOSTSIZE);
+ w->host[UT_HOSTSIZE] = 0;
+ w->loginat = ll.ll_time;
+ }
+}
+
+void
+enter_where(ut, pn)
+ struct utmp *ut;
+ PERSON *pn;
+{
+ register WHERE *w;
+
+ w = walloc(pn);
+ w->info = LOGGEDIN;
+ bcopy(ut->ut_line, w->tty, UT_LINESIZE);
+ w->tty[UT_LINESIZE] = 0;
+ bcopy(ut->ut_host, w->host, UT_HOSTSIZE);
+ w->host[UT_HOSTSIZE] = 0;
+ w->loginat = (time_t)ut->ut_time;
+ find_idle_and_ttywrite(w);
+}
+
+PERSON *
+enter_person(pw)
+ register struct passwd *pw;
+{
+ DBT data, key;
+ PERSON *pn;
+
+ if (db == NULL &&
+ (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL)
+ err(1, NULL);
+
+ key.data = pw->pw_name;
+ key.size = strlen(pw->pw_name);
+
+ switch ((*db->get)(db, &key, &data, 0)) {
+ case 0:
+ memmove(&pn, data.data, sizeof pn);
+ return (pn);
+ default:
+ case -1:
+ err(1, "db get");
+ /* NOTREACHED */
+ case 1:
+ ++entries;
+ pn = palloc();
+ userinfo(pn, pw);
+ pn->whead = NULL;
+
+ data.size = sizeof(PERSON *);
+ data.data = &pn;
+ if ((*db->put)(db, &key, &data, 0))
+ err(1, "db put");
+ return (pn);
+ }
+}
+
+PERSON *
+find_person(name)
+ char *name;
+{
+ register int cnt;
+ DBT data, key;
+ PERSON *p;
+ char buf[UT_NAMESIZE + 1];
+
+ if (!db)
+ return(NULL);
+
+ /* Name may be only UT_NAMESIZE long and not NUL terminated. */
+ for (cnt = 0; cnt < UT_NAMESIZE && *name; ++name, ++cnt)
+ buf[cnt] = *name;
+ buf[cnt] = '\0';
+ key.data = buf;
+ key.size = cnt;
+
+ if ((*db->get)(db, &key, &data, 0))
+ return (NULL);
+ memmove(&p, data.data, sizeof p);
+ return (p);
+}
+
+PERSON *
+palloc()
+{
+ PERSON *p;
+
+ if ((p = malloc((u_int) sizeof(PERSON))) == NULL)
+ err(1, NULL);
+ return(p);
+}
+
+static WHERE *
+walloc(pn)
+ register PERSON *pn;
+{
+ register WHERE *w;
+
+ if ((w = malloc((u_int) sizeof(WHERE))) == NULL)
+ err(1, NULL);
+ if (pn->whead == NULL)
+ pn->whead = pn->wtail = w;
+ else {
+ pn->wtail->next = w;
+ pn->wtail = w;
+ }
+ w->next = NULL;
+ return(w);
+}
+
+char *
+prphone(num)
+ char *num;
+{
+ register char *p;
+ int len;
+ static char pbuf[15];
+
+ /* don't touch anything if the user has their own formatting */
+ for (p = num; *p; ++p)
+ if (!isdigit(*p))
+ return(num);
+ len = p - num;
+ p = pbuf;
+ switch(len) {
+ case 11: /* +0-123-456-7890 */
+ *p++ = '+';
+ *p++ = *num++;
+ *p++ = '-';
+ /* FALLTHROUGH */
+ case 10: /* 012-345-6789 */
+ *p++ = *num++;
+ *p++ = *num++;
+ *p++ = *num++;
+ *p++ = '-';
+ /* FALLTHROUGH */
+ case 7: /* 012-3456 */
+ *p++ = *num++;
+ *p++ = *num++;
+ *p++ = *num++;
+ break;
+ case 5: /* x0-1234 */
+ *p++ = 'x';
+ *p++ = *num++;
+ break;
+ default:
+ return(num);
+ }
+ *p++ = '-';
+ *p++ = *num++;
+ *p++ = *num++;
+ *p++ = *num++;
+ *p++ = *num++;
+ *p = '\0';
+ return(pbuf);
+}
+
+static void
+find_idle_and_ttywrite(w)
+ register WHERE *w;
+{
+ extern time_t now;
+ struct stat sb;
+
+ (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty);
+ if (stat(tbuf, &sb) < 0) {
+ warn(tbuf);
+ return;
+ }
+ w->idletime = now < sb.st_atime ? 0 : now - sb.st_atime;
+
+#define TALKABLE 0220 /* tty is writable if 220 mode */
+ w->writable = ((sb.st_mode & TALKABLE) == TALKABLE);
+}
+
+static void
+userinfo(pn, pw)
+ register PERSON *pn;
+ register struct passwd *pw;
+{
+ register char *p, *t;
+ char *bp, name[1024];
+
+ pn->realname = pn->office = pn->officephone = pn->homephone = NULL;
+
+ pn->uid = pw->pw_uid;
+ pn->name = strdup(pw->pw_name);
+ pn->dir = strdup(pw->pw_dir);
+ pn->shell = strdup(pw->pw_shell);
+
+ /* why do we skip asterisks!?!? */
+ (void)strcpy(bp = tbuf, pw->pw_gecos);
+ if (*bp == '*')
+ ++bp;
+
+ /* ampersands get replaced by the login name */
+ if (!(p = strsep(&bp, ",")))
+ return;
+ for (t = name; (*t = *p) != '\0'; ++p)
+ if (*t == '&') {
+ (void)strcpy(t, pw->pw_name);
+ if (islower(*t))
+ *t = toupper(*t);
+ while (*++t);
+ }
+ else
+ ++t;
+ pn->realname = strdup(name);
+ pn->office = ((p = strsep(&bp, ",")) && *p) ?
+ strdup(p) : NULL;
+ pn->officephone = ((p = strsep(&bp, ",")) && *p) ?
+ strdup(p) : NULL;
+ pn->homephone = ((p = strsep(&bp, ",")) && *p) ?
+ strdup(p) : NULL;
+}
diff --git a/usr.bin/fstat/fstat.c b/usr.bin/fstat/fstat.c
new file mode 100644
index 000000000000..c2bf826d9e5a
--- /dev/null
+++ b/usr.bin/fstat/fstat.c
@@ -0,0 +1,748 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)fstat.c 8.3 (Berkeley) 5/2/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/unpcb.h>
+#include <sys/sysctl.h>
+#include <sys/filedesc.h>
+#define KERNEL
+#include <sys/file.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#undef KERNEL
+#define NFS
+#include <sys/mount.h>
+#include <nfs/nfsproto.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+#undef NFS
+
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define TEXT -1
+#define CDIR -2
+#define RDIR -3
+#define TRACE -4
+
+typedef struct devs {
+ struct devs *next;
+ long fsid;
+ ino_t ino;
+ char *name;
+} DEVS;
+DEVS *devs;
+
+struct filestat {
+ long fsid;
+ long fileid;
+ mode_t mode;
+ u_long size;
+ dev_t rdev;
+};
+
+#ifdef notdef
+struct nlist nl[] = {
+ { "" },
+};
+#endif
+
+int fsflg, /* show files on same filesystem as file(s) argument */
+ pflg, /* show files open by a particular pid */
+ uflg; /* show files open by a particular (effective) user */
+int checkfile; /* true if restricting to particular files or filesystems */
+int nflg; /* (numerical) display f.s. and rdev as dev_t */
+int vflg; /* display errors in locating kernel data objects etc... */
+
+#define dprintf if (vflg) fprintf
+
+struct file **ofiles; /* buffer of pointers to file structures */
+int maxfiles;
+#define ALLOC_OFILES(d) \
+ if ((d) > maxfiles) { \
+ free(ofiles); \
+ ofiles = malloc((d) * sizeof(struct file *)); \
+ if (ofiles == NULL) { \
+ fprintf(stderr, "fstat: %s\n", strerror(errno)); \
+ exit(1); \
+ } \
+ maxfiles = (d); \
+ }
+
+/*
+ * a kvm_read that returns true if everything is read
+ */
+#define KVM_READ(kaddr, paddr, len) \
+ (kvm_read(kd, (u_long)(kaddr), (char *)(paddr), (len)) == (len))
+
+kvm_t *kd;
+
+int ufs_filestat(), nfs_filestat();
+void dofiles(), getinetproto(), socktrans();
+void usage(), vtrans();
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ register struct passwd *passwd;
+ struct kinfo_proc *p, *plast;
+ int arg, ch, what;
+ char *memf, *nlistf;
+ char buf[_POSIX2_LINE_MAX];
+ int cnt;
+
+ arg = 0;
+ what = KERN_PROC_ALL;
+ nlistf = memf = NULL;
+ while ((ch = getopt(argc, argv, "fnp:u:vNM")) != EOF)
+ switch((char)ch) {
+ case 'f':
+ fsflg = 1;
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'n':
+ nflg = 1;
+ break;
+ case 'p':
+ if (pflg++)
+ usage();
+ if (!isdigit(*optarg)) {
+ fprintf(stderr,
+ "fstat: -p requires a process id\n");
+ usage();
+ }
+ what = KERN_PROC_PID;
+ arg = atoi(optarg);
+ break;
+ case 'u':
+ if (uflg++)
+ usage();
+ if (!(passwd = getpwnam(optarg))) {
+ fprintf(stderr, "%s: unknown uid\n",
+ optarg);
+ exit(1);
+ }
+ what = KERN_PROC_UID;
+ arg = passwd->pw_uid;
+ break;
+ case 'v':
+ vflg = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (*(argv += optind)) {
+ for (; *argv; ++argv) {
+ if (getfname(*argv))
+ checkfile = 1;
+ }
+ if (!checkfile) /* file(s) specified, but none accessable */
+ exit(1);
+ }
+
+ ALLOC_OFILES(256); /* reserve space for file pointers */
+
+ if (fsflg && !checkfile) {
+ /* -f with no files means use wd */
+ if (getfname(".") == 0)
+ exit(1);
+ checkfile = 1;
+ }
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (nlistf != NULL || memf != NULL)
+ setgid(getgid());
+
+ if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf)) == NULL) {
+ fprintf(stderr, "fstat: %s\n", buf);
+ exit(1);
+ }
+#ifdef notdef
+ if (kvm_nlist(kd, nl) != 0) {
+ fprintf(stderr, "fstat: no namelist: %s\n", kvm_geterr(kd));
+ exit(1);
+ }
+#endif
+ if ((p = kvm_getprocs(kd, what, arg, &cnt)) == NULL) {
+ fprintf(stderr, "fstat: %s\n", kvm_geterr(kd));
+ exit(1);
+ }
+ if (nflg)
+ printf("%s",
+"USER CMD PID FD DEV INUM MODE SZ|DV R/W");
+ else
+ printf("%s",
+"USER CMD PID FD MOUNT INUM MODE SZ|DV R/W");
+ if (checkfile && fsflg == 0)
+ printf(" NAME\n");
+ else
+ putchar('\n');
+
+ for (plast = &p[cnt]; p < plast; ++p) {
+ if (p->kp_proc.p_stat == SZOMB)
+ continue;
+ dofiles(p);
+ }
+ exit(0);
+}
+
+char *Uname, *Comm;
+int Pid;
+
+#define PREFIX(i) printf("%-8.8s %-10s %5d", Uname, Comm, Pid); \
+ switch(i) { \
+ case TEXT: \
+ printf(" text"); \
+ break; \
+ case CDIR: \
+ printf(" wd"); \
+ break; \
+ case RDIR: \
+ printf(" root"); \
+ break; \
+ case TRACE: \
+ printf(" tr"); \
+ break; \
+ default: \
+ printf(" %4d", i); \
+ break; \
+ }
+
+/*
+ * print open files attributed to this process
+ */
+void
+dofiles(kp)
+ struct kinfo_proc *kp;
+{
+ int i, last;
+ struct file file;
+ struct filedesc0 filed0;
+#define filed filed0.fd_fd
+ struct proc *p = &kp->kp_proc;
+ struct eproc *ep = &kp->kp_eproc;
+
+ extern char *user_from_uid();
+
+ Uname = user_from_uid(ep->e_ucred.cr_uid, 0);
+ Pid = p->p_pid;
+ Comm = p->p_comm;
+
+ if (p->p_fd == NULL)
+ return;
+ if (!KVM_READ(p->p_fd, &filed0, sizeof (filed0))) {
+ dprintf(stderr, "can't read filedesc at %x for pid %d\n",
+ p->p_fd, Pid);
+ return;
+ }
+ /*
+ * root directory vnode, if one
+ */
+ if (filed.fd_rdir)
+ vtrans(filed.fd_rdir, RDIR, FREAD);
+ /*
+ * current working directory vnode
+ */
+ vtrans(filed.fd_cdir, CDIR, FREAD);
+ /*
+ * ktrace vnode, if one
+ */
+ if (p->p_tracep)
+ vtrans(p->p_tracep, TRACE, FREAD|FWRITE);
+ /*
+ * open files
+ */
+#define FPSIZE (sizeof (struct file *))
+ ALLOC_OFILES(filed.fd_lastfile+1);
+ if (filed.fd_nfiles > NDFILE) {
+ if (!KVM_READ(filed.fd_ofiles, ofiles,
+ (filed.fd_lastfile+1) * FPSIZE)) {
+ dprintf(stderr,
+ "can't read file structures at %x for pid %d\n",
+ filed.fd_ofiles, Pid);
+ return;
+ }
+ } else
+ bcopy(filed0.fd_dfiles, ofiles, (filed.fd_lastfile+1) * FPSIZE);
+ for (i = 0; i <= filed.fd_lastfile; i++) {
+ if (ofiles[i] == NULL)
+ continue;
+ if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) {
+ dprintf(stderr, "can't read file %d at %x for pid %d\n",
+ i, ofiles[i], Pid);
+ continue;
+ }
+ if (file.f_type == DTYPE_VNODE)
+ vtrans((struct vnode *)file.f_data, i, file.f_flag);
+ else if (file.f_type == DTYPE_SOCKET) {
+ if (checkfile == 0)
+ socktrans((struct socket *)file.f_data, i);
+ }
+ else {
+ dprintf(stderr,
+ "unknown file type %d for file %d of pid %d\n",
+ file.f_type, i, Pid);
+ }
+ }
+}
+
+void
+vtrans(vp, i, flag)
+ struct vnode *vp;
+ int i;
+ int flag;
+{
+ struct vnode vn;
+ struct filestat fst;
+ char rw[3], mode[15];
+ char *badtype = NULL, *filename, *getmnton();
+
+ filename = badtype = NULL;
+ if (!KVM_READ(vp, &vn, sizeof (struct vnode))) {
+ dprintf(stderr, "can't read vnode at %x for pid %d\n",
+ vp, Pid);
+ return;
+ }
+ if (vn.v_type == VNON || vn.v_tag == VT_NON)
+ badtype = "none";
+ else if (vn.v_type == VBAD)
+ badtype = "bad";
+ else
+ switch (vn.v_tag) {
+ case VT_UFS:
+ if (!ufs_filestat(&vn, &fst))
+ badtype = "error";
+ break;
+ case VT_MFS:
+ if (!ufs_filestat(&vn, &fst))
+ badtype = "error";
+ break;
+ case VT_NFS:
+ if (!nfs_filestat(&vn, &fst))
+ badtype = "error";
+ break;
+ default: {
+ static char unknown[10];
+ sprintf(badtype = unknown, "?(%x)", vn.v_tag);
+ break;;
+ }
+ }
+ if (checkfile) {
+ int fsmatch = 0;
+ register DEVS *d;
+
+ if (badtype)
+ return;
+ for (d = devs; d != NULL; d = d->next)
+ if (d->fsid == fst.fsid) {
+ fsmatch = 1;
+ if (d->ino == fst.fileid) {
+ filename = d->name;
+ break;
+ }
+ }
+ if (fsmatch == 0 || (filename == NULL && fsflg == 0))
+ return;
+ }
+ PREFIX(i);
+ if (badtype) {
+ (void)printf(" - - %10s -\n", badtype);
+ return;
+ }
+ if (nflg)
+ (void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid));
+ else
+ (void)printf(" %-8s", getmnton(vn.v_mount));
+ if (nflg)
+ (void)sprintf(mode, "%o", fst.mode);
+ else
+ strmode(fst.mode, mode);
+ (void)printf(" %6d %10s", fst.fileid, mode);
+ switch (vn.v_type) {
+ case VBLK:
+ case VCHR: {
+ char *name;
+
+ if (nflg || ((name = devname(fst.rdev, vn.v_type == VCHR ?
+ S_IFCHR : S_IFBLK)) == NULL))
+ printf(" %2d,%-2d", major(fst.rdev), minor(fst.rdev));
+ else
+ printf(" %6s", name);
+ break;
+ }
+ default:
+ printf(" %6d", fst.size);
+ }
+ rw[0] = '\0';
+ if (flag & FREAD)
+ strcat(rw, "r");
+ if (flag & FWRITE)
+ strcat(rw, "w");
+ printf(" %2s", rw);
+ if (filename && !fsflg)
+ printf(" %s", filename);
+ putchar('\n');
+}
+
+int
+ufs_filestat(vp, fsp)
+ struct vnode *vp;
+ struct filestat *fsp;
+{
+ struct inode inode;
+
+ if (!KVM_READ(VTOI(vp), &inode, sizeof (inode))) {
+ dprintf(stderr, "can't read inode at %x for pid %d\n",
+ VTOI(vp), Pid);
+ return 0;
+ }
+ fsp->fsid = inode.i_dev & 0xffff;
+ fsp->fileid = (long)inode.i_number;
+ fsp->mode = (mode_t)inode.i_mode;
+ fsp->size = (u_long)inode.i_size;
+ fsp->rdev = inode.i_rdev;
+
+ return 1;
+}
+
+int
+nfs_filestat(vp, fsp)
+ struct vnode *vp;
+ struct filestat *fsp;
+{
+ struct nfsnode nfsnode;
+ register mode_t mode;
+
+ if (!KVM_READ(VTONFS(vp), &nfsnode, sizeof (nfsnode))) {
+ dprintf(stderr, "can't read nfsnode at %x for pid %d\n",
+ VTONFS(vp), Pid);
+ return 0;
+ }
+ fsp->fsid = nfsnode.n_vattr.va_fsid;
+ fsp->fileid = nfsnode.n_vattr.va_fileid;
+ fsp->size = nfsnode.n_size;
+ fsp->rdev = nfsnode.n_vattr.va_rdev;
+ mode = (mode_t)nfsnode.n_vattr.va_mode;
+ switch (vp->v_type) {
+ case VREG:
+ mode |= S_IFREG;
+ break;
+ case VDIR:
+ mode |= S_IFDIR;
+ break;
+ case VBLK:
+ mode |= S_IFBLK;
+ break;
+ case VCHR:
+ mode |= S_IFCHR;
+ break;
+ case VLNK:
+ mode |= S_IFLNK;
+ break;
+ case VSOCK:
+ mode |= S_IFSOCK;
+ break;
+ case VFIFO:
+ mode |= S_IFIFO;
+ break;
+ };
+ fsp->mode = mode;
+
+ return 1;
+}
+
+
+char *
+getmnton(m)
+ struct mount *m;
+{
+ static struct mount mount;
+ static struct mtab {
+ struct mtab *next;
+ struct mount *m;
+ char mntonname[MNAMELEN];
+ } *mhead = NULL;
+ register struct mtab *mt;
+
+ for (mt = mhead; mt != NULL; mt = mt->next)
+ if (m == mt->m)
+ return (mt->mntonname);
+ if (!KVM_READ(m, &mount, sizeof(struct mount))) {
+ fprintf(stderr, "can't read mount table at %x\n", m);
+ return (NULL);
+ }
+ if ((mt = malloc(sizeof (struct mtab))) == NULL) {
+ fprintf(stderr, "fstat: %s\n", strerror(errno));
+ exit(1);
+ }
+ mt->m = m;
+ bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN);
+ mt->next = mhead;
+ mhead = mt;
+ return (mt->mntonname);
+}
+
+void
+socktrans(sock, i)
+ struct socket *sock;
+ int i;
+{
+ static char *stypename[] = {
+ "unused", /* 0 */
+ "stream", /* 1 */
+ "dgram", /* 2 */
+ "raw", /* 3 */
+ "rdm", /* 4 */
+ "seqpak" /* 5 */
+ };
+#define STYPEMAX 5
+ struct socket so;
+ struct protosw proto;
+ struct domain dom;
+ struct inpcb inpcb;
+ struct unpcb unpcb;
+ int len;
+ char dname[32], *strcpy();
+
+ PREFIX(i);
+
+ /* fill in socket */
+ if (!KVM_READ(sock, &so, sizeof(struct socket))) {
+ dprintf(stderr, "can't read sock at %x\n", sock);
+ goto bad;
+ }
+
+ /* fill in protosw entry */
+ if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) {
+ dprintf(stderr, "can't read protosw at %x", so.so_proto);
+ goto bad;
+ }
+
+ /* fill in domain */
+ if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) {
+ dprintf(stderr, "can't read domain at %x\n", proto.pr_domain);
+ goto bad;
+ }
+
+ if ((len = kvm_read(kd, (u_long)dom.dom_name, dname,
+ sizeof(dname) - 1)) < 0) {
+ dprintf(stderr, "can't read domain name at %x\n",
+ dom.dom_name);
+ dname[0] = '\0';
+ }
+ else
+ dname[len] = '\0';
+
+ if ((u_short)so.so_type > STYPEMAX)
+ printf("* %s ?%d", dname, so.so_type);
+ else
+ printf("* %s %s", dname, stypename[so.so_type]);
+
+ /*
+ * protocol specific formatting
+ *
+ * Try to find interesting things to print. For tcp, the interesting
+ * thing is the address of the tcpcb, for udp and others, just the
+ * inpcb (socket pcb). For unix domain, its the address of the socket
+ * pcb and the address of the connected pcb (if connected). Otherwise
+ * just print the protocol number and address of the socket itself.
+ * The idea is not to duplicate netstat, but to make available enough
+ * information for further analysis.
+ */
+ switch(dom.dom_family) {
+ case AF_INET:
+ getinetproto(proto.pr_protocol);
+ if (proto.pr_protocol == IPPROTO_TCP ) {
+ if (so.so_pcb) {
+ if (kvm_read(kd, (u_long)so.so_pcb,
+ (char *)&inpcb, sizeof(struct inpcb))
+ != sizeof(struct inpcb)) {
+ dprintf(stderr,
+ "can't read inpcb at %x\n",
+ so.so_pcb);
+ goto bad;
+ }
+ printf(" %x", (int)inpcb.inp_ppcb);
+ }
+ }
+ else if (so.so_pcb)
+ printf(" %x", (int)so.so_pcb);
+ break;
+ case AF_UNIX:
+ /* print address of pcb and connected pcb */
+ if (so.so_pcb) {
+ printf(" %x", (int)so.so_pcb);
+ if (kvm_read(kd, (u_long)so.so_pcb, (char *)&unpcb,
+ sizeof(struct unpcb)) != sizeof(struct unpcb)){
+ dprintf(stderr, "can't read unpcb at %x\n",
+ so.so_pcb);
+ goto bad;
+ }
+ if (unpcb.unp_conn) {
+ char shoconn[4], *cp;
+
+ cp = shoconn;
+ if (!(so.so_state & SS_CANTRCVMORE))
+ *cp++ = '<';
+ *cp++ = '-';
+ if (!(so.so_state & SS_CANTSENDMORE))
+ *cp++ = '>';
+ *cp = '\0';
+ printf(" %s %x", shoconn,
+ (int)unpcb.unp_conn);
+ }
+ }
+ break;
+ default:
+ /* print protocol number and socket address */
+ printf(" %d %x", proto.pr_protocol, (int)sock);
+ }
+ printf("\n");
+ return;
+bad:
+ printf("* error\n");
+}
+
+/*
+ * getinetproto --
+ * print name of protocol number
+ */
+void
+getinetproto(number)
+ int number;
+{
+ char *cp;
+
+ switch(number) {
+ case IPPROTO_IP:
+ cp = "ip"; break;
+ case IPPROTO_ICMP:
+ cp ="icmp"; break;
+ case IPPROTO_GGP:
+ cp ="ggp"; break;
+ case IPPROTO_TCP:
+ cp ="tcp"; break;
+ case IPPROTO_EGP:
+ cp ="egp"; break;
+ case IPPROTO_PUP:
+ cp ="pup"; break;
+ case IPPROTO_UDP:
+ cp ="udp"; break;
+ case IPPROTO_IDP:
+ cp ="idp"; break;
+ case IPPROTO_RAW:
+ cp ="raw"; break;
+ default:
+ printf(" %d", number);
+ return;
+ }
+ printf(" %s", cp);
+}
+
+getfname(filename)
+ char *filename;
+{
+ struct stat statbuf;
+ DEVS *cur;
+
+ if (stat(filename, &statbuf)) {
+ fprintf(stderr, "fstat: %s: %s\n", filename, strerror(errno));
+ return(0);
+ }
+ if ((cur = malloc(sizeof(DEVS))) == NULL) {
+ fprintf(stderr, "fstat: %s\n", strerror(errno));
+ exit(1);
+ }
+ cur->next = devs;
+ devs = cur;
+
+ cur->ino = statbuf.st_ino;
+ cur->fsid = statbuf.st_dev & 0xffff;
+ cur->name = filename;
+ return(1);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: fstat [-fnv] [-p pid] [-u user] [-N system] [-M core] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/head/head.c b/usr.bin/head/head.c
new file mode 100644
index 000000000000..a3ad1de14d4b
--- /dev/null
+++ b/usr.bin/head/head.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 1980, 1987, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)head.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * head - give the first few lines of a stream or of each of a set of files
+ *
+ * Bill Joy UCB August 24, 1977
+ */
+
+void err __P((int, const char *, ...));
+void head __P((FILE *, int));
+void obsolete __P((char *[]));
+void usage __P((void));
+
+int eval;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int ch;
+ FILE *fp;
+ int first, linecnt;
+ char *ep;
+
+ obsolete(argv);
+ linecnt = 10;
+ while ((ch = getopt(argc, argv, "n:")) != EOF)
+ switch(ch) {
+ case 'n':
+ linecnt = strtol(optarg, &ep, 10);
+ if (*ep || linecnt <= 0)
+ err(1, "illegal line count -- %s", optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv)
+ for (first = 1; *argv; ++argv) {
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ err(0, "%s: %s", *argv, strerror(errno));
+ continue;
+ }
+ if (argc > 1) {
+ (void)printf("%s==> %s <==\n",
+ first ? "" : "\n", *argv);
+ first = 0;
+ }
+ head(fp, linecnt);
+ (void)fclose(fp);
+ }
+ else
+ head(stdin, linecnt);
+ exit(eval);
+}
+
+void
+head(fp, cnt)
+ FILE *fp;
+ register int cnt;
+{
+ register int ch;
+
+ while (cnt--)
+ while ((ch = getc(fp)) != EOF) {
+ if (putchar(ch) == EOF)
+ err(1, "stdout: %s", strerror(errno));
+ if (ch == '\n')
+ break;
+ }
+}
+
+void
+obsolete(argv)
+ char *argv[];
+{
+ char *ap;
+
+ while (ap = *++argv) {
+ /* Return if "--" or not "-[0-9]*". */
+ if (ap[0] != '-' || ap[1] == '-' || !isdigit(ap[1]))
+ return;
+ if ((ap = malloc(strlen(*argv) + 2)) == NULL)
+ err(1, "%s", strerror(errno));
+ ap[0] = '-';
+ ap[1] = 'n';
+ (void)strcpy(ap + 2, *argv + 1);
+ *argv = ap;
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: head [-n lines] [file ...]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(int fatal, const char *fmt, ...)
+#else
+err(fatal, fmt, va_alist)
+ int fatal;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "head: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ if (fatal)
+ exit(1);
+ eval = 1;
+}
diff --git a/usr.bin/hexdump/hexsyntax.c b/usr.bin/hexdump/hexsyntax.c
new file mode 100644
index 000000000000..300d43ec16b2
--- /dev/null
+++ b/usr.bin/hexdump/hexsyntax.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hexsyntax.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hexdump.h"
+
+off_t skip; /* bytes to skip */
+
+void
+newsyntax(argc, argvp)
+ int argc;
+ char ***argvp;
+{
+ extern enum _vflag vflag;
+ extern FS *fshead;
+ extern int length;
+ int ch;
+ char *p, **argv;
+
+ argv = *argvp;
+ while ((ch = getopt(argc, argv, "bcde:f:n:os:vx")) != EOF)
+ switch (ch) {
+ case 'b':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"");
+ break;
+ case 'c':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"");
+ break;
+ case 'd':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \" %05u \" \"\\n\"");
+ break;
+ case 'e':
+ add(optarg);
+ break;
+ case 'f':
+ addfile(optarg);
+ break;
+ case 'n':
+ if ((length = atoi(optarg)) < 0)
+ err("%s: bad length value", optarg);
+ break;
+ case 'o':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"");
+ break;
+ case 's':
+ if ((skip = strtol(optarg, &p, 0)) < 0)
+ err("%s: bad skip value", optarg);
+ switch(*p) {
+ case 'b':
+ skip *= 512;
+ break;
+ case 'k':
+ skip *= 1024;
+ break;
+ case 'm':
+ skip *= 1048576;
+ break;
+ }
+ break;
+ case 'v':
+ vflag = ALL;
+ break;
+ case 'x':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \" %04x \" \"\\n\"");
+ break;
+ case '?':
+ usage();
+ }
+
+ if (!fshead) {
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
+ }
+
+ *argvp += optind;
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"hexdump: [-bcdovx] [-e fmt] [-f fmt_file] [-n length] [-s skip] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/hexdump/odsyntax.c b/usr.bin/hexdump/odsyntax.c
new file mode 100644
index 000000000000..46f308b30a59
--- /dev/null
+++ b/usr.bin/hexdump/odsyntax.c
@@ -0,0 +1,265 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)odsyntax.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "hexdump.h"
+
+int deprecated;
+
+static void odoffset __P((int, char ***));
+static void odprecede __P((void));
+
+void
+oldsyntax(argc, argvp)
+ int argc;
+ char ***argvp;
+{
+ extern enum _vflag vflag;
+ extern FS *fshead;
+ int ch;
+ char **argv;
+
+ deprecated = 1;
+ argv = *argvp;
+ while ((ch = getopt(argc, argv, "aBbcDdeFfHhIiLlOoPpswvXx")) != EOF)
+ switch (ch) {
+ case 'a':
+ odprecede();
+ add("16/1 \"%3_u \" \"\\n\"");
+ break;
+ case 'B':
+ case 'o':
+ odprecede();
+ add("8/2 \" %06o \" \"\\n\"");
+ break;
+ case 'b':
+ odprecede();
+ add("16/1 \"%03o \" \"\\n\"");
+ break;
+ case 'c':
+ odprecede();
+ add("16/1 \"%3_c \" \"\\n\"");
+ break;
+ case 'd':
+ odprecede();
+ add("8/2 \" %05u \" \"\\n\"");
+ break;
+ case 'D':
+ odprecede();
+ add("4/4 \" %010u \" \"\\n\"");
+ break;
+ case 'e': /* undocumented in od */
+ case 'F':
+ odprecede();
+ add("2/8 \" %21.14e \" \"\\n\"");
+ break;
+
+ case 'f':
+ odprecede();
+ add("4/4 \" %14.7e \" \"\\n\"");
+ break;
+ case 'H':
+ case 'X':
+ odprecede();
+ add("4/4 \" %08x \" \"\\n\"");
+ break;
+ case 'h':
+ case 'x':
+ odprecede();
+ add("8/2 \" %04x \" \"\\n\"");
+ break;
+ case 'I':
+ case 'L':
+ case 'l':
+ odprecede();
+ add("4/4 \" %11d \" \"\\n\"");
+ break;
+ case 'i':
+ odprecede();
+ add("8/2 \" %6d \" \"\\n\"");
+ break;
+ case 'O':
+ odprecede();
+ add("4/4 \" %011o \" \"\\n\"");
+ break;
+ case 'v':
+ vflag = ALL;
+ break;
+ case 'P':
+ case 'p':
+ case 's':
+ case 'w':
+ case '?':
+ default:
+ (void)fprintf(stderr,
+ "od: od(1) has been deprecated for hexdump(1).\n");
+ if (ch != '?')
+ (void)fprintf(stderr,
+"od: hexdump(1) compatibility doesn't support the -%c option%s\n",
+ ch, ch == 's' ? "; see strings(1)." : ".");
+ usage();
+ }
+
+ if (!fshead) {
+ add("\"%07.7_Ao\n\"");
+ add("\"%07.7_ao \" 8/2 \"%06o \" \"\\n\"");
+ }
+
+ argc -= optind;
+ *argvp += optind;
+
+ if (argc)
+ odoffset(argc, argvp);
+}
+
+static void
+odoffset(argc, argvp)
+ int argc;
+ char ***argvp;
+{
+ extern off_t skip;
+ register char *num, *p;
+ int base;
+ char *end;
+
+ /*
+ * The offset syntax of od(1) was genuinely bizarre. First, if
+ * it started with a plus it had to be an offset. Otherwise, if
+ * there were at least two arguments, a number or lower-case 'x'
+ * followed by a number makes it an offset. By default it was
+ * octal; if it started with 'x' or '0x' it was hex. If it ended
+ * in a '.', it was decimal. If a 'b' or 'B' was appended, it
+ * multiplied the number by 512 or 1024 byte units. There was
+ * no way to assign a block count to a hex offset.
+ *
+ * We assume it's a file if the offset is bad.
+ */
+ p = argc == 1 ? (*argvp)[0] : (*argvp)[1];
+
+ if (*p != '+' && (argc < 2 ||
+ (!isdigit(p[0]) && (p[0] != 'x' || !isxdigit(p[1])))))
+ return;
+
+ base = 0;
+ /*
+ * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
+ * set base.
+ */
+ if (p[0] == '+')
+ ++p;
+ if (p[0] == 'x' && isxdigit(p[1])) {
+ ++p;
+ base = 16;
+ } else if (p[0] == '0' && p[1] == 'x') {
+ p += 2;
+ base = 16;
+ }
+
+ /* skip over the number */
+ if (base == 16)
+ for (num = p; isxdigit(*p); ++p);
+ else
+ for (num = p; isdigit(*p); ++p);
+
+ /* check for no number */
+ if (num == p)
+ return;
+
+ /* if terminates with a '.', base is decimal */
+ if (*p == '.') {
+ if (base)
+ return;
+ base = 10;
+ }
+
+ skip = strtol(num, &end, base ? base : 8);
+
+ /* if end isn't the same as p, we got a non-octal digit */
+ if (end != p) {
+ skip = 0;
+ return;
+ }
+
+ if (*p)
+ if (*p == 'B') {
+ skip *= 1024;
+ ++p;
+ } else if (*p == 'b') {
+ skip *= 512;
+ ++p;
+ }
+
+ if (*p) {
+ skip = 0;
+ return;
+ }
+
+ /*
+ * If the offset uses a non-octal base, the base of the offset
+ * is changed as well. This isn't pretty, but it's easy.
+ */
+#define TYPE_OFFSET 7
+ if (base == 16) {
+ fshead->nextfu->fmt[TYPE_OFFSET] = 'x';
+ fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x';
+ } else if (base == 10) {
+ fshead->nextfu->fmt[TYPE_OFFSET] = 'd';
+ fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd';
+ }
+
+ /* Terminate file list. */
+ (*argvp)[1] = NULL;
+}
+
+static void
+odprecede()
+{
+ static int first = 1;
+
+ if (first) {
+ first = 0;
+ add("\"%07.7_Ao\n\"");
+ add("\"%07.7_ao \"");
+ } else
+ add("\" \"");
+}
diff --git a/usr.bin/join/join.1 b/usr.bin/join/join.1
new file mode 100644
index 000000000000..a1855cf00d37
--- /dev/null
+++ b/usr.bin/join/join.1
@@ -0,0 +1,215 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)join.1 8.3 (Berkeley) 4/28/95
+.\"
+.Dd April 28, 1995
+.Dt JOIN 1
+.Os
+.Sh NAME
+.Nm join
+.Nd relational database operator
+.Sh SYNOPSIS
+.Nm join
+.Oo
+.Fl a Ar file_number | Fl v Ar file_number
+.Oc
+.Op Fl e Ar string
+.Op Fl j Ar file_number field
+.Op Fl o Ar list
+.Bk -words
+.Ek
+.Op Fl t Ar char
+.Op Fl \&1 Ar field
+.Op Fl \&2 Ar field
+.Ar file1
+.Ar file2
+.Sh DESCRIPTION
+The join utility performs an ``equality join'' on the specified files
+and writes the result to the standard output.
+The ``join field'' is the field in each file by which the files are compared.
+The first field in each line is used by default.
+There is one line in the output for each pair of lines in
+.Ar file1
+and
+.Ar file2
+which have identical join fields.
+Each output line consists of the join field, the remaining fields from
+.Ar file1
+and then the remaining fields from
+.Ar file2 .
+.Pp
+The default field separators are tab and space characters.
+In this case, multiple tabs and spaces count as a single field separator,
+and leading tabs and spaces are ignored.
+The default output field separator is a single space character.
+.Pp
+Many of the options use file and field numbers.
+Both file numbers and field numbers are 1 based, i.e. the first file on
+the command line is file number 1 and the first field is field number 1.
+The following options are available:
+.Bl -tag -width Fl
+.It Fl a Ar file_number
+In addition to the default output, produce a line for each unpairable
+line in file
+.Ar file_number .
+(The argument to
+.Fl a
+must not be preceded by a space; see the
+.Sx COMPATIBILITY
+section.)
+.It Fl e Ar string
+Replace empty output fields with
+.Ar string .
+.It Fl o Ar list
+The
+.Fl o
+option specifies the fields that will be output from each file for
+each line with matching join fields.
+Each element of
+.Ar list
+has the form
+.Ql file_number.field ,
+where
+.Ar file_number
+is a file number and
+.Ar field
+is a field number.
+The elements of list must be either comma (``,'') or whitespace separated.
+(The latter requires quoting to protect it from the shell, or, a simpler
+approach is to use multiple
+.Fl o
+options.)
+.It Fl t Ar char
+Use character
+.Ar char
+as a field delimiter for both input and output.
+Every occurrence of
+.Ar char
+in a line is significant.
+.It Fl v Ar file_number
+Do not display the default output, but display a line for each unpairable
+line in file
+.Ar file_number .
+The options
+.Fl v Ar 1
+and
+.Fl v Ar 2
+may be specified at the same time.
+.It Fl 1 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 1.
+.It Fl 2 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 2.
+.El
+.Pp
+When the default field delimiter characters are used, the files to be joined
+should be ordered in the collating sequence of
+.Xr sort 1 ,
+using the
+.Fl b
+option, on the fields on which they are to be joined, otherwise
+.Nm join
+may not report all field matches.
+When the field delimiter characters are specified by the
+.Fl t
+option, the collating sequence should be the same as
+.Xr sort
+without the
+.Fl b
+option.
+.Pp
+If one of the arguments
+.Ar file1
+or
+.Ar file2
+is ``-'', the standard input is used.
+.Pp
+The
+.Nm join
+utility exits 0 on success, and >0 if an error occurs.
+.Sh COMPATIBILITY
+For compatibility with historic versions of
+.Nm join ,
+the following options are available:
+.Bl -tag -width Fl
+.It Fl a
+In addition to the default output, produce a line for each unpairable line
+in both file 1 and file 2.
+(To distinguish between this and
+.Fl a Ar file_number ,
+.Nm join
+currently requires that the latter not include any white space.)
+.It Fl j1 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 1.
+.It Fl j2 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 2.
+.It Fl j Ar field
+Join on the
+.Ar field Ns 'th
+field of both file 1 and file 2.
+.It Fl o Ar list ...
+Historical implementations of
+.Nm join
+permitted multiple arguments to the
+.Fl o
+option.
+These arguments were of the form ``file_number.field_number'' as described
+for the current
+.Fl o
+option.
+This has obvious difficulties in the presence of files named ``1.2''.
+.El
+.Pp
+These options are available only so historic shellscripts don't require
+modification and should not be used.
+.Sh STANDARDS
+The
+.Nm join
+command is expected to be
+.St -p1003.2
+compatible.
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr comm 1 ,
+.Xr paste 1 ,
+.Xr sort 1 ,
+.Xr uniq 1
diff --git a/usr.bin/join/join.c b/usr.bin/join/join.c
new file mode 100644
index 000000000000..122c62f30086
--- /dev/null
+++ b/usr.bin/join/join.c
@@ -0,0 +1,587 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Steve Hayman of the Computer Science Department, Indiana University,
+ * Michiro Hikida and David Goodenough.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)join.c 8.6 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * There's a structure per input file which encapsulates the state of the
+ * file. We repeatedly read lines from each file until we've read in all
+ * the consecutive lines from the file with a common join field. Then we
+ * compare the set of lines with an equivalent set from the other file.
+ */
+typedef struct {
+ char *line; /* line */
+ u_long linealloc; /* line allocated count */
+ char **fields; /* line field(s) */
+ u_long fieldcnt; /* line field(s) count */
+ u_long fieldalloc; /* line field(s) allocated count */
+} LINE;
+
+typedef struct {
+ FILE *fp; /* file descriptor */
+ u_long joinf; /* join field (-1, -2, -j) */
+ int unpair; /* output unpairable lines (-a) */
+ int number; /* 1 for file 1, 2 for file 2 */
+
+ LINE *set; /* set of lines with same field */
+ int pushbool; /* if pushback is set */
+ u_long pushback; /* line on the stack */
+ u_long setcnt; /* set count */
+ u_long setalloc; /* set allocated count */
+} INPUT;
+INPUT input1 = { NULL, 0, 0, 1, NULL, 0, 0, 0, },
+ input2 = { NULL, 0, 0, 2, NULL, 0, 0, 0, };
+
+typedef struct {
+ u_long filenum; /* file number */
+ u_long fieldno; /* field number */
+} OLIST;
+OLIST *olist; /* output field list */
+u_long olistcnt; /* output field list count */
+u_long olistalloc; /* output field allocated count */
+
+int joinout = 1; /* show lines with matched join fields (-v) */
+int needsep; /* need separator character */
+int spans = 1; /* span multiple delimiters (-t) */
+char *empty; /* empty field replacement string (-e) */
+char *tabchar = " \t"; /* delimiter characters (-t) */
+
+int cmp __P((LINE *, u_long, LINE *, u_long));
+void fieldarg __P((char *));
+void joinlines __P((INPUT *, INPUT *));
+void obsolete __P((char **));
+void outfield __P((LINE *, u_long, int));
+void outoneline __P((INPUT *, LINE *));
+void outtwoline __P((INPUT *, LINE *, INPUT *, LINE *));
+void slurp __P((INPUT *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ INPUT *F1, *F2;
+ int aflag, ch, cval, vflag;
+ char *end;
+
+ F1 = &input1;
+ F2 = &input2;
+
+ aflag = vflag = 0;
+ obsolete(argv);
+ while ((ch = getopt(argc, argv, "\01a:e:j:1:2:o:t:v:")) != EOF) {
+ switch (ch) {
+ case '\01': /* See comment in obsolete(). */
+ aflag = 1;
+ F1->unpair = F2->unpair = 1;
+ break;
+ case '1':
+ if ((F1->joinf = strtol(optarg, &end, 10)) < 1)
+ errx(1, "-1 option field number less than 1");
+ if (*end)
+ errx(1, "illegal field number -- %s", optarg);
+ --F1->joinf;
+ break;
+ case '2':
+ if ((F2->joinf = strtol(optarg, &end, 10)) < 1)
+ errx(1, "-2 option field number less than 1");
+ if (*end)
+ errx(1, "illegal field number -- %s", optarg);
+ --F2->joinf;
+ break;
+ case 'a':
+ aflag = 1;
+ switch(strtol(optarg, &end, 10)) {
+ case 1:
+ F1->unpair = 1;
+ break;
+ case 2:
+ F2->unpair = 1;
+ break;
+ default:
+ errx(1, "-a option file number not 1 or 2");
+ break;
+ }
+ if (*end)
+ errx(1, "illegal file number -- %s", optarg);
+ break;
+ case 'e':
+ empty = optarg;
+ break;
+ case 'j':
+ if ((F1->joinf = F2->joinf =
+ strtol(optarg, &end, 10)) < 1)
+ errx(1, "-j option field number less than 1");
+ if (*end)
+ errx(1, "illegal field number -- %s", optarg);
+ --F1->joinf;
+ --F2->joinf;
+ break;
+ case 'o':
+ fieldarg(optarg);
+ break;
+ case 't':
+ spans = 0;
+ if (strlen(tabchar = optarg) != 1)
+ errx(1, "illegal tab character specification");
+ break;
+ case 'v':
+ vflag = 1;
+ joinout = 0;
+ switch (strtol(optarg, &end, 10)) {
+ case 1:
+ F1->unpair = 1;
+ break;
+ case 2:
+ F2->unpair = 1;
+ break;
+ default:
+ errx(1, "-v option file number not 1 or 2");
+ break;
+ }
+ if (*end)
+ errx(1, "illegal file number -- %s", optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (aflag && vflag)
+ errx(1, "the -a and -v options are mutually exclusive");
+
+ if (argc != 2)
+ usage();
+
+ /* Open the files; "-" means stdin. */
+ if (!strcmp(*argv, "-"))
+ F1->fp = stdin;
+ else if ((F1->fp = fopen(*argv, "r")) == NULL)
+ err(1, "%s", *argv);
+ ++argv;
+ if (!strcmp(*argv, "-"))
+ F2->fp = stdin;
+ else if ((F2->fp = fopen(*argv, "r")) == NULL)
+ err(1, "%s", *argv);
+ if (F1->fp == stdin && F2->fp == stdin)
+ errx(1, "only one input file may be stdin");
+
+ slurp(F1);
+ slurp(F2);
+ while (F1->setcnt && F2->setcnt) {
+ cval = cmp(F1->set, F1->joinf, F2->set, F2->joinf);
+ if (cval == 0) {
+ /* Oh joy, oh rapture, oh beauty divine! */
+ if (joinout)
+ joinlines(F1, F2);
+ slurp(F1);
+ slurp(F2);
+ } else if (cval < 0) {
+ /* File 1 takes the lead... */
+ if (F1->unpair)
+ joinlines(F1, NULL);
+ slurp(F1);
+ } else {
+ /* File 2 takes the lead... */
+ if (F2->unpair)
+ joinlines(F2, NULL);
+ slurp(F2);
+ }
+ }
+
+ /*
+ * Now that one of the files is used up, optionally output any
+ * remaining lines from the other file.
+ */
+ if (F1->unpair)
+ while (F1->setcnt) {
+ joinlines(F1, NULL);
+ slurp(F1);
+ }
+ if (F2->unpair)
+ while (F2->setcnt) {
+ joinlines(F2, NULL);
+ slurp(F2);
+ }
+ exit(0);
+}
+
+void
+slurp(F)
+ INPUT *F;
+{
+ LINE *lp, *lastlp, tmp;
+ size_t len;
+ int cnt;
+ char *bp, *fieldp;
+
+ /*
+ * Read all of the lines from an input file that have the same
+ * join field.
+ */
+ F->setcnt = 0;
+ for (lastlp = NULL;; ++F->setcnt, lastlp = lp) {
+ /*
+ * If we're out of space to hold line structures, allocate
+ * more. Initialize the structure so that we know that this
+ * is new space.
+ */
+ if (F->setcnt == F->setalloc) {
+ cnt = F->setalloc;
+ F->setalloc += 50;
+ if ((F->set = realloc(F->set,
+ F->setalloc * sizeof(LINE))) == NULL)
+ err(1, NULL);
+ memset(F->set + cnt, 0, 50 * sizeof(LINE));
+
+ /* re-set lastlp in case it moved */
+ if (lastlp != NULL)
+ lastlp = &F->set[F->setcnt - 1];
+ }
+
+ /*
+ * Get any pushed back line, else get the next line. Allocate
+ * space as necessary. If taking the line from the stack swap
+ * the two structures so that we don't lose space allocated to
+ * either structure. This could be avoided by doing another
+ * level of indirection, but it's probably okay as is.
+ * but it's probably okay as is.
+ */
+ lp = &F->set[F->setcnt];
+ if (F->pushbool) {
+ tmp = F->set[F->setcnt];
+ F->set[F->setcnt] = F->set[F->pushback];
+ F->set[F->pushback] = tmp;
+ F->pushbool = 0;
+ continue;
+ }
+ if ((bp = fgetln(F->fp, &len)) == NULL)
+ return;
+ if (lp->linealloc <= len + 1) {
+ lp->linealloc += MAX(100, len + 1 - lp->linealloc);
+ if ((lp->line =
+ realloc(lp->line, lp->linealloc)) == NULL)
+ err(1, NULL);
+ }
+ memmove(lp->line, bp, len);
+
+ /* Replace trailing newline, if it exists. */
+ if (bp[len - 1] == '\n')
+ lp->line[len - 1] = '\0';
+ else
+ lp->line[len] = '\0';
+ bp = lp->line;
+
+ /* Split the line into fields, allocate space as necessary. */
+ lp->fieldcnt = 0;
+ while ((fieldp = strsep(&bp, tabchar)) != NULL) {
+ if (spans && *fieldp == '\0')
+ continue;
+ if (lp->fieldcnt == lp->fieldalloc) {
+ lp->fieldalloc += 50;
+ if ((lp->fields = realloc(lp->fields,
+ lp->fieldalloc * sizeof(char *))) == NULL)
+ err(1, NULL);
+ }
+ lp->fields[lp->fieldcnt++] = fieldp;
+ }
+
+ /* See if the join field value has changed. */
+ if (lastlp != NULL && cmp(lp, F->joinf, lastlp, F->joinf)) {
+ F->pushbool = 1;
+ F->pushback = F->setcnt;
+ break;
+ }
+ }
+}
+
+int
+cmp(lp1, fieldno1, lp2, fieldno2)
+ LINE *lp1, *lp2;
+ u_long fieldno1, fieldno2;
+{
+ if (lp1->fieldcnt <= fieldno1)
+ return (lp2->fieldcnt <= fieldno2 ? 0 : 1);
+ if (lp2->fieldcnt <= fieldno2)
+ return (-1);
+ return (strcmp(lp1->fields[fieldno1], lp2->fields[fieldno2]));
+}
+
+void
+joinlines(F1, F2)
+ INPUT *F1, *F2;
+{
+ int cnt1, cnt2;
+
+ /*
+ * Output the results of a join comparison. The output may be from
+ * either file 1 or file 2 (in which case the first argument is the
+ * file from which to output) or from both.
+ */
+ if (F2 == NULL) {
+ for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1)
+ outoneline(F1, &F1->set[cnt1]);
+ return;
+ }
+ for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1)
+ for (cnt2 = 0; cnt2 < F2->setcnt; ++cnt2)
+ outtwoline(F1, &F1->set[cnt1], F2, &F2->set[cnt2]);
+}
+
+void
+outoneline(F, lp)
+ INPUT *F;
+ LINE *lp;
+{
+ int cnt;
+
+ /*
+ * Output a single line from one of the files, according to the
+ * join rules. This happens when we are writing unmatched single
+ * lines. Output empty fields in the right places.
+ */
+ if (olist)
+ for (cnt = 0; cnt < olistcnt; ++cnt) {
+ if (olist[cnt].filenum == F->number)
+ outfield(lp, olist[cnt].fieldno, 0);
+ else
+ outfield(lp, 0, 1);
+ }
+ else
+ for (cnt = 0; cnt < lp->fieldcnt; ++cnt)
+ outfield(lp, cnt, 0);
+ (void)printf("\n");
+ if (ferror(stdout))
+ err(1, "stdout");
+ needsep = 0;
+}
+
+void
+outtwoline(F1, lp1, F2, lp2)
+ INPUT *F1, *F2;
+ LINE *lp1, *lp2;
+{
+ int cnt;
+
+ /* Output a pair of lines according to the join list (if any). */
+ if (olist)
+ for (cnt = 0; cnt < olistcnt; ++cnt)
+ if (olist[cnt].filenum == 1)
+ outfield(lp1, olist[cnt].fieldno, 0);
+ else /* if (olist[cnt].filenum == 2) */
+ outfield(lp2, olist[cnt].fieldno, 0);
+ else {
+ /*
+ * Output the join field, then the remaining fields from F1
+ * and F2.
+ */
+ outfield(lp1, F1->joinf, 0);
+ for (cnt = 0; cnt < lp1->fieldcnt; ++cnt)
+ if (F1->joinf != cnt)
+ outfield(lp1, cnt, 0);
+ for (cnt = 0; cnt < lp2->fieldcnt; ++cnt)
+ if (F2->joinf != cnt)
+ outfield(lp2, cnt, 0);
+ }
+ (void)printf("\n");
+ if (ferror(stdout))
+ err(1, "stdout");
+ needsep = 0;
+}
+
+void
+outfield(lp, fieldno, out_empty)
+ LINE *lp;
+ u_long fieldno;
+ int out_empty;
+{
+ if (needsep++)
+ (void)printf("%c", *tabchar);
+ if (!ferror(stdout))
+ if (lp->fieldcnt < fieldno || out_empty) {
+ if (empty != NULL)
+ (void)printf("%s", empty);
+ } else {
+ if (*lp->fields[fieldno] == '\0')
+ return;
+ (void)printf("%s", lp->fields[fieldno]);
+ }
+ if (ferror(stdout))
+ err(1, "stdout");
+}
+
+/*
+ * Convert an output list argument "2.1, 1.3, 2.4" into an array of output
+ * fields.
+ */
+void
+fieldarg(option)
+ char *option;
+{
+ u_long fieldno;
+ char *end, *token;
+
+ while ((token = strsep(&option, ", \t")) != NULL) {
+ if (*token == '\0')
+ continue;
+ if (token[0] != '1' && token[0] != '2' || token[1] != '.')
+ errx(1, "malformed -o option field");
+ fieldno = strtol(token + 2, &end, 10);
+ if (*end)
+ errx(1, "malformed -o option field");
+ if (fieldno == 0)
+ errx(1, "field numbers are 1 based");
+ if (olistcnt == olistalloc) {
+ olistalloc += 50;
+ if ((olist = realloc(olist,
+ olistalloc * sizeof(OLIST))) == NULL)
+ err(1, NULL);
+ }
+ olist[olistcnt].filenum = token[0] - '0';
+ olist[olistcnt].fieldno = fieldno - 1;
+ ++olistcnt;
+ }
+}
+
+void
+obsolete(argv)
+ char **argv;
+{
+ int len;
+ char **p, *ap, *t;
+
+ while ((ap = *++argv) != NULL) {
+ /* Return if "--". */
+ if (ap[0] == '-' && ap[1] == '-')
+ return;
+ switch (ap[1]) {
+ case 'a':
+ /*
+ * The original join allowed "-a", which meant the
+ * same as -a1 plus -a2. POSIX 1003.2, Draft 11.2
+ * only specifies this as "-a 1" and "a -2", so we
+ * have to use another option flag, one that is
+ * unlikely to ever be used or accidentally entered
+ * on the command line. (Well, we could reallocate
+ * the argv array, but that hardly seems worthwhile.)
+ */
+ if (ap[2] == '\0')
+ ap[1] = '\01';
+ break;
+ case 'j':
+ /*
+ * The original join allowed "-j[12] arg" and "-j arg".
+ * Convert the former to "-[12] arg". Don't convert
+ * the latter since getopt(3) can handle it.
+ */
+ switch(ap[2]) {
+ case '1':
+ if (ap[3] != '\0')
+ goto jbad;
+ ap[1] = '1';
+ ap[2] = '\0';
+ break;
+ case '2':
+ if (ap[3] != '\0')
+ goto jbad;
+ ap[1] = '2';
+ ap[2] = '\0';
+ break;
+ case '\0':
+ break;
+ default:
+jbad: errx(1, "illegal option -- %s", ap);
+ usage();
+ }
+ break;
+ case 'o':
+ /*
+ * The original join allowed "-o arg arg".
+ * Convert to "-o arg -o arg".
+ */
+ if (ap[2] != '\0')
+ break;
+ for (p = argv + 2; *p; ++p) {
+ if (p[0][0] != '1' &&
+ p[0][0] != '2' || p[0][1] != '.')
+ break;
+ len = strlen(*p);
+ if (len - 2 != strspn(*p + 2, "0123456789"))
+ break;
+ if ((t = malloc(len + 3)) == NULL)
+ err(1, NULL);
+ t[0] = '-';
+ t[1] = 'o';
+ memmove(t + 2, *p, len + 1);
+ *p = t;
+ }
+ argv = p - 1;
+ break;
+ }
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "%s%s\n",
+ "usage: join [-a fileno | -v fileno ] [-e string] [-1 field] ",
+ "[-2 field]\n [-o list] [-t char] file1 file2");
+ exit(1);
+}
diff --git a/usr.bin/look/look.c b/usr.bin/look/look.c
new file mode 100644
index 000000000000..ddbd67fd05da
--- /dev/null
+++ b/usr.bin/look/look.c
@@ -0,0 +1,359 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)look.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+/*
+ * look -- find lines in a sorted list.
+ *
+ * The man page said that TABs and SPACEs participate in -d comparisons.
+ * In fact, they were ignored. This implements historic practice, not
+ * the manual page.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+
+/*
+ * FOLD and DICT convert characters to a normal form for comparison,
+ * according to the user specified flags.
+ *
+ * DICT expects integers because it uses a non-character value to
+ * indicate a character which should not participate in comparisons.
+ */
+#define EQUAL 0
+#define GREATER 1
+#define LESS (-1)
+#define NO_COMPARE (-2)
+
+#define FOLD(c) (isascii(c) && isupper(c) ? tolower(c) : (c))
+#define DICT(c) (isascii(c) && isalnum(c) ? (c) : NO_COMPARE)
+
+int dflag, fflag;
+
+char *binary_search __P((char *, char *, char *));
+int compare __P((char *, char *, char *));
+void err __P((const char *fmt, ...));
+char *linear_search __P((char *, char *, char *));
+int look __P((char *, char *, char *));
+void print_from __P((char *, char *, char *));
+
+static void usage __P((void));
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb;
+ int ch, fd, termchar;
+ char *back, *file, *front, *string, *p;
+
+ file = _PATH_WORDS;
+ termchar = '\0';
+ while ((ch = getopt(argc, argv, "dft:")) != EOF)
+ switch(ch) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 't':
+ termchar = *optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 2: /* Don't set -df for user. */
+ string = *argv++;
+ file = *argv;
+ break;
+ case 1: /* But set -df by default. */
+ dflag = fflag = 1;
+ string = *argv;
+ break;
+ default:
+ usage();
+ }
+
+ if (termchar != '\0' && (p = strchr(string, termchar)) != NULL)
+ *++p = '\0';
+
+ if ((fd = open(file, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
+ err("%s: %s", file, strerror(errno));
+ if (sb.st_size > SIZE_T_MAX)
+ err("%s: %s", file, strerror(EFBIG));
+ if ((front = mmap(NULL,
+ (size_t)sb.st_size, PROT_READ, 0, fd, (off_t)0)) == NULL)
+ err("%s: %s", file, strerror(errno));
+ back = front + sb.st_size;
+ exit(look(string, front, back));
+}
+
+look(string, front, back)
+ char *string, *front, *back;
+{
+ register int ch;
+ register char *readp, *writep;
+
+ /* Reformat string string to avoid doing it multiple times later. */
+ for (readp = writep = string; ch = *readp++;) {
+ if (fflag)
+ ch = FOLD(ch);
+ if (dflag)
+ ch = DICT(ch);
+ if (ch != NO_COMPARE)
+ *(writep++) = ch;
+ }
+ *writep = '\0';
+
+ front = binary_search(string, front, back);
+ front = linear_search(string, front, back);
+
+ if (front)
+ print_from(string, front, back);
+ return (front ? 0 : 1);
+}
+
+
+/*
+ * Binary search for "string" in memory between "front" and "back".
+ *
+ * This routine is expected to return a pointer to the start of a line at
+ * *or before* the first word matching "string". Relaxing the constraint
+ * this way simplifies the algorithm.
+ *
+ * Invariants:
+ * front points to the beginning of a line at or before the first
+ * matching string.
+ *
+ * back points to the beginning of a line at or after the first
+ * matching line.
+ *
+ * Base of the Invariants.
+ * front = NULL;
+ * back = EOF;
+ *
+ * Advancing the Invariants:
+ *
+ * p = first newline after halfway point from front to back.
+ *
+ * If the string at "p" is not greater than the string to match,
+ * p is the new front. Otherwise it is the new back.
+ *
+ * Termination:
+ *
+ * The definition of the routine allows it return at any point,
+ * since front is always at or before the line to print.
+ *
+ * In fact, it returns when the chosen "p" equals "back". This
+ * implies that there exists a string is least half as long as
+ * (back - front), which in turn implies that a linear search will
+ * be no more expensive than the cost of simply printing a string or two.
+ *
+ * Trying to continue with binary search at this point would be
+ * more trouble than it's worth.
+ */
+#define SKIP_PAST_NEWLINE(p, back) \
+ while (p < back && *p++ != '\n');
+
+char *
+binary_search(string, front, back)
+ register char *string, *front, *back;
+{
+ register char *p;
+
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+
+ /*
+ * If the file changes underneath us, make sure we don't
+ * infinitely loop.
+ */
+ while (p < back && back > front) {
+ if (compare(string, p, back) == GREATER)
+ front = p;
+ else
+ back = p;
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+ }
+ return (front);
+}
+
+/*
+ * Find the first line that starts with string, linearly searching from front
+ * to back.
+ *
+ * Return NULL for no such line.
+ *
+ * This routine assumes:
+ *
+ * o front points at the first character in a line.
+ * o front is before or at the first line to be printed.
+ */
+char *
+linear_search(string, front, back)
+ char *string, *front, *back;
+{
+ while (front < back) {
+ switch (compare(string, front, back)) {
+ case EQUAL: /* Found it. */
+ return (front);
+ break;
+ case LESS: /* No such string. */
+ return (NULL);
+ break;
+ case GREATER: /* Keep going. */
+ break;
+ }
+ SKIP_PAST_NEWLINE(front, back);
+ }
+ return (NULL);
+}
+
+/*
+ * Print as many lines as match string, starting at front.
+ */
+void
+print_from(string, front, back)
+ register char *string, *front, *back;
+{
+ for (; front < back && compare(string, front, back) == EQUAL; ++front) {
+ for (; front < back && *front != '\n'; ++front)
+ if (putchar(*front) == EOF)
+ err("stdout: %s", strerror(errno));
+ if (putchar('\n') == EOF)
+ err("stdout: %s", strerror(errno));
+ }
+}
+
+/*
+ * Return LESS, GREATER, or EQUAL depending on how the string1 compares with
+ * string2 (s1 ??? s2).
+ *
+ * o Matches up to len(s1) are EQUAL.
+ * o Matches up to len(s2) are GREATER.
+ *
+ * Compare understands about the -f and -d flags, and treats comparisons
+ * appropriately.
+ *
+ * The string "s1" is null terminated. The string s2 is '\n' terminated (or
+ * "back" terminated).
+ */
+int
+compare(s1, s2, back)
+ register char *s1, *s2, *back;
+{
+ register int ch;
+
+ for (; *s1 && s2 < back && *s2 != '\n'; ++s1, ++s2) {
+ ch = *s2;
+ if (fflag)
+ ch = FOLD(ch);
+ if (dflag)
+ ch = DICT(ch);
+
+ if (ch == NO_COMPARE) {
+ ++s2; /* Ignore character in comparison. */
+ continue;
+ }
+ if (*s1 != ch)
+ return (*s1 < ch ? LESS : GREATER);
+ }
+ return (*s1 ? GREATER : EQUAL);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: look [-df] [-t char] string [file]\n");
+ exit(2);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "look: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(2);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/mt/mt.c b/usr.bin/mt/mt.c
new file mode 100644
index 000000000000..172a424cfff0
--- /dev/null
+++ b/usr.bin/mt/mt.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mt.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+/*
+ * mt --
+ * magnetic tape manipulation program
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct commands {
+ char *c_name;
+ int c_code;
+ int c_ronly;
+} com[] = {
+ { "bsf", MTBSF, 1 },
+ { "bsr", MTBSR, 1 },
+ { "eof", MTWEOF, 0 },
+ { "fsf", MTFSF, 1 },
+ { "fsr", MTFSR, 1 },
+ { "offline", MTOFFL, 1 },
+ { "rewind", MTREW, 1 },
+ { "rewoffl", MTOFFL, 1 },
+ { "status", MTNOP, 1 },
+ { "weof", MTWEOF, 0 },
+ { NULL }
+};
+
+void err __P((const char *, ...));
+void printreg __P((char *, u_int, char *));
+void status __P((struct mtget *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct commands *comp;
+ struct mtget mt_status;
+ struct mtop mt_com;
+ int ch, len, mtfd;
+ char *p, *tape;
+
+ if ((tape = getenv("TAPE")) == NULL)
+ tape = DEFTAPE;
+
+ while ((ch = getopt(argc, argv, "f:t:")) != EOF)
+ switch(ch) {
+ case 'f':
+ case 't':
+ tape = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1 || argc > 2)
+ usage();
+
+ len = strlen(p = *argv++);
+ for (comp = com;; comp++) {
+ if (comp->c_name == NULL)
+ err("%s: unknown command", p);
+ if (strncmp(p, comp->c_name, len) == 0)
+ break;
+ }
+ if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0)
+ err("%s: %s", tape, strerror(errno));
+ if (comp->c_code != MTNOP) {
+ mt_com.mt_op = comp->c_code;
+ if (*argv) {
+ mt_com.mt_count = strtol(*argv, &p, 10);
+ if (mt_com.mt_count <= 0 || *p)
+ err("%s: illegal count", *argv);
+ }
+ else
+ mt_com.mt_count = 1;
+ if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0)
+ err("%s: %s: %s", tape, comp->c_name, strerror(errno));
+ } else {
+ if (ioctl(mtfd, MTIOCGET, &mt_status) < 0)
+ err("%s", strerror(errno));
+ status(&mt_status);
+ }
+ exit (0);
+ /* NOTREACHED */
+}
+
+#ifdef vax
+#include <vax/mba/mtreg.h>
+#include <vax/mba/htreg.h>
+
+#include <vax/uba/utreg.h>
+#include <vax/uba/tmreg.h>
+#undef b_repcnt /* argh */
+#include <vax/uba/tsreg.h>
+#endif
+
+#ifdef sun
+#include <sundev/tmreg.h>
+#include <sundev/arreg.h>
+#endif
+
+#ifdef tahoe
+#include <tahoe/vba/cyreg.h>
+#endif
+
+struct tape_desc {
+ short t_type; /* type of magtape device */
+ char *t_name; /* printing name */
+ char *t_dsbits; /* "drive status" register */
+ char *t_erbits; /* "error" register */
+} tapes[] = {
+#ifdef vax
+ { MT_ISTS, "ts11", 0, TSXS0_BITS },
+ { MT_ISHT, "tm03", HTDS_BITS, HTER_BITS },
+ { MT_ISTM, "tm11", 0, TMER_BITS },
+ { MT_ISMT, "tu78", MTDS_BITS, 0 },
+ { MT_ISUT, "tu45", UTDS_BITS, UTER_BITS },
+#endif
+#ifdef sun
+ { MT_ISCPC, "TapeMaster", TMS_BITS, 0 },
+ { MT_ISAR, "Archive", ARCH_CTRL_BITS, ARCH_BITS },
+#endif
+#ifdef tahoe
+ { MT_ISCY, "cipher", CYS_BITS, CYCW_BITS },
+#endif
+ { 0 }
+};
+
+/*
+ * Interpret the status buffer returned
+ */
+void
+status(bp)
+ register struct mtget *bp;
+{
+ register struct tape_desc *mt;
+
+ for (mt = tapes;; mt++) {
+ if (mt->t_type == 0) {
+ (void)printf("%d: unknown tape drive type\n",
+ bp->mt_type);
+ return;
+ }
+ if (mt->t_type == bp->mt_type)
+ break;
+ }
+ (void)printf("%s tape drive, residual=%d\n", mt->t_name, bp->mt_resid);
+ printreg("ds", bp->mt_dsreg, mt->t_dsbits);
+ printreg("\ner", bp->mt_erreg, mt->t_erbits);
+ (void)putchar('\n');
+}
+
+/*
+ * Print a register a la the %b format of the kernel's printf.
+ */
+void
+printreg(s, v, bits)
+ char *s;
+ register u_int v;
+ register char *bits;
+{
+ register int i, any = 0;
+ register char c;
+
+ if (bits && *bits == 8)
+ printf("%s=%o", s, v);
+ else
+ printf("%s=%x", s, v);
+ bits++;
+ if (v && bits) {
+ putchar('<');
+ while (i = *bits++) {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: mt [-f device] command [ count ]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "mt: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/printenv/printenv.c b/usr.bin/printenv/printenv.c
new file mode 100644
index 000000000000..fc40e3d32444
--- /dev/null
+++ b/usr.bin/printenv/printenv.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)printenv.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+void usage __P((void));
+
+/*
+ * printenv
+ *
+ * Bill Joy, UCB
+ * February, 1979
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char **environ;
+ register char *cp, **ep;
+ register size_t len;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ for (ep = environ; *ep; ep++)
+ (void)printf("%s\n", *ep);
+ exit(0);
+ }
+ len = strlen(*argv);
+ for (ep = environ; *ep; ep++)
+ if (!memcmp(*ep, *argv, len)) {
+ cp = *ep + len;
+ if (!*cp || *cp == '=') {
+ (void)printf("%s\n", *cp ? cp + 1 : cp);
+ exit(0);
+ }
+ }
+ exit(1);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: printenv [name]\n");
+ exit(1);
+}
diff --git a/usr.bin/rev/rev.c b/usr.bin/rev/rev.c
new file mode 100644
index 000000000000..2fe272e94d88
--- /dev/null
+++ b/usr.bin/rev/rev.c
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 1987, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rev.c 8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *filename, *p, *t;
+ FILE *fp;
+ size_t len;
+ int ch, rval;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ fp = stdin;
+ filename = "stdin";
+ rval = 0;
+ do {
+ if (*argv) {
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ warn("%s", *argv);
+ rval = 1;
+ ++argv;
+ continue;
+ }
+ filename = *argv++;
+ }
+ while ((p = fgetln(fp, &len)) != NULL) {
+ if (p[len - 1] == '\n')
+ --len;
+ t = p + len - 1;
+ for (t = p + len - 1; t >= p; --t)
+ putchar(*t);
+ putchar('\n');
+ }
+ if (ferror(fp)) {
+ warn("%s", filename);
+ rval = 1;
+ }
+ (void)fclose(fp);
+ } while(*argv);
+ exit(rval);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: rev [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/tr/str.c b/usr.bin/tr/str.c
new file mode 100644
index 000000000000..367e1b1f4d55
--- /dev/null
+++ b/usr.bin/tr/str.c
@@ -0,0 +1,342 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)str.c 8.2 (Berkeley) 4/28/95";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extern.h"
+
+static int backslash __P((STR *));
+static int bracket __P((STR *));
+static int c_class __P((const void *, const void *));
+static void genclass __P((STR *));
+static void genequiv __P((STR *));
+static int genrange __P((STR *));
+static void genseq __P((STR *));
+
+int
+next(s)
+ register STR *s;
+{
+ register int ch;
+
+ switch (s->state) {
+ case EOS:
+ return (0);
+ case INFINITE:
+ return (1);
+ case NORMAL:
+ switch (ch = *s->str) {
+ case '\0':
+ s->state = EOS;
+ return (0);
+ case '\\':
+ s->lastch = backslash(s);
+ break;
+ case '[':
+ if (bracket(s))
+ return (next(s));
+ /* FALLTHROUGH */
+ default:
+ ++s->str;
+ s->lastch = ch;
+ break;
+ }
+
+ /* We can start a range at any time. */
+ if (s->str[0] == '-' && genrange(s))
+ return (next(s));
+ return (1);
+ case RANGE:
+ if (s->cnt-- == 0) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ ++s->lastch;
+ return (1);
+ case SEQUENCE:
+ if (s->cnt-- == 0) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ return (1);
+ case SET:
+ if ((s->lastch = s->set[s->cnt++]) == OOBCH) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ return (1);
+ }
+ /* NOTREACHED */
+}
+
+static int
+bracket(s)
+ register STR *s;
+{
+ register char *p;
+
+ switch (s->str[1]) {
+ case ':': /* "[:class:]" */
+ if ((p = strstr(s->str + 2, ":]")) == NULL)
+ return (0);
+ *p = '\0';
+ s->str += 2;
+ genclass(s);
+ s->str = p + 2;
+ return (1);
+ case '=': /* "[=equiv=]" */
+ if ((p = strstr(s->str + 2, "=]")) == NULL)
+ return (0);
+ s->str += 2;
+ genequiv(s);
+ return (1);
+ default: /* "[\###*n]" or "[#*n]" */
+ if ((p = strpbrk(s->str + 2, "*]")) == NULL)
+ return (0);
+ if (p[0] != '*' || index(p, ']') == NULL)
+ return (0);
+ s->str += 1;
+ genseq(s);
+ return (1);
+ }
+ /* NOTREACHED */
+}
+
+int isalnum __P((int)),
+ isalpha __P((int)),
+ isblank __P((int)),
+ isspace __P((int)),
+ iscntrl __P((int)),
+ isdigit __P((int)),
+ isgraph __P((int)),
+ islower __P((int)),
+ isprint __P((int)),
+ ispunct __P((int)),
+ isupper __P((int)),
+ isxdigit __P((int));
+
+typedef struct {
+ char *name;
+ int (*func) __P((int));
+ int *set;
+} CLASS;
+
+static CLASS classes[] = {
+ { "alnum", isalnum, },
+ { "alpha", isalpha, },
+ { "blank", isblank, },
+ { "cntrl", iscntrl, },
+ { "digit", isdigit, },
+ { "graph", isgraph, },
+ { "lower", islower, },
+ { "print", isupper, },
+ { "punct", ispunct, },
+ { "space", isspace, },
+ { "upper", isupper, },
+ { "xdigit", isxdigit, },
+};
+
+static void
+genclass(s)
+ STR *s;
+{
+ register int cnt, (*func) __P((int));
+ CLASS *cp, tmp;
+ int *p;
+
+ tmp.name = s->str;
+ if ((cp = (CLASS *)bsearch(&tmp, classes, sizeof(classes) /
+ sizeof(CLASS), sizeof(CLASS), c_class)) == NULL)
+ err("unknown class %s", s->str);
+
+ if ((cp->set = p = malloc((NCHARS + 1) * sizeof(int))) == NULL)
+ err("%s", strerror(errno));
+ bzero(p, (NCHARS + 1) * sizeof(int));
+ for (cnt = 0, func = cp->func; cnt < NCHARS; ++cnt)
+ if ((func)(cnt))
+ *p++ = cnt;
+ *p = OOBCH;
+
+ s->cnt = 0;
+ s->state = SET;
+ s->set = cp->set;
+}
+
+static int
+c_class(a, b)
+ const void *a, *b;
+{
+ return (strcmp(((CLASS *)a)->name, ((CLASS *)b)->name));
+}
+
+/*
+ * English doesn't have any equivalence classes, so for now
+ * we just syntax check and grab the character.
+ */
+static void
+genequiv(s)
+ STR *s;
+{
+ if (*s->str == '\\') {
+ s->equiv[0] = backslash(s);
+ if (*s->str != '=')
+ err("misplaced equivalence equals sign");
+ } else {
+ s->equiv[0] = s->str[0];
+ if (s->str[1] != '=')
+ err("misplaced equivalence equals sign");
+ }
+ s->str += 2;
+ s->cnt = 0;
+ s->state = SET;
+ s->set = s->equiv;
+}
+
+static int
+genrange(s)
+ STR *s;
+{
+ int stopval;
+ char *savestart;
+
+ savestart = s->str;
+ stopval = *++s->str == '\\' ? backslash(s) : *s->str++;
+ if (stopval < (u_char)s->lastch) {
+ s->str = savestart;
+ return (0);
+ }
+ s->cnt = stopval - s->lastch + 1;
+ s->state = RANGE;
+ --s->lastch;
+ return (1);
+}
+
+static void
+genseq(s)
+ STR *s;
+{
+ char *ep;
+
+ if (s->which == STRING1)
+ err("sequences only valid in string2");
+
+ if (*s->str == '\\')
+ s->lastch = backslash(s);
+ else
+ s->lastch = *s->str++;
+ if (*s->str != '*')
+ err("misplaced sequence asterisk");
+
+ switch (*++s->str) {
+ case '\\':
+ s->cnt = backslash(s);
+ break;
+ case ']':
+ s->cnt = 0;
+ ++s->str;
+ break;
+ default:
+ if (isdigit(*s->str)) {
+ s->cnt = strtol(s->str, &ep, 0);
+ if (*ep == ']') {
+ s->str = ep + 1;
+ break;
+ }
+ }
+ err("illegal sequence count");
+ /* NOTREACHED */
+ }
+
+ s->state = s->cnt ? SEQUENCE : INFINITE;
+}
+
+/* Use the #defines isXXX() here, DON'T use them above. */
+#include <ctype.h>
+
+/*
+ * Translate \??? into a character. Up to 3 octal digits, if no digits either
+ * an escape code or a literal character.
+ */
+static int
+backslash(s)
+ register STR *s;
+{
+ register int ch, cnt, val;
+
+ for (cnt = val = 0;;) {
+ ch = *++s->str;
+ if (!isascii(ch) || !isdigit(ch))
+ break;
+ val = val * 8 + ch - '0';
+ if (++cnt == 3) {
+ ++s->str;
+ break;
+ }
+ }
+ if (cnt)
+ return (val);
+ if (ch != '\0')
+ ++s->str;
+ switch (ch) {
+ case 'a': /* escape characters */
+ return ('\7');
+ case 'b':
+ return ('\b');
+ case 'f':
+ return ('\f');
+ case 'n':
+ return ('\n');
+ case 'r':
+ return ('\r');
+ case 't':
+ return ('\t');
+ case 'v':
+ return ('\13');
+ case '\0': /* \" -> \ */
+ s->state = EOS;
+ return ('\\');
+ default: /* \x" -> x */
+ return (ch);
+ }
+}
diff --git a/usr.bin/tr/tr.c b/usr.bin/tr/tr.c
new file mode 100644
index 000000000000..c34e51d7605d
--- /dev/null
+++ b/usr.bin/tr/tr.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tr.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static int string1[NCHARS] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* ASCII */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+}, string2[NCHARS];
+
+STR s1 = { STRING1, NORMAL, 0, OOBCH, { 0, OOBCH }, NULL, NULL };
+STR s2 = { STRING2, NORMAL, 0, OOBCH, { 0, OOBCH }, NULL, NULL };
+
+static void setup __P((int *, char *, STR *, int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int ch, cnt, lastch, *p;
+ int cflag, dflag, sflag, isstring2;
+
+ cflag = dflag = sflag = 0;
+ while ((ch = getopt(argc, argv, "cds")) != EOF)
+ switch((char)ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch(argc) {
+ case 0:
+ default:
+ usage();
+ /* NOTREACHED */
+ case 1:
+ isstring2 = 0;
+ break;
+ case 2:
+ isstring2 = 1;
+ break;
+ }
+
+ /*
+ * tr -ds [-c] string1 string2
+ * Delete all characters (or complemented characters) in string1.
+ * Squeeze all characters in string2.
+ */
+ if (dflag && sflag) {
+ if (!isstring2)
+ usage();
+
+ setup(string1, argv[0], &s1, cflag);
+ setup(string2, argv[1], &s2, 0);
+
+ for (lastch = OOBCH; (ch = getchar()) != EOF;)
+ if (!string1[ch] && (!string2[ch] || lastch != ch)) {
+ lastch = ch;
+ (void)putchar(ch);
+ }
+ exit(0);
+ }
+
+ /*
+ * tr -d [-c] string1
+ * Delete all characters (or complemented characters) in string1.
+ */
+ if (dflag) {
+ if (isstring2)
+ usage();
+
+ setup(string1, argv[0], &s1, cflag);
+
+ while ((ch = getchar()) != EOF)
+ if (!string1[ch])
+ (void)putchar(ch);
+ exit(0);
+ }
+
+ /*
+ * tr -s [-c] string1
+ * Squeeze all characters (or complemented characters) in string1.
+ */
+ if (sflag && !isstring2) {
+ setup(string1, argv[0], &s1, cflag);
+
+ for (lastch = OOBCH; (ch = getchar()) != EOF;)
+ if (!string1[ch] || lastch != ch) {
+ lastch = ch;
+ (void)putchar(ch);
+ }
+ exit(0);
+ }
+
+ /*
+ * tr [-cs] string1 string2
+ * Replace all characters (or complemented characters) in string1 with
+ * the character in the same position in string2. If the -s option is
+ * specified, squeeze all the characters in string2.
+ */
+ if (!isstring2)
+ usage();
+
+ s1.str = argv[0];
+ s2.str = argv[1];
+
+ if (cflag)
+ for (cnt = NCHARS, p = string1; cnt--;)
+ *p++ = OOBCH;
+
+ if (!next(&s2))
+ err("empty string2");
+
+ /* If string2 runs out of characters, use the last one specified. */
+ if (sflag)
+ while (next(&s1)) {
+ string1[s1.lastch] = ch = s2.lastch;
+ string2[ch] = 1;
+ (void)next(&s2);
+ }
+ else
+ while (next(&s1)) {
+ string1[s1.lastch] = ch = s2.lastch;
+ (void)next(&s2);
+ }
+
+ if (cflag)
+ for (cnt = 0, p = string1; cnt < NCHARS; ++p, ++cnt)
+ *p = *p == OOBCH ? ch : cnt;
+
+ if (sflag)
+ for (lastch = OOBCH; (ch = getchar()) != EOF;) {
+ ch = string1[ch];
+ if (!string2[ch] || lastch != ch) {
+ lastch = ch;
+ (void)putchar(ch);
+ }
+ }
+ else
+ while ((ch = getchar()) != EOF)
+ (void)putchar(string1[ch]);
+ exit (0);
+}
+
+static void
+setup(string, arg, str, cflag)
+ int *string;
+ char *arg;
+ STR *str;
+ int cflag;
+{
+ register int cnt, *p;
+
+ str->str = arg;
+ bzero(string, NCHARS * sizeof(int));
+ while (next(str))
+ string[str->lastch] = 1;
+ if (cflag)
+ for (p = string, cnt = NCHARS; cnt--; ++p)
+ *p = !*p;
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: tr [-cs] string1 string2\n");
+ (void)fprintf(stderr, " tr [-c] -d string1\n");
+ (void)fprintf(stderr, " tr [-c] -s string1\n");
+ (void)fprintf(stderr, " tr [-c] -ds string1 string2\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "tr: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/uname/uname.c b/usr.bin/uname/uname.c
new file mode 100644
index 000000000000..86b5dcea4de7
--- /dev/null
+++ b/usr.bin/uname/uname.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uname.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#define MFLAG 0x01
+#define NFLAG 0x02
+#define RFLAG 0x04
+#define SFLAG 0x08
+#define VFLAG 0x10
+ u_int flags;
+ int ch, mib[2];
+ size_t len, tlen;
+ char *p, *prefix, buf[1024];
+
+ flags = 0;
+ while ((ch = getopt(argc, argv, "amnrsv")) != EOF)
+ switch(ch) {
+ case 'a':
+ flags |= (MFLAG | NFLAG | RFLAG | SFLAG | VFLAG);
+ break;
+ case 'm':
+ flags |= MFLAG;
+ break;
+ case 'n':
+ flags |= NFLAG;
+ break;
+ case 'r':
+ flags |= RFLAG;
+ break;
+ case 's':
+ flags |= SFLAG;
+ break;
+ case 'v':
+ flags |= VFLAG;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ if (!flags)
+ flags |= SFLAG;
+
+ prefix = "";
+
+ if (flags & SFLAG) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSTYPE;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ if (flags & NFLAG) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_HOSTNAME;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ if (flags & RFLAG) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSRELEASE;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ if (flags & VFLAG) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_VERSION;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ for (p = buf, tlen = len; tlen--; ++p)
+ if (*p == '\n' || *p == '\t')
+ *p = ' ';
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ if (flags & MFLAG) {
+ mib[0] = CTL_HW;
+ mib[1] = HW_MACHINE;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ (void)printf("\n");
+ exit (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: uname [-amnrsv]\n");
+ exit(1);
+}
diff --git a/usr.bin/uniq/uniq.c b/usr.bin/uniq/uniq.c
new file mode 100644
index 000000000000..b2b85e108611
--- /dev/null
+++ b/usr.bin/uniq/uniq.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Case Larsen.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uniq.c 8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXLINELEN (8 * 1024)
+
+int cflag, dflag, uflag;
+int numchars, numfields, repeats;
+
+void err __P((const char *, ...));
+FILE *file __P((char *, char *));
+void show __P((FILE *, char *));
+char *skip __P((char *));
+void obsolete __P((char *[]));
+void usage __P((void));
+
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *t1, *t2;
+ FILE *ifp, *ofp;
+ int ch;
+ char *prevline, *thisline, *p;
+
+ obsolete(argv);
+ while ((ch = getopt(argc, argv, "-cdf:s:u")) != EOF)
+ switch (ch) {
+ case '-':
+ --optind;
+ goto done;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ numfields = strtol(optarg, &p, 10);
+ if (numfields < 0 || *p)
+ err("illegal field skip value: %s", optarg);
+ break;
+ case 's':
+ numchars = strtol(optarg, &p, 10);
+ if (numchars < 0 || *p)
+ err("illegal character skip value: %s", optarg);
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+done: argc -= optind;
+ argv +=optind;
+
+ /* If no flags are set, default is -d -u. */
+ if (cflag) {
+ if (dflag || uflag)
+ usage();
+ } else if (!dflag && !uflag)
+ dflag = uflag = 1;
+
+ switch(argc) {
+ case 0:
+ ifp = stdin;
+ ofp = stdout;
+ break;
+ case 1:
+ ifp = file(argv[0], "r");
+ ofp = stdout;
+ break;
+ case 2:
+ ifp = file(argv[0], "r");
+ ofp = file(argv[1], "w");
+ break;
+ default:
+ usage();
+ }
+
+ prevline = malloc(MAXLINELEN);
+ thisline = malloc(MAXLINELEN);
+ if (prevline == NULL || thisline == NULL)
+ err("%s", strerror(errno));
+
+ if (fgets(prevline, MAXLINELEN, ifp) == NULL)
+ exit(0);
+
+ while (fgets(thisline, MAXLINELEN, ifp)) {
+ /* If requested get the chosen fields + character offsets. */
+ if (numfields || numchars) {
+ t1 = skip(thisline);
+ t2 = skip(prevline);
+ } else {
+ t1 = thisline;
+ t2 = prevline;
+ }
+
+ /* If different, print; set previous to new value. */
+ if (strcmp(t1, t2)) {
+ show(ofp, prevline);
+ t1 = prevline;
+ prevline = thisline;
+ thisline = t1;
+ repeats = 0;
+ } else
+ ++repeats;
+ }
+ show(ofp, prevline);
+ exit(0);
+}
+
+/*
+ * show --
+ * Output a line depending on the flags and number of repetitions
+ * of the line.
+ */
+void
+show(ofp, str)
+ FILE *ofp;
+ char *str;
+{
+
+ if (cflag && *str)
+ (void)fprintf(ofp, "%4d %s", repeats + 1, str);
+ if (dflag && repeats || uflag && !repeats)
+ (void)fprintf(ofp, "%s", str);
+}
+
+char *
+skip(str)
+ register char *str;
+{
+ register int infield, nchars, nfields;
+
+ for (nfields = numfields, infield = 0; nfields && *str; ++str)
+ if (isspace(*str)) {
+ if (infield) {
+ infield = 0;
+ --nfields;
+ }
+ } else if (!infield)
+ infield = 1;
+ for (nchars = numchars; nchars-- && *str; ++str);
+ return(str);
+}
+
+FILE *
+file(name, mode)
+ char *name, *mode;
+{
+ FILE *fp;
+
+ if ((fp = fopen(name, mode)) == NULL)
+ err("%s: %s", name, strerror(errno));
+ return(fp);
+}
+
+void
+obsolete(argv)
+ char *argv[];
+{
+ int len;
+ char *ap, *p, *start;
+
+ while (ap = *++argv) {
+ /* Return if "--" or not an option of any form. */
+ if (ap[0] != '-') {
+ if (ap[0] != '+')
+ return;
+ } else if (ap[1] == '-')
+ return;
+ if (!isdigit(ap[1]))
+ continue;
+ /*
+ * Digit signifies an old-style option. Malloc space for dash,
+ * new option and argument.
+ */
+ len = strlen(ap);
+ if ((start = p = malloc(len + 3)) == NULL)
+ err("%s", strerror(errno));
+ *p++ = '-';
+ *p++ = ap[0] == '+' ? 's' : 'f';
+ (void)strcpy(p, ap + 1);
+ *argv = start;
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: uniq [-c | -du] [-f fields] [-s chars] [input [output]]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "uniq: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}