Fix tail to work on files bigger than 2GB.

PR:		14786
Reviewed by:	iedowse
This commit is contained in:
David Malone 2001-03-27 20:37:34 +00:00
parent 5c620e2dad
commit 726098d35e
4 changed files with 150 additions and 69 deletions

View File

@ -31,11 +31,24 @@
* SUCH DAMAGE.
*
* @(#)extern.h 8.1 (Berkeley) 6/6/93
*
* $FreeBSD$
*/
#define WR(p, size) \
#define WR(p, size) do { \
if (write(STDOUT_FILENO, p, size) != size) \
oerr();
oerr(); \
} while(0)
#define TAILMAPLEN (4<<20)
struct mapinfo {
off_t mapoff;
off_t maxoff;
size_t maplen;
char *start;
int fd;
};
enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE };
@ -47,6 +60,8 @@ int lines __P((FILE *, off_t));
void ierr __P((void));
void oerr __P((void));
int mapprint __P((struct mapinfo *, off_t, off_t));
int maparound __P((struct mapinfo *, off_t));
extern int Fflag, fflag, rflag, rval;
extern char *fname;

View File

@ -267,55 +267,46 @@ rlines(fp, off, sbp)
long off;
struct stat *sbp;
{
off_t size;
char *p;
char *start;
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;
/*
* size not passed directly to mmap() below because unclear error
* diagnostic "Invalid argument".
* Last char is special, ignore whether newline or not. Note that
* size == 0 is dealt with above, and size == 1 sets curoff to -1.
*/
if (size > SIZE_T_MAX || size < 0) {
errno = EFBIG;
ierr();
exit(1);
}
/*
* XXX: FIXME - mmap() not support files over 2GB
* Large file processing require alternative implementation,
* for now print nice error diagnostic at least.
*/
if (size > SSIZE_MAX) {
errno = EFBIG;
ierr();
exit(1);
}
if ((start = mmap(NULL, (size_t)size,
PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
ierr();
exit(1);
}
/* Last char is special, ignore whether newline or not. */
for (p = start + size - 1; --size;)
if (*--p == '\n' && !--off) {
++p;
break;
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. */
size = sbp->st_size - size;
WR(p, size);
if (fseek(fp, 0L, SEEK_END) == -1) {
if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
ierr();
return;
}
if (munmap(start, (size_t)sbp->st_size)) {
if (map.start != NULL && munmap(map.start, map.maplen)) {
ierr();
return;
}

View File

@ -44,6 +44,7 @@ static const char rcsid[] =
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
@ -64,3 +65,57 @@ 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(mip, startoff, len)
struct mapinfo *mip;
off_t startoff, len;
{
int n;
while (len > 0) {
if (startoff < mip->mapoff || startoff >= mip->mapoff +
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(mip, offset)
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 (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);
}

View File

@ -114,43 +114,63 @@ r_reg(fp, style, off, sbp)
long off;
struct stat *sbp;
{
off_t size;
int llen;
char *p;
char *start;
struct mapinfo map;
off_t curoff, size, lineend;
int i;
if (!(size = sbp->st_size))
return;
if (size > SIZE_T_MAX) {
errno = EFBIG;
ierr();
return;
}
map.start = NULL;
map.mapoff = map.maxoff = size;
map.fd = fileno(fp);
if ((start = mmap(NULL, (size_t)size,
PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
ierr();
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;
/*
* 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 + map.maplen) {
if (maparound(&map, curoff) != 0) {
ierr();
return;
}
}
if (llen)
WR(p, llen);
if (munmap(start, (size_t)sbp->st_size))
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();
}