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