diff options
Diffstat (limited to 'src/csplit.c')
-rw-r--r-- | src/csplit.c | 819 |
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); } |