1 #ifndef lint 2 static char sccsid[] = "@(#)tac.c 1.5 06/24/90"; 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 void (*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 } 274