Mercurial > illumos > illumos-gate
changeset 13197:1b557c5c9727
244 Need replacement for closed /usr/bin/tail and /usr/xpg4/bin/tail
Reviewed by: garrett@nexenta.com
Reviewed by: richlowe@richlowe.net
Reviewed by: gordon.w.ross@gmail.com
Approved by: garrett@nexenta.com
author | Chris Love <cjlove@san.rr.com> |
---|---|
date | Fri, 01 Oct 2010 20:02:51 -0700 |
parents | 5c9ec1505f4b |
children | f4ee39d1ce08 |
files | exception_lists/closed-bins usr/src/Makefile.lint usr/src/cmd/Makefile usr/src/cmd/tail/Makefile usr/src/cmd/tail/THIRDPARTYLICENSE usr/src/cmd/tail/THIRDPARTYLICENSE.descrip usr/src/cmd/tail/extern.h usr/src/cmd/tail/forward.c usr/src/cmd/tail/misc.c usr/src/cmd/tail/read.c usr/src/cmd/tail/reverse.c usr/src/cmd/tail/tail.c usr/src/cmd/tail/tests/sun_solaris_tail.sh usr/src/cmd/tail/tests/tailtests.sh usr/src/pkg/manifests/SUNWcs.mf usr/src/pkg/manifests/system-xopen-xcu4.mf |
diffstat | 16 files changed, 2190 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- 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