summaryrefslogtreecommitdiff
path: root/src/csplit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/csplit.c')
-rw-r--r--src/csplit.c819
1 files changed, 418 insertions, 401 deletions
diff --git a/src/csplit.c b/src/csplit.c
index c2105bc..ac92d4c 100644
--- a/src/csplit.c
+++ b/src/csplit.c
@@ -1,10 +1,10 @@
/* csplit - split a file into sections determined by context lines
- Copyright (C) 91, 1995-2007 Free Software Foundation, Inc.
+ Copyright (C) 1991-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -12,14 +12,14 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Stuart Kemp, cpsrk@groper.jcu.edu.au.
Modified by David MacKenzie, djm@gnu.ai.mit.edu. */
#include <config.h>
+#include <assert.h>
#include <getopt.h>
#include <sys/types.h>
#include <signal.h>
@@ -30,30 +30,18 @@
#include "error.h"
#include "fd-reopen.h"
-#include "inttostr.h"
#include "quote.h"
#include "safe-read.h"
#include "stdio--.h"
+#include "xdectoint.h"
#include "xstrtol.h"
-/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
- present. */
-#ifndef SA_NOCLDSTOP
-# define SA_NOCLDSTOP 0
-# define sigprocmask(How, Set, Oset) /* empty */
-# define sigset_t int
-# if ! HAVE_SIGINTERRUPT
-# define siginterrupt(sig, flag) /* empty */
-# endif
-#endif
-
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "csplit"
-#define AUTHORS "Stuart Kemp", "David MacKenzie"
-
-/* Increment size of area for control records. */
-#define ALLOC_SIZE 20
+#define AUTHORS \
+ proper_name ("Stuart Kemp"), \
+ proper_name ("David MacKenzie")
/* The default prefix for output file names. */
#define DEFAULT_PREFIX "xx"
@@ -65,7 +53,7 @@ struct control
uintmax_t lines_required; /* Number of lines required. */
uintmax_t repeat; /* Repeat count. */
int argnum; /* ARGV index. */
- bool repeat_forever; /* True if `*' used as a repeat count. */
+ bool repeat_forever; /* True if '*' used as a repeat count. */
bool ignore; /* If true, produce no output (for regexp). */
bool regexpr; /* True if regular expression was used. */
struct re_pattern_buffer re_compiled; /* Compiled regular expression. */
@@ -125,10 +113,6 @@ static void close_output_file (void);
static void create_output_file (void);
static void delete_all_files (bool);
static void save_line_to_file (const struct cstring *line);
-void usage (int status);
-
-/* The name this program was run with. */
-char *program_name;
/* Start of buffer list. */
static struct buffer_record *head = NULL;
@@ -136,7 +120,7 @@ static struct buffer_record *head = NULL;
/* Partially read line. */
static char *hold_area = NULL;
-/* Number of bytes in `hold_area'. */
+/* Number of bytes in 'hold_area'. */
static size_t hold_count = 0;
/* Number of the last line in the buffers. */
@@ -184,16 +168,26 @@ static bool volatile remove_files;
/* If true, remove all output files which have a zero length. */
static bool elide_empty_files;
+/* If true, suppress the lines that match the PATTERN */
+static bool suppress_matched;
+
/* The compiled pattern arguments, which determine how to split
the input file. */
static struct control *controls;
-/* Number of elements in `controls'. */
+/* Number of elements in 'controls'. */
static size_t control_used;
/* The set of signals that are caught. */
static sigset_t caught_signals;
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ SUPPRESS_MATCHED_OPTION = CHAR_MAX + 1
+};
+
static struct option const longopts[] =
{
{"digits", required_argument, NULL, 'n'},
@@ -203,6 +197,7 @@ static struct option const longopts[] =
{"elide-empty-files", no_argument, NULL, 'z'},
{"prefix", required_argument, NULL, 'f'},
{"suffix-format", required_argument, NULL, 'b'},
+ {"suppress-matched", no_argument, NULL, SUPPRESS_MATCHED_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -241,12 +236,11 @@ xalloc_die (void)
static void
interrupt_handler (int sig)
{
- if (! SA_NOCLDSTOP)
- signal (sig, SIG_IGN);
-
delete_all_files (true);
-
signal (sig, SIG_DFL);
+ /* The signal has been reset to SIG_DFL, but blocked during this
+ handler. Force the default action of this signal once the
+ handler returns and the block is removed. */
raise (sig);
}
@@ -361,11 +355,11 @@ record_line_starts (struct buffer_record *b)
line_start = b->buffer;
bytes_left = b->bytes_used;
- for (;;)
+ while (true)
{
line_end = memchr (line_start, '\n', bytes_left);
if (line_end == NULL)
- break;
+ break;
line_length = line_end - line_start + 1;
keep_new_line (b, line_start, line_length);
bytes_left -= line_length;
@@ -377,12 +371,12 @@ record_line_starts (struct buffer_record *b)
if (bytes_left)
{
if (have_read_eof)
- {
- keep_new_line (b, line_start, bytes_left);
- lines++;
- }
+ {
+ keep_new_line (b, line_start, bytes_left);
+ lines++;
+ }
else
- save_to_hold_area (xmemdup (line_start, bytes_left), bytes_left);
+ save_to_hold_area (xmemdup (line_start, bytes_left), bytes_left);
}
b->num_lines = lines;
@@ -437,6 +431,14 @@ get_new_buffer (size_t min_size)
static void
free_buffer (struct buffer_record *buf)
{
+ struct line *l;
+ for (l = buf->line_start; l;)
+ {
+ struct line *n = l->next;
+ free (l);
+ l = n;
+ }
+ buf->line_start = NULL;
free (buf->buffer);
buf->buffer = NULL;
}
@@ -457,7 +459,7 @@ save_buffer (struct buffer_record *buf)
else
{
for (p = head; p->next; p = p->next)
- /* Do nothing. */ ;
+ /* Do nothing. */ ;
p->next = buf;
}
}
@@ -498,27 +500,25 @@ load_buffer (void)
bytes_avail = b->bytes_alloc; /* Size of buffer returned. */
p = b->buffer;
- /* First check the `holding' area for a partial line. */
+ /* First check the 'holding' area for a partial line. */
if (hold_count)
- {
- memcpy (p, hold_area, hold_count);
- p += hold_count;
- b->bytes_used += hold_count;
- bytes_avail -= hold_count;
- hold_count = 0;
- }
+ {
+ memcpy (p, hold_area, hold_count);
+ p += hold_count;
+ b->bytes_used += hold_count;
+ bytes_avail -= hold_count;
+ hold_count = 0;
+ }
b->bytes_used += read_input (p, bytes_avail);
lines_found = record_line_starts (b);
- if (!lines_found)
- free_buffer (b);
if (lines_found || have_read_eof)
- break;
+ break;
if (xalloc_oversized (2, b->bytes_alloc))
- xalloc_die ();
+ xalloc_die ();
bytes_wanted = 2 * b->bytes_alloc;
free_buffer (b);
free (b);
@@ -527,7 +527,10 @@ load_buffer (void)
if (lines_found)
save_buffer (b);
else
- free (b);
+ {
+ free_buffer (b);
+ free (b);
+ }
return lines_found != 0;
}
@@ -561,6 +564,7 @@ remove_line (void)
if (prev_buf)
{
free_buffer (prev_buf);
+ free (prev_buf);
prev_buf = NULL;
}
@@ -582,13 +586,13 @@ remove_line (void)
/* Go on to the next line record. */
head->curr_line = l->next;
if (head->curr_line == NULL || head->curr_line->used == 0)
- {
- /* Go on to the next data block.
- but first record the current one so we can free it
- once the line we're returning has been processed. */
- prev_buf = head;
- head = head->next;
- }
+ {
+ /* Go on to the next data block.
+ but first record the current one so we can free it
+ once the line we're returning has been processed. */
+ prev_buf = head;
+ head = head->next;
+ }
}
return line;
@@ -610,24 +614,25 @@ find_line (uintmax_t linenum)
for (b = head;;)
{
+ assert (b);
if (linenum < b->start_line + b->num_lines)
- {
- /* The line is in this buffer. */
- struct line *l;
- size_t offset; /* How far into the buffer the line is. */
-
- l = b->line_start;
- offset = linenum - b->start_line;
- /* Find the control record. */
- while (offset >= CTRL_SIZE)
- {
- l = l->next;
- offset -= CTRL_SIZE;
- }
- return &l->starts[offset];
- }
+ {
+ /* The line is in this buffer. */
+ struct line *l;
+ size_t offset; /* How far into the buffer the line is. */
+
+ l = b->line_start;
+ offset = linenum - b->start_line;
+ /* Find the control record. */
+ while (offset >= CTRL_SIZE)
+ {
+ l = l->next;
+ offset -= CTRL_SIZE;
+ }
+ return &l->starts[offset];
+ }
if (b->next == NULL && !load_buffer ())
- return NULL;
+ return NULL;
b = b->next; /* Try the next data block. */
}
}
@@ -646,7 +651,8 @@ static void
set_input_file (const char *name)
{
if (! STREQ (name, "-") && fd_reopen (STDIN_FILENO, name, O_RDONLY, 0) < 0)
- error (EXIT_FAILURE, errno, _("cannot open %s for reading"), quote (name));
+ error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
+ quoteaf (name));
}
/* Write all lines from the beginning of the buffer up to, but
@@ -666,7 +672,8 @@ write_to_file (uintmax_t last_line, bool ignore, int argnum)
if (first_line > last_line)
{
- error (0, 0, _("%s: line number out of range"), global_argv[argnum]);
+ error (0, 0, _("%s: line number out of range"),
+ quote (global_argv[argnum]));
cleanup_fatal ();
}
@@ -676,12 +683,13 @@ write_to_file (uintmax_t last_line, bool ignore, int argnum)
{
line = remove_line ();
if (line == NULL)
- {
- error (0, 0, _("%s: line number out of range"), global_argv[argnum]);
- cleanup_fatal ();
- }
+ {
+ error (0, 0, _("%s: line number out of range"),
+ quote (global_argv[argnum]));
+ cleanup_fatal ();
+ }
if (!ignore)
- save_line_to_file (line);
+ save_line_to_file (line);
}
}
@@ -707,7 +715,7 @@ handle_line_error (const struct control *p, uintmax_t repetition)
char buf[INT_BUFSIZE_BOUND (uintmax_t)];
fprintf (stderr, _("%s: %s: line number out of range"),
- program_name, quote (umaxtostr (p->lines_required, buf)));
+ program_name, quote (umaxtostr (p->lines_required, buf)));
if (repetition)
fprintf (stderr, _(" on repetition %s\n"), umaxtostr (repetition, buf));
else
@@ -726,25 +734,32 @@ process_line_count (const struct control *p, uintmax_t repetition)
{
uintmax_t linenum;
uintmax_t last_line_to_save = p->lines_required * (repetition + 1);
- struct cstring *line;
create_output_file ();
- linenum = get_first_line_in_buffer ();
+ /* Ensure that the line number specified is not 1 greater than
+ the number of lines in the file.
+ When suppressing matched lines, check before the loop. */
+ if (no_more_lines () && suppress_matched)
+ handle_line_error (p, repetition);
+ linenum = get_first_line_in_buffer ();
while (linenum++ < last_line_to_save)
{
- line = remove_line ();
+ struct cstring *line = remove_line ();
if (line == NULL)
- handle_line_error (p, repetition);
+ handle_line_error (p, repetition);
save_line_to_file (line);
}
close_output_file ();
+ if (suppress_matched)
+ remove_line ();
+
/* Ensure that the line number specified is not 1 greater than
the number of lines in the file. */
- if (no_more_lines ())
+ if (no_more_lines () && !suppress_matched)
handle_line_error (p, repetition);
}
@@ -753,7 +768,7 @@ static void
regexp_error (struct control *p, uintmax_t repetition, bool ignore)
{
fprintf (stderr, _("%s: %s: match not found"),
- program_name, quote (global_argv[p->argnum]));
+ program_name, quote (global_argv[p->argnum]));
if (repetition)
{
@@ -787,81 +802,84 @@ process_regexp (struct control *p, uintmax_t repetition)
if (!ignore)
create_output_file ();
+ if (suppress_matched && current_line > 0)
+ remove_line ();
+
/* If there is no offset for the regular expression, or
it is positive, then it is not necessary to buffer the lines. */
if (p->offset >= 0)
{
- for (;;)
- {
- line = find_line (++current_line);
- if (line == NULL)
- {
- if (p->repeat_forever)
- {
- if (!ignore)
- {
- dump_rest_of_file ();
- close_output_file ();
- }
- exit (EXIT_SUCCESS);
- }
- else
- regexp_error (p, repetition, ignore);
- }
- line_len = line->len;
- if (line->str[line_len - 1] == '\n')
- line_len--;
- ret = re_search (&p->re_compiled, line->str, line_len,
- 0, line_len, NULL);
- if (ret == -2)
- {
- error (0, 0, _("error in regular expression search"));
- cleanup_fatal ();
- }
- if (ret == -1)
- {
- line = remove_line ();
- if (!ignore)
- save_line_to_file (line);
- }
- else
- break;
- }
+ while (true)
+ {
+ line = find_line (++current_line);
+ if (line == NULL)
+ {
+ if (p->repeat_forever)
+ {
+ if (!ignore)
+ {
+ dump_rest_of_file ();
+ close_output_file ();
+ }
+ exit (EXIT_SUCCESS);
+ }
+ else
+ regexp_error (p, repetition, ignore);
+ }
+ line_len = line->len;
+ if (line->str[line_len - 1] == '\n')
+ line_len--;
+ ret = re_search (&p->re_compiled, line->str, line_len,
+ 0, line_len, NULL);
+ if (ret == -2)
+ {
+ error (0, 0, _("error in regular expression search"));
+ cleanup_fatal ();
+ }
+ if (ret == -1)
+ {
+ line = remove_line ();
+ if (!ignore)
+ save_line_to_file (line);
+ }
+ else
+ break;
+ }
}
else
{
/* Buffer the lines. */
- for (;;)
- {
- line = find_line (++current_line);
- if (line == NULL)
- {
- if (p->repeat_forever)
- {
- if (!ignore)
- {
- dump_rest_of_file ();
- close_output_file ();
- }
- exit (EXIT_SUCCESS);
- }
- else
- regexp_error (p, repetition, ignore);
- }
- line_len = line->len;
- if (line->str[line_len - 1] == '\n')
- line_len--;
- ret = re_search (&p->re_compiled, line->str, line_len,
- 0, line_len, NULL);
- if (ret == -2)
- {
- error (0, 0, _("error in regular expression search"));
- cleanup_fatal ();
- }
- if (ret != -1)
- break;
- }
+ while (true)
+ {
+ line = find_line (++current_line);
+ if (line == NULL)
+ {
+ if (p->repeat_forever)
+ {
+ if (!ignore)
+ {
+ dump_rest_of_file ();
+ close_output_file ();
+ }
+ exit (EXIT_SUCCESS);
+ }
+ else
+ regexp_error (p, repetition, ignore);
+ }
+ line_len = line->len;
+ if (line->str[line_len - 1] == '\n')
+ line_len--;
+ ret = re_search (&p->re_compiled, line->str, line_len,
+ 0, line_len, NULL);
+ if (ret == -2)
+ {
+ error (0, 0, _("error in regular expression search"));
+ cleanup_fatal ();
+ }
+ if (ret != -1)
+ break;
+ }
}
/* Account for any offset from this regexp. */
@@ -887,17 +905,17 @@ split_file (void)
{
uintmax_t j;
if (controls[i].regexpr)
- {
- for (j = 0; (controls[i].repeat_forever
- || j <= controls[i].repeat); j++)
- process_regexp (&controls[i], j);
- }
+ {
+ for (j = 0; (controls[i].repeat_forever
+ || j <= controls[i].repeat); j++)
+ process_regexp (&controls[i], j);
+ }
else
- {
- for (j = 0; (controls[i].repeat_forever
- || j <= controls[i].repeat); j++)
- process_line_count (&controls[i], j);
- }
+ {
+ for (j = 0; (controls[i].repeat_forever
+ || j <= controls[i].repeat); j++)
+ process_line_count (&controls[i], j);
+ }
}
create_output_file ();
@@ -928,23 +946,31 @@ make_filename (unsigned int num)
static void
create_output_file (void)
{
- sigset_t oldset;
bool fopen_ok;
int fopen_errno;
output_filename = make_filename (files_created);
- /* Create the output file in a critical section, to avoid races. */
- sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
- output_stream = fopen (output_filename, "w");
- fopen_ok = (output_stream != NULL);
- fopen_errno = errno;
- files_created += fopen_ok;
- sigprocmask (SIG_SETMASK, &oldset, NULL);
+ if (files_created == UINT_MAX)
+ {
+ fopen_ok = false;
+ fopen_errno = EOVERFLOW;
+ }
+ else
+ {
+ /* Create the output file in a critical section, to avoid races. */
+ sigset_t oldset;
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
+ output_stream = fopen (output_filename, "w");
+ fopen_ok = (output_stream != NULL);
+ fopen_errno = errno;
+ files_created += fopen_ok;
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
+ }
if (! fopen_ok)
{
- error (0, fopen_errno, "%s", output_filename);
+ error (0, fopen_errno, "%s", quotef (output_filename));
cleanup_fatal ();
}
bytes_written = 0;
@@ -965,7 +991,7 @@ delete_all_files (bool in_signal_handler)
{
const char *name = make_filename (i);
if (unlink (name) != 0 && !in_signal_handler)
- error (0, errno, "%s", name);
+ error (0, errno, "%s", quotef (name));
}
files_created = 0;
@@ -980,41 +1006,41 @@ close_output_file (void)
if (output_stream)
{
if (ferror (output_stream))
- {
- error (0, 0, _("write error for %s"), quote (output_filename));
- output_stream = NULL;
- cleanup_fatal ();
- }
+ {
+ error (0, 0, _("write error for %s"), quoteaf (output_filename));
+ output_stream = NULL;
+ cleanup_fatal ();
+ }
if (fclose (output_stream) != 0)
- {
- error (0, errno, "%s", output_filename);
- output_stream = NULL;
- cleanup_fatal ();
- }
+ {
+ error (0, errno, "%s", quotef (output_filename));
+ output_stream = NULL;
+ cleanup_fatal ();
+ }
if (bytes_written == 0 && elide_empty_files)
- {
- sigset_t oldset;
- bool unlink_ok;
- int unlink_errno;
-
- /* Remove the output file in a critical section, to avoid races. */
- sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
- unlink_ok = (unlink (output_filename) == 0);
- unlink_errno = errno;
- files_created -= unlink_ok;
- sigprocmask (SIG_SETMASK, &oldset, NULL);
-
- if (! unlink_ok)
- error (0, unlink_errno, "%s", output_filename);
- }
+ {
+ sigset_t oldset;
+ bool unlink_ok;
+ int unlink_errno;
+
+ /* Remove the output file in a critical section, to avoid races. */
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
+ unlink_ok = (unlink (output_filename) == 0);
+ unlink_errno = errno;
+ files_created -= unlink_ok;
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
+
+ if (! unlink_ok)
+ error (0, unlink_errno, "%s", quotef (output_filename));
+ }
else
- {
- if (!suppress_count)
- {
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- fprintf (stdout, "%s\n", umaxtostr (bytes_written, buf));
- }
- }
+ {
+ if (!suppress_count)
+ {
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ fprintf (stdout, "%s\n", umaxtostr (bytes_written, buf));
+ }
+ }
output_stream = NULL;
}
}
@@ -1025,7 +1051,13 @@ close_output_file (void)
static void
save_line_to_file (const struct cstring *line)
{
- fwrite (line->str, sizeof (char), line->len, output_stream);
+ size_t l = fwrite (line->str, sizeof (char), line->len, output_stream);
+ if (l != line->len)
+ {
+ error (0, errno, _("write error for %s"), quoteaf (output_filename));
+ output_stream = NULL;
+ cleanup_fatal ();
+ }
bytes_written += line->len;
}
@@ -1057,7 +1089,8 @@ static void
check_for_offset (struct control *p, const char *str, const char *num)
{
if (xstrtoimax (num, NULL, 10, &p->offset, "") != LONGINT_OK)
- error (EXIT_FAILURE, 0, _("%s: integer expected after delimiter"), str);
+ error (EXIT_FAILURE, 0, _("%s: integer expected after delimiter"),
+ quote (str));
}
/* Given that the first character of command line arg STR is '{',
@@ -1073,7 +1106,8 @@ parse_repeat_count (int argnum, struct control *p, char *str)
end = str + strlen (str) - 1;
if (*end != '}')
- error (EXIT_FAILURE, 0, _("%s: `}' is required in repeat count"), str);
+ error (EXIT_FAILURE, 0, _("%s: '}' is required in repeat count"),
+ quote (str));
*end = '\0';
if (str+1 == end-1 && *(str+1) == '*')
@@ -1081,11 +1115,11 @@ parse_repeat_count (int argnum, struct control *p, char *str)
else
{
if (xstrtoumax (str + 1, NULL, 10, &val, "") != LONGINT_OK)
- {
- error (EXIT_FAILURE, 0,
- _("%s}: integer required between `{' and `}'"),
- global_argv[argnum]);
- }
+ {
+ error (EXIT_FAILURE, 0,
+ _("%s}: integer required between '{' and '}'"),
+ quote (global_argv[argnum]));
+ }
p->repeat = val;
}
@@ -1110,7 +1144,7 @@ extract_regexp (int argnum, bool ignore, char const *str)
closing_delim = strrchr (str + 1, delim);
if (closing_delim == NULL)
error (EXIT_FAILURE, 0,
- _("%s: closing delimiter `%c' missing"), str, delim);
+ _("%s: closing delimiter '%c' missing"), str, delim);
len = closing_delim - str - 1;
p = new_control_record ();
@@ -1127,7 +1161,7 @@ extract_regexp (int argnum, bool ignore, char const *str)
err = re_compile_pattern (str + 1, len, &p->re_compiled);
if (err)
{
- error (0, 0, _("%s: invalid regular expression: %s"), str, err);
+ error (0, 0, _("%s: invalid regular expression: %s"), quote (str), err);
cleanup_fatal ();
}
@@ -1151,122 +1185,105 @@ parse_patterns (int argc, int start, char **argv)
for (i = start; i < argc; i++)
{
if (*argv[i] == '/' || *argv[i] == '%')
- {
- p = extract_regexp (i, *argv[i] == '%', argv[i]);
- }
+ {
+ p = extract_regexp (i, *argv[i] == '%', argv[i]);
+ }
else
- {
- p = new_control_record ();
- p->argnum = i;
-
- if (xstrtoumax (argv[i], NULL, 10, &val, "") != LONGINT_OK)
- error (EXIT_FAILURE, 0, _("%s: invalid pattern"), argv[i]);
- if (val == 0)
- error (EXIT_FAILURE, 0,
- _("%s: line number must be greater than zero"),
- argv[i]);
- if (val < last_val)
- {
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- error (EXIT_FAILURE, 0,
- _("line number %s is smaller than preceding line number, %s"),
- quote (argv[i]), umaxtostr (last_val, buf));
- }
-
- if (val == last_val)
- error (0, 0,
- _("warning: line number %s is the same as preceding line number"),
- quote (argv[i]));
-
- last_val = val;
-
- p->lines_required = val;
- }
+ {
+ p = new_control_record ();
+ p->argnum = i;
+
+ if (xstrtoumax (argv[i], NULL, 10, &val, "") != LONGINT_OK)
+ error (EXIT_FAILURE, 0, _("%s: invalid pattern"), quote (argv[i]));
+ if (val == 0)
+ error (EXIT_FAILURE, 0,
+ _("%s: line number must be greater than zero"),
+ argv[i]);
+ if (val < last_val)
+ {
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ error (EXIT_FAILURE, 0,
+ _("line number %s is smaller than preceding line number, %s"),
+ quote (argv[i]), umaxtostr (last_val, buf));
+ }
+
+ if (val == last_val)
+ error (0, 0,
+ _("warning: line number %s is the same as preceding line number"),
+ quote (argv[i]));
+
+ last_val = val;
+
+ p->lines_required = val;
+ }
if (i + 1 < argc && *argv[i + 1] == '{')
- {
- /* We have a repeat count. */
- i++;
- parse_repeat_count (i, p, argv[i]);
- }
- }
-}
-
-static unsigned int
-get_format_flags (char **format_ptr)
-{
- unsigned int count = 0;
-
- for (; **format_ptr; (*format_ptr)++)
- {
- switch (**format_ptr)
- {
- case '-':
- break;
-
- case '+':
- case ' ':
- count |= 1;
- break;
-
- case '#':
- count |= 2; /* Allow for 0x prefix preceding an `x' conversion. */
- break;
-
- default:
- return count;
- }
+ {
+ /* We have a repeat count. */
+ i++;
+ parse_repeat_count (i, p, argv[i]);
+ }
}
- return count;
}
-static size_t
-get_format_width (char **format_ptr)
-{
- unsigned long int val = 0;
- if (ISDIGIT (**format_ptr)
- && (xstrtoul (*format_ptr, format_ptr, 10, &val, NULL) != LONGINT_OK
- || SIZE_MAX < val))
- error (EXIT_FAILURE, 0, _("invalid format width"));
- /* Allow for enough octal digits to represent the value of UINT_MAX,
- even if the field width is less than that. */
- return MAX (val, (sizeof (unsigned int) * CHAR_BIT + 2) / 3);
-}
+/* Names for the printf format flags ' and #. These can be ORed together. */
+enum { FLAG_THOUSANDS = 1, FLAG_ALTERNATIVE = 2 };
+/* Scan the printf format flags in FORMAT, storing info about the
+ flags into *FLAGS_PTR. Return the number of flags found. */
static size_t
-get_format_prec (char **format_ptr)
+get_format_flags (char const *format, int *flags_ptr)
{
- if (**format_ptr != '.')
- return 0;
- (*format_ptr)++;
+ int flags = 0;
- if (! ISDIGIT (**format_ptr))
- return 0;
- else
+ for (size_t count = 0; ; count++)
{
- unsigned long int val;
- if (xstrtoul (*format_ptr, format_ptr, 10, &val, NULL) != LONGINT_OK
- || SIZE_MAX < val)
- error (EXIT_FAILURE, 0, _("invalid format precision"));
- return val;
+ switch (format[count])
+ {
+ case '-':
+ case '0':
+ break;
+
+ case '\'':
+ flags |= FLAG_THOUSANDS;
+ break;
+
+ case '#':
+ flags |= FLAG_ALTERNATIVE;
+ break;
+
+ default:
+ *flags_ptr = flags;
+ return count;
+ }
}
}
+/* Check that the printf format conversion specifier *FORMAT is valid
+ and compatible with FLAGS. Change it to 'u' if it is 'd' or 'i',
+ since the format will be used with an unsigned value. */
static void
-get_format_conv_type (char **format_ptr)
+check_format_conv_type (char *format, int flags)
{
- unsigned char ch = *(*format_ptr)++;
+ unsigned char ch = *format;
+ int compatible_flags = FLAG_THOUSANDS;
switch (ch)
{
case 'd':
case 'i':
- case 'o':
+ *format = 'u';
+ break;
+
case 'u':
+ break;
+
+ case 'o':
case 'x':
case 'X':
+ compatible_flags = FLAG_ALTERNATIVE;
break;
case 0:
@@ -1276,60 +1293,60 @@ get_format_conv_type (char **format_ptr)
default:
if (isprint (ch))
error (EXIT_FAILURE, 0,
- _("invalid conversion specifier in suffix: %c"), ch);
+ _("invalid conversion specifier in suffix: %c"), ch);
else
- error (EXIT_FAILURE, 0,
- _("invalid conversion specifier in suffix: \\%.3o"), ch);
+ error (EXIT_FAILURE, 0,
+ _("invalid conversion specifier in suffix: \\%.3o"), ch);
}
+
+ if (flags & ~ compatible_flags)
+ error (EXIT_FAILURE, 0,
+ _("invalid flags in conversion specification: %%%c%c"),
+ (flags & ~ compatible_flags & FLAG_ALTERNATIVE ? '#' : '\''), ch);
}
+/* Return the maximum number of bytes that can be generated by
+ applying FORMAT to an unsigned int value. If the format is
+ invalid, diagnose the problem and exit. */
static size_t
max_out (char *format)
{
- size_t out_count = 0;
bool percent = false;
- while (*format)
- {
- if (*format++ != '%')
- out_count++;
- else if (*format == '%')
- {
- format++;
- out_count++;
- }
- else
- {
- if (percent)
- error (EXIT_FAILURE, 0,
- _("too many %% conversion specifications in suffix"));
- percent = true;
- out_count += get_format_flags (&format);
- {
- size_t width = get_format_width (&format);
- size_t prec = get_format_prec (&format);
-
- out_count += MAX (width, prec);
- }
- get_format_conv_type (&format);
- }
- }
+ for (char *f = format; *f; f++)
+ if (*f == '%' && *++f != '%')
+ {
+ if (percent)
+ error (EXIT_FAILURE, 0,
+ _("too many %% conversion specifications in suffix"));
+ percent = true;
+ int flags;
+ f += get_format_flags (f, &flags);
+ while (ISDIGIT (*f))
+ f++;
+ if (*f == '.')
+ while (ISDIGIT (*++f))
+ continue;
+ check_format_conv_type (f, flags);
+ }
if (! percent)
error (EXIT_FAILURE, 0,
- _("missing %% conversion specification in suffix"));
+ _("missing %% conversion specification in suffix"));
- return out_count;
+ int maxlen = snprintf (NULL, 0, format, UINT_MAX);
+ if (! (0 <= maxlen && maxlen <= SIZE_MAX))
+ xalloc_die ();
+ return maxlen;
}
int
main (int argc, char **argv)
{
int optc;
- unsigned long int val;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -1341,60 +1358,67 @@ main (int argc, char **argv)
control_used = 0;
suppress_count = false;
remove_files = true;
+ suppress_matched = false;
prefix = DEFAULT_PREFIX;
while ((optc = getopt_long (argc, argv, "f:b:kn:sqz", longopts, NULL)) != -1)
switch (optc)
{
case 'f':
- prefix = optarg;
- break;
+ prefix = optarg;
+ break;
case 'b':
- suffix = optarg;
- break;
+ suffix = optarg;
+ break;
case 'k':
- remove_files = false;
- break;
+ remove_files = false;
+ break;
case 'n':
- if (xstrtoul (optarg, NULL, 10, &val, "") != LONGINT_OK
- || val > INT_MAX)
- error (EXIT_FAILURE, 0, _("%s: invalid number"), optarg);
- digits = val;
- break;
+ digits = xdectoimax (optarg, 0, MIN (INT_MAX, SIZE_MAX), "",
+ _("invalid number"), 0);
+ break;
case 's':
case 'q':
- suppress_count = true;
- break;
+ suppress_count = true;
+ break;
case 'z':
- elide_empty_files = true;
- break;
+ elide_empty_files = true;
+ break;
+
+ case SUPPRESS_MATCHED_OPTION:
+ suppress_matched = true;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
- usage (EXIT_FAILURE);
+ usage (EXIT_FAILURE);
}
if (argc - optind < 2)
{
if (argc <= optind)
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
usage (EXIT_FAILURE);
}
- if (suffix)
- filename_space = xmalloc (strlen (prefix) + max_out (suffix) + 2);
- else
- filename_space = xmalloc (strlen (prefix) + digits + 2);
+ size_t prefix_len = strlen (prefix);
+ size_t max_digit_string_len
+ = (suffix
+ ? max_out (suffix)
+ : MAX (INT_STRLEN_BOUND (unsigned int), digits));
+ if (SIZE_MAX - 1 - prefix_len < max_digit_string_len)
+ xalloc_die ();
+ filename_space = xmalloc (prefix_len + max_digit_string_len + 1);
set_input_file (argv[optind++]);
@@ -1404,35 +1428,34 @@ main (int argc, char **argv)
int i;
static int const sig[] =
{
- /* The usual suspects. */
- SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
+ /* The usual suspects. */
+ SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
#ifdef SIGPOLL
- SIGPOLL,
+ SIGPOLL,
#endif
#ifdef SIGPROF
- SIGPROF,
+ SIGPROF,
#endif
#ifdef SIGVTALRM
- SIGVTALRM,
+ SIGVTALRM,
#endif
#ifdef SIGXCPU
- SIGXCPU,
+ SIGXCPU,
#endif
#ifdef SIGXFSZ
- SIGXFSZ,
+ SIGXFSZ,
#endif
};
- enum { nsigs = sizeof sig / sizeof sig[0] };
+ enum { nsigs = ARRAY_CARDINALITY (sig) };
-#if SA_NOCLDSTOP
struct sigaction act;
sigemptyset (&caught_signals);
for (i = 0; i < nsigs; i++)
{
- sigaction (sig[i], NULL, &act);
- if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, sig[i]);
+ sigaction (sig[i], NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, sig[i]);
}
act.sa_handler = interrupt_handler;
@@ -1441,15 +1464,7 @@ main (int argc, char **argv)
for (i = 0; i < nsigs; i++)
if (sigismember (&caught_signals, sig[i]))
- sigaction (sig[i], &act, NULL);
-#else
- for (i = 0; i < nsigs; i++)
- if (signal (sig[i], SIG_IGN) != SIG_IGN)
- {
- signal (sig[i], interrupt_handler);
- siginterrupt (sig[i], 1);
- }
-#endif
+ sigaction (sig[i], &act, NULL);
}
split_file ();
@@ -1460,35 +1475,40 @@ main (int argc, char **argv)
cleanup_fatal ();
}
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... FILE PATTERN...\n\
"),
- program_name);
+ program_name);
fputs (_("\
-Output pieces of FILE separated by PATTERN(s) to files `xx00', `xx01', ...,\n\
+Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ...,\n\
and output byte counts of each piece to standard output.\n\
-\n\
"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+ fputs (_("\
+\n\
+Read standard input if FILE is -\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
-b, --suffix-format=FORMAT use sprintf FORMAT instead of %02d\n\
- -f, --prefix=PREFIX use PREFIX instead of `xx'\n\
+ -f, --prefix=PREFIX use PREFIX instead of 'xx'\n\
-k, --keep-files do not remove output files on errors\n\
"), stdout);
fputs (_("\
+ --suppress-matched suppress the lines matching PATTERN\n\
+"), stdout);
+ fputs (_("\
-n, --digits=DIGITS use specified number of digits instead of 2\n\
-s, --quiet, --silent do not print counts of output file sizes\n\
-z, --elide-empty-files remove empty output files\n\
@@ -1497,19 +1517,16 @@ Mandatory arguments to long options are mandatory for short options too.\n\
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-Read standard input if FILE is -. Each PATTERN may be:\n\
-"), stdout);
- fputs (_("\
-\n\
+Each PATTERN may be:\n\
INTEGER copy up to but not including specified line number\n\
/REGEXP/[OFFSET] copy up to but not including a matching line\n\
%REGEXP%[OFFSET] skip to, but not including a matching line\n\
{INTEGER} repeat the previous pattern specified number of times\n\
{*} repeat the previous pattern as many times as possible\n\
\n\
-A line OFFSET is a required `+' or `-' followed by a positive integer.\n\
+A line OFFSET is a required '+' or '-' followed by a positive integer.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}