Hi Kevin,

> Date: 2026-08-04 13:18:19+0800
> From: "Kevin J. McCarthy" <[email protected]>
>
> Thanks to Alejandro Colomar for this suggestion, and I used his
> examples directly for LOWER_C to PFCHAR_C.  The HEX additions are
> mine, so you can blame those on me. ;-)

:-)

Actually, since there's the standard [:xdigit:] and isxdigit(3), I'd
call it (and actually called it in shadow-utils) CTYPE_XDIGIT_C:

        #define CTYPE_XDIGIT_C         CTYPE_DIGIT_C "abcdefABCDEF"

Here's the full set of APIs I have (they may inspire you to add a few
more similar ones):

        #define CTYPE_CNTRL_C                                                 \
                "\x7F"                                                        \
                
"\x1F\x1E\x1D\x1C\x1B\x1A\x19\x18\x17\x16\x15\x14\x13\x12\x11\x10" \
                "\x0F\x0E\x0D\x0C\x0B\x0A\x09\x08\x07\x06\x05\x04\x03\x02\x01" 
/*NUL*/

        #define CTYPE_LOWER_C          "abcdefghijklmnopqrstuvwxyz"
        #define CTYPE_UPPER_C          "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        #define CTYPE_DIGIT_C          "0123456789"
        #define CTYPE_PUNCT_C          "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
        #define CTYPE_BLANK_C          " \t"
        #define CTYPE_SPACE_C          CTYPE_BLANK_C "\n\v\f\r"
        #define CTYPE_ALPHA_C          CTYPE_LOWER_C CTYPE_UPPER_C
        #define CTYPE_ALNUM_C          CTYPE_ALPHA_C CTYPE_DIGIT_C
        #define CTYPE_GRAPH_C          CTYPE_ALNUM_C CTYPE_PUNCT_C
        #define CTYPE_PRINT_C          CTYPE_GRAPH_C " "
        #define CTYPE_XDIGIT_C         CTYPE_DIGIT_C "abcdefABCDEF"
        #define CTYPE_ASCII_C          CTYPE_PRINT_C CTYPE_CNTRL_C /*NUL*/
        #define CTYPE_PFCHAR_C         CTYPE_ALNUM_C "._-"  // portable 
filename character set
        #define CTYPE_LDH_RFC1035_C    CTYPE_ALNUM_C "-"  // letter, digit, 
hyphen


        // isascii_c - is [:ascii:] C-locale
        #define isascii_c(c)           (!!strchr(CTYPE_ASCII_C, c))
        #define iscntrl_c(c)           (!!strchr(CTYPE_CNTRL_C, c))
        #define islower_c(c)           (!streq(strchrnul(CTYPE_LOWER_C, c), ""))
        #define isupper_c(c)           (!streq(strchrnul(CTYPE_UPPER_C, c), ""))
        #define isdigit_c(c)           (!streq(strchrnul(CTYPE_DIGIT_C, c), ""))
        #define ispunct_c(c)           (!streq(strchrnul(CTYPE_PUNCT_C, c), ""))
        #define isblank_c(c)           (!streq(strchrnul(CTYPE_BLANK_C, c), ""))
        #define isspace_c(c)           (!streq(strchrnul(CTYPE_SPACE_C, c), ""))
        #define isalpha_c(c)           (!streq(strchrnul(CTYPE_ALPHA_C, c), ""))
        #define isalnum_c(c)           (!streq(strchrnul(CTYPE_ALNUM_C, c), ""))
        #define isgraph_c(c)           (!streq(strchrnul(CTYPE_GRAPH_C, c), ""))
        #define isprint_c(c)           (!streq(strchrnul(CTYPE_PRINT_C, c), ""))
        #define isxdigit_c(c)          (!streq(strchrnul(CTYPE_XDIGIT_C, c), 
""))
        #define ispfchar_c(c)          (!streq(strchrnul(CTYPE_PFCHAR_C, c), 
""))
        #define isldh_rfc1035_c(c)     (!streq(strchrnul(CTYPE_LDH_RFC1035_C, 
c), ""))


        // strisascii_c - string is [:ascii:] C-locale
        #define strisdigit_c(s)        streq(stpspn(s, CTYPE_DIGIT_C), "")
        #define strisprint_c(s)        streq(stpspn(s, CTYPE_PRINT_C), "")
        #define strispfchar_c(s)       streq(stpspn(s, CTYPE_PFCHAR_C), "")
        #define strisldh_rfc1035_c(s)  streq(stpspn(s, CTYPE_LDH_RFC1035_C), "")


        // strchriscntrl_c - string character is [:cntrl:] C-locale
        #define strchriscntrl_c(s)     (!!strpbrk(s, CTYPE_CNTRL_C))

> ---
> 
> Please let me know what you think.  Does it help readability for the
> cases where we use the CTYPE along with extra characters?
> e.g. imap/command.c, lib.c, muttlib.c  below.

Yup, IMO.  Where thr RFCs use a name for that, I'd give them a new name
(see for example, CTYPE_LDH_RFC1035_C above, which corresponds to
RFC1035's <ldh-str>), but where there's not a standard-ish name, I guess
direct use like this is fine.
<https://www.rfc-editor.org/info/rfc1035/#section-2.3.1>

> 
>  crypt.c         |  2 +-
>  imap/command.c  |  2 +-
>  lib.c           |  2 +-
>  lib.h           | 13 +++++++++++++
>  mutt_sasl_gnu.c |  2 +-
>  muttlib.c       |  2 +-
>  rfc2047.c       |  2 +-
>  url.c           |  2 +-
>  8 files changed, 20 insertions(+), 7 deletions(-)
> 
> diff --git a/crypt.c b/crypt.c
> index e4c8051d..d141ac6f 100644
> --- a/crypt.c
> +++ b/crypt.c
> @@ -1295,7 +1295,7 @@ short crypt_is_numerical_keyid(const char *s)
>    if (strlen(s) % 8)
>      return 0;
>    while (*s)
> -    if (strchr("0123456789ABCDEFabcdef", *s++) == NULL)
> +    if (strchr(CTYPE_HEX_C, *s++) == NULL)

This is certainly useful!

>        return 0;
>  
>    return 1;
> diff --git a/imap/command.c b/imap/command.c
> index 63dedf6c..94c98883 100644
> --- a/imap/command.c
> +++ b/imap/command.c
> @@ -701,7 +701,7 @@ static void cmd_parse_vanished(IMAP_DATA *idata, char *s)
>    end_of_seqset = s;
>    while (*end_of_seqset)
>    {
> -    if (!strchr("0123456789:,", *end_of_seqset))
> +    if (!strchr(CTYPE_DIGIT_C ":,", *end_of_seqset))

I wonder if this character set has any name in RFC 7162.  I can't find
it, though.  The only reference to DIGIT seems to be in
mod-sequence-value.

>        *end_of_seqset = '\0';
>      else
>        end_of_seqset++;
> diff --git a/lib.c b/lib.c
> index 9776fcdb..fdecda25 100644
> --- a/lib.c
> +++ b/lib.c
> @@ -573,7 +573,7 @@ success:
>  }
>  
>  
> -static const char safe_chars[] = 
> "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%";
> +static const char safe_chars[] = CTYPE_PFCHAR_C "+@{}:%";

LGTM.  Are those extra safe chars specified by any standard, or is it
just of this project?

>  
>  void mutt_sanitize_filename(char *f, int flags)
>  {
> diff --git a/lib.h b/lib.h
> index 25875656..b622e9c0 100644
> --- a/lib.h
> +++ b/lib.h
> @@ -147,6 +147,19 @@ static inline char *skip_email_wsp(const char *s)
>     on some systems */
>  #define SKIP_LOCALE_WS(c) while (*(c) && IS_LOCALE_WS(*(c))) c++;
>  
> +/*
> + * Various useful sets of characters
> + */
> +#define CTYPE_LOWER_C        "abcdefghijklmnopqrstuvwxyz"
> +#define CTYPE_UPPER_C        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
> +#define CTYPE_DIGIT_C        "0123456789"
> +#define CTYPE_ALPHA_C        CTYPE_LOWER_C CTYPE_UPPER_C
> +#define CTYPE_ALNUM_C        CTYPE_ALPHA_C CTYPE_DIGIT_C
> +#define CTYPE_PFCHAR_C       CTYPE_ALNUM_C "._-"    // POSIX.1-2008 portable
> +                                                    // filename character set

Actually, that was already true as of POSIX.1-2001 (Issue 6); the first
modern-day POSIX, where POSIX and SUS (the Single UNIX Specification)
were unified

I suspect this was already true way earlier, since I see a reference to
the 'portable filename character set' in XPG Issue 4 (v2) (from which
modern-day POSIX derives).
<https://pubs.opengroup.org/onlinepubs/009656499/toc.pdf#page=423>
I don't see the the definition of the p.f.c.s. in that document, because
it was in a separate volume, and I can't find it.  It's also difficult
to find older standards, unless you know where they are.  :)

> +#define CTYPE_UHEX_C         CTYPE_DIGIT_C "ABCDEF" // uppercase hex

Hmmm, I'd maybe call this UXDIGIT?  (Considering that the below would be
XDIGIT, per the usual standards.)

> +#define CTYPE_HEX_C          CTYPE_UHEX_C "abcdef"
> +
>  /*
>   * These functions aren't defined in lib.c, but
>   * they are used there.
> diff --git a/mutt_sasl_gnu.c b/mutt_sasl_gnu.c
> index 9a7a4b87..2bd5afbf 100644
> --- a/mutt_sasl_gnu.c
> +++ b/mutt_sasl_gnu.c
> @@ -66,7 +66,7 @@ void mutt_gsasl_done(void)
>  }
>  
>  static const char *VALID_MECHANISM_CHARACTERS =
> -  "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
> +  CTYPE_UPPER_C CTYPE_DIGIT_C "-_";

LGTM.


Have a lovely day!
Alex

>  
>  /* This logic is derived from the libgsasl suggest code */
>  static int mechlist_contains(const char *uc_mech, const char *uc_mechlist)
> diff --git a/muttlib.c b/muttlib.c
> index d24a37f0..ee29cc17 100644
> --- a/muttlib.c
> +++ b/muttlib.c
> @@ -1178,7 +1178,7 @@ void _mutt_buffer_quote_filename(BUFFER *d, const char 
> *f, int add_outer)
>      mutt_buffer_addch(d, '\'');
>  }
>  
> -static const char safe_chars[] = 
> "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%";
> +static const char safe_chars[] = CTYPE_PFCHAR_C "+@{}:%";
>  
>  void mutt_buffer_sanitize_filename(BUFFER *d, const char *f, int flags)
>  {
> diff --git a/rfc2047.c b/rfc2047.c
> index 2bdd8553..1bacb54f 100644
> --- a/rfc2047.c
> +++ b/rfc2047.c
> @@ -257,7 +257,7 @@ static size_t b_encoder(char *s, ICONV_CONST char *d, 
> size_t dlen,
>  static size_t q_encoder(char *s, ICONV_CONST char *d, size_t dlen,
>                          const char *tocode)
>  {
> -  static const char hex[] = "0123456789ABCDEF";
> +  static const char hex[] = CTYPE_UHEX_C;
>    char *s0 = s;
>  
>    memcpy(s, "=?", 2), s += 2;
> diff --git a/url.c b/url.c
> index 02dbf34c..176877cd 100644
> --- a/url.c
> +++ b/url.c
> @@ -193,7 +193,7 @@ int url_parse_ciss(ciss_url_t *ciss, char *src)
>  
>  static void url_pct_encode(char *dst, size_t l, const char *src)
>  {
> -  static const char *alph = "0123456789ABCDEF";
> +  static const char *alph = CTYPE_UHEX_C;
>  
>    *dst = 0;
>    l--;
> -- 
> 2.55.0
> 

-- 
<https://www.alejandro-colomar.es>

Attachment: signature.asc
Description: PGP signature

Reply via email to