Skip to contentSkip to conversation

Simplify implementation of strscpy()

PATCH In review Alejandro Colomar 3 participants 6 messages Nov 3

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover Simplify implementation of strscpy()

Hi!

Here's a simpler implementation of the default arguments in strscpy(9). It avoids having to build concatenated names, the ignored '...' in __strscpy0, counting arguments, etc. It's replaced by a quite simple macro that takes two arguments, and picks the last non-empty one.

It requires a C23 feature, but it's supported as a GNU extension in GNU C11 mode, and was implemented a long time ago, so it should be fine.

Have a lovely day!
Alex

Alejandro Colomar (2):
args: Add DEFARG() to implement macro default arguments
string: Simplify strscpy[_pad]()

 include/linux/args.h   |  5 +++++
 include/linux/string.h | 22 ++++------------------
 2 files changed, 9 insertions(+), 18 deletions(-)

Range-diff against v0:

-:  ------------ > 1:  be1057524eb2 args: Add DEFARG() to implement macro default arguments
-:  ------------ > 2:  31096484d9be string: Simplify strscpy[_pad]()
Signature · 1 line
2.55.0
#1 lore raw

PATCH 1/2 args: Add DEFARG() to implement macro default arguments

It needs __VA_OPT__, which is a C23 feature, but it's provided by GCC and Clang in GNU-C mode as an extension even without C23. __VA_OPT__ was implemented in GCC 9 and Clang 6, I think, so it should be widely available.

Here's a small example program showing how this works:

	alx@devuan:~/tmp$ cat d.c
	#define D__(x, ...)  (x)
	#define D_(x, ...)   D__(__VA_ARGS__ __VA_OPT__(,) x)
	#define D(a, b)      D_(a, b)
	D(1, 2)
	D(1, )
	D(, 2)
	D(,)
	alx@devuan:~/tmp$ gcc -E -Wall -Wextra d.c | grep '^[^#]'
	(2)
	(1)
	(2)
	()
---
 include/linux/args.h | 5 +++++
 1 file changed, 5 insertions(+)
include/linux/args.h +5 -0
index 0562dc51435e..5886011cf791 100644
@@ -3,6 +3,11 @@
 #ifndef _LINUX_ARGS_H
 #define _LINUX_ARGS_H
 
+/* Expand to 'arg' if non-empty, or to 'def' if 'arg' is empty.  */
+#define ___DEFARG(x, ...)   (x)
+#define __DEFARG(def, ...)  ___DEFARG(__VA_ARGS__ __VA_OPT__(,) def)
+#define DEFARG(def, arg)    __DEFARG(def, arg)
+
 /*
  * How do these macros work?
  *
Signature · 1 line
2.55.0
#2 lore raw

Re: [PATCH v1 1/2] args: Add DEFARG() to implement macro default arguments

Quoted 6 lines

Date: 2026-11-03 14:27:11+0100
From: Alejandro Colomar <[email protected]>

It needs __VA_OPT__, which is a C23 feature, but it's provided by GCC and Clang in GNU-C mode as an extension even without C23. __VA_OPT__ was implemented in GCC 9 and Clang 6, I think, so it should be widely

Actually, it seems it was implemented in Clang 12, not Clang 6. I got that 6 from llvmorg-6.0.0-rc1, but that version number seems to not be the same as the Clang version number. Anyway, Clang 12 should be fine, I think.

Cheers,
Alex

Quoted 42 lines

available.

Here's a small example program showing how this works:

	alx@devuan:~/tmp$ cat d.c
	#define D__(x, ...)  (x)
	#define D_(x, ...)   D__(__VA_ARGS__ __VA_OPT__(,) x)
	#define D(a, b)      D_(a, b)
	D(1, 2)
	D(1, )
	D(, 2)
	D(,)
	alx@devuan:~/tmp$ gcc -E -Wall -Wextra d.c | grep '^[^#]'
	(2)
	(1)
	(2)
	()
---
 include/linux/args.h | 5 +++++
 1 file changed, 5 insertions(+)
include/linux/args.h +5 -0
index 0562dc51435e..5886011cf791 100644
@@ -3,6 +3,11 @@
 #ifndef _LINUX_ARGS_H
 #define _LINUX_ARGS_H
 
+/* Expand to 'arg' if non-empty, or to 'def' if 'arg' is empty.  */
+#define ___DEFARG(x, ...)   (x)
+#define __DEFARG(def, ...)  ___DEFARG(__VA_ARGS__ __VA_OPT__(,) def)
+#define DEFARG(def, arg)    __DEFARG(def, arg)
+
 /*
  * How do these macros work?
  *
Signature · 1 line
2.55.0
Signature · 1 line
<https://www.alejandro-colomar.es>
#3 lore raw

PATCH 2/2 string: Simplify strscpy[_pad]()

In reply to Alejandro Colomar

Remove the need for intermediate macros and COUNT_ARGS().

---
 include/linux/string.h | 22 ++++------------------
 1 file changed, 4 insertions(+), 18 deletions(-)
include/linux/string.h +4 -18
index 6cb5cdd01158..b17c7b9d8bc9 100644
@@ -69,22 +69,6 @@ extern char * strcpy(char *,const char *);
 #endif
 ssize_t sized_strscpy(char *, const char *, size_t);
 
-/*
- * The 2 argument style can only be used when dst is an array with a
- * known size.
- */
-#define __strscpy0(dst, src, ...)	\
-	sized_strscpy(dst, src, sizeof(dst) + __must_be_array(dst) +	\
-				__must_be_cstr(dst) + __must_be_cstr(src))
-#define __strscpy1(dst, src, size)	\
-	sized_strscpy(dst, src, size + __must_be_cstr(dst) + __must_be_cstr(src))
-
-#define __strscpy_pad0(dst, src, ...)	\
-	sized_strscpy_pad(dst, src, sizeof(dst) + __must_be_array(dst) +	\
-				    __must_be_cstr(dst) + __must_be_cstr(src))
-#define __strscpy_pad1(dst, src, size)	\
-	sized_strscpy_pad(dst, src, size + __must_be_cstr(dst) + __must_be_cstr(src))
-
 /**
  * strscpy - Copy a C-string into a sized buffer
  * @dst: Where to copy the string to
@@ -108,7 +92,8 @@ ssize_t sized_strscpy(char *, const char *, size_t);
  * truncated.
  */
 #define strscpy(dst, src, ...)	\
-	CONCATENATE(__strscpy, COUNT_ARGS(__VA_ARGS__))(dst, src, __VA_ARGS__)
+	sized_strscpy(dst, src, DEFARG(sizeof(dst) + __must_be_array(dst), __VA_ARGS__) + \
+				__must_be_cstr(dst) + __must_be_cstr(src))
 
 #define sized_strscpy_pad(dest, src, count)	({			\
 	char *__dst = (dest);						\
@@ -143,7 +128,8 @@ ssize_t sized_strscpy(char *, const char *, size_t);
  * * -E2BIG if count is 0 or @src was truncated.
  */
 #define strscpy_pad(dst, src, ...)	\
-	CONCATENATE(__strscpy_pad, COUNT_ARGS(__VA_ARGS__))(dst, src, __VA_ARGS__)
+	sized_strscpy_pad(dst, src, DEFARG(sizeof(dst) + __must_be_array(dst), __VA_ARGS__) + \
+				__must_be_cstr(dst) + __must_be_cstr(src))
 
 #ifndef __HAVE_ARCH_STRCAT
 extern char * strcat(char *, const char *);
Signature · 1 line
2.55.0
#4 lore raw
In reply to Alejandro Colomar
Quoted 8 lines · Alejandro Colomar

Here's a simpler implementation of the default arguments in strscpy(9). It avoids having to build concatenated names, the ignored '...' in __strscpy0, counting arguments, etc. It's replaced by a quite simple macro that takes two arguments, and picks the last non-empty one.

It requires a C23 feature, but it's supported as a GNU extension in GNU C11 mode, and was implemented a long time ago, so it should be fine.

Yes and no.
See 551d44200152 ("default_gfp(): avoid using the "newfangled" __VA_OPT__ trick") for the details.
So far NAK from me.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#5 lore raw

Hi Andy,

Quoted 10 lines

Date: 2026-11-03 21:10:23+0200
From: Andy Shevchenko <[email protected]>

Quoted 6 lines · Alejandro Colomar

Here's a simpler implementation of the default arguments in strscpy(9). It avoids having to build concatenated names, the ignored '...' in __strscpy0, counting arguments, etc. It's replaced by a quite simple macro that takes two arguments, and picks the last non-empty one.

Quoted 6 lines
Quoted 2 lines

It requires a C23 feature, but it's supported as a GNU extension in GNU C11 mode, and was implemented a long time ago, so it should be fine.

Yes and no.
See 551d44200152 ("default_gfp(): avoid using the "newfangled" __VA_OPT__ trick") for the details.

Hmmmm, so sparse is a problem. DEFARG() cannot be implemented with ##__VA_ARGS__ (I believe), so I think this is frozen until sparse is improved to add support for __VA_OPT__.

The implementation in that commit you pointed takes advantage of the fact that the default value is hard-coded in the macro, and the macro has zero fixed parameters, which makes it work, but if you add one fixed parameter (which we need for a generic DEFARG() macro), then it doesn't work. GCC's ## is quite limited compared to __VA_OPT__().

I've CCed the sparse matinainer and mailing list. Let's see if they can add support for __VA_OPT__().

Quoted 1 line

So far NAK from me.

Makes sense. Thanks for the review!

Have a lovely night!
Alex

Quoted 7 lines
Signature · 3 lines
-- 
With Best Regards,
Andy Shevchenko
Signature · 1 line
<https://www.alejandro-colomar.es>