293 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2014
 | 
						|
 *	The President and Fellows of Harvard College.
 | 
						|
 *
 | 
						|
 * 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 UNIVERSITY 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 UNIVERSITY 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.
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * tac - print file backwards line by line (reverse cat)
 | 
						|
 * usage: tac [files]
 | 
						|
 *
 | 
						|
 * This implementation copies the input to a scratch file, using a
 | 
						|
 * second scratch file to keep notes, and then prints the scratch file
 | 
						|
 * backwards. This is inefficient, but has the side effect of testing
 | 
						|
 * the behavior of scratch files that have been unlinked.
 | 
						|
 *
 | 
						|
 * Note that if the remove system call isn't implemented, unlinking
 | 
						|
 * the scratch files will fail and the scratch files will get left
 | 
						|
 * behind. To avoid unnecessary noise (e.g. on emufs) we won't
 | 
						|
 * complain about this.
 | 
						|
 *
 | 
						|
 * This program uses these system calls:
 | 
						|
 *    getpid open read write lseek close remove _exit
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <assert.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <err.h>
 | 
						|
 | 
						|
struct indexentry {
 | 
						|
	off_t pos;
 | 
						|
	off_t len;
 | 
						|
};
 | 
						|
 | 
						|
static int datafd = -1, indexfd = -1;
 | 
						|
static char dataname[64], indexname[64];
 | 
						|
 | 
						|
static char buf[4096];
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
// string ops
 | 
						|
 | 
						|
/* this is standard and should go into libc */
 | 
						|
static
 | 
						|
void *
 | 
						|
memchr(const void *buf, int ch, size_t buflen)
 | 
						|
{
 | 
						|
	const unsigned char *ubuf = buf;
 | 
						|
	size_t i;
 | 
						|
 | 
						|
	for (i=0; i<buflen; i++) {
 | 
						|
		if (ubuf[i] == ch) {
 | 
						|
			/* this must launder const */
 | 
						|
			return (void *)(ubuf + i);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
// syscall wrappers
 | 
						|
 | 
						|
static
 | 
						|
size_t
 | 
						|
doread(int fd, const char *name, void *buf, size_t len)
 | 
						|
{
 | 
						|
	ssize_t r;
 | 
						|
 | 
						|
	r = read(fd, buf, len);
 | 
						|
	if (r == -1) {
 | 
						|
		err(1, "%s: read", name);
 | 
						|
	}
 | 
						|
	return (size_t)r;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
dowrite(int fd, const char *name, const void *buf, size_t len)
 | 
						|
{
 | 
						|
	ssize_t r;
 | 
						|
 | 
						|
	r = write(fd, buf, len);
 | 
						|
	if (r == -1) {
 | 
						|
		err(1, "%s: write", name);
 | 
						|
	}
 | 
						|
	else if ((size_t)r != len) {
 | 
						|
		errx(1, "%s: write: Unexpected short count %zd of %zu",
 | 
						|
		     r, len);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
off_t
 | 
						|
dolseek(int fd, const char *name, off_t pos, int whence)
 | 
						|
{
 | 
						|
	off_t ret;
 | 
						|
 | 
						|
	ret = lseek(fd, pos, whence);
 | 
						|
	if (ret == -1) {
 | 
						|
		err(1, "%s: lseek", name);
 | 
						|
	}
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
// file I/O
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
readfile(const char *name)
 | 
						|
{
 | 
						|
	int fd, closefd;
 | 
						|
	struct indexentry x;
 | 
						|
	size_t len, remaining, here;
 | 
						|
	const char *s, *t;
 | 
						|
	
 | 
						|
	if (name == NULL || !strcmp(name, "-")) {
 | 
						|
		fd = STDIN_FILENO;
 | 
						|
		closefd = -1;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		fd = open(name, O_RDONLY);
 | 
						|
		if (fd < 0) {
 | 
						|
			err(1, "%s", name);
 | 
						|
		}
 | 
						|
		closefd = fd;
 | 
						|
	}
 | 
						|
 | 
						|
	x.pos = 0;
 | 
						|
	x.len = 0;
 | 
						|
	while (1) {
 | 
						|
		len = doread(fd, name, buf, sizeof(buf));
 | 
						|
		if (len == 0) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		remaining = len;
 | 
						|
		for (s = buf; s != NULL; s = t) {
 | 
						|
			t = memchr(s, '\n', remaining);
 | 
						|
			if (t != NULL) {
 | 
						|
				t++;
 | 
						|
				here = (t - s);
 | 
						|
				x.len += here;
 | 
						|
				remaining -= here;
 | 
						|
				dowrite(indexfd, indexname, &x, sizeof(x));
 | 
						|
				x.pos += x.len;
 | 
						|
				x.len = 0;
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				x.len += remaining;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		dowrite(datafd, dataname, buf, len);
 | 
						|
	}
 | 
						|
	if (x.len > 0) {
 | 
						|
		dowrite(indexfd, indexname, &x, sizeof(x));
 | 
						|
	}
 | 
						|
 | 
						|
	if (closefd != -1) {
 | 
						|
		close(closefd);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
dumpdata(void)
 | 
						|
{
 | 
						|
	struct indexentry x;
 | 
						|
	off_t indexsize, pos, done;
 | 
						|
	size_t amount, len;
 | 
						|
 | 
						|
	indexsize = dolseek(indexfd, indexname, 0, SEEK_CUR);
 | 
						|
	pos = indexsize;
 | 
						|
	assert(pos % sizeof(x) == 0);
 | 
						|
	while (pos > 0) {
 | 
						|
		pos -= sizeof(x);
 | 
						|
		assert(pos >= 0);
 | 
						|
		dolseek(indexfd, indexname, pos, SEEK_SET);
 | 
						|
 | 
						|
		len = doread(indexfd, indexname, &x, sizeof(x));
 | 
						|
		if (len != sizeof(x)) {
 | 
						|
			errx(1, "%s: read: Unexpected EOF", indexname);
 | 
						|
		}
 | 
						|
		dolseek(datafd, dataname, x.pos, SEEK_SET);
 | 
						|
 | 
						|
		for (done = 0; done < x.len; done += amount) {
 | 
						|
			amount = sizeof(buf);
 | 
						|
			if ((off_t)amount > x.len - done) {
 | 
						|
				amount = x.len - done;
 | 
						|
			}
 | 
						|
			len = doread(datafd, dataname, buf, amount);
 | 
						|
			if (len != amount) {
 | 
						|
				errx(1, "%s: read: Unexpected short count"
 | 
						|
				     " %zu of %zu", dataname, len, amount);
 | 
						|
			}
 | 
						|
			dowrite(STDOUT_FILENO, "stdout", buf, len);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////
 | 
						|
// main
 | 
						|
 | 
						|
static
 | 
						|
int
 | 
						|
openscratch(const char *name, int flags, mode_t mode)
 | 
						|
{
 | 
						|
	int fd;
 | 
						|
 | 
						|
	fd = open(name, flags, mode);
 | 
						|
	if (fd < 0) {
 | 
						|
		err(1, "%s", name);
 | 
						|
	}
 | 
						|
	if (remove(name) < 0) {
 | 
						|
		if (errno != ENOSYS) {
 | 
						|
			err(1, "%s: remove", name);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return fd;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
openfiles(void)
 | 
						|
{
 | 
						|
	pid_t pid;
 | 
						|
 | 
						|
	pid = getpid();
 | 
						|
 | 
						|
	snprintf(dataname, sizeof(dataname), ".tmp.tacdata.%d", (int)pid);
 | 
						|
	datafd = openscratch(dataname, O_RDWR|O_CREAT|O_TRUNC, 0664);
 | 
						|
 | 
						|
	snprintf(indexname, sizeof(indexname), ".tmp.tacindex.%d", (int)pid);
 | 
						|
	indexfd = openscratch(indexname, O_RDWR|O_CREAT|O_TRUNC, 0664);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
closefiles(void)
 | 
						|
{
 | 
						|
	close(datafd);
 | 
						|
	close(indexfd);
 | 
						|
	indexfd = datafd = -1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
main(int argc, char *argv[])
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	openfiles();
 | 
						|
 | 
						|
	if (argc > 1) {
 | 
						|
		for (i=1; i<argc; i++) {
 | 
						|
			readfile(argv[i]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		readfile(NULL);
 | 
						|
	}
 | 
						|
 | 
						|
	dumpdata();
 | 
						|
 | 
						|
	closefiles();
 | 
						|
	return 0;
 | 
						|
}
 |