home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2778 / tac.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-18  |  5.8 KB  |  274 lines

  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. }
  274.