/* Given the list of field or byte range specifications FIELDSTR, allocate and initialize the RP array. FIELDSTR should be composed of one or more numbers or ranges of numbers, separated by blanks or commas. Incomplete ranges may be given: '-m' means '1-m'; 'n-' means 'n' through end of line. Return true if FIELDSTR contains at least one field specification, false otherwise. */ static bool set_fields (const char *fieldstr) { size_t initial = 1; /* Value of first number in a range. */ size_t value = 0; /* If nonzero, a number being accumulated. */ bool lhs_specified = false; bool rhs_specified = false; bool dash_found = false; /* True if a '-' is found in this field. */ bool field_found = false; /* True if at least one field spec has been processed. */ size_t i; bool in_digits = false; /* Collect and store in RP the range end points. */ while (true) { if (*fieldstr == '-') { in_digits = false; /* Starting a range. */ if (dash_found) FATAL_ERROR (_("invalid byte, character or field list")); dash_found = true; fieldstr++; if (lhs_specified && !value) FATAL_ERROR (_("fields and positions are numbered from 1")); initial = (lhs_specified ? value : 1); value = 0; } else if (*fieldstr == ',' || isblank (to_uchar (*fieldstr)) || *fieldstr == '\0') { in_digits = false; /* Ending the string, or this field/byte sublist. */ if (dash_found) { dash_found = false; if (!lhs_specified && !rhs_specified) FATAL_ERROR (_("invalid range with no endpoint: -")); /* A range. Possibilities: -n, m-n, n-. In any case, 'initial' contains the start of the range. */ if (!rhs_specified) { /* 'n-'. From 'initial' to end of line. */ add_range_pair (initial, SIZE_MAX); field_found = true; } else { /* 'm-n' or '-n' (1-n). */ if (value < initial) FATAL_ERROR (_("invalid decreasing range")); add_range_pair (initial, value); field_found = true; } value = 0; } else { /* A simple field number, not a range. */ if (value == 0) FATAL_ERROR (_("fields and positions are numbered from 1")); add_range_pair (value, value); value = 0; field_found = true; } if (*fieldstr == '\0') break; fieldstr++; lhs_specified = false; rhs_specified = false; } else if (ISDIGIT (*fieldstr)) { /* Record beginning of digit string, in case we have to complain about it. */ static char const *num_start; if (!in_digits || !num_start) num_start = fieldstr; in_digits = true; if (dash_found) rhs_specified = 1; else lhs_specified = 1; /* Detect overflow. */ if (!DECIMAL_DIGIT_ACCUMULATE (value, *fieldstr - '0', size_t) || value == SIZE_MAX) { /* In case the user specified -c$(echo 2^64|bc),22, complain only about the first number. */ /* Determine the length of the offending number. */ size_t len = strspn (num_start, "0123456789"); char *bad_num = xstrndup (num_start, len); if (operating_mode == byte_mode) error (0, 0, _("byte offset %s is too large"), quote (bad_num)); else error (0, 0, _("field number %s is too large"), quote (bad_num)); free (bad_num); exit (EXIT_FAILURE); } fieldstr++; } else FATAL_ERROR (_("invalid byte, character or field list")); } qsort (rp, n_rp, sizeof (rp[0]), compare_ranges); /* Merge range pairs (e.g. `2-5,3-4' becomes `2-5'). */ for (i = 0; i < n_rp; ++i) { for (size_t j = i + 1; j < n_rp; ++j) { if (rp[j].lo <= rp[i].hi) { rp[i].hi = MAX (rp[j].hi, rp[i].hi); memmove (rp + j, rp + j + 1, (n_rp - j - 1) * sizeof *rp); n_rp--; j--; } else break; } } complement_rp (); /* After merging, reallocate RP so we release memory to the system. Also add a sentinel at the end of RP, to avoid out of bounds access and for performance reasons. */ ++n_rp; rp = xrealloc (rp, n_rp * sizeof (struct range_pair)); rp[n_rp - 1].lo = rp[n_rp - 1].hi = SIZE_MAX; return field_found; }