]> Zhao Yanbai Git Server - minix.git/commitdiff
importing tail
authorClaudio Martella <claudio@apache.org>
Mon, 31 Mar 2014 20:02:03 +0000 (22:02 +0200)
committerLionel Sambuc <lionel@minix3.org>
Mon, 28 Jul 2014 15:05:21 +0000 (17:05 +0200)
Change-Id: Ieda9690195ccd78f081b383cb530702d45537122

14 files changed:
commands/Makefile
commands/tail/Makefile [deleted file]
commands/tail/tail.c [deleted file]
man/man1/Makefile
man/man1/tail.1 [deleted file]
usr.bin/Makefile
usr.bin/tail/Makefile [new file with mode: 0644]
usr.bin/tail/extern.h [new file with mode: 0644]
usr.bin/tail/forward.c [new file with mode: 0644]
usr.bin/tail/misc.c [new file with mode: 0644]
usr.bin/tail/read.c [new file with mode: 0644]
usr.bin/tail/reverse.c [new file with mode: 0644]
usr.bin/tail/tail.1 [new file with mode: 0644]
usr.bin/tail/tail.c [new file with mode: 0644]

index bcf8f8717e0dbd66963e28c2a3b31f445c47d963..04f9c536e1d4cfa588d1fdc3c3d9d4b8ca0cdb5c 100644 (file)
@@ -25,7 +25,7 @@ SUBDIR=       add_route arp ash at backup btrace \
        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 \
diff --git a/commands/tail/Makefile b/commands/tail/Makefile
deleted file mode 100644 (file)
index 91982ec..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-PROG=  tail
-MAN=
-
-.include <bsd.prog.mk>
diff --git a/commands/tail/tail.c b/commands/tail/tail.c
deleted file mode 100644 (file)
index 09f2854..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-/* 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);
-}
index f6aa1517f3f1e12fd704abc37ae3a9329cd8cf58..2a3717b27293c87c811d8700e3e0d1b605ea74de 100644 (file)
@@ -16,7 +16,7 @@ MAN=  ash.1 at.1 \
        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 \
diff --git a/man/man1/tail.1 b/man/man1/tail.1
deleted file mode 100644 (file)
index cee0df1..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-.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).
index 046617037cf99073b39597540bb81dac21e7ebff..377f04e60db1c7da5263ab389b3044310aeaaaf9 100644 (file)
@@ -25,7 +25,7 @@ SUBDIR= asa \
        \
        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 \
diff --git a/usr.bin/tail/Makefile b/usr.bin/tail/Makefile
new file mode 100644 (file)
index 0000000..d189e21
--- /dev/null
@@ -0,0 +1,7 @@
+#      $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>
diff --git a/usr.bin/tail/extern.h b/usr.bin/tail/extern.h
new file mode 100644 (file)
index 0000000..24507b7
--- /dev/null
@@ -0,0 +1,52 @@
+/*     $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;
diff --git a/usr.bin/tail/forward.c b/usr.bin/tail/forward.c
new file mode 100644 (file)
index 0000000..4d355f5
--- /dev/null
@@ -0,0 +1,367 @@
+/*     $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;
+}
diff --git a/usr.bin/tail/misc.c b/usr.bin/tail/misc.c
new file mode 100644 (file)
index 0000000..0af8f68
--- /dev/null
@@ -0,0 +1,89 @@
+/*-
+ * 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;
+}
diff --git a/usr.bin/tail/read.c b/usr.bin/tail/read.c
new file mode 100644 (file)
index 0000000..4e9374d
--- /dev/null
@@ -0,0 +1,210 @@
+/*     $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;
+}
diff --git a/usr.bin/tail/reverse.c b/usr.bin/tail/reverse.c
new file mode 100644 (file)
index 0000000..bb8bcd9
--- /dev/null
@@ -0,0 +1,269 @@
+/*     $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;
+       }
+}
diff --git a/usr.bin/tail/tail.1 b/usr.bin/tail/tail.1
new file mode 100644 (file)
index 0000000..4a32a3b
--- /dev/null
@@ -0,0 +1,192 @@
+.\"    $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.
diff --git a/usr.bin/tail/tail.c b/usr.bin/tail/tail.c
new file mode 100644 (file)
index 0000000..75c7b2b
--- /dev/null
@@ -0,0 +1,305 @@
+/*-
+ * 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);
+}