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: }

Defined functions

cleanup defined in line 268; used 7 times
main defined in line 46; never used
output defined in line 239; used 2 times
prterr defined in line 260; used 7 times
savestdin defined in line 109; used 1 times
  • in line 97
tacit defined in line 132; used 2 times
tacstdin defined in line 85; used 2 times

Defined variables

buf defined in line 35; used 16 times
bufsize defined in line 38; used 3 times
numerr defined in line 40; used 8 times
readsize defined in line 37; used 16 times
right defined in line 31; used 3 times
sccsid defined in line 2; never used
targ defined in line 32; used 6 times
targlen defined in line 39; used 10 times
template defined in line 106; used 2 times
tfile defined in line 34; used 7 times
workplate defined in line 107; used 2 times

Defined macros

FAST_FWRITE defined in line 23; used 1 times
register defined in line 27; never used
Last modified: 1986-06-06
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1471
Valid CSS Valid XHTML 1.0 Strict