#include "system.h"
#include "error.h"
#include "safe-read.h"
+#include "xstrtol.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "tr"
#define N_CHARS (UCHAR_MAX + 1)
-/* A pointer to a function that returns a `long'. */
-typedef long (*PFL) (/* unsigned char *, long int, PFL */);
+/* A pointer to a filtering function. */
+typedef size_t (*Filter) (/* unsigned char *, size_t, Filter */);
/* Convert from character C to its index in the collating
sequence array. Just cast to an unsigned int to avoid
return 0;
}
-/* Convert a string S with explicit length LEN, possibly
- containing embedded zero bytes, to a long integer value.
- If the string represents a negative value, a value larger
- than LONG_MAX, or if all LEN characters do not represent a
- valid integer, return nonzero and do not modify *VAL.
- Otherwise, return zero and set *VAL to the converted value. */
-
-static int
-non_neg_strtol (const unsigned char *s, size_t len, size_t *val)
-{
- size_t i;
- unsigned long sum = 0;
- unsigned int base;
-
- if (len <= 0)
- return 1;
- if (s[0] == '0')
- base = 8;
- else if (ISDIGIT (s[0]))
- base = 10;
- else
- return 1;
-
- for (i = 0; i < len; i++)
- {
- unsigned int c;
-
- if (s[i] < '0')
- return 1;
-
- c = s[i] - '0';
- if (c >= base)
- return 1;
-
- if (sum > (LONG_MAX - c) / base)
- return 1;
- sum = sum * base + c;
- }
- *val = sum;
- return 0;
-}
-
/* Parse the bracketed repeat-char syntax. If the P_LEN characters
beginning with P[ START_IDX ] comprise a valid [c*n] construct,
then set *CHAR_TO_REPEAT, *REPEAT_COUNT, and *CLOSING_BRACKET_IDX
{
if (ES_MATCH (es, i, ']'))
{
- const unsigned char *digit_str;
size_t digit_str_len = i - start_idx - 2;
*char_to_repeat = es->s[start_idx];
}
/* Here, we have found [c*s] where s should be a string
- of octal or decimal digits. */
- digit_str = &es->s[start_idx + 2];
- if (non_neg_strtol (digit_str, digit_str_len, repeat_count)
- || *repeat_count > BEGIN_STATE)
- {
- char *tmp = make_printable_str (digit_str, digit_str_len);
- error (0, 0, _("invalid repeat count `%s' in [c*n] construct"),
- tmp);
- free (tmp);
- return -2;
- }
+ of octal (if it starts with `0') or decimal digits. */
+ {
+ const char *digit_str = &es->s[start_idx + 2];
+ unsigned long int tmp_ulong;
+ char *d_end;
+ int base = 10;
+ /* Select the base manually so we can be sure it's either 8 or 10.
+ If the spec allowed it to be interpreted as hexadecimal, we
+ could have used `0' and let xstrtoul decide. */
+ if (*digit_str == '0')
+ {
+ base = 8;
+ ++digit_str;
+ --digit_str_len;
+ }
+ if (xstrtoul (digit_str, &d_end, base, &tmp_ulong, NULL) != LONGINT_OK
+ || BEGIN_STATE < tmp_ulong
+ || d_end - digit_str != digit_str_len)
+ {
+ char *tmp = make_printable_str (es->s + start_idx + 2,
+ i - start_idx - 2);
+ error (0, 0, _("invalid repeat count `%s' in [c*n] construct"),
+ tmp);
+ free (tmp);
+ return -2;
+ }
+ *repeat_count = tmp_ulong;
+ }
*closing_bracket_idx = i;
return 0;
}
character is in the squeeze set. */
static void
-squeeze_filter (unsigned char *buf, long int size, PFL reader)
+squeeze_filter (unsigned char *buf, size_t size, Filter reader)
{
unsigned int char_to_squeeze = NOT_A_CHAR;
- long i = 0;
- long nr = 0;
+ size_t i = 0;
+ ssize_t nr = 0;
for (;;)
{
- long begin;
+ size_t begin;
if (i >= nr)
{
if (reader == NULL)
- nr = safe_read (0, (char *) buf, size);
+ {
+ ssize_t signed_nr = safe_read (0, (char *) buf, size);
+ if (signed_nr < 0)
+ error (EXIT_FAILURE, errno, _("read error"));
+ nr = signed_nr;
+ }
else
- nr = (*reader) (buf, size, NULL);
+ {
+ nr = (*reader) (buf, size, NULL);
+ }
- if (nr < 0)
- error (EXIT_FAILURE, errno, _("read error"));
if (nr == 0)
break;
i = 0;
if (char_to_squeeze == NOT_A_CHAR)
{
- long out_len;
+ size_t out_len;
/* Here, by being a little tricky, we can get a significant
performance increase in most cases when the input is
reasonably large. Since tr will modify the input only
in the delete set, and return the number of characters saved
or 0 upon EOF. */
-static long
-read_and_delete (unsigned char *buf, long int size, PFL not_used)
+static size_t
+read_and_delete (unsigned char *buf, size_t size, Filter not_used)
{
- long n_saved;
+ size_t n_saved;
static int hit_eof = 0;
assert (not_used == NULL);
- assert (size > 0);
if (hit_eof)
return 0;
just deleted all the characters in a buffer. */
do
{
- int i;
- int nr = safe_read (0, (char *) buf, size);
+ size_t i;
+ ssize_t nr = safe_read (0, (char *) buf, size);
if (nr < 0)
error (EXIT_FAILURE, errno, _("read error"));
perform the in-place and one-to-one mapping specified by the global
array `xlate'. Return the number of characters read, or 0 upon EOF. */
-static long
-read_and_xlate (unsigned char *buf, long int size, PFL not_used)
+static size_t
+read_and_xlate (unsigned char *buf, size_t size, Filter not_used)
{
- long chars_read = 0;
+ ssize_t chars_read = 0;
static int hit_eof = 0;
- int i;
+ size_t i;
assert (not_used == NULL);
- assert (size > 0);
if (hit_eof)
return 0;
set_initialize (struct Spec_list *s, int complement_this_set, SET_TYPE *in_set)
{
int c;
- int i;
+ size_t i;
memset (in_set, 0, N_CHARS * sizeof (in_set[0]));
s->state = BEGIN_STATE;
}
else if (delete && non_option_args == 1)
{
- long nr;
+ size_t nr;
set_initialize (s1, complement, in_delete_set);
do
}
else
{
- long chars_read;
+ size_t chars_read;
do
{