diff options
author | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
---|---|---|
committer | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
commit | 5fd83771641d15c418f747bd343ba6738d3875f7 (patch) | |
tree | 5abf0f78f680d9837dbd93d4d4c3933bb7509599 /text_cmds/tail | |
download | apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip |
Import macOS userland
adv_cmds-176
basic_cmds-55
bootstrap_cmds-116.100.1
developer_cmds-66
diskdev_cmds-667.40.1
doc_cmds-53.60.1
file_cmds-321.40.3
mail_cmds-35
misc_cmds-34
network_cmds-606.40.1
patch_cmds-17
remote_cmds-63
shell_cmds-216.60.1
system_cmds-880.60.2
text_cmds-106
Diffstat (limited to 'text_cmds/tail')
-rw-r--r-- | text_cmds/tail/extern.h | 76 | ||||
-rw-r--r-- | text_cmds/tail/forward.c | 520 | ||||
-rw-r--r-- | text_cmds/tail/misc.c | 119 | ||||
-rw-r--r-- | text_cmds/tail/read.c | 214 | ||||
-rw-r--r-- | text_cmds/tail/reverse.c | 287 | ||||
-rw-r--r-- | text_cmds/tail/tail.1 | 188 | ||||
-rw-r--r-- | text_cmds/tail/tail.c | 349 |
7 files changed, 1753 insertions, 0 deletions
diff --git a/text_cmds/tail/extern.h b/text_cmds/tail/extern.h new file mode 100644 index 0000000..d956680 --- /dev/null +++ b/text_cmds/tail/extern.h @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + * + * $FreeBSD$ + */ + +#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 *, enum STYLE, off_t, struct stat *); +void reverse(FILE *, enum STYLE, off_t, struct stat *); + +int bytes(FILE *, off_t); +int lines(FILE *, off_t); + +void ierr(void); +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; +extern const char *fname; diff --git a/text_cmds/tail/forward.c b/text_cmds/tail/forward.c new file mode 100644 index 0000000..172898a --- /dev/null +++ b/text_cmds/tail/forward.c @@ -0,0 +1,520 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Sze-Tyan Wang. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/mman.h> +#include <sys/event.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 *, off_t, struct stat *); +static void show(file_info_t *); +static void set_events(file_info_t *files); + +/* defines for inner loop actions */ +#define USE_SLEEP 0 +#define USE_KQUEUE 1 +#define ADD_EVENTS 2 + +struct kevent *ev; +int action = USE_SLEEP; +int kq; + +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, 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(); + return; + } + } else while (off--) + if ((ch = getc(fp)) == EOF) { + if (ferror(fp)) { + ierr(); + return; + } + break; + } + break; + case FLINES: + if (off == 0) + break; + for (;;) { + if ((ch = getc(fp)) == EOF) { + if (ferror(fp)) { + ierr(); + return; + } + break; + } + if (ch == '\n' && !--off) + break; + } + break; + case RBYTES: + if (S_ISREG(sbp->st_mode)) { + if (sbp->st_size >= off && + fseeko(fp, -off, SEEK_END) == -1) { + ierr(); + return; + } + } else if (off == 0) { + while (getc(fp) != EOF); + if (ferror(fp)) { + ierr(); + return; + } + } else + if (bytes(fp, off)) + return; + break; + case RLINES: + if (S_ISREG(sbp->st_mode)) + if (!off) { + if (fseeko(fp, (off_t)0, SEEK_END) == -1) { + ierr(); + return; + } + } else + rlines(fp, off, sbp); + else if (off == 0) { + while (getc(fp) != EOF); + if (ferror(fp)) { + ierr(); + return; + } + } else + if (lines(fp, off)) + return; + break; + default: + break; + } + + while ((ch = getc(fp)) != EOF) + if (putchar(ch) == EOF) + oerr(); + if (ferror(fp)) { + ierr(); + return; + } + (void)fflush(stdout); +} + +/* + * rlines -- display the last offset lines of the file. + */ +static void +rlines(fp, off, sbp) + FILE *fp; + off_t off; + struct stat *sbp; +{ +#ifdef __APPLE__ + /* Using mmap on network filesystems can frequently lead + to distress, and even on local file systems other processes + truncating the file can also lead to upset. */ + + /* Seek to sbp->st_blksize before the end of the file, find + all the newlines. If there are enough, print the last off + lines. Otherwise go back another sbp->st_blksize bytes, + and count newlines. Avoid re-reading blocks when possible. */ + + // +1 because we capture line ends and we want off line _starts_, + // +1 because the first line might be partial when try_at != 0 + off_t search_for = off +2; + off_t try_at = sbp->st_size; + off_t last_try = sbp->st_size; + off_t found_this_pass = 0; + off_t found_total = 0; + off_t *found_at = calloc(search_for, sizeof(off_t)); + + flockfile(fp); + + if (found_at == NULL) { + ierr(); + goto done; + } + + if (off == 0 || sbp->st_size == 0) { + goto done; + } + + /* The last character is special. Check to make sure that it is a \n, + * and if not, subtract one from the number of \n we need to search for. + */ + if (0 != fseeko(fp, sbp->st_size - 1, SEEK_SET)) { + ierr(); + goto done; + } + if ('\n' != getc_unlocked(fp)) { + search_for--; + } + + while(try_at != 0) { + found_this_pass = 0; + + if (try_at < sbp->st_blksize) { + found_at[found_this_pass++] = 0; + try_at = 0; + } else { + last_try = try_at; + try_at -= sbp->st_blksize; + } + + if (0 != fseeko(fp, try_at, SEEK_SET)) { + ierr(); + goto done; + } + + char ch; + while(EOF != (ch = getc_unlocked(fp))) { + if (ch == '\n') { + found_at[found_this_pass++ % search_for] = ftello(fp); + found_total++; + } + if (ftello(fp) == last_try && found_total < search_for) { + // We just reached the last block we scanned, + // and we know there arn't enough lines found + // so far to be happy, so we don't have to + // read it again. + break; + } + } + + if (found_this_pass >= search_for || try_at == 0) { + off_t min = found_at[0]; + int min_i = 0; + int i; + int lim = (found_this_pass < search_for) ? found_this_pass : search_for; + for(i = 1; i < lim; i++) { + if (found_at[i] < min) { + min = found_at[i]; + min_i = i; + } + } + + off_t target = min; + + if (found_this_pass >= search_for) { + // min_i might be a partial line (unless + // try_at is 0). If we found search_for + // lines, min_i+1 is the first known full line + // _and_ because we look for an extra line we + // don't need to show it. + target = found_at[(min_i + 1) % search_for]; + } + + if (0 != fseeko(fp, target, SEEK_SET)) { + ierr(); + goto done; + } + + flockfile(stdout); + while(EOF != (ch = getc_unlocked(fp))) { + if (EOF == putchar_unlocked(ch)) { + funlockfile(stdout); + oerr(); + goto done; + } + } + funlockfile(stdout); + goto done; + } + } + +done: + funlockfile(fp); + free(found_at); + return; +#else + struct mapinfo map; + off_t curoff, size; + int i; + + if (!(size = sbp->st_size)) + 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(); + 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(); + exit(1); + } + + /* Set the file pointer to reflect the length displayed. */ + if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) { + ierr(); + return; + } + if (map.start != NULL && munmap(map.start, map.maplen)) { + ierr(); + return; + } +#endif +} + +static void +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)) { + file->fp = NULL; + fname = file->file_name; + ierr(); + fname = NULL; + } else + clearerr(file->fp); +} + +static void +set_events(file_info_t *files) +{ + int i, n = 0; + file_info_t *file; + struct timespec ts; + struct statfs sf; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + action = USE_KQUEUE; + for (i = 0, file = files; i < no_files; i++, file++) { + if (! file->fp) + continue; + + if (fstatfs(fileno(file->fp), &sf) == 0 && + (sf.f_flags & MNT_LOCAL) == 0) { + action = USE_SLEEP; + return; + } + + if (Fflag && fileno(file->fp) != STDIN_FILENO) { + EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE, + EV_ADD | EV_ENABLE | EV_CLEAR, + NOTE_DELETE | NOTE_RENAME, 0, 0); + n++; + } + EV_SET(&ev[n], fileno(file->fp), EVFILT_READ, + EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); + n++; + } + + if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { + action = USE_SLEEP; + } +} + +/* + * follow -- display the file, from an offset, forward. + * + */ +void +follow(file_info_t *files, enum STYLE style, off_t off) +{ + int active, 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); + fname = file->file_name; + forward(file->fp, style, off, &file->st); + fname = NULL; + if (Fflag && fileno(file->fp) != STDIN_FILENO) + n++; + } + } + if (! active) + return; + + last = --file; + + kq = kqueue(); + if (kq < 0) + err(1, "kqueue"); + ev = malloc(n * sizeof(struct kevent)); + if (! ev) + err(1, "Couldn't allocate memory for kevents."); + set_events(files); + + for (;;) { + for (i = 0, file = files; i < no_files; i++, file++) { + if (! file->fp) + continue; + if (Fflag && file->fp && fileno(file->fp) != STDIN_FILENO) { + if (stat(file->file_name, &sb2) == 0 && + (sb2.st_ino != file->st.st_ino || + sb2.st_dev != file->st.st_dev || + sb2.st_nlink == 0)) { + show(file); + file->fp = freopen(file->file_name, "r", file->fp); + if (file->fp == NULL) { + ierr(); + continue; + } else { + memcpy(&file->st, &sb2, sizeof(struct stat)); + set_events(files); + } + } + } + show(file); + } + + switch (action) { + case USE_KQUEUE: + 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 = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL); + if (n < 0) + err(1, "kevent"); + if (n == 0) { + /* timeout */ + break; + } else if (ev->filter == EVFILT_READ && ev->data < 0) { + /* file shrank, reposition to end */ + if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) { + ierr(); + continue; + } + } + break; + + case USE_SLEEP: + (void) usleep(250000); + break; + } + } +} diff --git a/text_cmds/tail/misc.c b/text_cmds/tail/misc.c new file mode 100644 index 0000000..584e3d1 --- /dev/null +++ b/text_cmds/tail/misc.c @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Sze-Tyan Wang. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; +#endif + +#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() +{ + warn("%s", fname); + rval = 1; +} + +void +oerr() +{ + 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); +} diff --git a/text_cmds/tail/read.c b/text_cmds/tail/read.c new file mode 100644 index 0000000..85d4d6e --- /dev/null +++ b/text_cmds/tail/read.c @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Sze-Tyan Wang. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/6/93"; +#endif + +#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 <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, 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(); + 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, off_t off) +{ + struct { + int blen; + u_int 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)); + 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(); + 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); +} diff --git a/text_cmds/tail/reverse.c b/text_cmds/tail/reverse.c new file mode 100644 index 0000000..f0a52aa --- /dev/null +++ b/text_cmds/tail/reverse.c @@ -0,0 +1,287 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Sze-Tyan Wang. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)reverse.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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 *); +static void r_reg(FILE *, enum STYLE, off_t, struct stat *); + +/* + * reverse -- display input in reverse order by line. + * + * There are six separate cases for this -- regular and non-regular + * files by bytes, lines or the whole file. + * + * BYTES display N bytes + * REG mmap the file and display the lines + * NOREG cyclically read characters into a wrap-around buffer + * + * LINES display N lines + * REG mmap the file and display the lines + * NOREG cyclically read lines into a wrap-around array of buffers + * + * FILE display the entire file + * REG mmap the file and display the lines + * NOREG cyclically read input into a linked list of buffers + */ +void +reverse(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) +{ + if (style != REVERSE && off == 0) + return; + + if (S_ISREG(sbp->st_mode)) + r_reg(fp, style, off, sbp); + else + switch(style) { + case FBYTES: + case RBYTES: + bytes(fp, off); + break; + case FLINES: + case RLINES: + lines(fp, off); + break; + case REVERSE: + r_buf(fp); + break; + default: + break; + } +} + +/* + * r_reg -- display a regular file in reverse order by line. + */ +static void +r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) +{ + struct mapinfo map; + off_t curoff, size, lineend; + int i; + + if (!(size = sbp->st_size)) + 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(); + 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(); + 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(); + return; + } + if (map.start != NULL && munmap(map.start, map.maplen)) + ierr(); +} + +typedef struct bf { + struct bf *next; + struct bf *prev; + int len; + char *l; +} BF; + +/* + * r_buf -- display a non-regular file in reverse order by line. + * + * This is the function that saves the entire input, storing the data in a + * doubly linked list of buffers and then displays them in reverse order. + * It has the usual nastiness of trying to find the newlines, as there's no + * guarantee that a newline occurs anywhere in the file, let alone in any + * particular buffer. If we run out of memory, input is discarded (and the + * user warned). + */ +static void +r_buf(FILE *fp) +{ + BF *mark, *tl, *tr; + int ch, len, llen; + char *p; + off_t enomem; + +#define BSZ (128 * 1024) + 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(); + 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; + } +} diff --git a/text_cmds/tail/tail.1 b/text_cmds/tail/tail.1 new file mode 100644 index 0000000..251511a --- /dev/null +++ b/text_cmds/tail/tail.1 @@ -0,0 +1,188 @@ +.\" Copyright (c) 1980, 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tail.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD$ +.\" +.Dd June 29, 2006 +.Dt TAIL 1 +.Os +.Sh NAME +.Nm tail +.Nd display the last part of a file +.Sh SYNOPSIS +.Nm +.Op Fl F | f | r +.Op Fl q +.Oo +.Fl b Ar number | Fl c Ar number | Fl n Ar number +.Oc +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility displays the contents of +.Ar file +or, by default, its standard input, to the standard output. +.Pp +The display begins at a byte, line or 512-byte block location in the +input. +Numbers having a leading plus +.Pq Ql + +sign are relative to the beginning +of the input, for example, +.Dq Li "-c +2" +starts the display at the second +byte of the input. +Numbers having a leading minus +.Pq Ql - +sign or no explicit sign are +relative to the end of the input, for example, +.Dq Li "-n 2" +displays the last two lines of the input. +The default starting location is +.Dq Li "-n 10" , +or the last 10 lines of the input. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl b Ar number +The location is +.Ar number +512-byte blocks. +.It Fl c Ar number +The location is +.Ar number +bytes. +.It Fl f +The +.Fl f +option causes +.Nm +to not stop when end of file is reached, but rather to wait for additional +data to be appended to the input. +The +.Fl f +option is ignored if the standard input is a pipe, but not if it is a FIFO. +.It Fl F +The +.Fl F +option implies the +.Fl f +option, but +.Nm +will also check to see if the file being followed has been renamed or rotated. +The file is closed and reopened when +.Nm +detects that the filename being read from has a new inode number. +The +.Fl F +option is ignored if reading from standard input rather than a file. +.It Fl n Ar number +The location is +.Ar number +lines. +.It Fl q +Suppresses printing of headers when multiple files are being examined. +.It Fl r +The +.Fl r +option causes the input to be displayed in reverse order, by line. +Additionally, this option changes the meaning of the +.Fl b , c +and +.Fl n +options. +When the +.Fl r +option is specified, these options specify the number of bytes, lines +or 512-byte blocks to display, instead of the bytes, lines or blocks +from the beginning or end of the input from which to begin the display. +The default for the +.Fl r +option is to display all of the input. +.El +.Pp +If more than a single file is specified, each file is preceded by a +header consisting of the string +.Dq Li "==> " Ns Ar XXX Ns Li " <==" +where +.Ar XXX +is the name of the file unless +.Fl q +flag is specified. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr cat 1 , +.Xr head 1 , +.Xr sed 1 +.Sh STANDARDS +The +.Nm +utility is expected to be a superset of the +.St -p1003.2-92 +specification. +In particular, the +.Fl F , +.Fl b +and +.Fl r +options are extensions to that standard. +.Pp +The historic command line syntax of +.Nm +is supported by this implementation. +The only difference between this implementation and historic versions +of +.Nm , +once the command line syntax translation has been done, is that the +.Fl b , +.Fl c +and +.Fl n +options modify the +.Fl r +option, i.e., +.Dq Li "-r -c 4" +displays the last 4 characters of the last line +of the input, while the historic tail (using the historic syntax +.Dq Li -4cr ) +would ignore the +.Fl c +option and display the last 4 lines of the input. +.Sh HISTORY +A +.Nm +command appeared in PWB UNIX. diff --git a/text_cmds/tail/tail.c b/text_cmds/tail/tail.c new file mode 100644 index 0000000..57129ef --- /dev/null +++ b/text_cmds/tail/tail.c @@ -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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD$"); + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#ifndef lint +static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; +#endif + +#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; +const char *fname; + +file_info_t *files; + +static void obsolete(char **); +static void usage(void); + +int +main(int argc, char *argv[]) +{ + struct stat sb; + 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; + 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 (fflag && !argc) { + /* + * 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 (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; (fname = argc ? *argv++ : "stdin"); file++) { + file->file_name = malloc(strlen(fname)+1); + if (! file->file_name) + errx(1, "Couldn't malloc space for file name."); + strncpy(file->file_name, fname, strlen(fname)+1); + file->fp = argc ? fopen(file->file_name, "r") : stdin; + if (file->fp == NULL || + fstat(fileno(file->fp), &file->st)) { + file->fp = NULL; + ierr(); + continue; + } + if (!argc) + break; + } + 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; (fname = *argv++);) { + if ((fp = fopen(fname, "r")) == NULL || + fstat(fileno(fp), &sb)) { + ierr(); + continue; + } + if (argc > 1 && !qflag) { + (void)printf("%s==> %s <==\n", + first ? "" : "\n", fname); + first = 0; + (void)fflush(stdout); + } + +#ifdef __APPLE__ + /* 3849683: don't read a directory */ + if (S_IFDIR == (sb.st_mode & S_IFMT)) + continue; +#endif + + if (rflag) + reverse(fp, style, off, &sb); + else + forward(fp, style, off, &sb); + } + } else { + fname = "stdin"; + + if (fstat(fileno(stdin), &sb)) { + ierr(); + exit(1); + } + + if (rflag) + reverse(stdin, style, off, &sb); + else + forward(stdin, style, off, &sb); + } + exit(rval); +} + +/* + * Convert the obsolete argument form into something that getopt can handle. + * This means that anything of the form [+-][0-9][0-9]*[lbc][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; + + /* + * Options w/ arguments, skip the argument and continue + * with the next option. + */ + case 'b': + case 'c': + case 'n': + if (!ap[1]) + ++argv; + /* FALLTHROUGH */ + /* Options w/o arguments, continue with the next option. */ + case 'F': + case '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); +} |