123469Slepreau #ifndef lint 2*29382Slepreau static char sccsid[] = "@(#)tac.c 1.3 06/05/86"; 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. 9*29382Slepreau * Rewritten in 1985 by Jay Lepreau, Univ of Utah, to allocate memory 10*29382Slepreau * dynamically, handle string bounded segments (suggested by Rob Pike), 11*29382Slepreau * and handle pipes. 1223469Slepreau */ 1323469Slepreau 1423469Slepreau #include <sys/types.h> 1523469Slepreau #include <sys/stat.h> 1623469Slepreau #include <stdio.h> 17*29382Slepreau #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 34*29382Slepreau char *tfile; 35*29382Slepreau char *buf; 36*29382Slepreau 3723469Slepreau int readsize = 4096; /* significantly faster than 1024 */ 3823469Slepreau int bufsize; 3923469Slepreau int targlen; 4023469Slepreau int numerr; 4123469Slepreau 42*29382Slepreau int cleanup(); 4323469Slepreau extern off_t lseek(); 44*29382Slepreau extern char *malloc(), *realloc(), *mktemp(); 4523469Slepreau 4623469Slepreau main(argc, argv) 4723469Slepreau int argc; 4823469Slepreau char **argv; 4923469Slepreau { 5023469Slepreau 5123469Slepreau #ifdef DEBUG 52*29382Slepreau if (argc > 1 && isdigit(*argv[1])) { 5323469Slepreau readsize = atoi(argv[1]); 5423469Slepreau argc--, argv++; 5523469Slepreau } 5623469Slepreau #endif 5723469Slepreau 58*29382Slepreau 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 74*29382Slepreau if (argc == 1) 75*29382Slepreau tacstdin(); 7623469Slepreau while (--argc) { 77*29382Slepreau if (strcmp(*++argv, "-") == 0) 78*29382Slepreau tacstdin(); 79*29382Slepreau else 80*29382Slepreau tacit(*argv); 81*29382Slepreau } 82*29382Slepreau exit(numerr > 0 ? 1 : 0); 83*29382Slepreau } 84*29382Slepreau 85*29382Slepreau tacstdin() 86*29382Slepreau { 87*29382Slepreau 88*29382Slepreau int (*sigint)(), (*sighup)(), (*sigterm)(); 89*29382Slepreau 90*29382Slepreau if ((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN) 91*29382Slepreau signal(SIGINT, cleanup); 92*29382Slepreau if ((sighup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) 93*29382Slepreau signal(SIGHUP, cleanup); 94*29382Slepreau if ((sigterm = signal(SIGTERM, SIG_IGN)) != SIG_IGN) 95*29382Slepreau signal(SIGTERM, cleanup); 96*29382Slepreau 97*29382Slepreau savestdin(); 98*29382Slepreau tacit(tfile); 99*29382Slepreau unlink(tfile); 100*29382Slepreau 101*29382Slepreau signal(SIGINT, sigint); 102*29382Slepreau signal(SIGHUP, sighup); 103*29382Slepreau signal(SIGTERM, sigterm); 104*29382Slepreau } 105*29382Slepreau 106*29382Slepreau char template[] = "/tmp/tacXXXXXX"; 107*29382Slepreau char workplate[sizeof template]; 108*29382Slepreau 109*29382Slepreau savestdin() 110*29382Slepreau { 111*29382Slepreau int fd; 112*29382Slepreau register int n; 113*29382Slepreau 114*29382Slepreau strcpy(workplate, template); 115*29382Slepreau tfile = mktemp(workplate); 116*29382Slepreau if ((fd = creat(tfile, 0600)) < 0) { 117*29382Slepreau prterr(tfile); 118*29382Slepreau cleanup(); 119*29382Slepreau } 120*29382Slepreau while ((n = read(0, buf, readsize)) > 0) 121*29382Slepreau if (write(fd, buf, n) != n) { 122*29382Slepreau prterr(tfile); 123*29382Slepreau cleanup(); 12423469Slepreau } 125*29382Slepreau close(fd); 126*29382Slepreau if (n < 0) { 127*29382Slepreau prterr("stdin read"); 128*29382Slepreau cleanup(); 129*29382Slepreau } 130*29382Slepreau } 13123469Slepreau 132*29382Slepreau tacit(name) 133*29382Slepreau char *name; 134*29382Slepreau { 135*29382Slepreau register char *p, *pastend; 136*29382Slepreau register int firstchar, targm1len; /* target length minus 1 */ 137*29382Slepreau struct stat st; 138*29382Slepreau off_t off; 139*29382Slepreau int fd, i; 14023469Slepreau 141*29382Slepreau firstchar = *targ; 142*29382Slepreau targm1len = targlen - 1; 143*29382Slepreau 144*29382Slepreau if (stat(name, &st) < 0) { 145*29382Slepreau prterr(name); 146*29382Slepreau numerr++; 147*29382Slepreau return; 148*29382Slepreau } 149*29382Slepreau if ((off = st.st_size) == 0) 150*29382Slepreau return; 151*29382Slepreau if ((fd = open(name, 0)) < 0) { 152*29382Slepreau prterr(name); 153*29382Slepreau numerr++; 154*29382Slepreau return; 155*29382Slepreau } 156*29382Slepreau 157*29382Slepreau /* 158*29382Slepreau * Arrange for the first read to lop off enough to 159*29382Slepreau * leave the rest of the file a multiple of readsize. 160*29382Slepreau * Since readsize can change, this may not always hold during 161*29382Slepreau * the pgm run, but since it usually will, leave it here 162*29382Slepreau * for i/o efficiency (page/sector boundaries and all that). 163*29382Slepreau * Note: the efficiency gain has not been verified. 164*29382Slepreau */ 165*29382Slepreau if ((i = off % readsize) == 0) 166*29382Slepreau i = readsize; 167*29382Slepreau off -= i; 168*29382Slepreau 169*29382Slepreau (void) lseek(fd, off, 0); 170*29382Slepreau if (read(fd, buf, i) != i) { 171*29382Slepreau prterr(name); 172*29382Slepreau (void) close(fd); 173*29382Slepreau numerr++; 174*29382Slepreau return; 175*29382Slepreau } 176*29382Slepreau p = pastend = buf + i; /* pastend always points to end+1 */ 177*29382Slepreau p -= targm1len; 178*29382Slepreau 179*29382Slepreau for (;;) { 180*29382Slepreau while ( *--p != firstchar || 181*29382Slepreau (targm1len && strncmp(p+1, targ+1, targm1len)) ) 18223469Slepreau continue; 183*29382Slepreau if (p < buf) { /* backed off front of buffer */ 184*29382Slepreau if (off == 0) { 185*29382Slepreau /* beginning of file: dump last segment */ 186*29382Slepreau output(p + targlen, pastend); 187*29382Slepreau (void) close(fd); 188*29382Slepreau break; 189*29382Slepreau } 190*29382Slepreau if ((i = pastend - buf) > readsize) { 191*29382Slepreau char *tbuf; 192*29382Slepreau int newbufsize = (readsize << 2) + targlen + 2; 193*29382Slepreau 194*29382Slepreau if ((tbuf = realloc(buf-targlen, (unsigned) newbufsize)) == NULL) { 195*29382Slepreau /* If realloc fails, old buf contents may be lost. */ 196*29382Slepreau perror("tac: segment too long; may have garbage here"); 197*29382Slepreau numerr++; 198*29382Slepreau i = readsize; 19923469Slepreau } 200*29382Slepreau else { 201*29382Slepreau tbuf += targlen; /* skip over the stop string */ 202*29382Slepreau p += tbuf - buf; 203*29382Slepreau pastend += tbuf - buf; 204*29382Slepreau buf = tbuf; 205*29382Slepreau bufsize = newbufsize; 206*29382Slepreau readsize = readsize << 1; 207*29382Slepreau /* guaranteed to fit now (I think!) */ 20823469Slepreau } 20923469Slepreau } 210*29382Slepreau if (off - readsize < 0) { 211*29382Slepreau readsize = off; 212*29382Slepreau off = 0; 213*29382Slepreau } 214*29382Slepreau else 215*29382Slepreau off -= readsize; 216*29382Slepreau (void) lseek(fd, off, 0); /* back up */ 217*29382Slepreau /* Shift pending old data right to make room for new */ 218*29382Slepreau bcopy(buf, p = buf + readsize, i); 219*29382Slepreau pastend = p + i; 220*29382Slepreau if (read(fd, buf, readsize) != readsize) { 221*29382Slepreau prterr(name); 222*29382Slepreau numerr++; 223*29382Slepreau (void) close(fd); 224*29382Slepreau break; 225*29382Slepreau } 226*29382Slepreau continue; 22723469Slepreau } 228*29382Slepreau /* Found a real instance of the target string */ 229*29382Slepreau output(right ? p + targlen : p, pastend); 230*29382Slepreau pastend = p; 231*29382Slepreau 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 */ 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 26023469Slepreau prterr(s) 26123469Slepreau char *s; 26223469Slepreau { 26323469Slepreau 26423469Slepreau fprintf(stderr, "tac: "); 26523469Slepreau perror(s); 26623469Slepreau } 267*29382Slepreau 268*29382Slepreau cleanup() 269*29382Slepreau { 270*29382Slepreau 271*29382Slepreau unlink(tfile); 272*29382Slepreau exit(1); 273*29382Slepreau } 274