summaryrefslogtreecommitdiff
path: root/src/tac.c
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>1997-02-12 16:25:40 +0000
committerJim Meyering <jim@meyering.net>1997-02-12 16:25:40 +0000
commit59f891e8b86da5aa51cbd2a16132eae943a6e83b (patch)
treedc61c68ee08512f08ef67f7ebbe45ab51862381f /src/tac.c
parent2be25646bd13b562584a317198ddae6c983ff452 (diff)
downloadcoreutils-59f891e8b86da5aa51cbd2a16132eae943a6e83b.tar.xz
FIXME: major revamp to use FILE*; passed a few more tests
Diffstat (limited to 'src/tac.c')
-rw-r--r--src/tac.c367
1 files changed, 182 insertions, 185 deletions
diff --git a/src/tac.c b/src/tac.c
index 2c63ae805..77c68342c 100644
--- a/src/tac.c
+++ b/src/tac.c
@@ -66,8 +66,9 @@ char *realloc ();
char *mktemp ();
-int full_write ();
int safe_read ();
+char *xmalloc ();
+char *xrealloc ();
/* The name this program was run with. */
char *program_name;
@@ -103,9 +104,6 @@ static unsigned G_buffer_size;
/* The compiled regular expression representing `separator'. */
static struct re_pattern_buffer compiled_separator;
-/* The name of a temporary file containing a copy of pipe input. */
-static char *tempfile;
-
/* If nonzero, display usage information and exit. */
static int show_help;
@@ -122,6 +120,31 @@ static struct option const longopts[] =
{NULL, 0, NULL, 0}
};
+/* Read LEN bytes at PTR from descriptor DESC, retrying if interrupted.
+ Return the actual number of bytes read, zero for EOF, or negative
+ for an error. */
+
+int
+safe_read (int desc, char *ptr, int len)
+{
+ int n_chars;
+
+ if (len <= 0)
+ return len;
+
+#ifdef EINTR
+ do
+ {
+ n_chars = read (desc, ptr, len);
+ }
+ while (n_chars < 0 && errno == EINTR);
+#else
+ n_chars = read (desc, ptr, len);
+#endif
+
+ return n_chars;
+}
+
static void
usage (int status)
{
@@ -149,76 +172,6 @@ With no FILE, or when FILE is -, read standard input.\n\
exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
-static void
-cleanup (void)
-{
- unlink (tempfile);
-}
-
-static void
-cleanup_fatal (void)
-{
- cleanup ();
- exit (EXIT_FAILURE);
-}
-
-static RETSIGTYPE
-sighandler (int sig)
-{
-#ifdef SA_INTERRUPT
- struct sigaction sigact;
-
- sigact.sa_handler = SIG_DFL;
- sigemptyset (&sigact.sa_mask);
- sigact.sa_flags = 0;
- sigaction (sig, &sigact, NULL);
-#else /* !SA_INTERRUPT */
- signal (sig, SIG_DFL);
-#endif /* SA_INTERRUPT */
- cleanup ();
- kill (getpid (), sig);
-}
-
-/* Allocate N bytes of memory dynamically, with error checking. */
-
-static char *
-xmalloc (unsigned int n)
-{
- char *p;
-
- p = malloc (n);
- if (p == 0)
- {
- error (0, 0, _("virtual memory exhausted"));
- cleanup_fatal ();
- }
- return p;
-}
-
-/* Change the size of memory area P to N bytes, with error checking. */
-
-static char *
-xrealloc (char *p, unsigned int n)
-{
- p = realloc (p, n);
- if (p == 0)
- {
- error (0, 0, _("virtual memory exhausted"));
- cleanup_fatal ();
- }
- return p;
-}
-
-static void
-xwrite (int desc, const char *buffer, int size)
-{
- if (full_write (desc, buffer, size) < 0)
- {
- error (0, errno, _("write error"));
- cleanup_fatal ();
- }
-}
-
/* Print the characters from START to PAST_END - 1.
If START is NULL, just flush the buffer. */
@@ -232,7 +185,7 @@ output (const char *start, const char *past_end)
if (start == 0)
{
- xwrite (STDOUT_FILENO, buffer, bytes_in_buffer);
+ fwrite (buffer, 1, bytes_in_buffer, stdout);
bytes_in_buffer = 0;
return;
}
@@ -243,7 +196,7 @@ output (const char *start, const char *past_end)
memcpy (buffer + bytes_in_buffer, start, bytes_available);
bytes_to_add -= bytes_available;
start += bytes_available;
- xwrite (STDOUT_FILENO, buffer, WRITESIZE);
+ fwrite (buffer, 1, WRITESIZE, stdout);
bytes_in_buffer = 0;
bytes_available = WRITESIZE;
}
@@ -256,16 +209,22 @@ output (const char *start, const char *past_end)
Return 0 if ok, 1 if an error occurs. */
static int
-tac (int fd, const char *file)
+tac_stream (FILE *in, const char *file)
{
/* Pointer to the location in `G_buffer' where the search for
the next separator will begin. */
char *match_start;
+
/* Pointer to one past the rightmost character in `G_buffer' that
has not been printed yet. */
char *past_end;
- unsigned saved_record_size; /* Length of the record growing in `G_buffer'. */
- off_t file_pos; /* Offset in the file of the next read. */
+
+ /* Length of the record growing in `G_buffer'. */
+ unsigned saved_record_size;
+
+ /* Offset in the file of the next read. */
+ off_t file_pos;
+
/* Nonzero if `output' has not been called yet for any file.
Only used when the separator is attached to the preceding record. */
int first_time = 1;
@@ -275,7 +234,7 @@ tac (int fd, const char *file)
struct re_registers regs;
/* Find the size of the input file. */
- file_pos = lseek (fd, (off_t) 0, SEEK_END);
+ file_pos = lseek (fileno (in), (off_t) 0, SEEK_END);
if (file_pos < 1)
return 0; /* It's an empty file. */
@@ -291,8 +250,8 @@ tac (int fd, const char *file)
/* `file_pos' now points to the start of the last (probably partial) block
in the input file. */
- lseek (fd, file_pos, SEEK_SET);
- if (safe_read (fd, G_buffer, saved_record_size) != saved_record_size)
+ lseek (fileno (in), file_pos, SEEK_SET);
+ if (safe_read (fileno (in), G_buffer, saved_record_size) != saved_record_size)
{
error (0, errno, "%s", file);
return 1;
@@ -321,8 +280,8 @@ tac (int fd, const char *file)
match_start = G_buffer - 1;
else if (ret == -2)
{
- error (0, 0, _("error in regular expression search"));
- cleanup_fatal ();
+ error (EXIT_FAILURE, 0,
+ _("error in regular expression search"));
}
else
{
@@ -377,7 +336,7 @@ tac (int fd, const char *file)
read_size = file_pos;
file_pos = 0;
}
- lseek (fd, file_pos, SEEK_SET);
+ lseek (fileno (in), file_pos, SEEK_SET);
/* Shift the pending record data right to make room for the new.
The source and destination regions probably overlap. */
@@ -389,7 +348,7 @@ tac (int fd, const char *file)
else
match_start = past_end;
- if (safe_read (fd, G_buffer, read_size) != read_size)
+ if (safe_read (fileno (in), G_buffer, read_size) != read_size)
{
error (0, errno, "%s", file);
return 1;
@@ -425,16 +384,17 @@ tac (int fd, const char *file)
static int
tac_file (const char *file)
{
- int fd, errors;
+ int errors;
+ FILE *in;
- fd = open (file, O_RDONLY);
- if (fd == -1)
+ in = fopen (file, "r");
+ if (in == NULL)
{
error (0, errno, "%s", file);
return 1;
}
- errors = tac (fd, file);
- if (close (fd) < 0)
+ errors = tac_stream (in, file);
+ if (ferror (in) || fclose (in) == EOF)
{
error (0, errno, "%s", file);
return 1;
@@ -442,15 +402,17 @@ tac_file (const char *file)
return errors;
}
-/* Make a copy of the standard input in `tempfile'. */
+/* Make a copy of the standard input in `FIXME'. */
static void
-save_stdin (void)
+save_stdin (FILE **g_tmp, char **g_tempfile)
{
static char *template = NULL;
static char *tempdir;
- int fd;
+ static char *tempfile;
+ FILE *tmp;
int bytes_read;
+ int fd;
if (template == NULL)
{
@@ -463,43 +425,39 @@ save_stdin (void)
tempfile = mktemp (template);
fd = creat (tempfile, 0600);
- if (fd == -1)
- {
- error (0, errno, "%s", tempfile);
- cleanup_fatal ();
- }
+ if (fd == -1 || (tmp = fdopen (fd, "rw")) == NULL)
+ error (EXIT_FAILURE, errno, "%s", tempfile);
+ tmp = fdopen (fd, "rw");
+ if (tmp == NULL)
+ error (EXIT_FAILURE, errno, "%s", tempfile);
+ unlink (tempfile);
+
while ((bytes_read = safe_read (0, G_buffer, read_size)) > 0)
- if (full_write (fd, G_buffer, bytes_read) < 0)
- {
- error (0, errno, "%s", tempfile);
- cleanup_fatal ();
- }
- if (close (fd) < 0)
- {
- error (0, errno, "%s", tempfile);
- cleanup_fatal ();
- }
+ fwrite (G_buffer, 1, bytes_read, tmp);
+
+ if (ferror (tmp) || fflush (tmp) == EOF)
+ error (EXIT_FAILURE, errno, "%s", tempfile);
+
+ if (fseek (tmp, (long int) 0, SEEK_SET))
+ error (EXIT_FAILURE, errno, "%s", tempfile);
+
if (bytes_read == -1)
- {
- error (0, errno, _("read error"));
- cleanup_fatal ();
- }
+ error (EXIT_FAILURE, errno, _("read error"));
+
+ *g_tmp = tmp;
+ *g_tempfile = tempfile;
}
/* Print the standard input in reverse, saving it to temporary
- file `tempfile' first if it is a pipe.
+ file first if it is a pipe.
Return 0 if ok, 1 if an error occurs. */
static int
tac_stdin (void)
{
/* Previous values of signal handlers. */
- RETSIGTYPE (*sigint) (), (*sighup) (), (*sigpipe) (), (*sigterm) ();
int errors;
struct stat stats;
-#ifdef SA_INTERRUPT
- struct sigaction oldact, newact;
-#endif /* SA_INTERRUPT */
/* No tempfile is needed for "tac < file".
Use fstat instead of checking for errno == ESPIPE because
@@ -510,74 +468,113 @@ tac_stdin (void)
error (0, errno, _("standard input"));
return 1;
}
+
if (S_ISREG (stats.st_mode))
- return tac (0, _("standard input"));
-
-#ifdef SA_INTERRUPT
- newact.sa_handler = sighandler;
- sigemptyset (&newact.sa_mask);
- newact.sa_flags = 0;
-
- sigaction (SIGINT, NULL, &oldact);
- sigint = oldact.sa_handler;
- if (sigint != SIG_IGN)
- sigaction (SIGINT, &newact, NULL);
-
- sigaction (SIGHUP, NULL, &oldact);
- sighup = oldact.sa_handler;
- if (sighup != SIG_IGN)
- sigaction (SIGHUP, &newact, NULL);
-
- sigaction (SIGPIPE, NULL, &oldact);
- sigpipe = oldact.sa_handler;
- if (sigpipe != SIG_IGN)
- sigaction (SIGPIPE, &newact, NULL);
-
- sigaction (SIGTERM, NULL, &oldact);
- sigterm = oldact.sa_handler;
- if (sigterm != SIG_IGN)
- sigaction (SIGTERM, &newact, NULL);
-#else /* !SA_INTERRUPT */
- sigint = signal (SIGINT, SIG_IGN);
- if (sigint != SIG_IGN)
- signal (SIGINT, sighandler);
-
- sighup = signal (SIGHUP, SIG_IGN);
- if (sighup != SIG_IGN)
- signal (SIGHUP, sighandler);
-
- sigpipe = signal (SIGPIPE, SIG_IGN);
- if (sigpipe != SIG_IGN)
- signal (SIGPIPE, sighandler);
-
- sigterm = signal (SIGTERM, SIG_IGN);
- if (sigterm != SIG_IGN)
- signal (SIGTERM, sighandler);
-#endif /* SA_INTERRUPT */
-
- save_stdin ();
-
- errors = tac_file (tempfile);
+ {
+ errors = tac_stream (stdin, _("standard input"));
+ }
+ else
+ {
+ FILE *tmp_stream;
+ char *tmp_file;
+ save_stdin (&tmp_stream, &tmp_file);
+ errors = tac_stream (tmp_stream, tmp_file);
+ }
- unlink (tempfile);
+ return errors;
+}
-#ifdef SA_INTERRUPT
- newact.sa_handler = sigint;
- sigaction (SIGINT, &newact, NULL);
- newact.sa_handler = sighup;
- sigaction (SIGHUP, &newact, NULL);
- newact.sa_handler = sigterm;
- sigaction (SIGTERM, &newact, NULL);
- newact.sa_handler = sigpipe;
- sigaction (SIGPIPE, &newact, NULL);
-#else /* !SA_INTERRUPT */
- signal (SIGINT, sigint);
- signal (SIGHUP, sighup);
- signal (SIGTERM, sigterm);
- signal (SIGPIPE, sigpipe);
-#endif /* SA_INTERRUPT */
+/* BUF_END_PLUS_ONE points one byte past the end of the buffer
+ to be searched. */
- return errors;
+static void *
+memrchr (const char *buf_start, const char *buf_end_plus_one, int c)
+{
+ const char *p = buf_end_plus_one;
+ while (buf_start <= --p)
+ {
+ if (*(const unsigned char *) p == c)
+ return (void *) p;
+ }
+ return NULL;
+}
+
+static int
+tac_mem (const char *buf, size_t n_bytes, FILE *out)
+{
+ if (n_bytes == 0)
+ return 0;
+
+ {
+ const char *nl = memrchr (buf, buf + n_bytes, '\n');
+ const char *bol = (nl == NULL ? buf : nl + 1);
+
+ /* If the last line of the input file has no terminating newline,
+ treat it as a special case. */
+ if (bol < buf + n_bytes)
+ {
+ /* Print out the line from bol to end of input. */
+ fwrite (bol, 1, (buf + n_bytes) - bol, out);
+
+ /* Add a newline here. Otherwise, the first and second lines
+ of output would appear to have been joined. */
+ fputc ('\n', out);
+ }
+
+ while ((nl = memrchr (buf, bol - 1, '\n')) != NULL)
+ {
+ /* Output the line (which includes a trailing newline)
+ from NL+1 to BOL-1. */
+ fwrite (nl + 1, 1, bol - 1 - (nl + 1), out);
+
+ bol = nl + 1;
+ }
+
+ /* If there's anything left, output the last line: BUF .. BOL-1.
+ When the first byte of the input is a newline, there is nothing
+ left to do here. */
+ if (buf < bol)
+ fwrite (buf, 1, bol - buf, out);
+ }
+}
+
+static int
+tac_stdin_to_mem (void)
+{
+ char *buf = NULL;
+ size_t bufsiz = 8 * BUFSIZ;
+ size_t delta = 8 * BUFSIZ;
+ size_t n_bytes = 0;
+
+ while (1)
+ {
+ int bytes_read;
+ if (buf == NULL)
+ buf = (char *) malloc (bufsiz);
+ else
+ buf = (char *) realloc (buf, bufsiz);
+
+ if (buf == NULL)
+ {
+ /* Free the buffer and fall back on the code that relies on a
+ temporary file. */
+ free (buf);
+ /* FIXME */
+ abort ();
+ }
+ bytes_read = safe_read (STDIN_FILENO, buf + n_bytes, bufsiz - n_bytes);
+ if (bytes_read == 0)
+ break;
+ n_bytes += bytes_read;
+ if (bytes_read < 0)
+ error (1, errno, _("read error"));
+
+ bufsiz += delta;
+ }
+
+ tac_mem (buf, n_bytes, stdout);
+
+ return 0;
}
int
@@ -660,7 +657,7 @@ main (int argc, char **argv)
if (optind == argc)
{
have_read_stdin = 1;
- errors = tac_stdin ();
+ errors = tac_stdin_to_mem ();
}
else
for (; optind < argc; ++optind)
@@ -668,7 +665,7 @@ main (int argc, char **argv)
if (strcmp (argv[optind], "-") == 0)
{
have_read_stdin = 1;
- errors |= tac_stdin ();
+ errors |= tac_stdin_to_mem ();
}
else
errors |= tac_file (argv[optind]);
@@ -679,7 +676,7 @@ main (int argc, char **argv)
if (have_read_stdin && close (0) < 0)
error (EXIT_FAILURE, errno, "-");
- if (close (1) < 0)
+ if (ferror (stdout) || fclose (stdout) == EOF)
error (EXIT_FAILURE, errno, _("write error"));
exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}