summaryrefslogtreecommitdiff
path: root/src/split.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/split.c')
-rw-r--r--src/split.c136
1 files changed, 73 insertions, 63 deletions
diff --git a/src/split.c b/src/split.c
index 0f0d3717e..252befbbb 100644
--- a/src/split.c
+++ b/src/split.c
@@ -160,11 +160,11 @@ ignorable (int err)
}
static void
-set_suffix_length (uintmax_t n_units, enum Split_type split_type)
+set_suffix_length (intmax_t n_units, enum Split_type split_type)
{
#define DEFAULT_SUFFIX_LENGTH 2
- uintmax_t suffix_length_needed = 0;
+ int suffix_length_needed = 0;
/* The suffix auto length feature is incompatible with
a user specified start value as the generated suffixes
@@ -176,20 +176,20 @@ set_suffix_length (uintmax_t n_units, enum Split_type split_type)
if (split_type == type_chunk_bytes || split_type == type_chunk_lines
|| split_type == type_rr)
{
- uintmax_t n_units_end = n_units - 1;
+ intmax_t n_units_end = n_units - 1;
if (numeric_suffix_start)
{
- uintmax_t n_start;
- strtol_error e = xstrtoumax (numeric_suffix_start, NULL, 10,
+ intmax_t n_start;
+ strtol_error e = xstrtoimax (numeric_suffix_start, NULL, 10,
&n_start, "");
- if (e == LONGINT_OK && n_start <= UINTMAX_MAX - n_units)
+ if (e == LONGINT_OK && n_start < n_units)
{
/* Restrict auto adjustment so we don't keep
incrementing a suffix size arbitrarily,
as that would break sort order for files
generated from multiple split runs. */
- if (n_start < n_units)
- n_units_end += n_start;
+ if (INT_ADD_WRAPV (n_units_end, n_start, &n_units_end))
+ n_units_end = INTMAX_MAX;
}
}
@@ -206,7 +206,7 @@ set_suffix_length (uintmax_t n_units, enum Split_type split_type)
if (suffix_length < suffix_length_needed)
{
die (EXIT_FAILURE, 0,
- _("the suffix length needs to be at least %"PRIuMAX),
+ _("the suffix length needs to be at least %d"),
suffix_length_needed);
}
suffix_auto = false;
@@ -619,14 +619,14 @@ cwrite (bool new_file_flag, char const *bp, size_t bytes)
BUF contains the first INITIAL_READ input bytes. */
static void
-bytes_split (uintmax_t n_bytes, uintmax_t rem_bytes,
+bytes_split (intmax_t n_bytes, intmax_t rem_bytes,
char *buf, size_t bufsize, ssize_t initial_read,
- uintmax_t max_files)
+ intmax_t max_files)
{
bool new_file_flag = true;
bool filter_ok = true;
- uintmax_t opened = 0;
- uintmax_t to_write = n_bytes + (0 < rem_bytes);
+ intmax_t opened = 0;
+ intmax_t to_write = n_bytes + (0 < rem_bytes);
bool eof = ! to_write;
while (! eof)
@@ -696,12 +696,12 @@ bytes_split (uintmax_t n_bytes, uintmax_t rem_bytes,
Use buffer BUF, whose size is BUFSIZE. */
static void
-lines_split (uintmax_t n_lines, char *buf, size_t bufsize)
+lines_split (intmax_t n_lines, char *buf, size_t bufsize)
{
ssize_t n_read;
char *bp, *bp_out, *eob;
bool new_file_flag = true;
- uintmax_t n = 0;
+ intmax_t n = 0;
do
{
@@ -743,10 +743,10 @@ lines_split (uintmax_t n_lines, char *buf, size_t bufsize)
where lines longer than N_BYTES bytes occur. */
static void
-line_bytes_split (uintmax_t n_bytes, char *buf, size_t bufsize)
+line_bytes_split (intmax_t n_bytes, char *buf, size_t bufsize)
{
ssize_t n_read;
- uintmax_t n_out = 0; /* for each split. */
+ intmax_t n_out = 0; /* for each split. */
size_t n_hold = 0;
char *hold = NULL; /* for lines > bufsize. */
size_t hold_size = 0;
@@ -857,14 +857,14 @@ line_bytes_split (uintmax_t n_bytes, char *buf, size_t bufsize)
if a line is so long as to completely overlap the partition. */
static void
-lines_chunk_split (uintmax_t k, uintmax_t n, char *buf, size_t bufsize,
+lines_chunk_split (intmax_t k, intmax_t n, char *buf, size_t bufsize,
ssize_t initial_read, off_t file_size)
{
assert (n && k <= n);
- uintmax_t rem_bytes = file_size % n;
+ intmax_t rem_bytes = file_size % n;
off_t chunk_size = file_size / n;
- uintmax_t chunk_no = 1;
+ intmax_t chunk_no = 1;
off_t chunk_end = chunk_size + (0 < rem_bytes);
off_t n_written = 0;
bool new_file_flag = true;
@@ -983,7 +983,7 @@ lines_chunk_split (uintmax_t k, uintmax_t n, char *buf, size_t bufsize,
/* -n K/N: Extract Kth of N chunks. */
static void
-bytes_chunk_extract (uintmax_t k, uintmax_t n, char *buf, size_t bufsize,
+bytes_chunk_extract (intmax_t k, intmax_t n, char *buf, size_t bufsize,
ssize_t initial_read, off_t file_size)
{
off_t start;
@@ -1129,14 +1129,14 @@ ofile_open (of_t *files, size_t i_check, size_t nfiles)
to opening and closing each file for each line. */
static void
-lines_rr (uintmax_t k, uintmax_t n, char *buf, size_t bufsize, of_t **filesp)
+lines_rr (intmax_t k, intmax_t n, char *buf, size_t bufsize, of_t **filesp)
{
bool wrapped = false;
bool wrote = false;
bool file_limit;
size_t i_file;
of_t *files IF_LINT (= NULL);
- uintmax_t line_no;
+ intmax_t line_no;
if (k)
line_no = 1;
@@ -1278,20 +1278,51 @@ no_filters:
} \
while (0)
+/* Report a string-to-integer conversion failure MSGID with ARG. */
+
+static _Noreturn void
+strtoint_die (char const *msgid, char const *arg)
+{
+ die (EXIT_FAILURE, errno == EINVAL ? 0 : errno, "%s: %s",
+ gettext (msgid), quote (arg));
+}
+
+/* Use OVERFLOW_OK when it is OK to ignore LONGINT_OVERFLOW errors, since the
+ extreme value will do the right thing anyway on any practical platform. */
+#define OVERFLOW_OK LONGINT_OVERFLOW
+
+/* Parse ARG for number of bytes or lines. The number can be followed
+ by MULTIPLIERS, and the resulting value must be positive.
+ If the number cannot be parsed, diagnose with MSG.
+ Return the number parsed, or an INTMAX_MAX on overflow. */
+
+static intmax_t
+parse_n_units (char const *arg, char const *multipliers, char const *msgid)
+{
+ intmax_t n;
+ if (OVERFLOW_OK < xstrtoimax (arg, NULL, 10, &n, multipliers) || n < 1)
+ strtoint_die (msgid, arg);
+ return n;
+}
/* Parse K/N syntax of chunk options. */
static void
-parse_chunk (uintmax_t *k_units, uintmax_t *n_units, char *slash)
+parse_chunk (intmax_t *k_units, intmax_t *n_units, char const *arg)
{
- *n_units = xdectoumax (slash + 1, 1, UINTMAX_MAX, "",
- _("invalid number of chunks"), 0);
- if (slash != optarg) /* a leading number is specified. */
+ char *argend;
+ strtol_error e = xstrtoimax (arg, &argend, 10, n_units, "");
+ if (e == LONGINT_INVALID_SUFFIX_CHAR && *argend == '/')
{
- *slash = '\0';
- *k_units = xdectoumax (optarg, 1, *n_units, "",
- _("invalid chunk number"), 0);
+ *k_units = *n_units;
+ *n_units = parse_n_units (argend + 1, "",
+ N_("invalid number of chunks"));
+ if (! (0 < *k_units && *k_units <= *n_units))
+ error (EXIT_FAILURE, 0, "%s: %s", _("invalid chunk number"),
+ quote_mem (arg, argend - arg));
}
+ else if (! (e <= OVERFLOW_OK && 0 < *n_units))
+ strtoint_die (N_("invalid number of chunks"), arg);
}
@@ -1301,8 +1332,8 @@ main (int argc, char **argv)
enum Split_type split_type = type_undef;
idx_t in_blk_size = 0; /* optimal block size of input file device */
size_t page_size = getpagesize ();
- uintmax_t k_units = 0;
- uintmax_t n_units = 0;
+ intmax_t k_units = 0;
+ intmax_t n_units = 0;
static char const multipliers[] = "bEGKkMmPQRTYZ0";
int c;
@@ -1326,7 +1357,6 @@ main (int argc, char **argv)
{
/* This is the argv-index of the option we will read next. */
int this_optind = optind ? optind : 1;
- char *slash;
c = getopt_long (argc, argv, "0123456789C:a:b:del:n:t:ux",
longopts, NULL);
@@ -1355,27 +1385,23 @@ main (int argc, char **argv)
if (split_type != type_undef)
FAIL_ONLY_ONE_WAY ();
split_type = type_bytes;
- /* Limit to OFF_T_MAX, because if input is a pipe, we could get more
- data than is possible to write to a single file, so indicate that
- immediately rather than having possibly future invocations fail. */
- n_units = xdectoumax (optarg, 1, OFF_T_MAX, multipliers,
- _("invalid number of bytes"), 0);
+ n_units = parse_n_units (optarg, multipliers,
+ N_("invalid number of bytes"));
break;
case 'l':
if (split_type != type_undef)
FAIL_ONLY_ONE_WAY ();
split_type = type_lines;
- n_units = xdectoumax (optarg, 1, UINTMAX_MAX, "",
- _("invalid number of lines"), 0);
+ n_units = parse_n_units (optarg, "", N_("invalid number of lines"));
break;
case 'C':
if (split_type != type_undef)
FAIL_ONLY_ONE_WAY ();
split_type = type_byteslines;
- n_units = xdectoumax (optarg, 1, MIN (SIZE_MAX, OFF_T_MAX),
- multipliers, _("invalid number of bytes"), 0);
+ n_units = parse_n_units (optarg, multipliers,
+ N_("invalid number of lines"));
break;
case 'n':
@@ -1396,11 +1422,7 @@ main (int argc, char **argv)
}
else
split_type = type_chunk_bytes;
- if ((slash = strchr (optarg, '/')))
- parse_chunk (&k_units, &n_units, slash);
- else
- n_units = xdectoumax (optarg, 1, UINTMAX_MAX, "",
- _("invalid number of chunks"), 0);
+ parse_chunk (&k_units, &n_units, optarg);
break;
case 'u':
@@ -1457,13 +1479,9 @@ main (int argc, char **argv)
if (digits_optind != 0 && digits_optind != this_optind)
n_units = 0; /* More than one number given; ignore other. */
digits_optind = this_optind;
- if (!DECIMAL_DIGIT_ACCUMULATE (n_units, c - '0', uintmax_t))
- {
- char buffer[INT_BUFSIZE_BOUND (uintmax_t)];
- die (EXIT_FAILURE, 0,
- _("line count option -%s%c... is too large"),
- umaxtostr (n_units, buffer), c);
- }
+ if (INT_MULTIPLY_WRAPV (n_units, 10, &n_units)
+ || INT_ADD_WRAPV (n_units, c - '0', &n_units))
+ n_units = INTMAX_MAX;
break;
case 'd':
@@ -1536,7 +1554,7 @@ main (int argc, char **argv)
if (n_units == 0)
{
- error (0, 0, "%s: %s", _("invalid number of lines"), quote ("0"));
+ error (0, 0, _("invalid number of lines: %s"), "0");
usage (EXIT_FAILURE);
}
@@ -1600,14 +1618,6 @@ main (int argc, char **argv)
die (EXIT_FAILURE, errno, _("%s: cannot determine file size"),
quotef (infile));
initial_read = MIN (file_size, in_blk_size);
- /* Overflow, and sanity checking. */
- if (OFF_T_MAX < n_units)
- {
- char buffer[INT_BUFSIZE_BOUND (uintmax_t)];
- die (EXIT_FAILURE, EOVERFLOW, "%s: %s",
- _("invalid number of chunks"),
- quote (umaxtostr (n_units, buffer)));
- }
}
/* When filtering, closure of one pipe must not terminate the process,