home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 9 / FreshFishVol9-CD2.bin / bbs / gnu / textutils-1.11-src.lha / textutils-1.11 / src / tac.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-13  |  17.2 KB  |  674 lines

  1. /* tac - concatenate and print files in reverse
  2.    Copyright (C) 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Jay Lepreau (lepreau@cs.utah.edu).
  19.    GNU enhancements by David MacKenzie (djm@gnu.ai.mit.edu). */
  20.  
  21. /* Copy each FILE, or the standard input if none are given or when a
  22.    FILE name of "-" is encountered, to the standard output with the
  23.    order of the records reversed.  The records are separated by
  24.    instances of a string, or a newline if none is given.  By default, the
  25.    separator string is attached to the end of the record that it
  26.    follows in the file.
  27.  
  28.    Options:
  29.    -b, --before            The separator is attached to the beginning
  30.                 of the record that it precedes in the file.
  31.    -r, --regex            The separator is a regular expression.
  32.    -s, --separator=separator    Use SEPARATOR as the record separator.
  33.  
  34.    To reverse a file byte by byte, use (in bash, ksh, or sh):
  35. tac -r -s '.\|
  36. ' file */
  37.  
  38. #include <config.h>
  39.  
  40. #include <stdio.h>
  41. #include <getopt.h>
  42. #include <sys/types.h>
  43. #include <signal.h>
  44. #include "system.h"
  45. #include "regex.h"    /* After "system.h" to avoid clashes with system defines */
  46. #include "version.h"
  47.  
  48. #ifndef STDC_HEADERS
  49. char *malloc ();
  50. char *realloc ();
  51. #endif
  52.  
  53. /* The number of bytes per atomic read. */
  54. #define INITIAL_READSIZE 8192
  55.  
  56. /* The number of bytes per atomic write. */
  57. #define WRITESIZE 8192
  58.  
  59. char *mktemp ();
  60.  
  61. static RETSIGTYPE cleanup ();
  62. static int tac ();
  63. static int tac_file ();
  64. static int tac_stdin ();
  65. static char *xmalloc ();
  66. static char *xrealloc ();
  67. static void output ();
  68. static void save_stdin ();
  69. static void xwrite ();
  70.  
  71. void error ();
  72. int full_write ();
  73. int safe_read ();
  74.  
  75. /* The name this program was run with. */
  76. char *program_name;
  77.  
  78. /* The string that separates the records of the file. */
  79. static char *separator;
  80.  
  81. /* If nonzero, print `separator' along with the record preceding it
  82.    in the file; otherwise with the record following it. */
  83. static int separator_ends_record;
  84.  
  85. /* 0 if `separator' is to be matched as a regular expression;
  86.    otherwise, the length of `separator', used as a sentinel to
  87.    stop the search. */
  88. static int sentinel_length;
  89.  
  90. /* The length of a match with `separator'.  If `sentinel_length' is 0,
  91.    `match_length' is computed every time a match succeeds;
  92.    otherwise, it is simply the length of `separator'. */
  93. static int match_length;
  94.  
  95. /* The input buffer. */
  96. static char *buffer;
  97.  
  98. /* The number of bytes to read at once into `buffer'. */
  99. static unsigned read_size;
  100.  
  101. /* The size of `buffer'.  This is read_size * 2 + sentinel_length + 2.
  102.    The extra 2 bytes allow `past_end' to have a value beyond the
  103.    end of `buffer' and `match_start' to run off the front of `buffer'. */
  104. static unsigned buffer_size;
  105.  
  106. /* The compiled regular expression representing `separator'. */
  107. static struct re_pattern_buffer compiled_separator;
  108.  
  109. /* If non-zero, display usage information and exit.  */
  110. static int show_help;
  111.  
  112. /* If non-zero, print the version on standard output then exit.  */
  113. static int show_version;
  114.  
  115. static struct option const longopts[] =
  116. {
  117.   {"before", no_argument, &separator_ends_record, 0},
  118.   {"regex", no_argument, &sentinel_length, 0},
  119.   {"separator", required_argument, NULL, 's'},
  120.   {"help", no_argument, &show_help, 1},
  121.   {"version", no_argument, &show_version, 1},
  122.   {NULL, 0, NULL, 0}
  123. };
  124.  
  125. static void
  126. usage (status)
  127.      int status;
  128. {
  129.   if (status != 0)
  130.     fprintf (stderr, "Try `%s --help' for more information.\n",
  131.          program_name);
  132.   else
  133.     {
  134.       printf ("\
  135. Usage: %s [OPTION]... [FILE]...\n\
  136. ",
  137.           program_name);
  138.       printf ("\
  139. \n\
  140.   -b, --before             attach the separator before instead of after\n\
  141.   -r, --regex              interpret the separator as a regular expression\n\
  142.   -s, --separator=STRING   use STRING as the separator instead of newline\n\
  143.       --help               display this help and exit\n\
  144.       --version            output version information and exit\n\
  145. \n\
  146. With no FILE, or when FILE is -, read standard input.\n\
  147. ");
  148.     }
  149.   exit (status);
  150. }
  151.  
  152. main (argc, argv)
  153.      int argc;
  154.      char **argv;
  155. {
  156.   const char *error_message;    /* Return value from re_compile_pattern. */
  157.   int optc, errors;
  158.   int have_read_stdin = 0;
  159.  
  160.   program_name = argv[0];
  161.   errors = 0;
  162.   separator = "\n";
  163.   sentinel_length = 1;
  164.   separator_ends_record = 1;
  165.  
  166.   while ((optc = getopt_long (argc, argv, "brs:", longopts, (int *) 0))
  167.      != EOF)
  168.     {
  169.       switch (optc)
  170.     {
  171.     case 0:
  172.       break;
  173.     case 'b':
  174.       separator_ends_record = 0;
  175.       break;
  176.     case 'r':
  177.       sentinel_length = 0;
  178.       break;
  179.     case 's':
  180.       separator = optarg;
  181.       if (*separator == 0)
  182.         error (1, 0, "separator cannot be empty");
  183.       break;
  184.     default:
  185.       usage (1);
  186.     }
  187.     }
  188.  
  189.   if (show_version)
  190.     {
  191.       printf ("tac - %s\n", version_string);
  192.       exit (0);
  193.     }
  194.  
  195.   if (show_help)
  196.     usage (0);
  197.  
  198.   if (sentinel_length == 0)
  199.     {
  200.       compiled_separator.allocated = 100;
  201.       compiled_separator.buffer = (unsigned char *)
  202.     xmalloc (compiled_separator.allocated);
  203.       compiled_separator.fastmap = xmalloc (256);
  204.       compiled_separator.translate = 0;
  205.       error_message = re_compile_pattern (separator, strlen (separator),
  206.                       &compiled_separator);
  207.       if (error_message)
  208.     error (1, 0, "%s", error_message);
  209.     }
  210.   else
  211.     match_length = sentinel_length = strlen (separator);
  212.  
  213.   read_size = INITIAL_READSIZE;
  214.   /* A precaution that will probably never be needed. */
  215.   while (sentinel_length * 2 >= read_size)
  216.     read_size *= 2;
  217.   buffer_size = read_size * 2 + sentinel_length + 2;
  218.   buffer = xmalloc (buffer_size);
  219.   if (sentinel_length)
  220.     {
  221.       strcpy (buffer, separator);
  222.       buffer += sentinel_length;
  223.     }
  224.   else
  225.     ++buffer;
  226.  
  227.   if (optind == argc)
  228.     {
  229.       have_read_stdin = 1;
  230.       errors = tac_stdin ();
  231.     }
  232.   else
  233.     for (; optind < argc; ++optind)
  234.       {
  235.     if (strcmp (argv[optind], "-") == 0)
  236.       {
  237.         have_read_stdin = 1;
  238.         errors |= tac_stdin ();
  239.       }
  240.     else
  241.       errors |= tac_file (argv[optind]);
  242.       }
  243.  
  244.   /* Flush the output buffer. */
  245.   output ((char *) NULL, (char *) NULL);
  246.  
  247.   if (have_read_stdin && close (0) < 0)
  248.     error (1, errno, "-");
  249.   if (close (1) < 0)
  250.     error (1, errno, "write error");
  251.   exit (errors);
  252. }
  253.  
  254. /* The name of a temporary file containing a copy of pipe input. */
  255. char *tempfile;
  256.  
  257. /* Print the standard input in reverse, saving it to temporary
  258.    file `tempfile' first if it is a pipe.
  259.    Return 0 if ok, 1 if an error occurs. */
  260.  
  261. static int
  262. tac_stdin ()
  263. {
  264.   /* Previous values of signal handlers. */
  265.   RETSIGTYPE (*sigint) (), (*sighup) (), (*sigpipe) (), (*sigterm) ();
  266.   int errors;
  267.   struct stat stats;
  268. #ifdef _POSIX_VERSION
  269.     struct sigaction oldact, newact;
  270. #endif                /* _POSIX_VERSION */
  271.  
  272.   /* No tempfile is needed for "tac < file".
  273.      Use fstat instead of checking for errno == ESPIPE because
  274.      lseek doesn't work on some special files but doesn't return an
  275.      error, either. */
  276.   if (fstat (0, &stats))
  277.     {
  278.       error (0, errno, "standard input");
  279.       return 1;
  280.     }
  281.   if (S_ISREG (stats.st_mode))
  282.     return tac (0, "standard input");
  283.  
  284. #ifdef _POSIX_VERSION
  285.   newact.sa_handler = cleanup;
  286.   sigemptyset (&newact.sa_mask);
  287.   newact.sa_flags = 0;
  288.  
  289.   sigaction (SIGINT, NULL, &oldact);
  290.   sigint = oldact.sa_handler;
  291.   if (sigint != SIG_IGN)
  292.     sigaction (SIGINT, &newact, NULL);
  293.  
  294.   sigaction (SIGHUP, NULL, &oldact);
  295.   sighup = oldact.sa_handler;
  296.   if (sighup != SIG_IGN)
  297.     sigaction (SIGHUP, &newact, NULL);
  298.  
  299.   sigaction (SIGPIPE, NULL, &oldact);
  300.   sigpipe = oldact.sa_handler;
  301.   if (sigpipe != SIG_IGN)
  302.     sigaction (SIGPIPE, &newact, NULL);
  303.  
  304.   sigaction (SIGTERM, NULL, &oldact);
  305.   sigterm = oldact.sa_handler;
  306.   if (sigterm != SIG_IGN)
  307.     sigaction (SIGTERM, &newact, NULL);
  308. #else                /* !_POSIX_VERSION */
  309.   sigint = signal (SIGINT, SIG_IGN);
  310.   if (sigint != SIG_IGN)
  311.     signal (SIGINT, cleanup);
  312.  
  313.   sighup = signal (SIGHUP, SIG_IGN);
  314.   if (sighup != SIG_IGN)
  315.     signal (SIGHUP, cleanup);
  316.  
  317.   sigpipe = signal (SIGPIPE, SIG_IGN);
  318.   if (sigpipe != SIG_IGN)
  319.     signal (SIGPIPE, cleanup);
  320.  
  321.   sigterm = signal (SIGTERM, SIG_IGN);
  322.   if (sigterm != SIG_IGN)
  323.     signal (SIGTERM, cleanup);
  324. #endif                /* _POSIX_VERSION */
  325.  
  326.   save_stdin ();
  327.  
  328.   errors = tac_file (tempfile);
  329.  
  330.   unlink (tempfile);
  331.  
  332. #ifdef _POSIX_VERSION
  333.   newact.sa_handler = sigint;
  334.   sigaction (SIGINT, &newact, NULL);
  335.   newact.sa_handler = sighup;
  336.   sigaction (SIGHUP, &newact, NULL);
  337.   newact.sa_handler = sigterm;
  338.   sigaction (SIGTERM, &newact, NULL);
  339.   newact.sa_handler = sigpipe;
  340.   sigaction (SIGPIPE, &newact, NULL);
  341. #else                /* !_POSIX_VERSION */
  342.   signal (SIGINT, sigint);
  343.   signal (SIGHUP, sighup);
  344.   signal (SIGTERM, sigterm);
  345.   signal (SIGPIPE, sigpipe);
  346. #endif                /* _POSIX_VERSION */
  347.  
  348.   return errors;
  349. }
  350.  
  351. /* Make a copy of the standard input in `tempfile'. */
  352.  
  353. static void
  354. save_stdin ()
  355. {
  356.   static char *template = NULL;
  357.   static char *tempdir;
  358.   int fd;
  359.   int bytes_read;
  360.  
  361.   if (template == NULL)
  362.     {
  363.       tempdir = getenv ("TMPDIR");
  364.       if (tempdir == NULL)
  365.     tempdir = "/tmp";
  366.       template = xmalloc (strlen (tempdir) + 11);
  367.     }
  368.   sprintf (template, "%s/tacXXXXXX", tempdir);
  369.   tempfile = mktemp (template);
  370.  
  371.   fd = creat (tempfile, 0600);
  372.   if (fd == -1)
  373.     {
  374.       error (0, errno, "%s", tempfile);
  375.       cleanup ();
  376.     }
  377.   while ((bytes_read = safe_read (0, buffer, read_size)) > 0)
  378.     if (full_write (fd, buffer, bytes_read) < 0)
  379.       {
  380.     error (0, errno, "%s", tempfile);
  381.     cleanup ();
  382.       }
  383.   if (close (fd) < 0)
  384.     {
  385.       error (0, errno, "%s", tempfile);
  386.       cleanup ();
  387.     }
  388.   if (bytes_read == -1)
  389.     {
  390.       error (0, errno, "read error");
  391.       cleanup ();
  392.     }
  393. }
  394.  
  395. /* Print FILE in reverse.
  396.    Return 0 if ok, 1 if an error occurs. */
  397.  
  398. static int
  399. tac_file (file)
  400.      char *file;
  401. {
  402.   int fd, errors;
  403.  
  404.   fd = open (file, O_RDONLY);
  405.   if (fd == -1)
  406.     {
  407.       error (0, errno, "%s", file);
  408.       return 1;
  409.     }
  410.   errors = tac (fd, file);
  411.   if (close (fd) < 0)
  412.     {
  413.       error (0, errno, "%s", file);
  414.       return 1;
  415.     }
  416.   return errors;
  417. }
  418.  
  419. /* Print in reverse the file open on descriptor FD for reading FILE.
  420.    Return 0 if ok, 1 if an error occurs. */
  421.  
  422. static int
  423. tac (fd, file)
  424.      int fd;
  425.      char *file;
  426. {
  427.   /* Pointer to the location in `buffer' where the search for
  428.      the next separator will begin. */
  429.   char *match_start;
  430.   /* Pointer to one past the rightmost character in `buffer' that
  431.      has not been printed yet. */
  432.   char *past_end;
  433.   unsigned saved_record_size;    /* Length of the record growing in `buffer'. */
  434.   off_t file_pos;        /* Offset in the file of the next read. */
  435.   /* Nonzero if `output' has not been called yet for any file.
  436.      Only used when the separator is attached to the preceding record. */
  437.   int first_time = 1;
  438.   char first_char = *separator;    /* Speed optimization, non-regexp. */
  439.   char *separator1 = separator + 1; /* Speed optimization, non-regexp. */
  440.   int match_length1 = match_length - 1; /* Speed optimization, non-regexp. */
  441.   struct re_registers regs;
  442.  
  443.   /* Find the size of the input file. */
  444.   file_pos = lseek (fd, (off_t) 0, SEEK_END);
  445.   if (file_pos < 1)
  446.     return 0;            /* It's an empty file. */
  447.  
  448.   /* Arrange for the first read to lop off enough to leave the rest of the
  449.      file a multiple of `read_size'.  Since `read_size' can change, this may
  450.      not always hold during the program run, but since it usually will, leave
  451.      it here for i/o efficiency (page/sector boundaries and all that).
  452.      Note: the efficiency gain has not been verified. */
  453.   saved_record_size = file_pos % read_size;
  454.   if (saved_record_size == 0)
  455.     saved_record_size = read_size;
  456.   file_pos -= saved_record_size;
  457.   /* `file_pos' now points to the start of the last (probably partial) block
  458.      in the input file. */
  459.  
  460.   lseek (fd, file_pos, SEEK_SET);
  461.   if (safe_read (fd, buffer, saved_record_size) != saved_record_size)
  462.     {
  463.       error (0, errno, "%s", file);
  464.       return 1;
  465.     }
  466.  
  467.   match_start = past_end = buffer + saved_record_size;
  468.   /* For non-regexp search, move past impossible positions for a match. */
  469.   if (sentinel_length)
  470.     match_start -= match_length1;
  471.  
  472.   for (;;)
  473.     {
  474.       /* Search backward from `match_start' - 1 to `buffer' for a match
  475.      with `separator'; for speed, use strncmp if `separator' contains no
  476.      metacharacters.
  477.      If the match succeeds, set `match_start' to point to the start of
  478.      the match and `match_length' to the length of the match.
  479.      Otherwise, make `match_start' < `buffer'. */
  480.       if (sentinel_length == 0)
  481.     {
  482.       int i = match_start - buffer;
  483.       int ret;
  484.  
  485.       ret = re_search (&compiled_separator, buffer, i, i - 1, -i, ®s);
  486.       if (ret == -1)
  487.         match_start = buffer - 1;
  488.       else if (ret == -2)
  489.         {
  490.           error (0, 0, "error in regular expression search");
  491.           cleanup ();
  492.         }
  493.       else
  494.         {
  495.           match_start = buffer + regs.start[0];
  496.           match_length = regs.end[0] - regs.start[0];
  497.         }
  498.     }
  499.       else
  500.     {
  501.       /* `match_length' is constant for non-regexp boundaries. */
  502.       while (*--match_start != first_char
  503.          || (match_length1 && strncmp (match_start + 1, separator1,
  504.                            match_length1)))
  505.         /* Do nothing. */ ;
  506.     }
  507.  
  508.       /* Check whether we backed off the front of `buffer' without finding
  509.          a match for `separator'. */
  510.       if (match_start < buffer)
  511.     {
  512.       if (file_pos == 0)
  513.         {
  514.           /* Hit the beginning of the file; print the remaining record. */
  515.           output (buffer, past_end);
  516.           return 0;
  517.         }
  518.  
  519.       saved_record_size = past_end - buffer;
  520.       if (saved_record_size > read_size)
  521.         {
  522.           /* `buffer_size' is about twice `read_size', so since
  523.          we want to read in another `read_size' bytes before
  524.          the data already in `buffer', we need to increase
  525.          `buffer_size'. */
  526.           char *newbuffer;
  527.           int offset = sentinel_length ? sentinel_length : 1;
  528.  
  529.           read_size *= 2;
  530.           buffer_size = read_size * 2 + sentinel_length + 2;
  531.           newbuffer = xrealloc (buffer - offset, buffer_size) + offset;
  532.           /* Adjust the pointers for the new buffer location.  */
  533.           match_start += newbuffer - buffer;
  534.           past_end += newbuffer - buffer;
  535.           buffer = newbuffer;
  536.         }
  537.  
  538.       /* Back up to the start of the next bufferfull of the file.  */
  539.       if (file_pos >= read_size)
  540.         file_pos -= read_size;
  541.       else
  542.         {
  543.           read_size = file_pos;
  544.           file_pos = 0;
  545.         }
  546.       lseek (fd, file_pos, SEEK_SET);
  547.  
  548.       /* Shift the pending record data right to make room for the new. */
  549.       bcopy (buffer, buffer + read_size, saved_record_size);
  550.       past_end = buffer + read_size + saved_record_size;
  551.       /* For non-regexp searches, avoid unneccessary scanning. */
  552.       if (sentinel_length)
  553.         match_start = buffer + read_size;
  554.       else
  555.         match_start = past_end;
  556.  
  557.       if (safe_read (fd, buffer, read_size) != read_size)
  558.         {
  559.           error (0, errno, "%s", file);
  560.           return 1;
  561.         }
  562.     }
  563.       else
  564.     {
  565.       /* Found a match of `separator'. */
  566.       if (separator_ends_record)
  567.         {
  568.           char *match_end = match_start + match_length;
  569.  
  570.           /* If this match of `separator' isn't at the end of the
  571.              file, print the record. */
  572.           if (first_time == 0 || match_end != past_end)
  573.         output (match_end, past_end);
  574.           past_end = match_end;
  575.           first_time = 0;
  576.         }
  577.       else
  578.         {
  579.           output (match_start, past_end);
  580.           past_end = match_start;
  581.         }
  582.       match_start -= match_length - 1;
  583.     }
  584.     }
  585. }
  586.  
  587. /* Print the characters from START to PAST_END - 1.
  588.    If START is NULL, just flush the buffer. */
  589.  
  590. static void
  591. output (start, past_end)
  592.      char *start;
  593.      char *past_end;
  594. {
  595.   static char buffer[WRITESIZE];
  596.   static int bytes_in_buffer = 0;
  597.   int bytes_to_add = past_end - start;
  598.   int bytes_available = WRITESIZE - bytes_in_buffer;
  599.  
  600.   if (start == 0)
  601.     {
  602.       xwrite (1, buffer, bytes_in_buffer);
  603.       bytes_in_buffer = 0;
  604.       return;
  605.     }
  606.  
  607.   /* Write out as many full buffers as possible. */
  608.   while (bytes_to_add >= bytes_available)
  609.     {
  610.       bcopy (start, buffer + bytes_in_buffer, bytes_available);
  611.       bytes_to_add -= bytes_available;
  612.       start += bytes_available;
  613.       xwrite (1, buffer, WRITESIZE);
  614.       bytes_in_buffer = 0;
  615.       bytes_available = WRITESIZE;
  616.     }
  617.  
  618.   bcopy (start, buffer + bytes_in_buffer, bytes_to_add);
  619.   bytes_in_buffer += bytes_to_add;
  620. }
  621.  
  622. static RETSIGTYPE
  623. cleanup ()
  624. {
  625.   unlink (tempfile);
  626.   exit (1);
  627. }
  628.  
  629. static void
  630. xwrite (desc, buffer, size)
  631.      int desc;
  632.      char *buffer;
  633.      int size;
  634. {
  635.   if (full_write (desc, buffer, size) < 0)
  636.     {
  637.       error (0, errno, "write error");
  638.       cleanup ();
  639.     }
  640. }
  641.  
  642. /* Allocate N bytes of memory dynamically, with error checking.  */
  643.  
  644. static char *
  645. xmalloc (n)
  646.      unsigned n;
  647. {
  648.   char *p;
  649.  
  650.   p = malloc (n);
  651.   if (p == 0)
  652.     {
  653.       error (0, 0, "virtual memory exhausted");
  654.       cleanup ();
  655.     }
  656.   return p;
  657. }
  658.  
  659. /* Change the size of memory area P to N bytes, with error checking. */
  660.  
  661. static char *
  662. xrealloc (p, n)
  663.      char *p;
  664.      unsigned n;
  665. {
  666.   p = realloc (p, n);
  667.   if (p == 0)
  668.     {
  669.       error (0, 0, "virtual memory exhausted");
  670.       cleanup ();
  671.     }
  672.   return p;
  673. }
  674.