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