--- a/exception_lists/closed-bins Sat Sep 25 11:51:56 2010 +0800
+++ b/exception_lists/closed-bins Fri Oct 01 20:02:51 2010 -0700
@@ -38,5 +38,8 @@
./usr/lib/localedef/src/iso_8859_1/localedef.src
./usr/bin/localedef
./usr/bin/tr
+./usr/bin/tail
+./usr/xpg4/bin/tail
+
./usr/xpg4/bin/tr
./usr/xpg6/bin/tr
--- a/usr/src/Makefile.lint Sat Sep 25 11:51:56 2010 +0800
+++ b/usr/src/Makefile.lint Fri Oct 01 20:02:51 2010 -0700
@@ -291,6 +291,7 @@
cmd/syseventd \
cmd/syslogd \
cmd/tabs \
+ cmd/tail \
cmd/th_tools \
cmd/tip \
cmd/touch \
@@ -476,7 +477,6 @@
$(CLOSED)/cmd/cmd-inet/usr.lib/in.iked \
$(CLOSED)/cmd/pax \
$(CLOSED)/cmd/sed_xpg4 \
- $(CLOSED)/cmd/tail \
$(CLOSED)/lib/libc_i18n
i386_SUBDIRS= \
--- a/usr/src/cmd/Makefile Sat Sep 25 11:51:56 2010 +0800
+++ b/usr/src/cmd/Makefile Fri Oct 01 20:02:51 2010 -0700
@@ -393,6 +393,7 @@
syseventadm \
syslogd \
tabs \
+ tail \
tar \
tbl \
tcopy \
@@ -473,8 +474,7 @@
$(CLOSED)/cmd/pax \
$(CLOSED)/cmd/printf \
$(CLOSED)/cmd/sed \
- $(CLOSED)/cmd/sed_xpg4 \
- $(CLOSED)/cmd/tail
+ $(CLOSED)/cmd/sed_xpg4
i386_SUBDIRS= \
acpihpd \
@@ -757,8 +757,7 @@
$(CLOSED)/cmd/pax \
$(CLOSED)/cmd/printf \
$(CLOSED)/cmd/sed \
- $(CLOSED)/cmd/sed_xpg4 \
- $(CLOSED)/cmd/tail
+ $(CLOSED)/cmd/sed_xpg4
sparc_MSGSUBDIRS= \
fruadm \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/Makefile Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,57 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy is of the CDDL is also available via the Internet
+# at http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2010 Chris Love. All rights reserved.
+#
+
+
+PROG= tail
+XPG4PROG= $(PROG)
+
+OBJS= forward.o misc.o read.o reverse.o tail.o
+SRCS= $(OBJS:%.o=%.c)
+
+include ../Makefile.cmd
+
+CLOBBERFILES= $(PROG)
+
+
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
+LINTFLAGS += -I. -erroff=E_CONSTANT_CONDITION
+
+# install rules
+$(ROOTINC)/% : %
+ $(INS.file)
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all .WAIT $(ROOTPROG) $(ROOTXPG4PROG)
+
+$(ROOTXPG4PROG):
+ -$(RM) $@
+ -$(LN) -s ../../bin/$(PROG) $@
+
+lint: lint_SRCS
+
+clean:
+ $(RM) $(OBJS)
+
+include ../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/THIRDPARTYLICENSE Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,76 @@
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+
+
+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.
+4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+Copyright (c) 1988, 1993
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/THIRDPARTYLICENSE.descrip Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,1 @@
+TAIL UTILITY
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/extern.h Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+
+#define WR(p, size) do { \
+ if (write(STDOUT_FILENO, p, size) != (ssize_t)size) \
+ oerr(); \
+ } while (0)
+
+#define TAILMAPLEN (4<<20)
+
+struct mapinfo {
+ off_t mapoff;
+ off_t maxoff;
+ size_t maplen;
+ char *start;
+ int fd;
+};
+
+struct file_info {
+ FILE *fp;
+ char *file_name;
+ struct stat st;
+};
+
+typedef struct file_info file_info_t;
+
+enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE };
+
+void follow(file_info_t *, enum STYLE, off_t);
+void forward(FILE *, const char *, enum STYLE, off_t, struct stat *);
+void reverse(FILE *, const char *, enum STYLE, off_t, struct stat *);
+
+int bytes(FILE *, const char *, off_t);
+int lines(FILE *, const char *, off_t);
+
+void ierr(const char *);
+void oerr(void);
+int mapprint(struct mapinfo *, off_t, off_t);
+int maparound(struct mapinfo *, off_t);
+
+extern int Fflag, fflag, qflag, rflag, rval, no_files;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/forward.c Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,397 @@
+/*
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Solaris porting notes: the original FreeBSD version made use of the
+ * BSD kqueue event notification framework; this
+ * was changed to use the Solaris event completion
+ * framework: port_create(), port_associate(),
+ * and port_get().
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <port.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void rlines(FILE *, const char *fn, off_t, struct stat *);
+static int show(file_info_t *);
+static void set_events(file_info_t *files);
+
+/* defines for inner loop actions */
+#define USE_SLEEP 0
+#define USE_PORT 1
+#define ADD_EVENTS 2
+
+int port;
+int action = USE_SLEEP;
+port_event_t ev;
+
+static const file_info_t *last;
+
+/*
+ * 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, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
+{
+ int ch;
+
+ 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(fn);
+ return;
+ }
+ } else while (off--)
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp)) {
+ ierr(fn);
+ return;
+ }
+ break;
+ }
+ break;
+ case FLINES:
+ if (off == 0)
+ break;
+ for (;;) {
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp)) {
+ ierr(fn);
+ 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(fn);
+ return;
+ }
+ } else if (off == 0) {
+ while (getc(fp) != EOF)
+ ;
+ if (ferror(fp)) {
+ ierr(fn);
+ return;
+ }
+ } else
+ if (bytes(fp, fn, off))
+ return;
+ break;
+ case RLINES:
+ if (S_ISREG(sbp->st_mode))
+ if (!off) {
+ if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
+ ierr(fn);
+ return;
+ }
+ } else
+ rlines(fp, fn, off, sbp);
+ else if (off == 0) {
+ while (getc(fp) != EOF)
+ ;
+ if (ferror(fp)) {
+ ierr(fn);
+ return;
+ }
+ } else
+ if (lines(fp, fn, off))
+ return;
+ break;
+ default:
+ break;
+ }
+
+ while ((ch = getc(fp)) != EOF)
+ if (putchar(ch) == EOF)
+ oerr();
+ if (ferror(fp)) {
+ ierr(fn);
+ return;
+ }
+ (void) fflush(stdout);
+}
+
+/*
+ * rlines -- display the last offset lines of the file.
+ */
+static void
+rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
+{
+ struct mapinfo map;
+ off_t curoff, size;
+ int i;
+
+ if ((size = sbp->st_size) == 0)
+ return;
+ map.start = NULL;
+ map.fd = fileno(fp);
+ map.mapoff = map.maxoff = size;
+
+ /*
+ * Last char is special, ignore whether newline or not. Note that
+ * size == 0 is dealt with above, and size == 1 sets curoff to -1.
+ */
+ curoff = size - 2;
+ while (curoff >= 0) {
+ if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
+ ierr(fn);
+ return;
+ }
+ for (i = curoff - map.mapoff; i >= 0; i--)
+ if (map.start[i] == '\n' && --off == 0)
+ break;
+ /* `i' is either the map offset of a '\n', or -1. */
+ curoff = map.mapoff + i;
+ if (i >= 0)
+ break;
+ }
+ curoff++;
+ if (mapprint(&map, curoff, size - curoff) != 0) {
+ ierr(fn);
+ exit(1);
+ }
+
+ /* Set the file pointer to reflect the length displayed. */
+ if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
+ ierr(fn);
+ return;
+ }
+ if (map.start != NULL && munmap(map.start, map.maplen)) {
+ ierr(fn);
+ return;
+ }
+}
+
+static int
+show(file_info_t *file)
+{
+ int ch;
+
+ while ((ch = getc(file->fp)) != EOF) {
+ if (last != file && no_files > 1) {
+ if (!qflag)
+ (void) printf("\n==> %s <==\n",
+ file->file_name);
+ last = file;
+ }
+ if (putchar(ch) == EOF)
+ oerr();
+ }
+ (void) fflush(stdout);
+ if (ferror(file->fp)) {
+ (void) fclose(file->fp);
+ file->fp = NULL;
+ ierr(file->file_name);
+ return (0);
+ }
+ clearerr(file->fp);
+ return (1);
+}
+
+static void
+set_events(file_info_t *files)
+{
+ int i;
+ file_info_t *file;
+
+ action = USE_PORT;
+ for (i = 0, file = files; i < no_files; i++, file++) {
+ if (! file->fp)
+ continue;
+
+ (void) fstat(fileno(file->fp), &file->st);
+ /* For -f or -F will both use Solaris port interface */
+ if (fflag && (fileno(file->fp) != STDIN_FILENO)) {
+ (void) port_associate(port, PORT_SOURCE_FD,
+ fileno(file->fp), POLLIN, (void*)file);
+ }
+ }
+}
+
+/*
+ * follow -- display the file, from an offset, forward.
+ *
+ */
+void
+follow(file_info_t *files, enum STYLE style, off_t off)
+{
+ int active, ev_change, i, n = -1;
+ struct stat sb2;
+ file_info_t *file;
+ struct timespec ts;
+
+ /* Position each of the files */
+
+ file = files;
+ active = 0;
+ n = 0;
+ for (i = 0; i < no_files; i++, file++) {
+ if (file->fp) {
+ active = 1;
+ n++;
+ if (no_files > 1 && !qflag)
+ (void) printf("\n==> %s <==\n",
+ file->file_name);
+ forward(file->fp, file->file_name, style, off,
+ &file->st);
+ if (Fflag && fileno(file->fp) != STDIN_FILENO)
+ n++;
+ }
+ }
+ if (!Fflag && !active)
+ return;
+
+ last = --file;
+ port = port_create();
+ set_events(files);
+
+ for (;;) {
+ ev_change = 0;
+ if (Fflag) {
+ for (i = 0, file = files; i < no_files; i++, file++) {
+ if (!file->fp) {
+ file->fp = fopen(file->file_name, "r");
+ if (file->fp != NULL &&
+ fstat(fileno(file->fp), &file->st)
+ == -1) {
+ (void) fclose(file->fp);
+ file->fp = NULL;
+ }
+ if (file->fp != NULL)
+ ev_change++;
+ continue;
+ }
+ if (fileno(file->fp) == STDIN_FILENO)
+ continue;
+ if (stat(file->file_name, &sb2) == -1) {
+ if (errno != ENOENT)
+ ierr(file->file_name);
+ (void) show(file);
+ (void) fclose(file->fp);
+ file->fp = NULL;
+ ev_change++;
+ continue;
+ }
+
+ if (sb2.st_ino != file->st.st_ino ||
+ sb2.st_dev != file->st.st_dev ||
+ sb2.st_nlink == 0) {
+ (void) show(file);
+ file->fp = freopen(file->file_name, "r",
+ file->fp);
+ if (file->fp != NULL)
+ (void) memcpy(&file->st, &sb2,
+ sizeof (struct stat));
+ else if (errno != ENOENT)
+ ierr(file->file_name);
+ ev_change++;
+ }
+ }
+ }
+
+ for (i = 0, file = files; i < no_files; i++, file++)
+ if (file->fp && !show(file))
+ ev_change++;
+
+ if (ev_change)
+ set_events(files);
+
+ switch (action) {
+ case USE_PORT:
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ /*
+ * In the -F case we set a timeout to ensure that
+ * we re-stat the file at least once every second.
+ */
+ n = port_get(port, &ev, Fflag? &ts : NULL);
+ if (n == 0) {
+ file = (file_info_t *)ev.portev_user;
+ (void) port_associate(port, PORT_SOURCE_FD,
+ fileno(file->fp), POLLIN, (void*)file);
+ }
+ break;
+
+ case USE_SLEEP:
+ (void) usleep(250000);
+ break;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/misc.c Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+void
+ierr(const char *fname)
+{
+ warn("%s", fname);
+ rval = 1;
+}
+
+void
+oerr(void)
+{
+ err(1, "stdout");
+}
+
+/*
+ * Print `len' bytes from the file associated with `mip', starting at
+ * absolute file offset `startoff'. May move map window.
+ */
+int
+mapprint(struct mapinfo *mip, off_t startoff, off_t len)
+{
+ int n;
+
+ while (len > 0) {
+ if (startoff < mip->mapoff || startoff >= mip->mapoff +
+ (off_t)mip->maplen) {
+ if (maparound(mip, startoff) != 0)
+ return (1);
+ }
+ n = (mip->mapoff + mip->maplen) - startoff;
+ if (n > len)
+ n = len;
+ WR(mip->start + (startoff - mip->mapoff), n);
+ startoff += n;
+ len -= n;
+ }
+ return (0);
+}
+
+/*
+ * Move the map window so that it contains the byte at absolute file
+ * offset `offset'. The start of the map window will be TAILMAPLEN
+ * aligned.
+ */
+int
+maparound(struct mapinfo *mip, off_t offset)
+{
+
+ if (mip->start != NULL && munmap(mip->start, mip->maplen) != 0)
+ return (1);
+
+ mip->mapoff = offset & ~((off_t)TAILMAPLEN - 1);
+ mip->maplen = TAILMAPLEN;
+ if ((off_t)mip->maplen > mip->maxoff - mip->mapoff)
+ mip->maplen = mip->maxoff - mip->mapoff;
+ if (mip->maplen == 0)
+ abort();
+ if ((mip->start = mmap(NULL, mip->maplen, PROT_READ, MAP_SHARED,
+ mip->fd, mip->mapoff)) == MAP_FAILED)
+ return (1);
+
+ return (0);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/read.c Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+/*
+ * bytes -- 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.
+ */
+int
+bytes(FILE *fp, const char *fn, off_t off)
+{
+ int ch, len, tlen;
+ char *ep, *p, *t;
+ int wrap;
+ char *sp;
+
+ if ((sp = p = malloc(off)) == NULL)
+ err(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(fn);
+ free(sp);
+ 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);
+ len = p - sp;
+ if (len)
+ WR(sp, len);
+ }
+
+ free(sp);
+ return (0);
+}
+
+/*
+ * lines -- 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.
+ */
+int
+lines(FILE *fp, const char *fn, off_t off)
+{
+ struct {
+ int blen;
+ uint_t len;
+ char *l;
+ } *llines;
+ int ch, rc;
+ char *p, *sp;
+ int blen, cnt, recno, wrap;
+
+ if ((llines = malloc(off * sizeof (*llines))) == NULL)
+ err(1, "malloc");
+ bzero(llines, off * sizeof (*llines));
+ p = sp = NULL;
+ blen = cnt = recno = wrap = 0;
+ rc = 0;
+
+ while ((ch = getc(fp)) != EOF) {
+ if (++cnt > blen) {
+ if ((sp = realloc(sp, blen += 1024)) == NULL)
+ err(1, "realloc");
+ p = sp + cnt - 1;
+ }
+ *p++ = ch;
+ if (ch == '\n') {
+ if ((int)llines[recno].blen < cnt) {
+ llines[recno].blen = cnt + 256;
+ if ((llines[recno].l = realloc(llines[recno].l,
+ llines[recno].blen)) == NULL)
+ err(1, "realloc");
+ }
+ bcopy(sp, llines[recno].l, llines[recno].len = cnt);
+ cnt = 0;
+ p = sp;
+ if (++recno == off) {
+ wrap = 1;
+ recno = 0;
+ }
+ }
+ }
+ if (ferror(fp)) {
+ ierr(fn);
+ rc = 1;
+ goto done;
+ }
+ if (cnt) {
+ llines[recno].l = sp;
+ sp = NULL;
+ llines[recno].len = cnt;
+ if (++recno == off) {
+ wrap = 1;
+ recno = 0;
+ }
+ }
+
+ if (rflag) {
+ for (cnt = recno - 1; cnt >= 0; --cnt)
+ WR(llines[cnt].l, llines[cnt].len);
+ if (wrap)
+ for (cnt = off - 1; cnt >= recno; --cnt)
+ WR(llines[cnt].l, llines[cnt].len);
+ } else {
+ if (wrap)
+ for (cnt = recno; cnt < off; ++cnt)
+ WR(llines[cnt].l, llines[cnt].len);
+ for (cnt = 0; cnt < recno; ++cnt)
+ WR(llines[cnt].l, llines[cnt].len);
+ }
+done:
+ for (cnt = 0; cnt < off; cnt++)
+ free(llines[cnt].l);
+ free(sp);
+ free(llines);
+ return (rc);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/reverse.c Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,275 @@
+/*
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void r_buf(FILE *, const char *);
+static void r_reg(FILE *, const char *, 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, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
+{
+ if (style != REVERSE && off == 0)
+ return;
+
+ if (S_ISREG(sbp->st_mode))
+ r_reg(fp, fn, style, off, sbp);
+ else
+ switch (style) {
+ case FBYTES:
+ case RBYTES:
+ (void) bytes(fp, fn, off);
+ break;
+ case FLINES:
+ case RLINES:
+ (void) lines(fp, fn, off);
+ break;
+ case REVERSE:
+ r_buf(fp, fn);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * r_reg -- display a regular file in reverse order by line.
+ */
+static void
+r_reg(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
+{
+ struct mapinfo map;
+ off_t curoff, size, lineend;
+ int i;
+
+ if ((size = sbp->st_size) == 0)
+ return;
+
+ map.start = NULL;
+ map.mapoff = map.maxoff = size;
+ map.fd = fileno(fp);
+
+ /*
+ * Last char is special, ignore whether newline or not. Note that
+ * size == 0 is dealt with above, and size == 1 sets curoff to -1.
+ */
+ curoff = size - 2;
+ lineend = size;
+ while (curoff >= 0) {
+ if (curoff < map.mapoff ||
+ curoff >= map.mapoff + (off_t)map.maplen) {
+ if (maparound(&map, curoff) != 0) {
+ ierr(fn);
+ return;
+ }
+ }
+ for (i = curoff - map.mapoff; i >= 0; i--) {
+ if (style == RBYTES && --off == 0)
+ break;
+ if (map.start[i] == '\n')
+ break;
+ }
+ /* `i' is either the map offset of a '\n', or -1. */
+ curoff = map.mapoff + i;
+ if (i < 0)
+ continue;
+
+ /* Print the line and update offsets. */
+ if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) {
+ ierr(fn);
+ return;
+ }
+ lineend = curoff + 1;
+ curoff--;
+
+ if (style == RLINES)
+ off--;
+
+ if (off == 0 && style != REVERSE) {
+ /* Avoid printing anything below. */
+ curoff = 0;
+ break;
+ }
+ }
+ if (curoff < 0 && mapprint(&map, 0, lineend) != 0) {
+ ierr(fn);
+ return;
+ }
+ if (map.start != NULL && munmap(map.start, map.maplen))
+ ierr(fn);
+}
+
+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, const char *fn)
+{
+ BF *mark, *tl, *tr;
+ int ch, len, llen;
+ char *p;
+ off_t enomem;
+
+ tl = NULL;
+#define BSZ (128 * 1024)
+ 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 || (tl = malloc(sizeof (BF))) == NULL ||
+ (tl->l = malloc(BSZ)) == NULL) {
+ if (!mark)
+ err(1, "malloc");
+ tl = enomem ? tl->next : 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. */
+ for (p = tl->l, len = 0;
+ len < BSZ && (ch = getc(fp)) != EOF; ++len)
+ *p++ = ch;
+
+ if (ferror(fp)) {
+ ierr(fn);
+ return;
+ }
+
+ /*
+ * If no input data for this block and we tossed some data,
+ * recover it.
+ */
+ if (!len && enomem) {
+ enomem -= tl->len;
+ tl = tl->prev;
+ break;
+ }
+
+ tl->len = len;
+ if (ch == EOF)
+ break;
+ }
+
+ if (enomem) {
+ warnx("warning: %jd bytes discarded", (intmax_t)enomem);
+ rval = 1;
+ }
+
+ /*
+ * 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 Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/tail.c Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,349 @@
+/*
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+int Fflag, fflag, qflag, rflag, rval, no_files;
+
+file_info_t *files;
+
+static void obsolete(char **);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ const char *fn;
+ FILE *fp;
+ off_t off;
+ enum STYLE style;
+ int i, ch, first;
+ file_info_t *file;
+ char *p;
+
+ /*
+ * 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) \
+ errx(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;
+ off = 0;
+ while ((ch = getopt(argc, argv, "Fb:c:fn:qr")) != -1)
+ switch (ch) {
+ case 'F': /* -F is superset of (and implies) -f */
+ Fflag = fflag = 1;
+ 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 'q':
+ qflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ no_files = argc ? argc : 1;
+
+ /*
+ * 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 && fflag) {
+ files = (struct file_info *)malloc(no_files *
+ sizeof (struct file_info));
+ if (!files)
+ err(1, "Couldn't malloc space for file descriptors.");
+
+ for (file = files; (fn = *argv++); file++) {
+ file->file_name = strdup(fn);
+ if (! file->file_name)
+ errx(1, "Couldn't malloc space for file name.");
+ if ((file->fp = fopen(file->file_name, "r")) == NULL ||
+ fstat(fileno(file->fp), &file->st)) {
+ if (file->fp != NULL) {
+ (void) fclose(file->fp);
+ file->fp = NULL;
+ }
+ if (!Fflag || errno != ENOENT)
+ ierr(file->file_name);
+ }
+ }
+ follow(files, style, off);
+ for (i = 0, file = files; i < no_files; i++, file++) {
+ free(file->file_name);
+ }
+ free(files);
+ } else if (*argv) {
+ for (first = 1; (fn = *argv++); ) {
+ if ((fp = fopen(fn, "r")) == NULL ||
+ fstat(fileno(fp), &sb)) {
+ ierr(fn);
+ continue;
+ }
+ if (argc > 1 && !qflag) {
+ (void) printf("%s==> %s <==\n",
+ first ? "" : "\n", fn);
+ first = 0;
+ (void) fflush(stdout);
+ }
+
+ if (rflag)
+ reverse(fp, fn, style, off, &sb);
+ else
+ forward(fp, fn, style, off, &sb);
+ }
+ } else {
+ fn = "stdin";
+
+ if (fstat(fileno(stdin), &sb)) {
+ ierr(fn);
+ 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, fn, style, off, &sb);
+ else
+ forward(stdin, fn, 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][Ffr] that isn't
+ * the option argument for a -b, -c or -n option gets converted.
+ */
+static void
+obsolete(char *argv[])
+{
+ char *ap, *p, *t;
+ size_t len;
+ char *start;
+
+ while ((ap = *++argv)) {
+ /* Return if "--" or not an option of any form. */
+ if (ap[0] != '-') {
+ if (ap[0] != '+')
+ return;
+ } else if (ap[1] == '-')
+ return;
+
+ 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)
+ err(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 == '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:
+ errx(1, "illegal option -- %s", *argv);
+ }
+ *p++ = *argv[0];
+ (void) strcpy(p, ap);
+ *argv = start;
+ continue;
+
+ /*
+ * Legacy Solaris tail supports "+c" "-c", "+l", "-l",
+ * "+b", and "-b" with an default number of 10. Map
+ * these arguments to an explicit +/-10 for FreeBSD.
+ * New argument will be of the form -[bcn][+-]10
+ */
+ case 'b':
+ case 'c':
+ case 'l':
+ if ((start = p = malloc(6)) == NULL)
+ err(1, "malloc");
+ *p++ = '-';
+ switch (ap[0]) {
+ case 'c':
+ *p++ = ap[0];
+ break;
+ case 'b':
+ *p++ = ap[0];
+ break;
+ case 'l':
+ *p++ = 'n';
+ break;
+ }
+ sprintf(p, "%c10", *argv[0]);
+ *argv = start;
+
+ continue;
+ /*
+ * Options w/ arguments, skip the argument and continue
+ * with the next option.
+ */
+ case 'n':
+ if (!ap[1])
+ ++argv;
+ /* FALLTHROUGH */
+ /* Options w/o arguments, continue with the next option. */
+ case 'F':
+ case 'f':
+ case 'r':
+ continue;
+
+ /* Illegal option, return and let getopt handle it. */
+ default:
+ return;
+ }
+ }
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]"
+ " [file ...]\n");
+ exit(1);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/tests/sun_solaris_tail.sh Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,505 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+#
+# Additional tests borrowed from ksh93 builtin tail test script
+# (usr/src/lib/libshell/common/tests/sun_solaris_builtin_tail). Modified
+# to use /usr/bin/tail rather than the ksh93 builtin.
+#
+TAIL=/usr/bin/tail
+
+# test setup
+function err_exit
+{
+ print -u2 -n "\t"
+ print -u2 -r ${Command}[$1]: "${@:2}"
+ (( Errors < 127 && Errors++ ))
+}
+alias err_exit='err_exit $LINENO'
+
+set -o nounset
+Command=${0##*/}
+integer Errors=0
+
+# common functions
+function isvalidpid
+{
+ kill -0 ${1} 2>/dev/null && return 0
+ return 1
+}
+
+function waitpidtimeout
+{
+ integer pid=$1
+ float timeout=$2
+ float i
+ float -r STEP=0.5 # const
+
+ (( timeout=timeout/STEP ))
+
+ for (( i=0 ; i < timeout ; i+=STEP )) ; do
+ isvalidpid ${pid} || break
+ sleep ${STEP}
+ done
+
+ return 0
+}
+
+function myintseq
+{
+ integer i
+ float arg1=$1
+ float arg2=$2
+ float arg3=$3
+
+ case $# in
+ 1)
+ for (( i=1 ; i <= arg1 ; i++ )) ; do
+ printf "%d\n" i
+ done
+ ;;
+ 2)
+ for (( i=arg1 ; i <= arg2 ; i++ )) ; do
+ printf "%d\n" i
+ done
+ ;;
+ 3)
+ for (( i=arg1 ; i <= arg3 ; i+=arg2 )) ; do
+ printf "%d\n" i
+ done
+ ;;
+ *)
+ print -u2 -f "%s: Illegal number of arguments %d\n" "$0" $#
+ return 1
+ ;;
+ esac
+
+ return 0
+}
+
+# quote input string but use single-backslash that "err_exit" prints
+# the strings correctly
+function singlebackslashquote
+{
+ typeset s
+ s="$(printf "%q\n" "$1")"
+ print -r "$s"
+ return 0
+}
+
+# quote input string but use double-backslash that "err_exit" prints
+# the strings correctly
+function doublebackslashquote
+{
+ typeset s
+ s="$(printf "%q\n" "$1")"
+ s="${s//\\/\\\\}"
+ print -r "$s"
+ return 0
+}
+
+
+# main
+builtin mktemp || err_exit "mktemp builtin not found"
+builtin rm || err_exit "rm builtin not found"
+# builtin tail || err_exit "tail builtin not found"
+
+typeset ocwd
+typeset tmpdir
+
+# create temporary test directory
+ocwd="$PWD"
+tmpdir="$(mktemp -t -d "test_sun_solaris_builtin_tail.XXXXXXXX")" || err_exit "Cannot create temporary directory"
+
+cd "${tmpdir}" || { err_exit "cd ${tmpdir} failed." ; exit $((Errors)) ; }
+
+
+# run tests:
+
+# test1: basic tests
+compound -a testcases=(
+ (
+ name="reverse_n"
+ input=$'hello\nworld'
+ compound -A tail_args=(
+ [legacy]=( argv=( "-r" ) )
+ )
+ expected_output=$'world\nhello'
+ )
+ (
+ name="revlist0n"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "-0" ) )
+# [std_like]=( argv=( "-n" "0" ) )
+ )
+ expected_output=$''
+ )
+ (
+ name="revlist0nr"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "-0r" ) )
+# [std_like]=( argv=( "-n" "0" "-r" ) )
+# [long_options]=( argv=( "--lines" "0" "--reverse" ) )
+ )
+ expected_output=$'' )
+ (
+ name="revlist1n"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "-1" ) )
+# [std_like]=( argv=( "-n" "1" ) )
+# [long_options]=( argv=( "--lines" "1" ) )
+ )
+ expected_output=$'4' )
+ (
+ name="revlist1nr"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "-1r" ) )
+# [std_like]=( argv=( "-n" "1" "-r" ) )
+# [long_options]=( argv=( "--lines" "1" "--reverse" ) )
+ )
+ expected_output=$'4'
+ )
+ (
+ name="revlist2n"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "-2" ) )
+# [std_like]=( argv=( "-n" "2" ) )
+ )
+ expected_output=$'3\n4'
+ )
+ (
+ name="revlist2nr"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "-2r" ) )
+# [std_like]=( argv=( "-n" "2" "-r" ) )
+ )
+ expected_output=$'4\n3'
+ )
+ (
+ name="revlist3nr"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "-3r" ) )
+# [std_like]=( argv=( "-n" "3" "-r" ) )
+ )
+ expected_output=$'4\n3\n2'
+ )
+ (
+ name="revlist2p"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "+2" ) )
+# [std_like]=( argv=( "-n" "+2" ) )
+ )
+ expected_output=$'2\n3\n4'
+ )
+# Note: following test case trips up legacy Solaris 'tail' as well
+# (
+# name="revlist2pr"
+# input=$'1\n2\n3\n4'
+# compound -A tail_args=(
+# [legacy]=( argv=( "+2r" ) )
+# [std_like]=( argv=( "-n" "+2" "-r" ) )
+# )
+# expected_output=$'4\n3\n2'
+# )
+ (
+ name="revlist3p"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "+3" ) )
+ [std_like]=( argv=( "-n" "+3" ) )
+ )
+ expected_output=$'3\n4'
+ )
+# Note: following test case trips up legacy Solaris 'tail' as well
+# (
+# name="revlist3pr"
+# input=$'1\n2\n3\n4'
+# compound -A tail_args=(
+# [legacy]=( argv=( "+3r" ) )
+# [std_like]=( argv=( "-n" "+3" "-r" ) )
+# )
+# expected_output=$'4\n3'
+# )
+ (
+ name="revlist4p"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "+4" ) )
+# [std_like]=( argv=( "-n" "+4" ) )
+ )
+ expected_output=$'4'
+ )
+# Note: following test case trips up legacy Solaris 'tail' as well
+# (
+# name="revlist4pr"
+# input=$'1\n2\n3\n4'
+# compound -A tail_args=(
+# [legacy]=( argv=( "+4r" ) )
+# [std_like]=( argv=( "-n" "+4" "-r" ) )
+# )
+# expected_output=$'4'
+# )
+ (
+ name="revlist5p"
+ input=$'1\n2\n3\n4'
+ compound -A tail_args=(
+ [legacy]=( argv=( "+5" ) )
+# [std_like]=( argv=( "-n" "+5" ) )
+ )
+ expected_output=$''
+ )
+# Note: following test case trips up legacy Solaris 'tail' as well
+# (
+# name="revlist5pr"
+# input=$'1\n2\n3\n4'
+# compound -A tail_args=(
+# [legacy]=( argv=( "+5r" ) )
+# [std_like]=( argv=( "-n" "+5" "-r" ) )
+# )
+# expected_output=$''
+# )
+)
+
+for testid in "${!testcases[@]}" ; do
+ nameref tc=testcases[${testid}]
+
+ for argv_variants in "${!tc.tail_args[@]}" ; do
+ nameref argv=tc.tail_args[${argv_variants}].argv
+ output=$(
+ set -o pipefail
+ (trap "" PIPE ; print -r -- "${tc.input}") | $TAIL "${argv[@]}"
+ ) || err_exit "test ${tc.name}/${argv_variants}: command failed with exit code $?"
+
+ [[ "${output}" == "${tc.expected_output}" ]] || err_exit "test ${tc.name}/${argv_variants}: Expected $(doublebackslashquote "${tc.expected_output}"), got $(doublebackslashquote "${output}")"
+ done
+done
+
+
+# test2: test "tail -r </etc/profile | rev -l" vs. "cat </etc/profile"
+[[ "$($TAIL -r </etc/profile | rev -l)" == "$( cat /etc/profile )" ]] || err_exit "'tail -r </etc/profile | rev -l' output does not match 'cat /etc/profile'"
+
+# Test case not applicable to FreeBSD 'tail'
+# test 3: ast-ksh.2009-05-05 "tail" builtin may crash if we pass unsupported long options
+#$SHELL -o errexit -c 'builtin tail ; print "hello" | tail --attack_of_chicken_monsters' >/dev/null 2>&1
+#(( $? == 2 )) || err_exit "expected exit code 2 for unsupported long option, got $?"
+
+
+# test 4: FIFO tests
+
+# FIFO test functions
+# (we use functions here to do propper garbage collection)
+function test_tail_fifo_1
+{
+ typeset tail_cmd="$1"
+ integer i
+ integer tail_pid=-1
+
+ # cleanup trap
+ trap "rm -f tailtestfifo tailout" EXIT
+
+ # create test FIFO
+ mkfifo tailtestfifo
+
+ ${tail_cmd} -f <tailtestfifo >tailout &
+ tail_pid=$!
+
+ myintseq 20 >tailtestfifo
+
+ waitpidtimeout ${tail_pid} 5
+
+ if isvalidpid ${tail_pid} ; then
+ err_exit "test_tail_fifo_1: # tail hung (not expected)"
+ kill -KILL ${tail_pid}
+ fi
+
+ wait || err_exit "tail child returned non-zero exit code=$?"
+
+ [[ "$(cat tailout)" == $'11\n12\n13\n14\n15\n16\n17\n18\n19\n20' ]] || err_exit "test_tail_fifo_1: Expected $(doublebackslashquote '11\n12\n13\n14\n15\n16\n17\n18\n19\n20'), got $(doublebackslashquote "$(cat tailout)")"
+
+ return 0
+}
+
+function test_tail_fifo_2
+{
+ typeset tail_cmd="$1"
+ integer i
+ integer tail_pid=-1
+
+ # cleanup trap
+ trap "rm -f tailtestfifo tailout" EXIT
+
+ # create test FIFO
+ mkfifo tailtestfifo
+
+ ${tail_cmd} -f tailtestfifo >tailout &
+ tail_pid=$!
+
+ myintseq 14 >tailtestfifo
+
+ waitpidtimeout ${tail_pid} 5
+
+ if isvalidpid ${tail_pid} ; then
+ [[ "$(cat tailout)" == $'5\n6\n7\n8\n9\n10\n11\n12\n13\n14' ]] || err_exit "test_tail_fifo_2: Expected $(doublebackslashquote $'5\n6\n7\n8\n9\n10\n11\n12\n13\n14'), got $(doublebackslashquote "$(cat tailout)")"
+
+ myintseq 15 >>tailtestfifo
+
+ waitpidtimeout ${tail_pid} 5
+
+ if isvalidpid ${tail_pid} ; then
+ kill -KILL ${tail_pid}
+ else
+ err_exit "test_tail_fifo_2: # tail exit with return code $? (not expected)"
+ fi
+ fi
+
+ wait || err_exit "tail child returned non-zero exit code=$?"
+
+ [[ "$(cat tailout)" == $'5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15' ]] || err_exit "test_tail_fifo_2: Expected $(doublebackslashquote $'5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15'), got $(doublebackslashquote "$(cat tailout)")"
+
+ return 0
+}
+
+# fixme: This should test /usr/bin/tail and /usr/xpg4/bin/tail in Solaris
+test_tail_fifo_1 "$TAIL"
+test_tail_fifo_2 "$TAIL"
+
+
+# test 5: "tail -f" tests
+function followtest1
+{
+ typeset -r FOLLOWFILE="followfile.txt"
+ typeset -r OUTFILE="outfile.txt"
+
+ typeset title="$1"
+ typeset testcmd="$2"
+ typeset usenewline=$3
+ typeset followstr=""
+ typeset newline=""
+ integer i
+ integer tailchild=-1
+
+ if ${usenewline} ; then
+ newline=$'\n'
+ fi
+
+ rm -f "${FOLLOWFILE}" "${OUTFILE}"
+ print -n "${newline}" > "${FOLLOWFILE}"
+
+ ${testcmd} -f "${FOLLOWFILE}" >"${OUTFILE}" &
+ (( tailchild=$! ))
+
+ for (( i=0 ; i < 10 ; i++)) ; do
+ followstr+="${newline}${i}"
+ print -n "${i}${newline}" >>"${FOLLOWFILE}"
+ sleep 2
+
+ [[ "$( < "${OUTFILE}")" == "${followstr}" ]] || err_exit "${title}: Expected $(doublebackslashquote "${followstr}"), got "$(doublebackslashquote "$( < "${OUTFILE}")")""
+ done
+
+ kill -KILL ${tailchild} 2>/dev/null
+ #kill -TERM ${tailchild} 2>/dev/null
+ waitpidtimeout ${tailchild} 5
+
+ if isvalidpid ${tailchild} ; then
+ err_exit "${title}: tail pid=${tailchild} hung."
+ kill -KILL ${tailchild} 2>/dev/null
+ fi
+
+ wait ${tailchild} 2>/dev/null
+
+ rm -f "${FOLLOWFILE}" "${OUTFILE}"
+
+ return 0
+}
+
+followtest1 "test5a" "$TAIL" true
+# fixme: later we should test this, too:
+#followtest1 "test5b" "tail" false
+#followtest1 "test5c" "/usr/xpg4/bin/tail" true
+#followtest1 "test5d" "/usr/xpg4/bin/tail" false
+#followtest1 "test5e" "/usr/bin/tail" true
+#followtest1 "test5f" "/usr/bin/tail" false
+
+
+# test 6: "tail -f" tests
+function followtest2
+{
+ typeset -r FOLLOWFILE="followfile.txt"
+ typeset -r OUTFILE="outfile.txt"
+
+ typeset title="$1"
+ typeset testcmd="$2"
+ integer tailchild=-1
+
+ rm -f "${FOLLOWFILE}" "${OUTFILE}"
+
+ myintseq 50000 >"${FOLLOWFILE}"
+
+ ${testcmd} -n 60000 -f "${FOLLOWFILE}" >"${OUTFILE}" &
+ (( tailchild=$! ))
+
+ sleep 10
+
+ kill -KILL ${tailchild} 2>/dev/null
+ #kill -TERM ${tailchild} 2>/dev/null
+ waitpidtimeout ${tailchild} 5
+
+ if isvalidpid ${tailchild} ; then
+ err_exit "${title}: tail pid=${tailchild} hung."
+ kill -KILL ${tailchild} 2>/dev/null
+ fi
+
+ wait ${tailchild} 2>/dev/null
+
+ # this tail should be an external process
+ outstr=$(/usr/bin/tail "${OUTFILE}") || err_exit "tail returned non-zero exit code $?"
+ [[ "${outstr}" == 49991*50000 ]] || err_exit "${title}: Expected match for 49991*50000, got "$(singlebackslashquote "${outstr}")""
+
+ rm -f "${FOLLOWFILE}" "${OUTFILE}"
+
+ return 0
+}
+
+followtest2 "test6a" "$TAIL"
+followtest2 "test6b" "$TAIL"
+# fixme: later we should test this, too:
+#followtest2 "test6c" "/usr/bin/tail"
+
+
+# cleanup
+cd "${ocwd}"
+rmdir "${tmpdir}" || err_exit "Cannot remove temporary directory ${tmpdir}".
+
+
+# tests done
+exit $((Errors))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/tail/tests/tailtests.sh Fri Oct 01 20:02:51 2010 -0700
@@ -0,0 +1,141 @@
+#!/bin/bash
+#
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy is of the CDDL is also available via the Internet
+# at http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2010 Chris Love. All rights reserved.
+#
+
+
+#
+# Test cases for 'tail', some based on CoreUtils test cases (validated
+# with legacy Solaris 'tail' and/or xpg4 'tail')
+#
+PROG=/usr/bin/tail
+
+case $1 in
+ -x)
+ PROG=/usr/xpg4/bin/tail
+ ;;
+ -o)
+ PROG=$2
+ ;;
+ -?)
+ echo "Usage: tailtests.sh [-x][-o <override tail executable>]"
+ exit 1
+ ;;
+esac
+
+echo "Using $PROG"
+
+o=`echo -e "bcd"`
+a=`echo -e "abcd" | $PROG +2c`
+[[ "$a" != "$o" ]] && echo "Fail test 1 - $a"
+
+o=`echo -e ""`
+a=`echo "abcd" | $PROG +8c`
+[[ "$a" != "$o" ]] && echo "Fail test 2 - $a"
+
+o=`echo -e "abcd"`
+a=`echo "abcd" | $PROG -9c`
+[[ "$a" != "$o" ]] && echo "Fail test 3 - $a"
+
+o=`echo -e "x"`
+a=`echo -e "x" | $PROG -1l`
+[[ "$a" != "x" ]] && echo "Fail test 4 - $a"
+
+o=`echo -e "\n"`
+a=`echo -e "x\ny\n" | $PROG -1l`
+[[ "$a" != "$o" ]] && echo "Fail test 5 - $a"
+
+o=`echo -e "y\n"`
+a=`echo -e "x\ny\n" | $PROG -2l`
+[[ "$a" != "$o" ]] && echo "Fail test 6 - $a"
+
+o=`echo -e "y"`
+a=`echo -e "x\ny" | $PROG -1l`
+[[ "$a" != "$o" ]] && echo "Fail test 7 - $a"
+
+o=`echo -e "x\ny\n"`
+a=`echo -e "x\ny\n" | $PROG +1l`
+[[ "$a" != "$o" ]] && echo "Fail test 8 - $a"
+
+o=`echo -e "y\n"`
+a=`echo -e "x\ny\n" | $PROG +2l`
+[[ "$a" != "$o" ]] && echo "Fail test 9 - $a"
+
+o=`echo -e "x"`
+a=`echo -e "x" | $PROG -1`
+[[ "$a" != "$o" ]] && echo "Fail test 10 - $a"
+
+o=`echo -e "\n"`
+a=`echo -e "x\ny\n" | $PROG -1`
+[[ "$a" != "$o" ]] && echo "Fail test 11 - $a"
+
+o=`echo -e "y\n"`
+a=`echo -e "x\ny\n" | $PROG -2`
+[[ "$a" != "$o" ]] && echo "Fail test 12 - $a"
+
+o=`echo -e "y"`
+a=`echo -e "x\ny" | $PROG -1`
+[[ "$a" != "$o" ]] && echo "Fail test 13 - $a"
+
+o=`echo -e "x\ny\n"`
+a=`echo -e "x\ny\n" | $PROG +1`
+[[ "$a" != "$o" ]] && echo "Fail test 14 - $a"
+
+o=`echo -e "y\n"`
+a=`echo -e "x\ny\n" | $PROG +2`
+[[ "$a" != "$o" ]] && echo "Fail test 15 - $a"
+
+# For compatibility with Legacy Solaris tail this should also work as '+c'
+o=`echo -e "yyz"`
+a=`echo -e "xyyyyyyyyyyz" | $PROG +10c`
+[[ "$a" != "$o" ]] && echo "Fail test 16 - $a"
+
+o=`echo -e "yyz"`
+a=`echo -e "xyyyyyyyyyyz" | $PROG +c`
+[[ "$a" != "$o" ]] && echo "Fail test 16a - $a"
+
+
+# For compatibility with Legacy Solaris tail this should also work as '+l'
+o=`echo -e "y\ny\nz"`
+a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +10l`
+[[ "$a" != "$o" ]] && echo "Fail test 17 - $a"
+
+o=`echo -e "y\ny\nz"`
+a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +l`
+[[ "$a" != "$o" ]] && echo "Fail test 17a - $a"
+
+
+# For compatibility with Legacy Solaris tail this should also work as '-l'
+o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
+a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -10l`
+[[ "$a" != "$o" ]] && echo "Fail test 18 - $a"
+
+o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
+a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -l`
+[[ "$a" != "$o" ]] && echo "Fail test 18a - $a"
+
+o=`echo -e "c\nb\na"`
+a=`echo -e "a\nb\nc" | $PROG -r`
+[[ "$a" != "$o" ]] && echo "Fail test 19 - $a"
+
+
+echo "Completed"
+
+exit 0
+
+# Template for additional test cases
+#o=`echo -e ""`
+#a=`echo -e "" | $PROG `
+#[[ "$a" != "$o" ]] && echo "Fail test - $a"
--- a/usr/src/pkg/manifests/SUNWcs.mf Sat Sep 25 11:51:56 2010 +0800
+++ b/usr/src/pkg/manifests/SUNWcs.mf Fri Oct 01 20:02:51 2010 -0700
@@ -2570,6 +2570,8 @@
license=usr/src/cmd/script/THIRDPARTYLICENSE
license usr/src/cmd/stat/vmstat/THIRDPARTYLICENSE \
license=usr/src/cmd/stat/vmstat/THIRDPARTYLICENSE
+license usr/src/cmd/tail/THIRDPARTYLICENSE \
+ license=usr/src/cmd/tail/THIRDPARTYLICENSE
license usr/src/cmd/tip/THIRDPARTYLICENSE \
license=usr/src/cmd/tip/THIRDPARTYLICENSE
license usr/src/cmd/tr/THIRDPARTYLICENSE \
--- a/usr/src/pkg/manifests/system-xopen-xcu4.mf Sat Sep 25 11:51:56 2010 +0800
+++ b/usr/src/pkg/manifests/system-xopen-xcu4.mf Fri Oct 01 20:02:51 2010 -0700
@@ -70,7 +70,6 @@
file path=usr/xpg4/bin/sed mode=0555
file path=usr/xpg4/bin/sort mode=0555
file path=usr/xpg4/bin/stty mode=0555
-file path=usr/xpg4/bin/tail mode=0555
file path=usr/xpg4/bin/who mode=0555
hardlink path=usr/xpg4/bin/bg target=../../../usr/xpg4/bin/alias
hardlink path=usr/xpg4/bin/cd target=../../../usr/xpg4/bin/alias
@@ -105,4 +104,5 @@
license lic_OSBL license=lic_OSBL
license lic_OSBL_preamble license=lic_OSBL_preamble
link path=usr/xpg4/bin/ipcs target=../../bin/ipcs
+link path=usr/xpg4/bin/tail target=../../bin/tail
link path=usr/xpg4/bin/tr target=../../bin/tr