trb@masscomp.UUCP (09/21/84)
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 <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>> # include <stdio.h> # include <sys/types.h> # include <sys/stat.h> /* tac.c (backwards cat) */ main(argc, argv) int argc; char **argv; { register char *p, *q; register int i; struct stat st; off_t off, lseek(); char buffer[ 1 + BUFSIZ + BUFSIZ + 1 ]; char *buf; char *readerr = "Read error at offset %lu of %s\n"; int fd, any; void output(), utcopyn(); if (argc < 2) { (void) fprintf(stderr, "Usage: tac file ...\n"); exit(1); } buf = buffer; *buf++ = '\n'; any = 0; while (--argc) { if (stat(p = *++argv, &st) == -1) { (void) fprintf(stderr , "Bad status for %s\n", p); any++; continue; } if ((off = st.st_size) == 0) continue; if ((fd = open(p, 0)) == -1) { (void) fprintf(stderr, "Can't open %s\n", p); any++; continue; } if ((i = off % BUFSIZ ) == 0) i = BUFSIZ; off -= i; (void) lseek(fd, off, 0); if (read(fd, buf, i) != i) { (void) fprintf(stderr, readerr, off, p); (void) close(fd); any++; continue; } 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); break; } (void) lseek(fd, off -= BUFSIZ, 0); if ((i = q - buf) > BUFSIZ) { (void) fprintf(stderr, "Line too long\n"); any++; i = BUFSIZ; } utcopyn(p = buf + BUFSIZ, buf, i); q = p + i; if (read(fd, buf, BUFSIZ) != BUFSIZ) { (void) fprintf(stderr, readerr, off, *argv); any++; (void) close(fd); break; } continue; } output(p, q); q = p; } } exit(any); } 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; } /* end of tac.c */
hansen@pegasus.UUCP (Tony L. Hansen) (09/24/84)
#!/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@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