tac.c a program to print lines in reverse order in linear time
Tony L. Hansen
hansen at pegasus.UUCP
Mon Sep 24 13:59:21 AEST 1984
#!/bin/sh
# This is a shar archive.
# The rest of this file is a shell script which will extract:
# README2 README tac.1 tac.c
# Archive created: Sun Sep 23 23:55:39 EDT 1984
echo x - README2
sed 's/^X//' > README2 << '~FUNKY STUFF~'
< Tac does not read stdin. Why? Think about it.
Why not? If it's coming from a file, use it directly. If it's coming from a
pipe, just copy it into a temp file before using it. That's simple enough,
don't you think?
The other thing missing was a manual page. One follows, taken from the
cat(1) manual page.
Thanks for reposting the program Andy! I missed it the last time around.
Things are always better the second time around.
Tony Hansen
pegasus!hansen
~FUNKY STUFF~
ls -l README2
echo x - README
sed 's/^X//' > README << '~FUNKY STUFF~'
>From hogpc!houti!ariel!vax135!cornell!uw-beaver!tektronix!hplabs!sdcrdcf!sdcsvax!dcdwest!ittvax!decvax!wivax!masscomp!trb Fri Sep 21 12:12:26 1984
Newsgroups: net.sources
Subject: tac.c a program to print lines in reverse order in linear time
Message-ID: <419 at masscomp.UUCP>
Organization: MASSCOMP, Westford, MA
Lines: 126
This program was first posted to net.sources in Oct 1982. What comes
around goes around, I always say, so here it is, tac.c.
Tac does not read stdin. Why? Think about it.
I think research!rob modified this to do squashing of multiple blank
lines and printing of control characters as ^X, but he wouldn't let me
give it out.
Andy Tannenbaum Masscomp Inc Westford MA (617) 692-6200 x274
<<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
~FUNKY STUFF~
ls -l README
echo x - tac.1
sed 's/^X//' > tac.1 << '~FUNKY STUFF~'
X.TH TAC 1
X.SH NAME
tac \- concatenate and print files in reverse
X.SH SYNOPSIS
X.B tac
file .\|.\|.
X.SH DESCRIPTION
X.I Tac\^
reads each
X.I file\^
in sequence
and writes it on the standard output with the lines in reverse order.
Thus:
X.PP
X.RS
tac file
X.RE
X.PP
prints the file in reverse, and:
X.PP
X.RS
tac file1 file2 >file3
X.RE
X.PP
concatenates the first two files, reversed, and places the result on the
third.
X.PP
X.RS
tac file | tac
X.RE
will reproduce the contents of the file.
X.PP
If no input file is given,
or if the argument
X.B \-
is encountered,
X.I tac\^
reads from the standard input.
X.SH WARNING
Command formats such as
X.RS
tac file1 file2 >file1
X.RE
will cause
the original data in \fIfile1\fP to be lost,
therefore, take care when using shell special characters.
X.SH SEE ALSO
cat(1).
X.\" @(#)tac.1 1.1 @(#)
~FUNKY STUFF~
ls -l tac.1
echo x - tac.c
sed 's/^X//' > tac.c << '~FUNKY STUFF~'
#ifndef lint
char SCCS[] = "@(#)tac.c 1.5 @(#)";
#endif lint
# include <stdio.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <errno.h>
/* tac.c (backwards cat) */
static char *progname;
static int errorcount = 0;
static char readerr[] = "%s: Read error at offset %lu of %s.\n";
extern off_t lseek();
main(argc, argv)
int argc;
char **argv;
{
char *filename;
struct stat st;
int fd;
int rmstdin = 0;
void output(), utcopyn(), reverse();
char *copystdin();
progname = argv[0];
/* no arguments means to read stdin */
if (argc == 1) {
argv[1] = "-";
argc = 2;
}
while (--argc > 0) {
filename = *++argv;
/* "-" means to read stdin */
if (filename[0] == '-' && !filename[1])
/* if it's a pipe, then copy to a temp file */
if ((lseek(fileno(stdin), 0, 0) == -1) &&
(errno == ESPIPE)) {
if ((filename = copystdin()) == 0)
continue;
rmstdin = 1;
} else { /* stdin's a file, so use it directly */
if (fstat(fileno(stdin), &st) == -1) {
(void) fprintf(stderr,
"%s: Bad status for stdin.\n",
progname);
errorcount++;
continue;
}
/* empty file? why bother. */
if (st.st_size == 0)
continue;
reverse(fileno(stdin), st.st_size, "stdin");
continue;
}
/* no file? */
if (stat(filename, &st) == -1) {
(void) fprintf(stderr , "%s: Bad status for %s\n",
progname, filename);
errorcount++;
continue;
}
/* empty file? why bother. */
if (st.st_size == 0)
continue;
/* open it up */
if ((fd = open(filename, 0)) == -1) {
(void) fprintf(stderr, "%s: Can't open %s\n",
progname, filename);
errorcount++;
continue;
}
/* reverse it */
reverse(fd, st.st_size, filename);
/* if it's a temp file from a pipe, remove it */
if (rmstdin) {
(void) unlink(filename);
rmstdin = 0;
}
}
exit(errorcount);
}
void
reverse (fd, off, filename)
register int fd;
register off_t off;
char *filename;
{
register int i;
register char *p, *q;
char buffer[ 1 + BUFSIZ + BUFSIZ + 1 ];
char *buf;
/* initialize buffer */
buf = buffer;
*buf++ = '\n';
if ((i = off % BUFSIZ ) == 0)
i = BUFSIZ;
off -= i;
(void) lseek(fd, off, 0);
if (read(fd, buf, i) != i) {
(void) fprintf(stderr, readerr, progname, off,
filename);
(void) close(fd);
errorcount++;
return;
}
p = q = buf + i;
if (*--p == '\n')
q = p;
for (;;) {
while (*--p != '\n')
;
if (p < buf) {
if (off == 0) {
output(p, q);
(void) close(fd);
return;
}
(void) lseek(fd, off -= BUFSIZ, 0);
if ((i = q - buf) > BUFSIZ) {
(void) fprintf(stderr,
"%s: Line too long in %s.\n",
progname, filename);
errorcount++;
i = BUFSIZ;
}
utcopyn(p = buf + BUFSIZ, buf, i);
q = p + i;
if (read(fd, buf, BUFSIZ) != BUFSIZ) {
(void) fprintf(stderr, readerr,
progname, off, filename);
errorcount++;
(void) close(fd);
return;
}
continue;
}
output(p, q);
q = p;
}
}
static void
output(p, q)
register char *p,*q;
{
register FILE *iop;
for (iop = stdout, p++; p < q; (void) putc(*p++, iop))
;
(void) putc('\n', iop);
}
static void
utcopyn(tp, fp, n)
register char *tp, *fp;
register int n;
{
while (--n >= 0)
*tp++ = *fp++;
return;
}
static char *template = "/tmp/tac.XXXXXX"; /* template for mktemp */
static char *
copystdin()
{
char buf[BUFSIZ];
int ofile, readcount;
char *mktemp();
(void) umask(066);
(void) mktemp(template);
ofile = creat(template, 0600);
if (ofile < 0)
{
(void) fprintf(stderr,
"%s: can't create temp file '%s' for use with stdin!\n",
progname, template);
return 0;
}
/* copy stdin to temp file */
while ((readcount = read (fileno (stdin), buf, sizeof buf)) > 0)
(void) write (ofile, buf, (unsigned) readcount);
(void) close(ofile);
(void) fclose(stdin);
return template;
}
/* end of tac.c */
~FUNKY STUFF~
ls -l tac.c
# The following exit is to ensure that extra garbage
# after the end of the shar file will be ignored.
exit 0
More information about the Comp.sources.unix
mailing list