~ubuntu-branches/ubuntu/lucid/coreutils/lucid-updates

« back to all changes in this revision

Viewing changes to src/tac.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Stone
  • Date: 2009-04-07 19:21:42 UTC
  • mfrom: (1.2.1 upstream) (5.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20090407192142-eez7bafqc4oqxox5
* new upstream version (Closes: #517558)
* [78] fix kfreebsd build problem (Closes: #520368)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* tac - concatenate and print files in reverse
 
2
   Copyright (C) 1988-1991, 1995-2006, 2008 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 3 of the License, or
 
7
   (at your option) 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, see <http://www.gnu.org/licenses/>.  */
 
16
 
 
17
/* Written by Jay Lepreau (lepreau@cs.utah.edu).
 
18
   GNU enhancements by David MacKenzie (djm@gnu.ai.mit.edu). */
 
19
 
 
20
/* Copy each FILE, or the standard input if none are given or when a
 
21
   FILE name of "-" is encountered, to the standard output with the
 
22
   order of the records reversed.  The records are separated by
 
23
   instances of a string, or a newline if none is given.  By default, the
 
24
   separator string is attached to the end of the record that it
 
25
   follows in the file.
 
26
 
 
27
   Options:
 
28
   -b, --before                 The separator is attached to the beginning
 
29
                                of the record that it precedes in the file.
 
30
   -r, --regex                  The separator is a regular expression.
 
31
   -s, --separator=separator    Use SEPARATOR as the record separator.
 
32
 
 
33
   To reverse a file byte by byte, use (in bash, ksh, or sh):
 
34
tac -r -s '.\|
 
35
' file */
 
36
 
 
37
#include <config.h>
 
38
 
 
39
#include <stdio.h>
 
40
#include <getopt.h>
 
41
#include <sys/types.h>
 
42
#include "system.h"
 
43
 
 
44
#include <regex.h>
 
45
 
 
46
#include "error.h"
 
47
#include "quote.h"
 
48
#include "quotearg.h"
 
49
#include "safe-read.h"
 
50
#include "stdlib--.h"
 
51
#include "xfreopen.h"
 
52
 
 
53
/* The official name of this program (e.g., no `g' prefix).  */
 
54
#define PROGRAM_NAME "tac"
 
55
 
 
56
#define AUTHORS \
 
57
  proper_name ("Jay Lepreau"), \
 
58
  proper_name ("David MacKenzie")
 
59
 
 
60
#if defined __MSDOS__ || defined _WIN32
 
61
/* Define this to non-zero on systems for which the regular mechanism
 
62
   (of unlinking an open file and expecting to be able to write, seek
 
63
   back to the beginning, then reread it) doesn't work.  E.g., on Windows
 
64
   and DOS systems.  */
 
65
# define DONT_UNLINK_WHILE_OPEN 1
 
66
#endif
 
67
 
 
68
 
 
69
#ifndef DEFAULT_TMPDIR
 
70
# define DEFAULT_TMPDIR "/tmp"
 
71
#endif
 
72
 
 
73
/* The number of bytes per atomic read. */
 
74
#define INITIAL_READSIZE 8192
 
75
 
 
76
/* The number of bytes per atomic write. */
 
77
#define WRITESIZE 8192
 
78
 
 
79
/* The string that separates the records of the file. */
 
80
static char const *separator;
 
81
 
 
82
/* True if we have ever read standard input.  */
 
83
static bool have_read_stdin = false;
 
84
 
 
85
/* If true, print `separator' along with the record preceding it
 
86
   in the file; otherwise with the record following it. */
 
87
static bool separator_ends_record;
 
88
 
 
89
/* 0 if `separator' is to be matched as a regular expression;
 
90
   otherwise, the length of `separator', used as a sentinel to
 
91
   stop the search. */
 
92
static size_t sentinel_length;
 
93
 
 
94
/* The length of a match with `separator'.  If `sentinel_length' is 0,
 
95
   `match_length' is computed every time a match succeeds;
 
96
   otherwise, it is simply the length of `separator'. */
 
97
static size_t match_length;
 
98
 
 
99
/* The input buffer. */
 
100
static char *G_buffer;
 
101
 
 
102
/* The number of bytes to read at once into `buffer'. */
 
103
static size_t read_size;
 
104
 
 
105
/* The size of `buffer'.  This is read_size * 2 + sentinel_length + 2.
 
106
   The extra 2 bytes allow `past_end' to have a value beyond the
 
107
   end of `G_buffer' and `match_start' to run off the front of `G_buffer'. */
 
108
static size_t G_buffer_size;
 
109
 
 
110
/* The compiled regular expression representing `separator'. */
 
111
static struct re_pattern_buffer compiled_separator;
 
112
static char compiled_separator_fastmap[UCHAR_MAX + 1];
 
113
static struct re_registers regs;
 
114
 
 
115
static struct option const longopts[] =
 
116
{
 
117
  {"before", no_argument, NULL, 'b'},
 
118
  {"regex", no_argument, NULL, 'r'},
 
119
  {"separator", required_argument, NULL, 's'},
 
120
  {GETOPT_HELP_OPTION_DECL},
 
121
  {GETOPT_VERSION_OPTION_DECL},
 
122
  {NULL, 0, NULL, 0}
 
123
};
 
124
 
 
125
void
 
126
usage (int status)
 
127
{
 
128
  if (status != EXIT_SUCCESS)
 
129
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
 
130
             program_name);
 
131
  else
 
132
    {
 
133
      printf (_("\
 
134
Usage: %s [OPTION]... [FILE]...\n\
 
135
"),
 
136
              program_name);
 
137
      fputs (_("\
 
138
Write each FILE to standard output, last line first.\n\
 
139
With no FILE, or when FILE is -, read standard input.\n\
 
140
\n\
 
141
"), stdout);
 
142
      fputs (_("\
 
143
Mandatory arguments to long options are mandatory for short options too.\n\
 
144
"), stdout);
 
145
      fputs (_("\
 
146
  -b, --before             attach the separator before instead of after\n\
 
147
  -r, --regex              interpret the separator as a regular expression\n\
 
148
  -s, --separator=STRING   use STRING as the separator instead of newline\n\
 
149
"), stdout);
 
150
      fputs (HELP_OPTION_DESCRIPTION, stdout);
 
151
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
 
152
      emit_bug_reporting_address ();
 
153
    }
 
154
  exit (status);
 
155
}
 
156
 
 
157
/* Print the characters from START to PAST_END - 1.
 
158
   If START is NULL, just flush the buffer. */
 
159
 
 
160
static void
 
161
output (const char *start, const char *past_end)
 
162
{
 
163
  static char buffer[WRITESIZE];
 
164
  static size_t bytes_in_buffer = 0;
 
165
  size_t bytes_to_add = past_end - start;
 
166
  size_t bytes_available = WRITESIZE - bytes_in_buffer;
 
167
 
 
168
  if (start == 0)
 
169
    {
 
170
      fwrite (buffer, 1, bytes_in_buffer, stdout);
 
171
      bytes_in_buffer = 0;
 
172
      return;
 
173
    }
 
174
 
 
175
  /* Write out as many full buffers as possible. */
 
176
  while (bytes_to_add >= bytes_available)
 
177
    {
 
178
      memcpy (buffer + bytes_in_buffer, start, bytes_available);
 
179
      bytes_to_add -= bytes_available;
 
180
      start += bytes_available;
 
181
      fwrite (buffer, 1, WRITESIZE, stdout);
 
182
      bytes_in_buffer = 0;
 
183
      bytes_available = WRITESIZE;
 
184
    }
 
185
 
 
186
  memcpy (buffer + bytes_in_buffer, start, bytes_to_add);
 
187
  bytes_in_buffer += bytes_to_add;
 
188
}
 
189
 
 
190
/* Print in reverse the file open on descriptor FD for reading FILE.
 
191
   Return true if successful.  */
 
192
 
 
193
static bool
 
194
tac_seekable (int input_fd, const char *file)
 
195
{
 
196
  /* Pointer to the location in `G_buffer' where the search for
 
197
     the next separator will begin. */
 
198
  char *match_start;
 
199
 
 
200
  /* Pointer to one past the rightmost character in `G_buffer' that
 
201
     has not been printed yet. */
 
202
  char *past_end;
 
203
 
 
204
  /* Length of the record growing in `G_buffer'. */
 
205
  size_t saved_record_size;
 
206
 
 
207
  /* Offset in the file of the next read. */
 
208
  off_t file_pos;
 
209
 
 
210
  /* True if `output' has not been called yet for any file.
 
211
     Only used when the separator is attached to the preceding record. */
 
212
  bool first_time = true;
 
213
  char first_char = *separator; /* Speed optimization, non-regexp. */
 
214
  char const *separator1 = separator + 1; /* Speed optimization, non-regexp. */
 
215
  size_t match_length1 = match_length - 1; /* Speed optimization, non-regexp. */
 
216
 
 
217
  /* Find the size of the input file. */
 
218
  file_pos = lseek (input_fd, (off_t) 0, SEEK_END);
 
219
  if (file_pos < 1)
 
220
    return true;                        /* It's an empty file. */
 
221
 
 
222
  /* Arrange for the first read to lop off enough to leave the rest of the
 
223
     file a multiple of `read_size'.  Since `read_size' can change, this may
 
224
     not always hold during the program run, but since it usually will, leave
 
225
     it here for i/o efficiency (page/sector boundaries and all that).
 
226
     Note: the efficiency gain has not been verified. */
 
227
  saved_record_size = file_pos % read_size;
 
228
  if (saved_record_size == 0)
 
229
    saved_record_size = read_size;
 
230
  file_pos -= saved_record_size;
 
231
  /* `file_pos' now points to the start of the last (probably partial) block
 
232
     in the input file. */
 
233
 
 
234
  if (lseek (input_fd, file_pos, SEEK_SET) < 0)
 
235
    error (0, errno, _("%s: seek failed"), quotearg_colon (file));
 
236
 
 
237
  if (safe_read (input_fd, G_buffer, saved_record_size) != saved_record_size)
 
238
    {
 
239
      error (0, errno, _("%s: read error"), quotearg_colon (file));
 
240
      return false;
 
241
    }
 
242
 
 
243
  match_start = past_end = G_buffer + saved_record_size;
 
244
  /* For non-regexp search, move past impossible positions for a match. */
 
245
  if (sentinel_length)
 
246
    match_start -= match_length1;
 
247
 
 
248
  for (;;)
 
249
    {
 
250
      /* Search backward from `match_start' - 1 to `G_buffer' for a match
 
251
         with `separator'; for speed, use strncmp if `separator' contains no
 
252
         metacharacters.
 
253
         If the match succeeds, set `match_start' to point to the start of
 
254
         the match and `match_length' to the length of the match.
 
255
         Otherwise, make `match_start' < `G_buffer'. */
 
256
      if (sentinel_length == 0)
 
257
        {
 
258
          size_t i = match_start - G_buffer;
 
259
          regoff_t ri = i;
 
260
          regoff_t range = 1 - ri;
 
261
          regoff_t ret;
 
262
 
 
263
          if (1 < range)
 
264
            error (EXIT_FAILURE, 0, _("record too large"));
 
265
 
 
266
          if (range == 1
 
267
              || ((ret = re_search (&compiled_separator, G_buffer,
 
268
                                    i, i - 1, range, &regs))
 
269
                  == -1))
 
270
            match_start = G_buffer - 1;
 
271
          else if (ret == -2)
 
272
            {
 
273
              error (EXIT_FAILURE, 0,
 
274
                     _("error in regular expression search"));
 
275
            }
 
276
          else
 
277
            {
 
278
              match_start = G_buffer + regs.start[0];
 
279
              match_length = regs.end[0] - regs.start[0];
 
280
            }
 
281
        }
 
282
      else
 
283
        {
 
284
          /* `match_length' is constant for non-regexp boundaries. */
 
285
          while (*--match_start != first_char
 
286
                 || (match_length1 && strncmp (match_start + 1, separator1,
 
287
                                               match_length1)))
 
288
            /* Do nothing. */ ;
 
289
        }
 
290
 
 
291
      /* Check whether we backed off the front of `G_buffer' without finding
 
292
         a match for `separator'. */
 
293
      if (match_start < G_buffer)
 
294
        {
 
295
          if (file_pos == 0)
 
296
            {
 
297
              /* Hit the beginning of the file; print the remaining record. */
 
298
              output (G_buffer, past_end);
 
299
              return true;
 
300
            }
 
301
 
 
302
          saved_record_size = past_end - G_buffer;
 
303
          if (saved_record_size > read_size)
 
304
            {
 
305
              /* `G_buffer_size' is about twice `read_size', so since
 
306
                 we want to read in another `read_size' bytes before
 
307
                 the data already in `G_buffer', we need to increase
 
308
                 `G_buffer_size'. */
 
309
              char *newbuffer;
 
310
              size_t offset = sentinel_length ? sentinel_length : 1;
 
311
              ptrdiff_t match_start_offset = match_start - G_buffer;
 
312
              ptrdiff_t past_end_offset = past_end - G_buffer;
 
313
              size_t old_G_buffer_size = G_buffer_size;
 
314
 
 
315
              read_size *= 2;
 
316
              G_buffer_size = read_size * 2 + sentinel_length + 2;
 
317
              if (G_buffer_size < old_G_buffer_size)
 
318
                xalloc_die ();
 
319
              newbuffer = xrealloc (G_buffer - offset, G_buffer_size);
 
320
              newbuffer += offset;
 
321
              /* Adjust the pointers for the new buffer location.  */
 
322
              match_start = newbuffer + match_start_offset;
 
323
              past_end = newbuffer + past_end_offset;
 
324
              G_buffer = newbuffer;
 
325
            }
 
326
 
 
327
          /* Back up to the start of the next bufferfull of the file.  */
 
328
          if (file_pos >= read_size)
 
329
            file_pos -= read_size;
 
330
          else
 
331
            {
 
332
              read_size = file_pos;
 
333
              file_pos = 0;
 
334
            }
 
335
          if (lseek (input_fd, file_pos, SEEK_SET) < 0)
 
336
            error (0, errno, _("%s: seek failed"), quotearg_colon (file));
 
337
 
 
338
          /* Shift the pending record data right to make room for the new.
 
339
             The source and destination regions probably overlap.  */
 
340
          memmove (G_buffer + read_size, G_buffer, saved_record_size);
 
341
          past_end = G_buffer + read_size + saved_record_size;
 
342
          /* For non-regexp searches, avoid unneccessary scanning. */
 
343
          if (sentinel_length)
 
344
            match_start = G_buffer + read_size;
 
345
          else
 
346
            match_start = past_end;
 
347
 
 
348
          if (safe_read (input_fd, G_buffer, read_size) != read_size)
 
349
            {
 
350
              error (0, errno, _("%s: read error"), quotearg_colon (file));
 
351
              return false;
 
352
            }
 
353
        }
 
354
      else
 
355
        {
 
356
          /* Found a match of `separator'. */
 
357
          if (separator_ends_record)
 
358
            {
 
359
              char *match_end = match_start + match_length;
 
360
 
 
361
              /* If this match of `separator' isn't at the end of the
 
362
                 file, print the record. */
 
363
              if (!first_time || match_end != past_end)
 
364
                output (match_end, past_end);
 
365
              past_end = match_end;
 
366
              first_time = false;
 
367
            }
 
368
          else
 
369
            {
 
370
              output (match_start, past_end);
 
371
              past_end = match_start;
 
372
            }
 
373
 
 
374
          /* For non-regex matching, we can back up.  */
 
375
          if (sentinel_length > 0)
 
376
            match_start -= match_length - 1;
 
377
        }
 
378
    }
 
379
}
 
380
 
 
381
#if DONT_UNLINK_WHILE_OPEN
 
382
 
 
383
/* FIXME-someday: remove all of this DONT_UNLINK_WHILE_OPEN junk.
 
384
   Using atexit like this is wrong, since it can fail
 
385
   when called e.g. 32 or more times.
 
386
   But this isn't a big deal, since the code is used only on WOE/DOS
 
387
   systems, and few people invoke tac on that many nonseekable files.  */
 
388
 
 
389
static const char *file_to_remove;
 
390
static FILE *fp_to_close;
 
391
 
 
392
static void
 
393
unlink_tempfile (void)
 
394
{
 
395
  fclose (fp_to_close);
 
396
  unlink (file_to_remove);
 
397
}
 
398
 
 
399
static void
 
400
record_or_unlink_tempfile (char const *fn, FILE *fp)
 
401
{
 
402
  if (!file_to_remove)
 
403
    {
 
404
      file_to_remove = fn;
 
405
      fp_to_close = fp;
 
406
      atexit (unlink_tempfile);
 
407
    }
 
408
}
 
409
 
 
410
#else
 
411
 
 
412
static void
 
413
record_or_unlink_tempfile (char const *fn, FILE *fp ATTRIBUTE_UNUSED)
 
414
{
 
415
  unlink (fn);
 
416
}
 
417
 
 
418
#endif
 
419
 
 
420
/* Copy from file descriptor INPUT_FD (corresponding to the named FILE) to
 
421
   a temporary file, and set *G_TMP and *G_TEMPFILE to the resulting stream
 
422
   and file name.  Return true if successful.  */
 
423
 
 
424
static bool
 
425
copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)
 
426
{
 
427
  static char *template = NULL;
 
428
  static char const *tempdir;
 
429
  char *tempfile;
 
430
  FILE *tmp;
 
431
  int fd;
 
432
 
 
433
  if (template == NULL)
 
434
    {
 
435
      char const * const Template = "%s/tacXXXXXX";
 
436
      tempdir = getenv ("TMPDIR");
 
437
      if (tempdir == NULL)
 
438
        tempdir = DEFAULT_TMPDIR;
 
439
 
 
440
      /* Subtract 2 for `%s' and add 1 for the trailing NUL byte.  */
 
441
      template = xmalloc (strlen (tempdir) + strlen (Template) - 2 + 1);
 
442
      sprintf (template, Template, tempdir);
 
443
    }
 
444
 
 
445
  /* FIXME: there's a small window between a successful mkstemp call
 
446
     and the unlink that's performed by record_or_unlink_tempfile.
 
447
     If we're interrupted in that interval, this code fails to remove
 
448
     the temporary file.  On systems that define DONT_UNLINK_WHILE_OPEN,
 
449
     the window is much larger -- it extends to the atexit-called
 
450
     unlink_tempfile.
 
451
     FIXME: clean up upon fatal signal.  Don't block them, in case
 
452
     $TMPFILE is a remote file system.  */
 
453
 
 
454
  tempfile = template;
 
455
  fd = mkstemp (template);
 
456
  if (fd < 0)
 
457
    {
 
458
      error (0, errno, _("cannot create temporary file in %s"),
 
459
             quote (tempdir));
 
460
      return false;
 
461
    }
 
462
 
 
463
  tmp = fdopen (fd, (O_BINARY ? "w+b" : "w+"));
 
464
  if (! tmp)
 
465
    {
 
466
      error (0, errno, _("cannot open %s for writing"), quote (tempfile));
 
467
      close (fd);
 
468
      unlink (tempfile);
 
469
      return false;
 
470
    }
 
471
 
 
472
  record_or_unlink_tempfile (tempfile, tmp);
 
473
 
 
474
  while (1)
 
475
    {
 
476
      size_t bytes_read = safe_read (input_fd, G_buffer, read_size);
 
477
      if (bytes_read == 0)
 
478
        break;
 
479
      if (bytes_read == SAFE_READ_ERROR)
 
480
        {
 
481
          error (0, errno, _("%s: read error"), quotearg_colon (file));
 
482
          goto Fail;
 
483
        }
 
484
 
 
485
      if (fwrite (G_buffer, 1, bytes_read, tmp) != bytes_read)
 
486
        {
 
487
          error (0, errno, _("%s: write error"), quotearg_colon (tempfile));
 
488
          goto Fail;
 
489
        }
 
490
    }
 
491
 
 
492
  if (fflush (tmp) != 0)
 
493
    {
 
494
      error (0, errno, _("%s: write error"), quotearg_colon (tempfile));
 
495
      goto Fail;
 
496
    }
 
497
 
 
498
  *g_tmp = tmp;
 
499
  *g_tempfile = tempfile;
 
500
  return true;
 
501
 
 
502
 Fail:
 
503
  fclose (tmp);
 
504
  return false;
 
505
}
 
506
 
 
507
/* Copy INPUT_FD to a temporary, then tac that file.
 
508
   Return true if successful.  */
 
509
 
 
510
static bool
 
511
tac_nonseekable (int input_fd, const char *file)
 
512
{
 
513
  FILE *tmp_stream;
 
514
  char *tmp_file;
 
515
  return (copy_to_temp (&tmp_stream, &tmp_file, input_fd, file)
 
516
          && tac_seekable (fileno (tmp_stream), tmp_file));
 
517
}
 
518
 
 
519
/* Print FILE in reverse, copying it to a temporary
 
520
   file first if it is not seekable.
 
521
   Return true if successful.  */
 
522
 
 
523
static bool
 
524
tac_file (const char *filename)
 
525
{
 
526
  bool ok;
 
527
  off_t file_size;
 
528
  int fd;
 
529
  bool is_stdin = STREQ (filename, "-");
 
530
 
 
531
  if (is_stdin)
 
532
    {
 
533
      have_read_stdin = true;
 
534
      fd = STDIN_FILENO;
 
535
      filename = _("standard input");
 
536
      if (O_BINARY && ! isatty (STDIN_FILENO))
 
537
        xfreopen (NULL, "rb", stdin);
 
538
    }
 
539
  else
 
540
    {
 
541
      fd = open (filename, O_RDONLY | O_BINARY);
 
542
      if (fd < 0)
 
543
        {
 
544
          error (0, errno, _("cannot open %s for reading"), quote (filename));
 
545
          return false;
 
546
        }
 
547
    }
 
548
 
 
549
  file_size = lseek (fd, (off_t) 0, SEEK_END);
 
550
 
 
551
  ok = (file_size < 0 || isatty (fd)
 
552
        ? tac_nonseekable (fd, filename)
 
553
        : tac_seekable (fd, filename));
 
554
 
 
555
  if (!is_stdin && close (fd) != 0)
 
556
    {
 
557
      error (0, errno, _("%s: read error"), quotearg_colon (filename));
 
558
      ok = false;
 
559
    }
 
560
  return ok;
 
561
}
 
562
 
 
563
int
 
564
main (int argc, char **argv)
 
565
{
 
566
  const char *error_message;    /* Return value from re_compile_pattern. */
 
567
  int optc;
 
568
  bool ok;
 
569
  size_t half_buffer_size;
 
570
 
 
571
  /* Initializer for file_list if no file-arguments
 
572
     were specified on the command line.  */
 
573
  static char const *const default_file_list[] = {"-", NULL};
 
574
  char const *const *file;
 
575
 
 
576
  initialize_main (&argc, &argv);
 
577
  set_program_name (argv[0]);
 
578
  setlocale (LC_ALL, "");
 
579
  bindtextdomain (PACKAGE, LOCALEDIR);
 
580
  textdomain (PACKAGE);
 
581
 
 
582
  atexit (close_stdout);
 
583
 
 
584
  separator = "\n";
 
585
  sentinel_length = 1;
 
586
  separator_ends_record = true;
 
587
 
 
588
  while ((optc = getopt_long (argc, argv, "brs:", longopts, NULL)) != -1)
 
589
    {
 
590
      switch (optc)
 
591
        {
 
592
        case 'b':
 
593
          separator_ends_record = false;
 
594
          break;
 
595
        case 'r':
 
596
          sentinel_length = 0;
 
597
          break;
 
598
        case 's':
 
599
          separator = optarg;
 
600
          if (*separator == 0)
 
601
            error (EXIT_FAILURE, 0, _("separator cannot be empty"));
 
602
          break;
 
603
        case_GETOPT_HELP_CHAR;
 
604
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
 
605
        default:
 
606
          usage (EXIT_FAILURE);
 
607
        }
 
608
    }
 
609
 
 
610
  if (sentinel_length == 0)
 
611
    {
 
612
      compiled_separator.buffer = NULL;
 
613
      compiled_separator.allocated = 0;
 
614
      compiled_separator.fastmap = compiled_separator_fastmap;
 
615
      compiled_separator.translate = NULL;
 
616
      error_message = re_compile_pattern (separator, strlen (separator),
 
617
                                          &compiled_separator);
 
618
      if (error_message)
 
619
        error (EXIT_FAILURE, 0, "%s", error_message);
 
620
    }
 
621
  else
 
622
    match_length = sentinel_length = strlen (separator);
 
623
 
 
624
  read_size = INITIAL_READSIZE;
 
625
  while (sentinel_length >= read_size / 2)
 
626
    {
 
627
      if (SIZE_MAX / 2 < read_size)
 
628
        xalloc_die ();
 
629
      read_size *= 2;
 
630
    }
 
631
  half_buffer_size = read_size + sentinel_length + 1;
 
632
  G_buffer_size = 2 * half_buffer_size;
 
633
  if (! (read_size < half_buffer_size && half_buffer_size < G_buffer_size))
 
634
    xalloc_die ();
 
635
  G_buffer = xmalloc (G_buffer_size);
 
636
  if (sentinel_length)
 
637
    {
 
638
      strcpy (G_buffer, separator);
 
639
      G_buffer += sentinel_length;
 
640
    }
 
641
  else
 
642
    {
 
643
      ++G_buffer;
 
644
    }
 
645
 
 
646
  file = (optind < argc
 
647
          ? (char const *const *) &argv[optind]
 
648
          : default_file_list);
 
649
 
 
650
  if (O_BINARY && ! isatty (STDOUT_FILENO))
 
651
    xfreopen (NULL, "wb", stdout);
 
652
 
 
653
  {
 
654
    size_t i;
 
655
    ok = true;
 
656
    for (i = 0; file[i]; ++i)
 
657
      ok &= tac_file (file[i]);
 
658
  }
 
659
 
 
660
  /* Flush the output buffer. */
 
661
  output ((char *) NULL, (char *) NULL);
 
662
 
 
663
  if (have_read_stdin && close (STDIN_FILENO) < 0)
 
664
    error (EXIT_FAILURE, errno, "-");
 
665
  exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
 
666
}