1: #ifndef lint 2: static char sccsid[] = "@(#)tac.c 1.4 6/5/86"; 3: #endif 4: 5: /* 6: * tac.c - Print file segments in reverse order 7: * 8: * Original line-only version by unknown author off the net. 9: * Rewritten in 1985 by Jay Lepreau, Univ of Utah, to allocate memory 10: * dynamically, handle string bounded segments (suggested by Rob Pike), 11: * and handle pipes. 12: */ 13: 14: #include <sys/types.h> 15: #include <sys/stat.h> 16: #include <stdio.h> 17: #include <signal.h> 18: 19: /* 20: * This should be defined for BSD releases later than 4.2 and for Sys V.2, 21: * at least. fwrite is faster than putc only if you have a new speedy fwrite. 22: */ 23: #define FAST_FWRITE 24: 25: #ifdef DEBUG /* dbx can't handle registers */ 26: #include <ctype.h> 27: # define register 28: #endif 29: 30: /* Default target string and bound */ 31: int right = 1; /* right or left bounded segments? */ 32: char *targ = "\n"; 33: 34: char *tfile; 35: char *buf; 36: 37: int readsize = 4096; /* significantly faster than 1024 */ 38: int bufsize; 39: int targlen; 40: int numerr; 41: 42: int cleanup(); 43: extern off_t lseek(); 44: extern char *strcpy(), *malloc(), *realloc(), *mktemp(); 45: 46: main(argc, argv) 47: int argc; 48: char **argv; 49: { 50: 51: #ifdef DEBUG 52: if (argc > 1 && isdigit(*argv[1])) { 53: readsize = atoi(argv[1]); 54: argc--, argv++; 55: } 56: #endif 57: 58: if (argc > 1 && (argv[1][0] == '+' || argv[1][0] == '-') && argv[1][1]) { 59: targ = &argv[1][1]; 60: right = (argv[1][0] == '+'); 61: argc--; argv++; 62: } 63: targlen = strlen(targ); 64: 65: bufsize = (readsize << 1) + targlen + 2; 66: if ((buf = malloc((unsigned) bufsize)) == NULL) { 67: perror("tac: initial malloc"); 68: exit(1); 69: } 70: 71: (void) strcpy(buf, targ); /* stop string at beginning */ 72: buf += targlen; 73: 74: if (argc == 1) 75: tacstdin(); 76: while (--argc) { 77: if (strcmp(*++argv, "-") == 0) 78: tacstdin(); 79: else 80: tacit(*argv); 81: } 82: exit(numerr > 0 ? 1 : 0); 83: } 84: 85: tacstdin() 86: { 87: 88: int (*sigint)(), (*sighup)(), (*sigterm)(); 89: 90: if ((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN) 91: (void) signal(SIGINT, cleanup); 92: if ((sighup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) 93: (void) signal(SIGHUP, cleanup); 94: if ((sigterm = signal(SIGTERM, SIG_IGN)) != SIG_IGN) 95: (void) signal(SIGTERM, cleanup); 96: 97: savestdin(); 98: tacit(tfile); 99: (void) unlink(tfile); 100: 101: (void) signal(SIGINT, sigint); 102: (void) signal(SIGHUP, sighup); 103: (void) signal(SIGTERM, sigterm); 104: } 105: 106: char template[] = "/tmp/tacXXXXXX"; 107: char workplate[sizeof template]; 108: 109: savestdin() 110: { 111: int fd; 112: register int n; 113: 114: (void) strcpy(workplate, template); 115: tfile = mktemp(workplate); 116: if ((fd = creat(tfile, 0600)) < 0) { 117: prterr(tfile); 118: cleanup(); 119: } 120: while ((n = read(0, buf, readsize)) > 0) 121: if (write(fd, buf, n) != n) { 122: prterr(tfile); 123: cleanup(); 124: } 125: (void) close(fd); 126: if (n < 0) { 127: prterr("stdin read"); 128: cleanup(); 129: } 130: } 131: 132: tacit(name) 133: char *name; 134: { 135: register char *p, *pastend; 136: register int firstchar, targm1len; /* target length minus 1 */ 137: struct stat st; 138: off_t off; 139: int fd, i; 140: 141: firstchar = *targ; 142: targm1len = targlen - 1; 143: 144: if (stat(name, &st) < 0) { 145: prterr(name); 146: numerr++; 147: return; 148: } 149: if ((off = st.st_size) == 0) 150: return; 151: if ((fd = open(name, 0)) < 0) { 152: prterr(name); 153: numerr++; 154: return; 155: } 156: 157: /* 158: * Arrange for the first read to lop off enough to 159: * leave the rest of the file a multiple of readsize. 160: * Since readsize can change, this may not always hold during 161: * the pgm run, but since it usually will, leave it here 162: * for i/o efficiency (page/sector boundaries and all that). 163: * Note: the efficiency gain has not been verified. 164: */ 165: if ((i = off % readsize) == 0) 166: i = readsize; 167: off -= i; 168: 169: (void) lseek(fd, off, 0); 170: if (read(fd, buf, i) != i) { 171: prterr(name); 172: (void) close(fd); 173: numerr++; 174: return; 175: } 176: p = pastend = buf + i; /* pastend always points to end+1 */ 177: p -= targm1len; 178: 179: for (;;) { 180: while ( *--p != firstchar || 181: (targm1len && strncmp(p+1, targ+1, targm1len)) ) 182: continue; 183: if (p < buf) { /* backed off front of buffer */ 184: if (off == 0) { 185: /* beginning of file: dump last segment */ 186: output(p + targlen, pastend); 187: (void) close(fd); 188: break; 189: } 190: if ((i = pastend - buf) > readsize) { 191: char *tbuf; 192: int newbufsize = (readsize << 2) + targlen + 2; 193: 194: if ((tbuf = realloc(buf-targlen, (unsigned) newbufsize)) == NULL) { 195: /* If realloc fails, old buf contents may be lost. */ 196: perror("tac: segment too long; may have garbage here"); 197: numerr++; 198: i = readsize; 199: } 200: else { 201: tbuf += targlen; /* skip over the stop string */ 202: p += tbuf - buf; 203: pastend += tbuf - buf; 204: buf = tbuf; 205: bufsize = newbufsize; 206: readsize = readsize << 1; 207: /* guaranteed to fit now (I think!) */ 208: } 209: } 210: if (off - readsize < 0) { 211: readsize = off; 212: off = 0; 213: } 214: else 215: off -= readsize; 216: (void) lseek(fd, off, 0); /* back up */ 217: /* Shift pending old data right to make room for new */ 218: bcopy(buf, p = buf + readsize, i); 219: pastend = p + i; 220: if (read(fd, buf, readsize) != readsize) { 221: prterr(name); 222: numerr++; 223: (void) close(fd); 224: break; 225: } 226: continue; 227: } 228: /* Found a real instance of the target string */ 229: output(right ? p + targlen : p, pastend); 230: pastend = p; 231: p -= targm1len; 232: } 233: } 234: 235: /* 236: * Dump chars from p to pastend-1. If right-bounded by target 237: * and not the first time through, append the target string. 238: */ 239: output(p, pastend) 240: register char *p; 241: char *pastend; 242: { 243: static short first = 1; 244: 245: #ifdef FAST_FWRITE 246: (void) fwrite(p, 1, pastend - p, stdout); 247: #else 248: while (p < pastend) 249: (void) putc(*p++, stdout); 250: #endif 251: if (right && !first) 252: (void) fwrite(targ, 1, targlen, stdout); 253: first = 0; 254: if ferror(stdout) { 255: perror("tac: fwrite/putc"); 256: exit(++numerr > 1 ? numerr : 1); 257: } 258: } 259: 260: prterr(s) 261: char *s; 262: { 263: 264: fprintf(stderr, "tac: "); 265: perror(s); 266: } 267: 268: cleanup() 269: { 270: 271: (void) unlink(tfile); 272: exit(1); 273: }