From 42a0e84308ceb0b80a6c7d2e4e106064485ee50b Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 4 Apr 1996 23:08:30 +0000 Subject: The following commands all failed unnecessarily. tr -cs '[:upper:]' '[X*]' tr -dcs '[:lower:]' n-rs-z tr -ds '[:xdigit:]' [:alnum:]' tr -dcs '[:alnum:]' [:digit:]' tr -dc '[:upper:]' Reported by Ross Ridge (ross@worf.mks.com). (validate): Add missing conjunct (translating) in test for `translating and complementing character classes' error. Before, valid uses of tr could fail. E.g. `tr -dcs '[:cntrl:]' '[:alnum:]''. (homogeneous_spec_list): New function. (validate): Use it to relax the old (overly restrictive) restriction that prohibited use of complemented character classes when translating. Now, that is allowed as long as the translation maps many to one. (get_spec_stats): Rename and redefine global has_char_class from has_upper_or_lower. --- src/tr.c | 66 ++++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/tr.c b/src/tr.c index 2d5c3f204..ccc047a84 100644 --- a/src/tr.c +++ b/src/tr.c @@ -204,9 +204,9 @@ struct Spec_list class construct e.g. [=c=]. */ int has_equiv_class; - /* Non-zero if this spec contains at least one of [:upper:] or - [:lower:] class constructs. */ - int has_upper_or_lower; + /* Non-zero if this spec contains at least one character class + construct. E.g. [:digit:]. */ + int has_char_class; /* Non-zero if this spec contains at least one of the character class constructs (all but upper and lower) that aren't allowed in s2. */ @@ -1333,7 +1333,7 @@ get_spec_stats (struct Spec_list *s) s->n_indefinite_repeats = 0; s->has_equiv_class = 0; s->has_restricted_char_class = 0; - s->has_upper_or_lower = 0; + s->has_char_class = 0; for (p = s->head->next; p; p = p->next) { switch (p->type) @@ -1349,6 +1349,7 @@ get_spec_stats (struct Spec_list *s) break; case RE_CHAR_CLASS: + s->has_char_class = 1; for (i = 0; i < N_CHARS; i++) if (is_char_class_member (p->u.char_class, i)) ++len; @@ -1356,7 +1357,6 @@ get_spec_stats (struct Spec_list *s) { case CC_UPPER: case CC_LOWER: - s->has_upper_or_lower = 1; break; default: s->has_restricted_char_class = 1; @@ -1502,6 +1502,27 @@ string2_extend (const struct Spec_list *s1, struct Spec_list *s2) s2->length = s1->length; } +/* Return non-zero if S is a non-empty list in which exactly one + character (but potentially, many instances of it) appears. + E.g. [X*] or xxxxxxxx. */ + +static int +homogeneous_spec_list (struct Spec_list *s) +{ + int b, c; + + s->state = BEGIN_STATE; + + if ((b = get_next (s, NULL)) == -1) + return 0; + + while ((c = get_next (s, NULL)) != -1) + if (c != b) + return 0; + + return 1; +} + /* Die with an error message if S1 and S2 describe strings that are not valid with the given command line switches. A side effect of this function is that if a valid [c*] or @@ -1520,25 +1541,9 @@ validate (struct Spec_list *s1, struct Spec_list *s2) _("the [c*] repeat construct may not appear in string1")); } - /* FIXME: it isn't clear from the POSIX spec that this is invalid, - but in the spirit of the other restrictions put on translation - with character classes, this seems a logical interpretation. */ - if (complement && s1->has_upper_or_lower) - { - error (EXIT_FAILURE, 0, - _("character classes may not be used when translating \ -and complementing")); - } - if (s2) { get_s2_spec_stats (s2, s1->length); - if (s2->has_restricted_char_class) - { - error (EXIT_FAILURE, 0, - _("when translating, the only character classes that may \ -appear in\n\tstring2 are `upper' and `lower'")); - } if (s2->n_indefinite_repeats > 1) { @@ -1569,10 +1574,21 @@ when translating")); } } - if (complement && s2->has_upper_or_lower) - error (EXIT_FAILURE, 0, - _("character classes may not be used when translating \ -and complementing")); + if (complement && s1->has_char_class + && ! (s2->length == s1->length && homogeneous_spec_list (s2))) + /* FIXME: update NEWS */ + { + error (EXIT_FAILURE, 0, + _("when translating with complemented character classes,\ +\nstring2 must map all characters in the domain to one")); + } + + if (s2->has_restricted_char_class) + { + error (EXIT_FAILURE, 0, + _("when translating, the only character classes that may \ +appear in\n\tstring2 are `upper' and `lower'")); + } } else /* Not translating. */ -- cgit v1.2.3-70-g09d2