rotate rsh rshd service setup shar \
slip spell sprofalyze sprofdiff srccrc \
svclog svrctl swifi synctree sysenv \
- syslogd tail tcpd tcpdp tcpstat telnet \
+ syslogd tcpd tcpdp tcpstat telnet \
telnetd term termcap tget time \
truncate udpstat umount \
unstack update uud uue version vol \
+++ /dev/null
-PROG= tail
-MAN=
-
-.include <bsd.prog.mk>
+++ /dev/null
-/* tail - copy the end of a file Author: Norbert Schlenker */
-
-/* Syntax: tail [-f] [-c number | -n number] [file]
- * tail -[number][c|l][f] [file] (obsolescent)
- * tail +[number][c|l][f] [file] (obsolescent)
- * Flags:
- * -c number Measure starting point in bytes. If number begins
- * with '+', the starting point is relative to the
- * the file's beginning. If number begins with '-'
- * or has no sign, the starting point is relative to
- * the end of the file.
- * -f Keep trying to read after EOF on files and FIFOs.
- * -n number Measure starting point in lines. The number
- * following the flag has significance similar to
- * that described for the -c flag.
- *
- * If neither -c nor -n are specified, the default is tail -n 10.
- *
- * In the obsolescent syntax, an argument with a 'c' following the
- * (optional) number is equivalent to "-c number" in the standard
- * syntax, with number including the leading sign ('+' or '-') of the
- * argument. An argument with 'l' following the number is equivalent
- * to "-n number" in the standard syntax. If the number is not
- * specified, 10 is used as the default. If neither 'c' nor 'l' are
- * specified, 'l' is assumed. The character 'f' may be suffixed to
- * the argument and is equivalent to specifying "-f" in the standard
- * syntax. Look for lines marked "OBSOLESCENT".
- *
- * If no file is specified, standard input is assumed.
- *
- * P1003.2 does not specify tail's behavior when a count of 0 is given.
- * It also does not specify clearly whether the first byte (line) of a
- * file should be numbered 0 or 1. Historical behavior is that the
- * first byte is actually number 1 (contrary to all Unix standards).
- * Historically, a count of 0 (or -0) results in no output whatsoever,
- * while a count of +0 results in the entire file being copied (just like
- * +1). The implementor does not agree with these behaviors, but has
- * copied them slavishly. Look for lines marked "HISTORICAL".
- *
- * Author: Norbert Schlenker
- * Copyright: None. Released to the public domain.
- * Reference: P1003.2 section 4.59 (draft 10)
- * Notes: Under Minix, this program requires chmem =30000.
- * Bugs: No internationalization support; all messages are in English.
- */
-
-/* Force visible Posix names */
-#ifndef _POSIX_SOURCE
-#define _POSIX_SOURCE 1
-#endif
-
-/* External interfaces */
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-/* External interfaces that should have been standardized into <getopt.h> */
-extern char *optarg;
-extern int optind;
-
-/* We expect this constant to be defined in <limits.h> in a Posix program,
- * but we'll specify it here just in case it's been left out.
- */
-#ifndef LINE_MAX
-#define LINE_MAX 2048 /* minimum acceptable lower bound */
-#endif
-
-/* Magic numbers suggested or required by Posix specification */
-#define SUCCESS 0 /* exit code in case of success */
-#define FAILURE 1 /* or failure */
-#define DEFAULT_COUNT 10 /* default number of lines or bytes */
-#define MIN_BUFSIZE (LINE_MAX * DEFAULT_COUNT)
-#define SLEEP_INTERVAL 1 /* sleep for one second intervals with -f */
-
-#define FALSE 0
-#define TRUE 1
-
-/* Internal functions - prototyped under Minix */
-int main(int argc, char **argv);
-int tail(int count, int bytes, int read_until_killed);
-int keep_reading(void);
-void usage(void);
-
-int main(argc, argv)
-int argc;
-char *argv[];
-{
- int cflag = FALSE;
- int nflag = FALSE;
- int fflag = FALSE;
- int number = -DEFAULT_COUNT;
- char *suffix;
- int opt;
- struct stat stat_buf;
-
-/* Determining whether this invocation is via the standard syntax or
- * via an obsolescent one is a nasty kludge. Here it is, but there is
- * no pretense at elegance.
- */
- if (argc == 1) { /* simple: default read of a pipe */
- exit(tail(-DEFAULT_COUNT, 0, fflag));
- }
- if ((argv[1][0] == '+') || /* OBSOLESCENT */
- (argv[1][0] == '-' && ((isdigit(argv[1][1])) ||
- (argv[1][1] == 'l') ||
- (argv[1][1] == 'c' && argv[1][2] == 'f')))) {
- --argc; ++argv;
- if (isdigit(argv[0][1])) {
- number = (int)strtol(argv[0], &suffix, 10);
- if (number == 0) { /* HISTORICAL */
- if (argv[0][0] == '+')
- number = 1;
- else
- exit(SUCCESS);
- }
- } else {
- number = (argv[0][0] == '+') ? DEFAULT_COUNT : -DEFAULT_COUNT;
- suffix = &(argv[0][1]);
- }
- if (*suffix != '\0') {
- if (*suffix == 'c') {
- cflag = TRUE;
- ++suffix;
- }
- else
- if (*suffix == 'l') {
- nflag = TRUE;
- ++suffix;
- }
- }
- if (*suffix != '\0') {
- if (*suffix == 'f') {
- fflag = TRUE;
- ++suffix;
- }
- }
- if (*suffix != '\0') { /* bad form: assume to be a file name */
- number = -DEFAULT_COUNT;
- cflag = nflag = FALSE;
- fflag = FALSE;
- } else {
- --argc; ++argv;
- }
- } else { /* new standard syntax */
- while ((opt = getopt(argc, argv, "c:fn:")) != EOF) {
- switch (opt) {
- case 'c':
- cflag = TRUE;
- if (*optarg == '+' || *optarg == '-')
- number = atoi(optarg);
- else
- if (isdigit(*optarg))
- number = -atoi(optarg);
- else
- usage();
- if (number == 0) { /* HISTORICAL */
- if (*optarg == '+')
- number = 1;
- else
- exit(SUCCESS);
- }
- break;
- case 'f':
- fflag = TRUE;
- break;
- case 'n':
- nflag = TRUE;
- if (*optarg == '+' || *optarg == '-')
- number = atoi(optarg);
- else
- if (isdigit(*optarg))
- number = -atoi(optarg);
- else
- usage();
- if (number == 0) { /* HISTORICAL */
- if (*optarg == '+')
- number = 1;
- else
- exit(SUCCESS);
- }
- break;
- default:
- usage();
- /* NOTREACHED */
- }
- }
- argc -= optind;
- argv += optind;
- }
-
- if (argc > 1 || /* too many arguments */
- (cflag && nflag)) { /* both bytes and lines specified */
- usage();
- }
-
- if (argc > 0) { /* an actual file */
- if (freopen(argv[0], "r", stdin) != stdin) {
- fputs("tail: could not open ", stderr);
- fputs(argv[0], stderr);
- fputs("\n", stderr);
- exit(FAILURE);
- }
- /* There is an optimization possibility here. If a file is being
- * read, we need not look at the front of it. If we seek backwards
- * from the end, we can (potentially) avoid looking at most of the
- * file. Some systems fail when asked to seek backwards to a point
- * before the start of the file, so we avoid that possibility.
- */
- if (number < 0 && fstat(fileno(stdin), &stat_buf) == 0) {
- long offset = cflag ? (long)number : (long)number * LINE_MAX;
-
- if (-offset < stat_buf.st_size)
- fseek(stdin, offset, SEEK_END);
- }
- } else {
- fflag = FALSE; /* force -f off when reading a pipe */
- }
- exit(tail(number, cflag, fflag));
- /* NOTREACHED */
-}
-
-int tail(count, bytes, read_until_killed)
-int count; /* lines or bytes desired */
-int bytes; /* TRUE if we want bytes */
-int read_until_killed; /* keep reading at EOF */
-{
- int c;
- char *buf; /* pointer to input buffer */
- char *buf_end; /* and one past its end */
- char *start; /* pointer to first desired character in buf */
- char *finish; /* pointer past last desired character */
- int wrapped_once = FALSE; /* TRUE after buf has been filled once */
-
-/* This is magic. If count is positive, it means start at the count'th
- * line or byte, with the first line or byte considered number 1. Thus,
- * we want to SKIP one less line or byte than the number specified. In
- * the negative case, we look backward from the end of the file for the
- * (count + 1)'th newline or byte, so we really want the count to be one
- * LARGER than was specified (in absolute value). In either case, the
- * right thing to do is:
- */
- --count;
-
-/* Count is positive: skip the desired lines or bytes and then copy. */
- if (count >= 0) {
- while (count > 0 && (c = getchar()) != EOF) {
- if (bytes || c == '\n')
- --count;
- }
- while ((c = getchar()) != EOF) {
- if (putchar(c) == EOF)
- return FAILURE;
- }
- if (read_until_killed)
- return keep_reading();
- return ferror(stdin) ? FAILURE : SUCCESS;
- }
-
-/* Count is negative: allocate a reasonably large buffer. */
- if ((buf = (char *)malloc(MIN_BUFSIZE + 1)) == (char *)NULL) {
- fputs("tail: out of memory\n", stderr);
- return FAILURE;
- }
- buf_end = buf + (MIN_BUFSIZE + 1);
-
-/* Read the entire file into the buffer. */
- finish = buf;
- while ((c = getchar()) != EOF) {
- *finish++ = c;
- if (finish == buf_end) {
- finish = buf;
- wrapped_once = TRUE;
- }
- }
- if (ferror(stdin))
- return FAILURE;
-
-/* Back up inside the buffer. The count has already been adjusted to
- * back up exactly one character too far, so we will bump the buffer
- * pointer once after we're done.
- *
- * BUG: For large line counts, the buffer may not be large enough to
- * hold all the lines. The specification allows the program to
- * fail in such a case - this program will simply dump the entire
- * buffer's contents as its best attempt at the desired behavior.
- */
- if (finish != buf || wrapped_once) { /* file was not empty */
- start = (finish == buf) ? buf_end - 1 : finish - 1;
- while (start != finish) {
- if ((bytes || *start == '\n') && ++count == 0)
- break;
- if (start == buf) {
- start = buf_end - 1;
- if (!wrapped_once) /* never wrapped: stop now */
- break;
- } else {
- --start;
- }
- }
- if (++start == buf_end) { /* bump after going too far */
- start = buf;
- }
- if (finish > start) {
- fwrite(start, 1, finish - start, stdout);
- } else {
- fwrite(start, 1, buf_end - start, stdout);
- fwrite(buf, 1, finish - buf, stdout);
- }
- }
- if (read_until_killed)
- return keep_reading();
- return ferror(stdout) ? FAILURE : SUCCESS;
-}
-
-/* Wake at intervals to reread standard input. Copy anything read to
- * standard output and then go to sleep again.
- */
-int keep_reading()
-{
- char buf[1024];
- int n;
- int i;
- off_t pos;
- struct stat st;
-
- fflush(stdout);
-
- pos = lseek(0, (off_t) 0, SEEK_CUR);
- for (;;) {
- for (i = 0; i < 60; i++) {
- while ((n = read(0, buf, sizeof(buf))) > 0) {
- if (write(1, buf, n) < 0) return FAILURE;
- }
- if (n < 0) return FAILURE;
-
- sleep(SLEEP_INTERVAL);
- }
-
- /* Rewind if suddenly truncated. */
- if (pos != -1) {
- if (fstat(0, &st) == -1) {
- pos = -1;
- } else
- if (st.st_size < pos) {
- pos = lseek(0, (off_t) 0, SEEK_SET);
- } else {
- pos = st.st_size;
- }
- }
- }
-}
-
-/* Tell the user the standard syntax. */
-void usage()
-{
- fputs("Usage: tail [-f] [-c number | -n number] [file]\n", stderr);
- exit(FAILURE);
-}
remsync.1 rget.1 rlogin.1 rsh.1 rz.1 \
shar.1 spell.1 \
svc.1 svrctl.1 \
- synctree.1 sysenv.1 sz.1 tail.1 telnet.1 template.1 \
+ synctree.1 sysenv.1 sz.1 telnet.1 template.1 \
term.1 termcap.1 tget.1 time.1 \
truncate.1 umount.1 \
uud.1 uue.1 vol.1 whereis.1 which.1 \
+++ /dev/null
-.TH TAIL 1
-.SH NAME
-tail \- print the last few lines of a file
-.SH SYNOPSIS
-\fBtail\fR [\fB\-c \fIn\fR] [\fB\-f] [\fB\-n \fIn\fR] [\fIfile\fR] ...\fR
-.br
-.de FL
-.TP
-\\fB\\$1\\fR
-\\$2
-..
-.de EX
-.TP 20
-\\fB\\$1\\fR
-# \\$2
-..
-.SH OPTIONS
-.TP 5
-.B \-c
-# The count refers to characters
-.TP 5
-.B \-f
-# On FIFO or special file, keep reading after EOF
-.TP 5
-.B \-n
-# The count refers to lines
-.SH EXAMPLES
-.TP 20
-.B tail \-n 6
-# Print last 6 lines of \fIstdin\fR
-.TP 20
-.B tail \-c 20 file
-# Print the last 20 characters of \fIfile\fR
-.TP 20
-.B tail \-n 1 file1 file2
-# Print last line of two files
-.TP 20
-.B tail \-n +8 file
-# Print the tail starting with line 8
-.SH DESCRIPTION
-.PP
-The last few lines of one or more files are printed.
-The default count is 10 lines.
-The default file is \fIstdin\fR.
-If the value of \fIn\fR for the \fB\-c\fR or \fB\-n\fR flags starts with
-a + sign, counting starts at the beginning, rather than the end of the file.
-.SH "SEE ALSO"
-.BR head (1).
\
sdiff sed seq shlock \
shuffle sort split stat su \
- tee tic touch tput \
+ tail tee tic touch tput \
tr true tsort tty ul uname unexpand unifdef \
uniq units unvis unzip users \
uuidgen vis \
--- /dev/null
+# $NetBSD: Makefile,v 1.3 1994/11/23 07:41:55 jtc Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= tail
+SRCS= forward.c misc.c read.c reverse.c tail.c
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $NetBSD: extern.h,v 1.10 2011/09/03 09:02:20 christos Exp $ */
+
+/*-
+ * 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. 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.1 (Berkeley) 6/6/93
+ */
+
+#define WR(p, size) \
+ if (write(STDOUT_FILENO, p, size) != size) \
+ oerr();
+
+enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE };
+
+void forward(FILE *, enum STYLE, off_t, struct stat *);
+void reverse(FILE *, enum STYLE, off_t, struct stat *);
+
+int displaybytes(FILE *, off_t);
+int displaylines(FILE *, off_t);
+
+void xerr(int fatal, const char *fmt, ...) __printflike(2, 3);
+void xerrx(int fatal, const char *fmt, ...) __printflike(2, 3);
+void ierr(void);
+void oerr(void);
+
+extern int fflag, rflag, rval;
+extern const char *fname;
--- /dev/null
+/* $NetBSD: forward.c,v 1.32 2013/10/18 20:47:07 christos Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93";
+#endif
+__RCSID("$NetBSD: forward.c,v 1.32 2013/10/18 20:47:07 christos Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/event.h>
+
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+static int rlines(FILE *, off_t, struct stat *);
+
+/* defines for inner loop actions */
+#define USE_SLEEP 0
+#define USE_KQUEUE 1
+#define ADD_EVENTS 2
+
+/*
+ * forward -- display the file, from an offset, forward.
+ *
+ * There are eight separate cases for this -- regular and non-regular
+ * files, by bytes or lines and from the beginning or end of the file.
+ *
+ * FBYTES byte offset from the beginning of the file
+ * REG seek
+ * NOREG read, counting bytes
+ *
+ * FLINES line offset from the beginning of the file
+ * REG read, counting lines
+ * NOREG read, counting lines
+ *
+ * RBYTES byte offset from the end of the file
+ * REG seek
+ * NOREG cyclically read characters into a wrap-around buffer
+ *
+ * RLINES
+ * REG mmap the file and step back until reach the correct offset.
+ * NOREG cyclically read lines into a wrap-around array of buffers
+ */
+void
+forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
+{
+#ifndef __minix
+ int ch, n;
+#else
+ int ch;
+#endif
+ int kq=-1, action=USE_SLEEP;
+ struct stat statbuf;
+#ifndef __minix
+ struct kevent ev[2];
+#endif
+
+ switch(style) {
+ case FBYTES:
+ if (off == 0)
+ break;
+ if (S_ISREG(sbp->st_mode)) {
+ if (sbp->st_size < off)
+ off = sbp->st_size;
+ if (fseeko(fp, off, SEEK_SET) == -1) {
+ ierr();
+ return;
+ }
+ } else while (off--)
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ break;
+ }
+ break;
+ case FLINES:
+ if (off == 0)
+ break;
+ for (;;) {
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ break;
+ }
+ if (ch == '\n' && !--off)
+ break;
+ }
+ break;
+ case RBYTES:
+ if (S_ISREG(sbp->st_mode)) {
+ if (sbp->st_size >= off &&
+ fseeko(fp, -off, SEEK_END) == -1) {
+ ierr();
+ return;
+ }
+ } else if (off == 0) {
+ while (getc(fp) != EOF);
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ } else {
+ if (displaybytes(fp, off))
+ return;
+ }
+ break;
+ case RLINES:
+ if (S_ISREG(sbp->st_mode)) {
+ if (!off) {
+ if (fseek(fp, 0L, SEEK_END) == -1) {
+ ierr();
+ return;
+ }
+ } else {
+ if (rlines(fp, off, sbp))
+ return;
+ }
+ } else if (off == 0) {
+ while (getc(fp) != EOF);
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ } else {
+ if (displaylines(fp, off))
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (fflag) {
+#ifndef __minix
+ kq = kqueue();
+ if (kq < 0)
+ xerr(1, "kqueue");
+ action = ADD_EVENTS;
+#else
+ action = USE_SLEEP;
+#endif
+ }
+
+ for (;;) {
+ while ((ch = getc(fp)) != EOF) {
+ if (putchar(ch) == EOF)
+ oerr();
+ }
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ (void)fflush(stdout);
+ if (!fflag)
+ break;
+
+ clearerr(fp);
+
+ switch (action) {
+#ifndef __minix
+ case ADD_EVENTS:
+ n = 0;
+
+ memset(ev, 0, sizeof(ev));
+ if (fflag == 2 && fileno(fp) != STDIN_FILENO) {
+ EV_SET(&ev[n], fileno(fp), EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_CLEAR,
+ NOTE_DELETE | NOTE_RENAME, 0, 0);
+ n++;
+ }
+ EV_SET(&ev[n], fileno(fp), EVFILT_READ,
+ EV_ADD | EV_ENABLE, 0, 0, 0);
+ n++;
+
+ if (kevent(kq, ev, n, NULL, 0, NULL) == -1) {
+ close(kq);
+ kq = -1;
+ action = USE_SLEEP;
+ } else {
+ action = USE_KQUEUE;
+ }
+ break;
+
+ case USE_KQUEUE:
+ if (kevent(kq, NULL, 0, ev, 1, NULL) == -1)
+ xerr(1, "kevent");
+
+ if (ev[0].filter == EVFILT_VNODE) {
+ /* file was rotated, wait until it reappears */
+ action = USE_SLEEP;
+ } else if (ev[0].data < 0) {
+ /* file shrank, reposition to end */
+ if (fseek(fp, 0L, SEEK_END) == -1) {
+ ierr();
+ return;
+ }
+ }
+ break;
+#endif
+
+ case USE_SLEEP:
+ /*
+ * We pause for one second after displaying any data
+ * that has accumulated since we read the file.
+ */
+ (void) sleep(1);
+
+ if (fflag == 2 && fileno(fp) != STDIN_FILENO &&
+ stat(fname, &statbuf) != -1) {
+ if (statbuf.st_ino != sbp->st_ino ||
+ statbuf.st_dev != sbp->st_dev ||
+ statbuf.st_rdev != sbp->st_rdev ||
+ statbuf.st_nlink == 0) {
+ fp = freopen(fname, "r", fp);
+ if (fp == NULL) {
+ ierr();
+ goto out;
+ }
+ *sbp = statbuf;
+ if (kq != -1)
+ action = ADD_EVENTS;
+ } else if (kq != -1)
+ action = USE_KQUEUE;
+ }
+ break;
+ }
+ }
+out:
+ if (fflag && kq != -1)
+ close(kq);
+}
+
+/*
+ * rlines -- display the last offset lines of the file.
+ *
+ * Non-zero return means than a (non-fatal) error occurred.
+ */
+static int
+rlines(FILE *fp, off_t off, struct stat *sbp)
+{
+ off_t file_size;
+ off_t file_remaining;
+ char *p = NULL;
+ char *start = NULL;
+ off_t mmap_size;
+ off_t mmap_offset;
+ off_t mmap_remaining = 0;
+
+#define MMAP_MAXSIZE (10 * 1024 * 1024)
+
+ if (!(file_size = sbp->st_size))
+ return 0;
+ file_remaining = file_size;
+
+ if (file_remaining > MMAP_MAXSIZE) {
+ mmap_size = MMAP_MAXSIZE;
+ mmap_offset = file_remaining - MMAP_MAXSIZE;
+ } else {
+ mmap_size = file_remaining;
+ mmap_offset = 0;
+ }
+
+ while (off) {
+ start = mmap(NULL, (size_t)mmap_size, PROT_READ,
+ MAP_FILE|MAP_SHARED, fileno(fp), mmap_offset);
+ if (start == MAP_FAILED) {
+ xerr(0, "%s", fname);
+ return 1;
+ }
+
+ mmap_remaining = mmap_size;
+ /* Last char is special, ignore whether newline or not. */
+ for (p = start + mmap_remaining - 1 ; --mmap_remaining ; )
+ if (*--p == '\n' && !--off) {
+ ++p;
+ break;
+ }
+
+ file_remaining -= mmap_size - mmap_remaining;
+
+ if (off == 0)
+ break;
+
+ if (file_remaining == 0)
+ break;
+
+ if (munmap(start, mmap_size)) {
+ xerr(0, "%s", fname);
+ return 1;
+ }
+
+ if (mmap_offset >= MMAP_MAXSIZE) {
+ mmap_offset -= MMAP_MAXSIZE;
+ } else {
+ mmap_offset = 0;
+ mmap_size = file_remaining;
+ }
+ }
+
+ /*
+ * Output the (perhaps partial) data in this mmap'd block.
+ */
+ WR(p, mmap_size - mmap_remaining);
+ file_remaining += mmap_size - mmap_remaining;
+ if (munmap(start, mmap_size)) {
+ xerr(0, "%s", fname);
+ return 1;
+ }
+
+ /*
+ * Set the file pointer to reflect the length displayed.
+ * This will cause the caller to redisplay the data if/when
+ * needed.
+ */
+ if (fseeko(fp, file_remaining, SEEK_SET) == -1) {
+ ierr();
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif
+__RCSID("$NetBSD: misc.c,v 1.7 2011/09/03 09:02:20 christos Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+#include "extern.h"
+
+void
+ierr(void)
+{
+ xerr(0, "%s", fname);
+}
+
+void
+oerr(void)
+{
+ xerr(1, "stdout");
+}
+
+void
+xerr(int fatal, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarn(fmt, ap);
+ va_end(ap);
+ if (fatal)
+ exit(1);
+ rval = 1;
+}
+
+void
+xerrx(int fatal, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ if (fatal)
+ exit(1);
+ rval = 1;
+}
--- /dev/null
+/* $NetBSD: read.c,v 1.17 2011/09/03 10:59:10 christos Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/6/93";
+#endif
+__RCSID("$NetBSD: read.c,v 1.17 2011/09/03 10:59:10 christos Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+/*
+ * displaybytes -- read bytes to an offset from the end and display.
+ *
+ * This is the function that reads to a byte offset from the end of the input,
+ * storing the data in a wrap-around buffer which is then displayed. If the
+ * rflag is set, the data is displayed in lines in reverse order, and this
+ * routine has the usual nastiness of trying to find the newlines. Otherwise,
+ * it is displayed from the character closest to the beginning of the input to
+ * the end.
+ *
+ * Non-zero return means than a (non-fatal) error occurred.
+ */
+int
+displaybytes(FILE *fp, off_t off)
+{
+ int ch, len, tlen;
+ char *ep, *p, *t;
+ int wrap;
+ char *sp;
+
+ if ((sp = p = malloc(off)) == NULL)
+ xerr(1, "malloc");
+
+ for (wrap = 0, ep = p + off; (ch = getc(fp)) != EOF;) {
+ *p = ch;
+ if (++p == ep) {
+ wrap = 1;
+ p = sp;
+ }
+ }
+ if (ferror(fp)) {
+ ierr();
+ return 1;
+ }
+
+ if (rflag) {
+ for (t = p - 1, len = 0; t >= sp; --t, ++len)
+ if (*t == '\n' && len) {
+ WR(t + 1, len);
+ len = 0;
+ }
+ if (wrap) {
+ tlen = len;
+ for (t = ep - 1, len = 0; t >= p; --t, ++len)
+ if (*t == '\n') {
+ if (len) {
+ WR(t + 1, len);
+ len = 0;
+ }
+ if (tlen) {
+ WR(sp, tlen);
+ tlen = 0;
+ }
+ }
+ if (len)
+ WR(t + 1, len);
+ if (tlen)
+ WR(sp, tlen);
+ }
+ } else {
+ if (wrap && (len = ep - p))
+ WR(p, len);
+ if ((len = p - sp) != 0)
+ WR(sp, len);
+ }
+ return 0;
+}
+
+/*
+ * displaylines -- read lines to an offset from the end and display.
+ *
+ * This is the function that reads to a line offset from the end of the input,
+ * storing the data in an array of buffers which is then displayed. If the
+ * rflag is set, the data is displayed in lines in reverse order, and this
+ * routine has the usual nastiness of trying to find the newlines. Otherwise,
+ * it is displayed from the line closest to the beginning of the input to
+ * the end.
+ *
+ * Non-zero return means than a (non-fatal) error occurred.
+ */
+int
+displaylines(FILE *fp, off_t off)
+{
+ struct {
+ int blen;
+ int len;
+ char *l;
+ } *lines;
+ int ch;
+ char *p;
+ int blen, cnt, recno, wrap;
+ char *sp, *n;
+
+ p = NULL;
+ if ((lines = malloc(off * sizeof(*lines))) == NULL)
+ xerr(1, "malloc");
+
+ memset(lines, 0, sizeof(*lines) * off);
+
+ sp = NULL;
+ blen = cnt = recno = wrap = 0;
+
+ while ((ch = getc(fp)) != EOF) {
+ if (++cnt > blen) {
+ if ((n = realloc(sp, blen + 1024)) == NULL)
+ xerr(1, "realloc");
+ sp = n;
+ blen += 1024;
+ p = sp + cnt - 1;
+ }
+ *p++ = ch;
+ if (ch == '\n') {
+ if (lines[recno].blen < cnt) {
+ if ((n = realloc(lines[recno].l,
+ cnt + 256)) == NULL)
+ xerr(1, "realloc");
+ lines[recno].l = n;
+ lines[recno].blen = cnt + 256;
+ }
+ memmove(lines[recno].l, sp, lines[recno].len = cnt);
+ cnt = 0;
+ p = sp;
+ if (++recno == off) {
+ wrap = 1;
+ recno = 0;
+ }
+ }
+ }
+ if (ferror(fp)) {
+ free(lines);
+ ierr();
+ return 1;
+ }
+ if (cnt) {
+ lines[recno].l = sp;
+ lines[recno].len = cnt;
+ if (++recno == off) {
+ wrap = 1;
+ recno = 0;
+ }
+ }
+
+ if (rflag) {
+ for (cnt = recno - 1; cnt >= 0; --cnt)
+ WR(lines[cnt].l, lines[cnt].len);
+ if (wrap)
+ for (cnt = off - 1; cnt >= recno; --cnt)
+ WR(lines[cnt].l, lines[cnt].len);
+ } else {
+ if (wrap)
+ for (cnt = recno; cnt < off; ++cnt)
+ WR(lines[cnt].l, lines[cnt].len);
+ for (cnt = 0; cnt < recno; ++cnt)
+ WR(lines[cnt].l, lines[cnt].len);
+ }
+ free(lines);
+ return 0;
+}
--- /dev/null
+/* $NetBSD: reverse.c,v 1.23 2011/09/03 10:59:11 christos Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)reverse.c 8.1 (Berkeley) 6/6/93";
+#endif
+__RCSID("$NetBSD: reverse.c,v 1.23 2011/09/03 10:59:11 christos Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+static void r_buf(FILE *);
+static void r_reg(FILE *, enum STYLE, off_t, struct stat *);
+
+/*
+ * reverse -- display input in reverse order by line.
+ *
+ * There are six separate cases for this -- regular and non-regular
+ * files by bytes, lines or the whole file.
+ *
+ * BYTES display N bytes
+ * REG mmap the file and display the lines
+ * NOREG cyclically read characters into a wrap-around buffer
+ *
+ * LINES display N lines
+ * REG mmap the file and display the lines
+ * NOREG cyclically read lines into a wrap-around array of buffers
+ *
+ * FILE display the entire file
+ * REG mmap the file and display the lines
+ * NOREG cyclically read input into a linked list of buffers
+ */
+void
+reverse(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
+{
+ if (style != REVERSE && off == 0)
+ return;
+
+ if (S_ISREG(sbp->st_mode))
+ r_reg(fp, style, off, sbp);
+ else
+ switch(style) {
+ case FBYTES:
+ case RBYTES:
+ (void)displaybytes(fp, off);
+ break;
+ case FLINES:
+ case RLINES:
+ (void)displaylines(fp, off);
+ break;
+ case REVERSE:
+ r_buf(fp);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * r_reg -- display a regular file in reverse order by line.
+ */
+static void
+r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
+{
+ off_t size;
+ int llen;
+ char *p;
+ char *start;
+
+ if (!(size = sbp->st_size))
+ return;
+
+ if ((uint64_t)size > SIZE_T_MAX) {
+ /* XXX: need a cleaner way to check this on amd64 */
+ errno = EFBIG;
+ xerr(0, "%s", fname);
+ return;
+ }
+
+ if ((start = mmap(NULL, (size_t)size, PROT_READ,
+ MAP_FILE|MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
+ xerr(0, "%s", fname);
+ return;
+ }
+ p = start + size - 1;
+
+ if (style == RBYTES && off < size)
+ size = off;
+
+ /* Last char is special, ignore whether newline or not. */
+ for (llen = 1; --size; ++llen)
+ if (*--p == '\n') {
+ WR(p + 1, llen);
+ llen = 0;
+ if (style == RLINES && !--off) {
+ ++p;
+ break;
+ }
+ }
+ if (llen)
+ WR(p, llen);
+ if (munmap(start, (size_t)sbp->st_size))
+ xerr(0, "%s", fname);
+}
+
+typedef struct bf {
+ struct bf *next;
+ struct bf *prev;
+ int len;
+ char *l;
+} BF;
+
+/*
+ * r_buf -- display a non-regular file in reverse order by line.
+ *
+ * This is the function that saves the entire input, storing the data in a
+ * doubly linked list of buffers and then displays them in reverse order.
+ * It has the usual nastiness of trying to find the newlines, as there's no
+ * guarantee that a newline occurs anywhere in the file, let alone in any
+ * particular buffer. If we run out of memory, input is discarded (and the
+ * user warned).
+ */
+static void
+r_buf(FILE *fp)
+{
+ BF *mark, *tl, *tr;
+ int ch, len, llen;
+ char *p;
+ off_t enomem;
+
+#define BSZ (128 * 1024)
+ tl = NULL;
+ for (mark = NULL, enomem = 0;;) {
+ /*
+ * Allocate a new block and link it into place in a doubly
+ * linked list. If out of memory, toss the LRU block and
+ * keep going.
+ */
+ if (enomem) {
+ if (!mark) {
+ errno = ENOMEM;
+ xerr(1, NULL);
+ }
+ tl = tl->next;
+ enomem += tl->len;
+ } else if ((tl = malloc(sizeof(*tl))) == NULL ||
+ (tl->l = malloc(BSZ)) == NULL) {
+ if (tl)
+ free(tl);
+ if (!mark) {
+ errno = ENOMEM;
+ xerr(1, NULL);
+ }
+ tl = mark;
+ enomem += tl->len;
+ } else if (mark) {
+ tl->next = mark;
+ tl->prev = mark->prev;
+ mark->prev->next = tl;
+ mark->prev = tl;
+ } else {
+ mark = tl;
+ mark->next = mark->prev = mark;
+ }
+
+ /* Fill the block with input data. */
+ ch = 0;
+ for (p = tl->l, len = 0;
+ len < BSZ && (ch = getc(fp)) != EOF; ++len)
+ *p++ = ch;
+
+ /*
+ * If no input data for this block and we tossed some data,
+ * recover it.
+ */
+ if (!len) {
+ if (enomem)
+ enomem -= tl->len;
+ tl = tl->prev;
+ break;
+ }
+
+ tl->len = len;
+ if (ch == EOF)
+ break;
+ }
+
+ if (enomem) {
+ xerrx(0, "Warning: %lld bytes discarded", (long long)enomem);
+ }
+
+ /*
+ * Step through the blocks in the reverse order read. The last char
+ * is special, ignore whether newline or not.
+ */
+ for (mark = tl;;) {
+ for (p = tl->l + (len = tl->len) - 1, llen = 0; len--;
+ --p, ++llen)
+ if (*p == '\n') {
+ if (llen) {
+ WR(p + 1, llen);
+ llen = 0;
+ }
+ if (tl == mark)
+ continue;
+ for (tr = tl->next; tr->len; tr = tr->next) {
+ WR(tr->l, tr->len);
+ tr->len = 0;
+ if (tr == mark)
+ break;
+ }
+ }
+ tl->len = llen;
+ if ((tl = tl->prev) == mark)
+ break;
+ }
+ tl = tl->next;
+ if (tl->len) {
+ WR(tl->l, tl->len);
+ tl->len = 0;
+ }
+ while ((tl = tl->next)->len) {
+ WR(tl->l, tl->len);
+ tl->len = 0;
+ }
+}
--- /dev/null
+.\" $NetBSD: tail.1,v 1.14 2013/01/31 23:09:06 wiz Exp $
+.\"
+.\" Copyright (c) 1980, 1990, 1991, 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. 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.
+.\"
+.\" @(#)tail.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TAIL 1
+.Os
+.Sh NAME
+.Nm tail
+.Nd display the last part of a file
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl f |
+.Fl F |
+.Fl r
+.Oc
+.Oo
+.Fl b Ar number |
+.Fl c Ar number |
+.Fl n Ar number
+.Oc
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm
+utility displays the contents of
+.Ar file
+or, by default, its standard input, to the standard output.
+.Pp
+The display begins at a byte, line or 512-byte block location in the
+input.
+Numbers having a leading plus (``+'') sign are relative to the beginning
+of the input, for example,
+.Dq -c +2
+starts the display at the second
+byte of the input.
+Numbers having a leading minus (``-'') sign or no explicit sign are
+relative to the end of the input, for example,
+.Dq -n 2
+displays the last two lines of the input.
+The default starting location is
+.Dq -n 10 ,
+or the last 10 lines of the input.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b Ar number
+The location is
+.Ar number
+512-byte blocks.
+.It Fl c Ar number
+The location is
+.Ar number
+bytes.
+.It Fl f
+The
+.Fl f
+option causes
+.Nm
+to not stop when end of file is reached, but rather to wait for additional
+data to be appended to the input.
+The
+.Fl f
+option is ignored if the standard input is a pipe, but not if it is a FIFO.
+.It Fl F
+The
+.Fl F
+option is the same as the
+.Fl f
+option, except that every five seconds
+.Nm
+will check to see if the file named on the command line has been
+shortened or moved (it is considered moved if the inode or device
+number changes) and, if so, it will close
+the current file, open the filename given, print out the entire
+contents, and continue to wait for more data to be appended.
+This option is used to follow log files though rotation by
+.Xr newsyslog 8
+or similar programs.
+.It Fl n Ar number
+The location is
+.Ar number
+lines.
+.It Fl r
+The
+.Fl r
+option causes the input to be displayed in reverse order, by line.
+Additionally, this option changes the meaning of the
+.Fl b ,
+.Fl c
+and
+.Fl n
+options.
+When the
+.Fl r
+option is specified, these options specify the number of bytes, lines
+or 512-byte blocks to display, instead of the bytes, lines or blocks
+from the beginning or end of the input from which to begin the display.
+The default for the
+.Fl r
+option is to display all of the input.
+.El
+.Pp
+If more than a single file is specified, each file is preceded by a
+header consisting of the string
+.Dq ==\*[Gt] XXX \*[Le]=
+where
+.Dq XXX
+is the name of the file.
+.Pp
+The
+.Nm
+utility exits 0 on success, and \*[Gt]0 if an error occurs.
+.Sh SEE ALSO
+.Xr cat 1 ,
+.Xr head 1 ,
+.Xr sed 1
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be a superset of the
+.St -p1003.2-92
+specification.
+In particular, the
+.Fl b ,
+.Fl r
+and
+.Fl F
+options are extensions to that standard.
+.Pp
+The historic command line syntax of
+.Nm
+is supported by this implementation.
+The only difference between this implementation and historic versions
+of
+.Nm ,
+once the command line syntax translation has been done, is that the
+.Fl b ,
+.Fl c
+and
+.Fl n
+options modify the
+.Fl r
+option, i.e., ``-r -c 4'' displays the last 4 characters of the last line
+of the input, while the historic tail (using the historic syntax ``-4cr'')
+would ignore the
+.Fl c
+option and display the last 4 lines of the input.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v7 .
+.Sh BUGS
+When using the
+.Fl F
+option,
+.Nm
+will not detect a file truncation if, between the truncation
+and the next check of the file size, data written to the file make
+it larger than the last known file size.
--- /dev/null
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * 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. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1991, 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
+#endif
+__RCSID("$NetBSD: tail.c,v 1.17 2013/01/31 23:09:06 wiz Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+int fflag, rflag, rval;
+const char *fname;
+
+static void obsolete(char **);
+static void usage(void) __dead;
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ FILE *fp;
+ off_t off;
+ enum STYLE style;
+ int ch, first;
+ char *p;
+
+ setprogname(argv[0]);
+ off = 0;
+ /*
+ * Tail's options are weird. First, -n10 is the same as -n-10, not
+ * -n+10. Second, the number options are 1 based and not offsets,
+ * so -n+1 is the first line, and -c-1 is the last byte. Third, the
+ * number options for the -r option specify the number of things that
+ * get displayed, not the starting point in the file. The one major
+ * incompatibility in this version as compared to historical versions
+ * is that the 'r' option couldn't be modified by the -lbc options,
+ * i.e., it was always done in lines. This version treats -rc as a
+ * number of characters in reverse order. Finally, the default for
+ * -r is the entire file, not 10 lines.
+ */
+#define ARG(units, forward, backward) { \
+ if (style) \
+ usage(); \
+ off = strtoll(optarg, &p, 10) * (units); \
+ if (*p) \
+ xerrx(1, "illegal offset -- %s", optarg); \
+ switch(optarg[0]) { \
+ case '+': \
+ if (off) \
+ off -= (units); \
+ style = (forward); \
+ break; \
+ case '-': \
+ off = -off; \
+ /* FALLTHROUGH */ \
+ default: \
+ style = (backward); \
+ break; \
+ } \
+}
+
+ obsolete(argv);
+ style = NOTSET;
+ while ((ch = getopt(argc, argv, "Fb:c:fn:r")) != -1)
+ switch(ch) {
+ case 'F':
+ fflag = 2;
+ break;
+ case 'b':
+ ARG(512, FBYTES, RBYTES);
+ break;
+ case 'c':
+ ARG(1, FBYTES, RBYTES);
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'n':
+ ARG(1, FLINES, RLINES);
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (fflag && argc > 1)
+ xerrx(1,
+ "-f and -F options only appropriate for a single file");
+
+ /*
+ * If displaying in reverse, don't permit follow option, and convert
+ * style values.
+ */
+ if (rflag) {
+ if (fflag)
+ usage();
+ if (style == FBYTES)
+ style = RBYTES;
+ else if (style == FLINES)
+ style = RLINES;
+ }
+
+ /*
+ * If style not specified, the default is the whole file for -r, and
+ * the last 10 lines if not -r.
+ */
+ if (style == NOTSET) {
+ if (rflag) {
+ off = 0;
+ style = REVERSE;
+ } else {
+ off = 10;
+ style = RLINES;
+ }
+ }
+ if (*argv)
+ for (first = 1; (fname = *argv++) != NULL;) {
+ if ((fp = fopen(fname, "r")) == NULL ||
+ fstat(fileno(fp), &sb)) {
+ ierr();
+ continue;
+ }
+ if (argc > 1) {
+ (void)printf("%s==> %s <==\n",
+ first ? "" : "\n", fname);
+ first = 0;
+ (void)fflush(stdout);
+ }
+
+ if (rflag)
+ reverse(fp, style, off, &sb);
+ else
+ forward(fp, style, off, &sb);
+ (void)fclose(fp);
+ }
+ else {
+ fname = "stdin";
+
+ if (fstat(fileno(stdin), &sb)) {
+ ierr();
+ exit(1);
+ }
+
+ /*
+ * Determine if input is a pipe. 4.4BSD will set the SOCKET
+ * bit in the st_mode field for pipes. Fix this then.
+ */
+ if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
+ errno == ESPIPE) {
+ errno = 0;
+ fflag = 0; /* POSIX.2 requires this. */
+ }
+
+ if (rflag)
+ reverse(stdin, style, off, &sb);
+ else
+ forward(stdin, style, off, &sb);
+ }
+ exit(rval);
+}
+
+/*
+ * Convert the obsolete argument form into something that getopt can handle.
+ * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't
+ * the option argument for a -b, -c or -n option gets converted.
+ */
+static void
+obsolete(char *argv[])
+{
+ char *ap, *p, *t;
+ int len;
+ char *start;
+
+ while ((ap = *++argv) != NULL) {
+ /* Return if "--" or not an option of any form. */
+ if (ap[0] != '-') {
+ if (ap[0] != '+')
+ return;
+ } else if (ap[1] == '-')
+ return;
+
+ switch (*++ap) {
+ /* Old-style option. */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+
+ /* Malloc space for dash, new option and argument. */
+ len = strlen(*argv);
+ if ((start = p = malloc(len + 3)) == NULL)
+ xerr(1, "malloc");
+ *p++ = '-';
+
+ /*
+ * Go to the end of the option argument. Save off any
+ * trailing options (-3lf) and translate any trailing
+ * output style characters.
+ */
+ t = *argv + len - 1;
+ if (*t == 'f' || *t == 'r') {
+ *p++ = *t;
+ *t-- = '\0';
+ }
+ switch(*t) {
+ case 'b':
+ *p++ = 'b';
+ *t = '\0';
+ break;
+ case 'c':
+ *p++ = 'c';
+ *t = '\0';
+ break;
+ case 'l':
+ *t = '\0';
+ /* FALLTHROUGH */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ *p++ = 'n';
+ break;
+ default:
+ xerrx(1, "illegal option -- %s", *argv);
+ }
+ *p++ = *argv[0];
+ (void)strcpy(p, ap);
+ *argv = start;
+ continue;
+
+ /*
+ * Options w/ arguments, skip the argument and continue
+ * with the next option.
+ */
+ case 'b':
+ case 'c':
+ case 'n':
+ if (!ap[1])
+ ++argv;
+ /* FALLTHROUGH */
+ /* Options w/o arguments, continue with the next option. */
+ case 'f':
+ case 'r':
+ continue;
+
+ /* Illegal option, return and let getopt handle it. */
+ default:
+ return;
+ }
+ }
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "Usage: %s [-f | -F | -r] [-b # | -c # | -n #] [file ...]\n",
+ getprogname());
+ exit(1);
+}