123469Slepreau #ifndef lint
2*43678Strent static char sccsid[] = "@(#)tac.c 1.5 06/24/90";
323469Slepreau #endif
423469Slepreau
523469Slepreau /*
623469Slepreau * tac.c - Print file segments in reverse order
723469Slepreau *
823469Slepreau * Original line-only version by unknown author off the net.
929382Slepreau * Rewritten in 1985 by Jay Lepreau, Univ of Utah, to allocate memory
1029382Slepreau * dynamically, handle string bounded segments (suggested by Rob Pike),
1129382Slepreau * and handle pipes.
1223469Slepreau */
1323469Slepreau
1423469Slepreau #include <sys/types.h>
1523469Slepreau #include <sys/stat.h>
1623469Slepreau #include <stdio.h>
1729382Slepreau #include <signal.h>
1823469Slepreau
1923469Slepreau /*
2023469Slepreau * This should be defined for BSD releases later than 4.2 and for Sys V.2,
2123469Slepreau * at least. fwrite is faster than putc only if you have a new speedy fwrite.
2223469Slepreau */
2323469Slepreau #define FAST_FWRITE
2423469Slepreau
2523469Slepreau #ifdef DEBUG /* dbx can't handle registers */
2623469Slepreau #include <ctype.h>
2723469Slepreau # define register
2823469Slepreau #endif
2923469Slepreau
3023469Slepreau /* Default target string and bound */
3123469Slepreau int right = 1; /* right or left bounded segments? */
3223469Slepreau char *targ = "\n";
3323469Slepreau
3429382Slepreau char *tfile;
3529382Slepreau char *buf;
3629382Slepreau
3723469Slepreau int readsize = 4096; /* significantly faster than 1024 */
3823469Slepreau int bufsize;
3923469Slepreau int targlen;
4023469Slepreau int numerr;
4123469Slepreau
4229382Slepreau int cleanup();
4323469Slepreau extern off_t lseek();
4429383Slepreau extern char *strcpy(), *malloc(), *realloc(), *mktemp();
4523469Slepreau
main(argc,argv)4623469Slepreau main(argc, argv)
4723469Slepreau int argc;
4823469Slepreau char **argv;
4923469Slepreau {
5023469Slepreau
5123469Slepreau #ifdef DEBUG
5229382Slepreau if (argc > 1 && isdigit(*argv[1])) {
5323469Slepreau readsize = atoi(argv[1]);
5423469Slepreau argc--, argv++;
5523469Slepreau }
5623469Slepreau #endif
5723469Slepreau
5829382Slepreau if (argc > 1 && (argv[1][0] == '+' || argv[1][0] == '-') && argv[1][1]) {
5923469Slepreau targ = &argv[1][1];
6023469Slepreau right = (argv[1][0] == '+');
6123469Slepreau argc--; argv++;
6223469Slepreau }
6323469Slepreau targlen = strlen(targ);
6423469Slepreau
6523469Slepreau bufsize = (readsize << 1) + targlen + 2;
6623469Slepreau if ((buf = malloc((unsigned) bufsize)) == NULL) {
6723469Slepreau perror("tac: initial malloc");
6823469Slepreau exit(1);
6923469Slepreau }
7023469Slepreau
7123469Slepreau (void) strcpy(buf, targ); /* stop string at beginning */
7223469Slepreau buf += targlen;
7323469Slepreau
7429382Slepreau if (argc == 1)
7529382Slepreau tacstdin();
7623469Slepreau while (--argc) {
7729382Slepreau if (strcmp(*++argv, "-") == 0)
7829382Slepreau tacstdin();
7929382Slepreau else
8029382Slepreau tacit(*argv);
8129382Slepreau }
8229382Slepreau exit(numerr > 0 ? 1 : 0);
8329382Slepreau }
8429382Slepreau
tacstdin()8529382Slepreau tacstdin()
8629382Slepreau {
8729382Slepreau
88*43678Strent void (*sigint)(), (*sighup)(), (*sigterm)();
8929382Slepreau
9029382Slepreau if ((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
9129383Slepreau (void) signal(SIGINT, cleanup);
9229382Slepreau if ((sighup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
9329383Slepreau (void) signal(SIGHUP, cleanup);
9429382Slepreau if ((sigterm = signal(SIGTERM, SIG_IGN)) != SIG_IGN)
9529383Slepreau (void) signal(SIGTERM, cleanup);
9629382Slepreau
9729382Slepreau savestdin();
9829382Slepreau tacit(tfile);
9929383Slepreau (void) unlink(tfile);
10029382Slepreau
10129383Slepreau (void) signal(SIGINT, sigint);
10229383Slepreau (void) signal(SIGHUP, sighup);
10329383Slepreau (void) signal(SIGTERM, sigterm);
10429382Slepreau }
10529382Slepreau
10629382Slepreau char template[] = "/tmp/tacXXXXXX";
10729382Slepreau char workplate[sizeof template];
10829382Slepreau
savestdin()10929382Slepreau savestdin()
11029382Slepreau {
11129382Slepreau int fd;
11229382Slepreau register int n;
11329382Slepreau
11429383Slepreau (void) strcpy(workplate, template);
11529382Slepreau tfile = mktemp(workplate);
11629382Slepreau if ((fd = creat(tfile, 0600)) < 0) {
11729382Slepreau prterr(tfile);
11829382Slepreau cleanup();
11929382Slepreau }
12029382Slepreau while ((n = read(0, buf, readsize)) > 0)
12129382Slepreau if (write(fd, buf, n) != n) {
12229382Slepreau prterr(tfile);
12329382Slepreau cleanup();
12423469Slepreau }
12529383Slepreau (void) close(fd);
12629382Slepreau if (n < 0) {
12729382Slepreau prterr("stdin read");
12829382Slepreau cleanup();
12929382Slepreau }
13029382Slepreau }
13123469Slepreau
tacit(name)13229382Slepreau tacit(name)
13329382Slepreau char *name;
13429382Slepreau {
13529382Slepreau register char *p, *pastend;
13629382Slepreau register int firstchar, targm1len; /* target length minus 1 */
13729382Slepreau struct stat st;
13829382Slepreau off_t off;
13929382Slepreau int fd, i;
14023469Slepreau
14129382Slepreau firstchar = *targ;
14229382Slepreau targm1len = targlen - 1;
14329382Slepreau
14429382Slepreau if (stat(name, &st) < 0) {
14529382Slepreau prterr(name);
14629382Slepreau numerr++;
14729382Slepreau return;
14829382Slepreau }
14929382Slepreau if ((off = st.st_size) == 0)
15029382Slepreau return;
15129382Slepreau if ((fd = open(name, 0)) < 0) {
15229382Slepreau prterr(name);
15329382Slepreau numerr++;
15429382Slepreau return;
15529382Slepreau }
15629382Slepreau
15729382Slepreau /*
15829382Slepreau * Arrange for the first read to lop off enough to
15929382Slepreau * leave the rest of the file a multiple of readsize.
16029382Slepreau * Since readsize can change, this may not always hold during
16129382Slepreau * the pgm run, but since it usually will, leave it here
16229382Slepreau * for i/o efficiency (page/sector boundaries and all that).
16329382Slepreau * Note: the efficiency gain has not been verified.
16429382Slepreau */
16529382Slepreau if ((i = off % readsize) == 0)
16629382Slepreau i = readsize;
16729382Slepreau off -= i;
16829382Slepreau
16929382Slepreau (void) lseek(fd, off, 0);
17029382Slepreau if (read(fd, buf, i) != i) {
17129382Slepreau prterr(name);
17229382Slepreau (void) close(fd);
17329382Slepreau numerr++;
17429382Slepreau return;
17529382Slepreau }
17629382Slepreau p = pastend = buf + i; /* pastend always points to end+1 */
17729382Slepreau p -= targm1len;
17829382Slepreau
17929382Slepreau for (;;) {
18029382Slepreau while ( *--p != firstchar ||
18129382Slepreau (targm1len && strncmp(p+1, targ+1, targm1len)) )
18223469Slepreau continue;
18329382Slepreau if (p < buf) { /* backed off front of buffer */
18429382Slepreau if (off == 0) {
18529382Slepreau /* beginning of file: dump last segment */
18629382Slepreau output(p + targlen, pastend);
18729382Slepreau (void) close(fd);
18829382Slepreau break;
18929382Slepreau }
19029382Slepreau if ((i = pastend - buf) > readsize) {
19129382Slepreau char *tbuf;
19229382Slepreau int newbufsize = (readsize << 2) + targlen + 2;
19329382Slepreau
19429382Slepreau if ((tbuf = realloc(buf-targlen, (unsigned) newbufsize)) == NULL) {
19529382Slepreau /* If realloc fails, old buf contents may be lost. */
19629382Slepreau perror("tac: segment too long; may have garbage here");
19729382Slepreau numerr++;
19829382Slepreau i = readsize;
19923469Slepreau }
20029382Slepreau else {
20129382Slepreau tbuf += targlen; /* skip over the stop string */
20229382Slepreau p += tbuf - buf;
20329382Slepreau pastend += tbuf - buf;
20429382Slepreau buf = tbuf;
20529382Slepreau bufsize = newbufsize;
20629382Slepreau readsize = readsize << 1;
20729382Slepreau /* guaranteed to fit now (I think!) */
20823469Slepreau }
20923469Slepreau }
21029382Slepreau if (off - readsize < 0) {
21129382Slepreau readsize = off;
21229382Slepreau off = 0;
21329382Slepreau }
21429382Slepreau else
21529382Slepreau off -= readsize;
21629382Slepreau (void) lseek(fd, off, 0); /* back up */
21729382Slepreau /* Shift pending old data right to make room for new */
21829382Slepreau bcopy(buf, p = buf + readsize, i);
21929382Slepreau pastend = p + i;
22029382Slepreau if (read(fd, buf, readsize) != readsize) {
22129382Slepreau prterr(name);
22229382Slepreau numerr++;
22329382Slepreau (void) close(fd);
22429382Slepreau break;
22529382Slepreau }
22629382Slepreau continue;
22723469Slepreau }
22829382Slepreau /* Found a real instance of the target string */
22929382Slepreau output(right ? p + targlen : p, pastend);
23029382Slepreau pastend = p;
23129382Slepreau p -= targm1len;
23223469Slepreau }
23323469Slepreau }
23423469Slepreau
23523469Slepreau /*
23623469Slepreau * Dump chars from p to pastend-1. If right-bounded by target
23723469Slepreau * and not the first time through, append the target string.
23823469Slepreau */
output(p,pastend)23923469Slepreau output(p, pastend)
24023469Slepreau register char *p;
24123469Slepreau char *pastend;
24223469Slepreau {
24323469Slepreau static short first = 1;
24423469Slepreau
24523469Slepreau #ifdef FAST_FWRITE
24623469Slepreau (void) fwrite(p, 1, pastend - p, stdout);
24723469Slepreau #else
24823469Slepreau while (p < pastend)
24923469Slepreau (void) putc(*p++, stdout);
25023469Slepreau #endif
25123469Slepreau if (right && !first)
25223469Slepreau (void) fwrite(targ, 1, targlen, stdout);
25323469Slepreau first = 0;
25423469Slepreau if ferror(stdout) {
25523469Slepreau perror("tac: fwrite/putc");
25623469Slepreau exit(++numerr > 1 ? numerr : 1);
25723469Slepreau }
25823469Slepreau }
25923469Slepreau
prterr(s)26023469Slepreau prterr(s)
26123469Slepreau char *s;
26223469Slepreau {
26323469Slepreau
26423469Slepreau fprintf(stderr, "tac: ");
26523469Slepreau perror(s);
26623469Slepreau }
26729382Slepreau
cleanup()26829382Slepreau cleanup()
26929382Slepreau {
27029382Slepreau
27129383Slepreau (void) unlink(tfile);
27229382Slepreau exit(1);
27329382Slepreau }
274