From 0da30e9aa76c5df66cd092f30b904824b0594ccc Mon Sep 17 00:00:00 2001 From: Peter Wemm Date: Tue, 11 Mar 1997 13:08:12 +0000 Subject: [PATCH] Import some parts of CSRG 4.4BSD-Lite2 usr.bin sources to fix tree build. --- usr.bin/basename/basename.c | 139 +++++ usr.bin/col/col.c | 538 ++++++++++++++++++ usr.bin/colrm/colrm.c | 168 ++++++ usr.bin/column/column.c | 305 ++++++++++ usr.bin/comm/comm.c | 187 +++++++ usr.bin/cut/cut.c | 298 ++++++++++ usr.bin/dirname/dirname.c | 145 +++++ usr.bin/du/du.c | 230 ++++++++ usr.bin/find/find.1 | 461 +++++++++++++++ usr.bin/find/find.c | 202 +++++++ usr.bin/find/function.c | 1055 +++++++++++++++++++++++++++++++++++ usr.bin/find/main.c | 154 +++++ usr.bin/finger/extern.h | 49 ++ usr.bin/finger/finger.1 | 166 ++++++ usr.bin/finger/finger.c | 268 +++++++++ usr.bin/finger/lprint.c | 336 +++++++++++ usr.bin/finger/net.c | 149 +++++ usr.bin/finger/sprint.c | 151 +++++ usr.bin/finger/util.c | 357 ++++++++++++ usr.bin/fstat/fstat.c | 748 +++++++++++++++++++++++++ usr.bin/head/head.c | 182 ++++++ usr.bin/hexdump/hexsyntax.c | 129 +++++ usr.bin/hexdump/odsyntax.c | 265 +++++++++ usr.bin/join/join.1 | 215 +++++++ usr.bin/join/join.c | 587 +++++++++++++++++++ usr.bin/look/look.c | 359 ++++++++++++ usr.bin/mt/mt.c | 276 +++++++++ usr.bin/printenv/printenv.c | 100 ++++ usr.bin/rev/rev.c | 110 ++++ usr.bin/tr/str.c | 342 ++++++++++++ usr.bin/tr/tr.c | 290 ++++++++++ usr.bin/uname/uname.c | 163 ++++++ usr.bin/uniq/uniq.c | 276 +++++++++ 33 files changed, 9400 insertions(+) create mode 100644 usr.bin/basename/basename.c create mode 100644 usr.bin/col/col.c create mode 100644 usr.bin/colrm/colrm.c create mode 100644 usr.bin/column/column.c create mode 100644 usr.bin/comm/comm.c create mode 100644 usr.bin/cut/cut.c create mode 100644 usr.bin/dirname/dirname.c create mode 100644 usr.bin/du/du.c create mode 100644 usr.bin/find/find.1 create mode 100644 usr.bin/find/find.c create mode 100644 usr.bin/find/function.c create mode 100644 usr.bin/find/main.c create mode 100644 usr.bin/finger/extern.h create mode 100644 usr.bin/finger/finger.1 create mode 100644 usr.bin/finger/finger.c create mode 100644 usr.bin/finger/lprint.c create mode 100644 usr.bin/finger/net.c create mode 100644 usr.bin/finger/sprint.c create mode 100644 usr.bin/finger/util.c create mode 100644 usr.bin/fstat/fstat.c create mode 100644 usr.bin/head/head.c create mode 100644 usr.bin/hexdump/hexsyntax.c create mode 100644 usr.bin/hexdump/odsyntax.c create mode 100644 usr.bin/join/join.1 create mode 100644 usr.bin/join/join.c create mode 100644 usr.bin/look/look.c create mode 100644 usr.bin/mt/mt.c create mode 100644 usr.bin/printenv/printenv.c create mode 100644 usr.bin/rev/rev.c create mode 100644 usr.bin/tr/str.c create mode 100644 usr.bin/tr/tr.c create mode 100644 usr.bin/uname/uname.c create mode 100644 usr.bin/uniq/uniq.c 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 +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#else +#include +#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 +#include + +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +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 +#else +#include +#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 +#include +#include + +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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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(¬used, &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 +#include + +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 */ + (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 or a 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define KERNEL +#include +#include +#include +#undef KERNEL +#define NFS +#include +#include +#include +#include +#include +#undef NFS + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include +#include + +/* + * 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 +#else +#include +#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 + +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#else +#include +#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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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 +#include + +#include +#include +#undef b_repcnt /* argh */ +#include +#endif + +#ifdef sun +#include +#include +#endif + +#ifdef tahoe +#include +#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 +#else +#include +#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 + +#include +#include +#include +#include + +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 + +#include +#include +#include +#include +#include +#include + +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 +#include + +#include +#include +#include +#include +#include + +#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 + +/* + * 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 + +#include +#include +#include +#include + +#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 +#else +#include +#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 +#include + +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +#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 +#else +#include +#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 */ +}