From 0c78ce137581664a2644973ef04ba54d56b0503e Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 2 Jan 2026 21:15:24 +0100 Subject: [PATCH 01/13] lib/string/ctype/isascii.[ch]: is*_c(): Add APIs These are like the isascii(3) family of APIs, but use the C locale, as the _c suffix hints. These macros behave well with non-casted input, unlike isascii(3). The isascii_c() and iscntrl_c() implementations are different from the rest because they must return true for '\0'. Reported-by: Tobias Stoeckmann Cc: Paul Eggert Signed-off-by: Alejandro Colomar --- lib/Makefile.am | 2 ++ lib/string/README | 6 +++++ lib/string/ctype/isascii.c | 7 ++++++ lib/string/ctype/isascii.h | 48 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 lib/string/ctype/isascii.c create mode 100644 lib/string/ctype/isascii.h diff --git a/lib/Makefile.am b/lib/Makefile.am index c402ff02a3..577c171789 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -186,6 +186,8 @@ libshadow_la_SOURCES = \ spawn.c \ sssd.c \ sssd.h \ + string/ctype/isascii.c \ + string/ctype/isascii.h \ string/ctype/strchrisascii/strchriscntrl.c \ string/ctype/strchrisascii/strchriscntrl.h \ string/ctype/strisascii/strisdigit.c \ diff --git a/lib/string/README b/lib/string/README index 9fbcbd842c..bb44d2f867 100644 --- a/lib/string/README +++ b/lib/string/README @@ -69,6 +69,12 @@ Specific guidelines: ctype/ - Character classification and conversion functions + isascii.h + The functions defined in this file + return true + if the character + belongs to the category specified in the function name. + strchrisascii/ The functions defined under this directory return true diff --git a/lib/string/ctype/isascii.c b/lib/string/ctype/isascii.c new file mode 100644 index 0000000000..5409883124 --- /dev/null +++ b/lib/string/ctype/isascii.c @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2024-2025, Alejandro Colomar +// SPDX-License-Identifier: BSD-3-Clause + + +#include "config.h" + +#include "string/ctype/isascii.h" diff --git a/lib/string/ctype/isascii.h b/lib/string/ctype/isascii.h new file mode 100644 index 0000000000..3bec7defba --- /dev/null +++ b/lib/string/ctype/isascii.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2024-2025, Alejandro Colomar +// SPDX-License-Identifier: BSD-3-Clause + + +#ifndef SHADOW_INCLUDE_LIB_STRING_CTYPE_ISASCII_H_ +#define SHADOW_INCLUDE_LIB_STRING_CTYPE_ISASCII_H_ + + +#include "config.h" + +#include + +#include "string/strcmp/streq.h" + + +#define CTYPE_CNTRL_C \ + "\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_SPACE_C " \t\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*/ + + +// 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 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), "")) + + +#endif // include guard From 0ae2f2f18cc4d55513797028074541def3a24f33 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 2 Jan 2026 21:29:27 +0100 Subject: [PATCH 02/13] lib/, src/: Use isascii_c() functions instead of isascii(3) We want to use the C locale. Reported-by: Tobias Stoeckmann Signed-off-by: Alejandro Colomar --- lib/getrange.c | 3 ++- lib/port.c | 7 ++++--- lib/setupenv.c | 2 +- lib/string/ctype/strchrisascii/strchriscntrl.h | 9 +++------ lib/string/ctype/strisascii/strisdigit.h | 6 +++--- lib/string/ctype/strisascii/strisprint.h | 9 +++------ src/newusers.c | 7 ++++--- 7 files changed, 20 insertions(+), 23 deletions(-) diff --git a/lib/getrange.c b/lib/getrange.c index a7f8f05963..a7dd844f67 100644 --- a/lib/getrange.c +++ b/lib/getrange.c @@ -13,6 +13,7 @@ #include "atoi/a2i.h" #include "defines.h" #include "prototypes.h" +#include "string/ctype/isascii.h" #include "string/strcmp/streq.h" @@ -57,7 +58,7 @@ getrange(const char *range, if (streq(end, "")) return 0; /* - */ parse_max: - if (!isdigit((unsigned char) *end)) + if (!isdigit_c(*end)) return -1; if (a2ul(max, end, NULL, 10, *min, ULONG_MAX) == -1) diff --git a/lib/port.c b/lib/port.c index 357be218cc..b12d7d282c 100644 --- a/lib/port.c +++ b/lib/port.c @@ -19,6 +19,7 @@ #include "defines.h" #include "port.h" #include "prototypes.h" +#include "string/ctype/isascii.h" #include "string/strcmp/streq.h" #include "string/strcmp/strprefix.h" #include "string/strtok/stpsep.h" @@ -211,7 +212,7 @@ getportent(void) * week or the other two values. */ - for (i = 0; isalpha(cp[i]) && ('\0' != cp[i + 1]); i += 2) { + for (i = 0; isalpha_c(cp[i]) && ('\0' != cp[i + 1]); i += 2) { switch ((cp[i] << 8) | (cp[i + 1])) { case ('S' << 8) | 'u': port.pt_times[j].t_days |= 01; @@ -260,7 +261,7 @@ getportent(void) * representing the times of day. */ - for (dtime = 0; isdigit (cp[i]); i++) { + for (dtime = 0; isdigit_c(cp[i]); i++) { dtime = dtime * 10 + cp[i] - '0'; } @@ -270,7 +271,7 @@ getportent(void) port.pt_times[j].t_start = dtime; cp = cp + i + 1; - for (dtime = 0, i = 0; isdigit (cp[i]); i++) { + for (dtime = 0, i = 0; isdigit_c(cp[i]); i++) { dtime = dtime * 10 + cp[i] - '0'; } diff --git a/lib/setupenv.c b/lib/setupenv.c index 263221636d..f109e10afc 100644 --- a/lib/setupenv.c +++ b/lib/setupenv.c @@ -105,7 +105,7 @@ static void read_env_file (const char *filename) } else if (*cp == '\0') { /* end of string */ goto finished; - } else if (isspace (*cp)) { + } else if (isspace_c(*cp)) { /* unescaped whitespace - end of string */ stpcpy(cp, ""); goto finished; diff --git a/lib/string/ctype/strchrisascii/strchriscntrl.h b/lib/string/ctype/strchrisascii/strchriscntrl.h index a1eaca371e..639a77264c 100644 --- a/lib/string/ctype/strchrisascii/strchriscntrl.h +++ b/lib/string/ctype/strchrisascii/strchriscntrl.h @@ -8,24 +8,21 @@ #include "config.h" -#include #include +#include "string/ctype/isascii.h" #include "string/strcmp/streq.h" inline bool strchriscntrl(const char *s); -// string character is [:cntrl:] -// Return true if any iscntrl(3) character is found in the string. +// strchriscntrl - string character is [:cntrl:] inline bool strchriscntrl(const char *s) { for (; !streq(s, ""); s++) { - unsigned char c = *s; - - if (iscntrl(c)) + if (iscntrl_c(*s)) return true; } diff --git a/lib/string/ctype/strisascii/strisdigit.h b/lib/string/ctype/strisascii/strisdigit.h index 0c5175fc8d..1e4f69d2cd 100644 --- a/lib/string/ctype/strisascii/strisdigit.h +++ b/lib/string/ctype/strisascii/strisdigit.h @@ -10,6 +10,7 @@ #include +#include "string/ctype/isascii.h" #include "string/strcmp/streq.h" #include "string/strspn/stpspn.h" @@ -17,15 +18,14 @@ inline bool strisdigit(const char *s); -// string is [:digit:] -// Like isdigit(3), but check all characters in the string. +// strisdigit - string is [:digit:] inline bool strisdigit(const char *s) { if (streq(s, "")) return false; - return streq(stpspn(s, "0123456789"), ""); + return streq(stpspn(s, CTYPE_DIGIT_C), ""); } diff --git a/lib/string/ctype/strisascii/strisprint.h b/lib/string/ctype/strisascii/strisprint.h index 566dbf886d..652c6544d3 100644 --- a/lib/string/ctype/strisascii/strisprint.h +++ b/lib/string/ctype/strisascii/strisprint.h @@ -8,17 +8,16 @@ #include "config.h" -#include #include +#include "string/ctype/isascii.h" #include "string/strcmp/streq.h" inline bool strisprint(const char *s); -// string is [:print:] -// Like isprint(3), but check all characters in the string. +// strisprint - string is [:print:] inline bool strisprint(const char *s) { @@ -26,9 +25,7 @@ strisprint(const char *s) return false; for (; !streq(s, ""); s++) { - unsigned char c = *s; - - if (!isprint(c)) + if (!isprint_c(*s)) return false; } diff --git a/src/newusers.c b/src/newusers.c index e9353fdc0a..6ac29c673d 100644 --- a/src/newusers.c +++ b/src/newusers.c @@ -56,6 +56,7 @@ #include "shadow/gshadow/sgrp.h" #include "shadowlog.h" #include "sssd.h" +#include "string/ctype/isascii.h" #include "string/sprintf/snprintf.h" #include "string/strcmp/streq.h" #include "string/strdup/strdup.h" @@ -248,7 +249,7 @@ static int add_group (const char *name, const char *gid, gid_t *ngid, uid_t uid) return 0; } - if (isdigit (gid[0])) { + if (isdigit_c(gid[0])) { /* * The GID is a number, which means either this is a brand * new group, or an existing group. @@ -292,7 +293,7 @@ static int add_group (const char *name, const char *gid, gid_t *ngid, uid_t uid) /* * Now I have all of the fields required to create the new group. */ - if (!streq(gid, "") && (!isdigit(gid[0]))) { + if (!streq(gid, "") && (!isdigit_c(gid[0]))) { grent.gr_name = xstrdup (gid); } else { grent.gr_name = xstrdup (name); @@ -357,7 +358,7 @@ static int get_user_id (const char *uid, uid_t *nuid) { * The first guess for the UID is either the numerical UID that the * caller provided, or the next available UID. */ - if (isdigit (uid[0])) { + if (isdigit_c(uid[0])) { if ((get_uid(uid, nuid) == -1) || (*nuid == (uid_t)-1)) { fprintf (stderr, _("%s: invalid user ID '%s'\n"), From 662e218750c3a7257191dcb95e0697838350689c Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 2 Jan 2026 13:57:19 +0100 Subject: [PATCH 03/13] lib/: Merge directories "lib/string/ctype/*" into unified files The APIs defined under each of those subdirs are too similar and related that it makes more sense to define them in the same files. (BTW, we only had one API per subdir, except in one subdir that had two APIs, so in the end, we have almost the same separation.) Signed-off-by: Alejandro Colomar --- lib/Makefile.am | 14 ++++---- lib/chkname.c | 4 +-- lib/fields.c | 4 +-- lib/obscure.c | 2 +- lib/string/README | 12 +++---- .../strchriscntrl.c => strchrisascii.c} | 2 +- .../strchriscntrl.h => strchrisascii.h} | 4 +-- .../{strisascii/strisprint.c => strisascii.c} | 3 +- .../{strisascii/strisdigit.h => strisascii.h} | 21 +++++++++-- lib/string/ctype/strisascii/strisdigit.c | 12 ------- lib/string/ctype/strisascii/strisprint.h | 36 ------------------- .../{strtoascii/strtolower.c => strtoascii.c} | 2 +- .../{strtoascii/strtolower.h => strtoascii.h} | 8 ++--- lib/subordinateio.c | 2 +- 14 files changed, 46 insertions(+), 80 deletions(-) rename lib/string/ctype/{strchrisascii/strchriscntrl.c => strchrisascii.c} (79%) rename lib/string/ctype/{strchrisascii/strchriscntrl.h => strchrisascii.h} (77%) rename lib/string/ctype/{strisascii/strisprint.c => strisascii.c} (70%) rename lib/string/ctype/{strisascii/strisdigit.h => strisascii.h} (58%) delete mode 100644 lib/string/ctype/strisascii/strisdigit.c delete mode 100644 lib/string/ctype/strisascii/strisprint.h rename lib/string/ctype/{strtoascii/strtolower.c => strtoascii.c} (78%) rename lib/string/ctype/{strtoascii/strtolower.h => strtoascii.h} (61%) diff --git a/lib/Makefile.am b/lib/Makefile.am index 577c171789..3312662b04 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -188,14 +188,12 @@ libshadow_la_SOURCES = \ sssd.h \ string/ctype/isascii.c \ string/ctype/isascii.h \ - string/ctype/strchrisascii/strchriscntrl.c \ - string/ctype/strchrisascii/strchriscntrl.h \ - string/ctype/strisascii/strisdigit.c \ - string/ctype/strisascii/strisdigit.h \ - string/ctype/strisascii/strisprint.c \ - string/ctype/strisascii/strisprint.h \ - string/ctype/strtoascii/strtolower.c \ - string/ctype/strtoascii/strtolower.h \ + string/ctype/strchrisascii.c \ + string/ctype/strchrisascii.h \ + string/ctype/strisascii.c \ + string/ctype/strisascii.h \ + string/ctype/strtoascii.c \ + string/ctype/strtoascii.h \ string/memset/memzero.c \ string/memset/memzero.h \ string/sprintf/aprintf.c \ diff --git a/lib/chkname.c b/lib/chkname.c index 0abee4d294..6b768b8c3e 100644 --- a/lib/chkname.c +++ b/lib/chkname.c @@ -33,8 +33,8 @@ #include "defines.h" #include "chkname.h" -#include "string/ctype/strchrisascii/strchriscntrl.h" -#include "string/ctype/strisascii/strisdigit.h" +#include "string/ctype/strchrisascii.h" +#include "string/ctype/strisascii.h" #include "string/strcmp/streq.h" #include "string/strcmp/strcaseeq.h" diff --git a/lib/fields.c b/lib/fields.c index 144e5fd09a..07a439feb1 100644 --- a/lib/fields.c +++ b/lib/fields.c @@ -17,8 +17,8 @@ #include #include "prototypes.h" -#include "string/ctype/strisascii/strisprint.h" -#include "string/ctype/strchrisascii/strchriscntrl.h" +#include "string/ctype/strisascii.h" +#include "string/ctype/strchrisascii.h" #include "string/strcmp/streq.h" #include "string/strspn/stpspn.h" #include "string/strspn/stprspn.h" diff --git a/lib/obscure.c b/lib/obscure.c index c572b96a34..485bfd2407 100644 --- a/lib/obscure.c +++ b/lib/obscure.c @@ -19,7 +19,7 @@ #include "prototypes.h" #include "defines.h" #include "getdef.h" -#include "string/ctype/strtoascii/strtolower.h" +#include "string/ctype/strtoascii.h" #include "string/memset/memzero.h" #include "string/sprintf/aprintf.h" #include "string/strcmp/streq.h" diff --git a/lib/string/README b/lib/string/README index bb44d2f867..c48b828cc0 100644 --- a/lib/string/README +++ b/lib/string/README @@ -75,21 +75,21 @@ ctype/ - Character classification and conversion functions if the character belongs to the category specified in the function name. - strchrisascii/ - The functions defined under this directory + strchrisascii.h + The functions defined in this file return true if the string has any characters that belong to the category specified in the function name. - strisascii/ - The functions defined under this directory + strisascii.h + The functions defined in this file return true if all of the characters of the string belong to the category specified in the function name and the string is not an empty string. - strtoascii/ - The functions defined under this directory + strtoascii.h + The functions defined in this file translate all characters in a string. memset/ - Memory zeroing diff --git a/lib/string/ctype/strchrisascii/strchriscntrl.c b/lib/string/ctype/strchrisascii.c similarity index 79% rename from lib/string/ctype/strchrisascii/strchriscntrl.c rename to lib/string/ctype/strchrisascii.c index ca77b4cfba..b9b7e919a9 100644 --- a/lib/string/ctype/strchrisascii/strchriscntrl.c +++ b/lib/string/ctype/strchrisascii.c @@ -4,7 +4,7 @@ #include "config.h" -#include "string/ctype/strchrisascii/strchriscntrl.h" +#include "string/ctype/strchrisascii.h" #include diff --git a/lib/string/ctype/strchrisascii/strchriscntrl.h b/lib/string/ctype/strchrisascii.h similarity index 77% rename from lib/string/ctype/strchrisascii/strchriscntrl.h rename to lib/string/ctype/strchrisascii.h index 639a77264c..24d8d34d72 100644 --- a/lib/string/ctype/strchrisascii/strchriscntrl.h +++ b/lib/string/ctype/strchrisascii.h @@ -2,8 +2,8 @@ // SPDX-License-Identifier: BSD-3-Clause -#ifndef SHADOW_INCLUDE_LIB_STRING_CTYPE_STRCHRISASCII_STRCHRISCNTRL_H_ -#define SHADOW_INCLUDE_LIB_STRING_CTYPE_STRCHRISASCII_STRCHRISCNTRL_H_ +#ifndef SHADOW_INCLUDE_LIB_STRING_CTYPE_STRCHRISASCII_H_ +#define SHADOW_INCLUDE_LIB_STRING_CTYPE_STRCHRISASCII_H_ #include "config.h" diff --git a/lib/string/ctype/strisascii/strisprint.c b/lib/string/ctype/strisascii.c similarity index 70% rename from lib/string/ctype/strisascii/strisprint.c rename to lib/string/ctype/strisascii.c index 47fb2a3bfe..564dac2504 100644 --- a/lib/string/ctype/strisascii/strisprint.c +++ b/lib/string/ctype/strisascii.c @@ -4,9 +4,10 @@ #include "config.h" -#include "string/ctype/strisascii/strisprint.h" +#include "string/ctype/strisascii.h" #include +extern inline bool strisdigit(const char *s); extern inline bool strisprint(const char *s); diff --git a/lib/string/ctype/strisascii/strisdigit.h b/lib/string/ctype/strisascii.h similarity index 58% rename from lib/string/ctype/strisascii/strisdigit.h rename to lib/string/ctype/strisascii.h index 1e4f69d2cd..1ee66c2fe3 100644 --- a/lib/string/ctype/strisascii/strisdigit.h +++ b/lib/string/ctype/strisascii.h @@ -2,8 +2,8 @@ // SPDX-License-Identifier: BSD-3-Clause -#ifndef SHADOW_INCLUDE_LIB_STRING_CTYPE_STRISASCII_STRISDIGIT_H_ -#define SHADOW_INCLUDE_LIB_STRING_CTYPE_STRISASCII_STRISDIGIT_H_ +#ifndef SHADOW_INCLUDE_LIB_STRING_CTYPE_STRISASCII_H_ +#define SHADOW_INCLUDE_LIB_STRING_CTYPE_STRISASCII_H_ #include "config.h" @@ -16,6 +16,7 @@ inline bool strisdigit(const char *s); +inline bool strisprint(const char *s); // strisdigit - string is [:digit:] @@ -29,4 +30,20 @@ strisdigit(const char *s) } +// strisprint - string is [:print:] +inline bool +strisprint(const char *s) +{ + if (streq(s, "")) + return false; + + for (; !streq(s, ""); s++) { + if (!isprint_c(*s)) + return false; + } + + return true; +} + + #endif // include guard diff --git a/lib/string/ctype/strisascii/strisdigit.c b/lib/string/ctype/strisascii/strisdigit.c deleted file mode 100644 index acd4522882..0000000000 --- a/lib/string/ctype/strisascii/strisdigit.c +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2024, Alejandro Colomar -// SPDX-License-Identifier: BSD-3-Clause - - -#include "config.h" - -#include "string/ctype/strisascii/strisdigit.h" - -#include - - -extern inline bool strisdigit(const char *s); diff --git a/lib/string/ctype/strisascii/strisprint.h b/lib/string/ctype/strisascii/strisprint.h deleted file mode 100644 index 652c6544d3..0000000000 --- a/lib/string/ctype/strisascii/strisprint.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025, Alejandro Colomar -// SPDX-License-Identifier: BSD-3-Clause - - -#ifndef SHADOW_INCLUDE_LIB_STRING_CTYPE_STRISASCII_STRISPRINT_H_ -#define SHADOW_INCLUDE_LIB_STRING_CTYPE_STRISASCII_STRISPRINT_H_ - - -#include "config.h" - -#include - -#include "string/ctype/isascii.h" -#include "string/strcmp/streq.h" - - -inline bool strisprint(const char *s); - - -// strisprint - string is [:print:] -inline bool -strisprint(const char *s) -{ - if (streq(s, "")) - return false; - - for (; !streq(s, ""); s++) { - if (!isprint_c(*s)) - return false; - } - - return true; -} - - -#endif // include guard diff --git a/lib/string/ctype/strtoascii/strtolower.c b/lib/string/ctype/strtoascii.c similarity index 78% rename from lib/string/ctype/strtoascii/strtolower.c rename to lib/string/ctype/strtoascii.c index 99ea4097ca..1e89acaaf5 100644 --- a/lib/string/ctype/strtoascii/strtolower.c +++ b/lib/string/ctype/strtoascii.c @@ -4,7 +4,7 @@ #include "config.h" -#include "string/ctype/strtoascii/strtolower.h" +#include "string/ctype/strtoascii.h" extern inline char *strtolower(char *str); diff --git a/lib/string/ctype/strtoascii/strtolower.h b/lib/string/ctype/strtoascii.h similarity index 61% rename from lib/string/ctype/strtoascii/strtolower.h rename to lib/string/ctype/strtoascii.h index d283fe1e87..c0c3be7177 100644 --- a/lib/string/ctype/strtoascii/strtolower.h +++ b/lib/string/ctype/strtoascii.h @@ -2,8 +2,8 @@ // SPDX-License-Identifier: BSD-3-Clause -#ifndef SHADOW_INCLUDE_LIB_STRING_CTYPE_STRTOASCII_STRTOLOWER_H_ -#define SHADOW_INCLUDE_LIB_STRING_CTYPE_STRTOASCII_STRTOLOWER_H_ +#ifndef SHADOW_INCLUDE_LIB_STRING_CTYPE_STRTOASCII_H_ +#define SHADOW_INCLUDE_LIB_STRING_CTYPE_STRTOASCII_H_ #include "config.h" @@ -16,9 +16,7 @@ inline char *strtolower(char *str); -// string convert-to lower-case -// Like tolower(3), but convert all characters in the string. -// Returns the input pointer. +// strtolower - string convert-to lower-case inline char * strtolower(char *str) { diff --git a/lib/subordinateio.c b/lib/subordinateio.c index acd3f1ffdc..e5552e1229 100644 --- a/lib/subordinateio.c +++ b/lib/subordinateio.c @@ -22,7 +22,7 @@ #include "alloc/malloc.h" #include "alloc/reallocf.h" #include "atoi/a2i.h" -#include "string/ctype/strisascii/strisdigit.h" +#include "string/ctype/strisascii.h" #include "string/sprintf/snprintf.h" #include "string/strcmp/streq.h" #include "string/strtok/strsep2arr.h" From fbbf189e50f9083859ef6b3cb911ff5e31411209 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 2 Jan 2026 21:37:55 +0100 Subject: [PATCH 04/13] lib/string/ctype/strisascii.h: strisprint(): Simplify implementation This also makes it consistent with strisdigit(). Signed-off-by: Alejandro Colomar --- lib/string/ctype/strisascii.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/string/ctype/strisascii.h b/lib/string/ctype/strisascii.h index 1ee66c2fe3..5ce5f6b0ab 100644 --- a/lib/string/ctype/strisascii.h +++ b/lib/string/ctype/strisascii.h @@ -37,12 +37,7 @@ strisprint(const char *s) if (streq(s, "")) return false; - for (; !streq(s, ""); s++) { - if (!isprint_c(*s)) - return false; - } - - return true; + return streq(stpspn(s, CTYPE_PRINT_C), ""); } From 81513e66fb164b349bc007db9b43218aca77af99 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 2 Jan 2026 21:40:57 +0100 Subject: [PATCH 05/13] lib/string/ctype/strisascii.h: Compact definitions By being closer together, I find them more readable. The pattern and the differences are easier to spot. Signed-off-by: Alejandro Colomar --- lib/string/ctype/strisascii.h | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/lib/string/ctype/strisascii.h b/lib/string/ctype/strisascii.h index 5ce5f6b0ab..2f58f43ea5 100644 --- a/lib/string/ctype/strisascii.h +++ b/lib/string/ctype/strisascii.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024, Alejandro Colomar +// SPDX-FileCopyrightText: 2024-2025, Alejandro Colomar // SPDX-License-Identifier: BSD-3-Clause @@ -15,29 +15,19 @@ #include "string/strspn/stpspn.h" -inline bool strisdigit(const char *s); -inline bool strisprint(const char *s); +inline bool strisdigit(const char *s); // strisdigit - string is [:digit:] +inline bool strisprint(const char *s); // strisprint - string is [:print:] -// strisdigit - string is [:digit:] inline bool strisdigit(const char *s) { - if (streq(s, "")) - return false; - - return streq(stpspn(s, CTYPE_DIGIT_C), ""); + return !streq(s, "") && streq(stpspn(s, CTYPE_DIGIT_C), ""); } - - -// strisprint - string is [:print:] inline bool strisprint(const char *s) { - if (streq(s, "")) - return false; - - return streq(stpspn(s, CTYPE_PRINT_C), ""); + return !streq(s, "") && streq(stpspn(s, CTYPE_PRINT_C), ""); } From 6d8bbc30c0bb7f12f7bb1b7acb49205aaa3cba65 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 2 Jan 2026 21:46:31 +0100 Subject: [PATCH 06/13] lib/: stris*(), strchris*(): Rename C-locale APIs with a _c suffix Signed-off-by: Alejandro Colomar --- lib/chkname.c | 4 ++-- lib/fields.c | 4 ++-- lib/string/ctype/strchrisascii.h | 6 +++--- lib/string/ctype/strisascii.c | 6 +++--- lib/string/ctype/strisascii.h | 8 ++++---- lib/subordinateio.c | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/chkname.c b/lib/chkname.c index 6b768b8c3e..4407eebda3 100644 --- a/lib/chkname.c +++ b/lib/chkname.c @@ -68,8 +68,8 @@ is_valid_name(const char *name) || streq(name, "..") || strspn(name, "-") || strpbrk(name, " \"#',/:;") - || strchriscntrl(name) - || strisdigit(name)) + || strchriscntrl_c(name) + || strisdigit_c(name)) { errno = EINVAL; return false; diff --git a/lib/fields.c b/lib/fields.c index 07a439feb1..d38f4df2fc 100644 --- a/lib/fields.c +++ b/lib/fields.c @@ -41,9 +41,9 @@ valid_field_(const char *field, const char *illegal) if (strpbrk(field, illegal)) return -1; - if (strchriscntrl(field)) + if (strchriscntrl_c(field)) return -1; - if (strisprint(field)) + if (strisprint_c(field)) return 0; if (streq(field, "")) return 0; diff --git a/lib/string/ctype/strchrisascii.h b/lib/string/ctype/strchrisascii.h index 24d8d34d72..2ccff362cf 100644 --- a/lib/string/ctype/strchrisascii.h +++ b/lib/string/ctype/strchrisascii.h @@ -14,12 +14,12 @@ #include "string/strcmp/streq.h" -inline bool strchriscntrl(const char *s); +inline bool strchriscntrl_c(const char *s); -// strchriscntrl - string character is [:cntrl:] +// strchriscntrl_c - string character is [:cntrl:] C-locale inline bool -strchriscntrl(const char *s) +strchriscntrl_c(const char *s) { for (; !streq(s, ""); s++) { if (iscntrl_c(*s)) diff --git a/lib/string/ctype/strisascii.c b/lib/string/ctype/strisascii.c index 564dac2504..c87c8f1b28 100644 --- a/lib/string/ctype/strisascii.c +++ b/lib/string/ctype/strisascii.c @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024, Alejandro Colomar +// SPDX-FileCopyrightText: 2024-2025, Alejandro Colomar // SPDX-License-Identifier: BSD-3-Clause @@ -9,5 +9,5 @@ #include -extern inline bool strisdigit(const char *s); -extern inline bool strisprint(const char *s); +extern inline bool strisdigit_c(const char *s); +extern inline bool strisprint_c(const char *s); diff --git a/lib/string/ctype/strisascii.h b/lib/string/ctype/strisascii.h index 2f58f43ea5..8e6b59b0bd 100644 --- a/lib/string/ctype/strisascii.h +++ b/lib/string/ctype/strisascii.h @@ -15,17 +15,17 @@ #include "string/strspn/stpspn.h" -inline bool strisdigit(const char *s); // strisdigit - string is [:digit:] -inline bool strisprint(const char *s); // strisprint - string is [:print:] +inline bool strisdigit_c(const char *s); // strisdigit - string is [:digit:] C-locale +inline bool strisprint_c(const char *s); // strisprint - string is [:print:] C-locale inline bool -strisdigit(const char *s) +strisdigit_c(const char *s) { return !streq(s, "") && streq(stpspn(s, CTYPE_DIGIT_C), ""); } inline bool -strisprint(const char *s) +strisprint_c(const char *s) { return !streq(s, "") && streq(stpspn(s, CTYPE_PRINT_C), ""); } diff --git a/lib/subordinateio.c b/lib/subordinateio.c index e5552e1229..5ed5288fbb 100644 --- a/lib/subordinateio.c +++ b/lib/subordinateio.c @@ -938,7 +938,7 @@ static int append_uids(uid_t **uids, const char *owner, int n) int i; uid_t owner_uid; - if (strisdigit(owner)) { + if (strisdigit_c(owner)) { i = sscanf(owner, "%d", &owner_uid); if (i != 1) { // should not happen From 347f93174c7b4648e1d112466eed7640abd11bd4 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Sat, 3 Jan 2026 00:37:18 +0100 Subject: [PATCH 07/13] lib/string/ctype/strchrisascii.h: Use strpbrk(3) to simplify This compacts it into a one-liner, more similar to the strisascii_c() functions. Since we only use the argument once, we can even turn this into a macro. Signed-off-by: Alejandro Colomar --- lib/string/ctype/strchrisascii.c | 5 ----- lib/string/ctype/strchrisascii.h | 17 ++--------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/lib/string/ctype/strchrisascii.c b/lib/string/ctype/strchrisascii.c index b9b7e919a9..905430628a 100644 --- a/lib/string/ctype/strchrisascii.c +++ b/lib/string/ctype/strchrisascii.c @@ -5,8 +5,3 @@ #include "config.h" #include "string/ctype/strchrisascii.h" - -#include - - -extern inline bool strchriscntrl(const char *s); diff --git a/lib/string/ctype/strchrisascii.h b/lib/string/ctype/strchrisascii.h index 2ccff362cf..6d1c4ff022 100644 --- a/lib/string/ctype/strchrisascii.h +++ b/lib/string/ctype/strchrisascii.h @@ -8,26 +8,13 @@ #include "config.h" -#include +#include #include "string/ctype/isascii.h" -#include "string/strcmp/streq.h" - - -inline bool strchriscntrl_c(const char *s); // strchriscntrl_c - string character is [:cntrl:] C-locale -inline bool -strchriscntrl_c(const char *s) -{ - for (; !streq(s, ""); s++) { - if (iscntrl_c(*s)) - return true; - } - - return false; -} +#define strchriscntrl_c(s) (!!strpbrk(s, CTYPE_CNTRL_C)) #endif // include guard From 7c37e884f8e0414ea739ebf1e829bfe29319cb5d Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Sat, 3 Jan 2026 14:33:44 +0100 Subject: [PATCH 08/13] lib/fields.c: valid_field(): Check empty string before strisprint_c() This allows us to not depend on whether strisprint_c("") returns true or false. Signed-off-by: Alejandro Colomar --- lib/fields.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fields.c b/lib/fields.c index d38f4df2fc..b8e3bb9e8a 100644 --- a/lib/fields.c +++ b/lib/fields.c @@ -43,10 +43,10 @@ valid_field_(const char *field, const char *illegal) return -1; if (strchriscntrl_c(field)) return -1; - if (strisprint_c(field)) - return 0; if (streq(field, "")) return 0; + if (strisprint_c(field)) + return 0; return 1; // !ASCII } From d311b0d09b9a69e3cd9c4cc67399ad9b099d5815 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Sat, 3 Jan 2026 14:37:43 +0100 Subject: [PATCH 09/13] lib/string/ctype/strisascii.*: Don't special-case "" It is not intuitive or clear what the right behavior should be for an empty string. If we define these APIs as "return true if all characters in the string belong to the specified character set", then an empty string should return true. On the other hand, if you ask me if an empty string is a numeric string, I might naively say no. It is irrelevant whether we return true or false for an empty string. All of the callers already handle correctly the case of an empty string. This makes the implementation simpler, using the argument only once. This allows implementing these as macros. Signed-off-by: Alejandro Colomar --- lib/string/README | 3 +-- lib/string/ctype/strisascii.c | 6 ------ lib/string/ctype/strisascii.h | 19 +++---------------- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/lib/string/README b/lib/string/README index c48b828cc0..6ad0e1f5c8 100644 --- a/lib/string/README +++ b/lib/string/README @@ -85,8 +85,7 @@ ctype/ - Character classification and conversion functions The functions defined in this file return true if all of the characters of the string - belong to the category specified in the function name - and the string is not an empty string. + belong to the category specified in the function name. strtoascii.h The functions defined in this file diff --git a/lib/string/ctype/strisascii.c b/lib/string/ctype/strisascii.c index c87c8f1b28..24e8a9cbb4 100644 --- a/lib/string/ctype/strisascii.c +++ b/lib/string/ctype/strisascii.c @@ -5,9 +5,3 @@ #include "config.h" #include "string/ctype/strisascii.h" - -#include - - -extern inline bool strisdigit_c(const char *s); -extern inline bool strisprint_c(const char *s); diff --git a/lib/string/ctype/strisascii.h b/lib/string/ctype/strisascii.h index 8e6b59b0bd..eb4d831bbd 100644 --- a/lib/string/ctype/strisascii.h +++ b/lib/string/ctype/strisascii.h @@ -8,27 +8,14 @@ #include "config.h" -#include - #include "string/ctype/isascii.h" #include "string/strcmp/streq.h" #include "string/strspn/stpspn.h" -inline bool strisdigit_c(const char *s); // strisdigit - string is [:digit:] C-locale -inline bool strisprint_c(const char *s); // strisprint - string is [:print:] C-locale - - -inline bool -strisdigit_c(const char *s) -{ - return !streq(s, "") && streq(stpspn(s, CTYPE_DIGIT_C), ""); -} -inline bool -strisprint_c(const char *s) -{ - return !streq(s, "") && streq(stpspn(s, CTYPE_PRINT_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), "") #endif // include guard From d9c21d8e5e4c2e5cfdfd6c3b07e7cff3b14d8848 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Mon, 23 Dec 2024 18:36:31 +0100 Subject: [PATCH 10/13] lib/string/ctype/isascii.h: ispfchar_c(): Add function This function returns true if the input character is a character from the POSIX portable filename character set. Link: Signed-off-by: Alejandro Colomar --- lib/string/ctype/isascii.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/string/ctype/isascii.h b/lib/string/ctype/isascii.h index 3bec7defba..d0f59fbd58 100644 --- a/lib/string/ctype/isascii.h +++ b/lib/string/ctype/isascii.h @@ -28,6 +28,7 @@ #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 // isascii_c - is [:ascii:] C-locale @@ -43,6 +44,7 @@ #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), "")) #endif // include guard From 44af79d71b98cb7d4d8415f40e6aaea968e350ab Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Mon, 23 Dec 2024 15:25:48 +0100 Subject: [PATCH 11/13] lib/chkname.c: is_valid_name(): Use isalnum(3) instead of its pattern Signed-off-by: Alejandro Colomar --- lib/chkname.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/chkname.c b/lib/chkname.c index 4407eebda3..2fc0711c4d 100644 --- a/lib/chkname.c +++ b/lib/chkname.c @@ -87,9 +87,7 @@ is_valid_name(const char *name) * sake of Samba 3.x "add machine script" */ - if (!((*name >= 'a' && *name <= 'z') || - (*name >= 'A' && *name <= 'Z') || - (*name >= '0' && *name <= '9') || + if (!(isalnum(*name) || *name == '_' || *name == '.')) { @@ -98,9 +96,7 @@ is_valid_name(const char *name) } while (!streq(++name, "")) { - if (!((*name >= 'a' && *name <= 'z') || - (*name >= 'A' && *name <= 'Z') || - (*name >= '0' && *name <= '9') || + if (!(isalnum(*name) || *name == '_' || *name == '.' || *name == '-' || From ea9364860219a86dffafc3cbe411507dc5fac20c Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Mon, 23 Dec 2024 17:30:57 +0100 Subject: [PATCH 12/13] lib/chkname.c: is_valid_name(): Split Samba check Signed-off-by: Alejandro Colomar --- lib/chkname.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/chkname.c b/lib/chkname.c index 2fc0711c4d..4dc09631c6 100644 --- a/lib/chkname.c +++ b/lib/chkname.c @@ -96,11 +96,13 @@ is_valid_name(const char *name) } while (!streq(++name, "")) { + if (streq(name, "$")) // Samba + return true; + if (!(isalnum(*name) || *name == '_' || *name == '.' || - *name == '-' || - streq(name, "$") + *name == '-' )) { errno = EILSEQ; From 000c8c170d17e37964943bf5e11c2ac6c1d6333b Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Mon, 23 Dec 2024 18:41:06 +0100 Subject: [PATCH 13/13] lib/chkname.c: is_valid_name(): Use ispfchar_c() to simplify In the first case, we can do the transformation because a few lines above, we explicitly reject a name starting with a '-'. In the second case, we're obviously using ispfchar_c() instead of its pattern. Signed-off-by: Alejandro Colomar --- lib/chkname.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/chkname.c b/lib/chkname.c index 4dc09631c6..57588220fb 100644 --- a/lib/chkname.c +++ b/lib/chkname.c @@ -33,6 +33,7 @@ #include "defines.h" #include "chkname.h" +#include "string/ctype/isascii.h" #include "string/ctype/strchrisascii.h" #include "string/ctype/strisascii.h" #include "string/strcmp/streq.h" @@ -87,10 +88,7 @@ is_valid_name(const char *name) * sake of Samba 3.x "add machine script" */ - if (!(isalnum(*name) || - *name == '_' || - *name == '.')) - { + if (!ispfchar_c(*name)) { errno = EILSEQ; return false; } @@ -99,12 +97,7 @@ is_valid_name(const char *name) if (streq(name, "$")) // Samba return true; - if (!(isalnum(*name) || - *name == '_' || - *name == '.' || - *name == '-' - )) - { + if (!ispfchar_c(*name)) { errno = EILSEQ; return false; }