]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
Add stpeprintf()
authorAlejandro Colomar <alx@kernel.org>
Sun, 29 Jan 2023 23:19:56 +0000 (00:19 +0100)
committerIker Pedrosa <ikerpedrosam@gmail.com>
Thu, 16 Feb 2023 10:29:33 +0000 (11:29 +0100)
[v]stpeprintf() are similar to [v]snprintf(3), but they allow chaining.
[v]snprintf(3) are very dangerous for catenating strings, since the
obvious ways to do it invoke Undefined Behavior, and the ways that avoid
UB are very error-prone.

Cc: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
configure.ac
lib/stpeprintf.h [new file with mode: 0644]
libmisc/Makefile.am
libmisc/stpeprintf.c [new file with mode: 0644]

index 8e978cf5dfccef9719e552baac261fafc2dd4ca6..89fc1b26746de887c96e67b53aacc28518f75163 100644 (file)
@@ -50,7 +50,7 @@ AC_CHECK_FUNCS(arc4random_buf futimes \
        initgroups lckpwdf lutimes \
        setgroups updwtmp updwtmpx innetgr \
        getspnam_r \
-       memset_explicit explicit_bzero)
+       memset_explicit explicit_bzero stpeprintf)
 AC_SYS_LARGEFILE
 
 dnl Checks for typedefs, structures, and compiler characteristics.
diff --git a/lib/stpeprintf.h b/lib/stpeprintf.h
new file mode 100644 (file)
index 0000000..4929019
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * SPDX-FileCopyrightText:  2022 - 2023, Alejandro Colomar <alx@kernel.org>
+ *
+ * SPDX-License-Identifier:  BSD-3-Clause
+ */
+
+
+#ifndef SHADOW_INCLUDE_LIB_STPEPRINTF_H_
+#define SHADOW_INCLUDE_LIB_STPEPRINTF_H_
+
+
+#include <config.h>
+
+#if !defined(HAVE_STPEPRINTF)
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "defines.h"
+
+
+format_attr(printf, 3, 4)
+inline char *stpeprintf(char *dst, char *end, const char *restrict fmt, ...);
+
+format_attr(printf, 3, 0)
+inline char *vstpeprintf(char *dst, char *end, const char *restrict fmt,
+    va_list ap);
+
+
+/*
+ * SYNOPSIS
+ *     [[gnu::format(printf, 3, 4)]]
+ *     char *_Nullable stpeprintf(char *_Nullable dst, char end[0],
+ *                                const char *restrict fmt, ...);
+ *
+ *     [[gnu::format(printf, 3, 0)]]
+ *     char *_Nullable vstpeprintf(char *_Nullable dst, char end[0],
+ *                                const char *restrict fmt, va_list ap);
+ *
+ *
+ * ARGUMENTS
+ *     dst     Destination buffer where to write a string.
+ *
+ *     end     Pointer to one after the last element of the buffer
+ *             pointed to by `dst`.  Usually, it should be calculated
+ *             as `dst + NITEMS(dst)`.
+ *
+ *     fmt     Format string
+ *
+ *     ...
+ *     ap      Variadic argument list
+ *
+ * DESCRIPTION
+ *     These functions are very similar to [v]snprintf(3).
+ *
+ *     The destination buffer is limited by a pointer to its end --one
+ *     after its last element-- instead of a size.  This allows
+ *     chaining calls to it safely, unlike [v]snprintf(3), which is
+ *     difficult to chain without invoking Undefined Behavior.
+ *
+ * RETURN VALUE
+ *     dst + strlen(dst)
+ *             •  On success, these functions return a pointer to the
+ *                terminating NUL byte.
+ *
+ *     end
+ *             •  If this call truncated the resulting string.
+ *             •  If `dst == end` (a previous chained call to these
+ *                functions truncated).
+ *     NULL
+ *             •  If this function failed (see ERRORS).
+ *             •  If `dst == NULL` (a previous chained call to these
+ *                functions failed).
+ *
+ * ERRORS
+ *     These functions may fail for the same reasons as vsnprintf(3).
+ */
+
+
+inline char *
+stpeprintf(char *dst, char *end, const char *restrict fmt, ...)
+{
+       char     *p;
+       va_list  ap;
+
+       va_start(ap, fmt);
+       p = vstpeprintf(dst, end, fmt, ap);
+       va_end(ap);
+
+       return p;
+}
+
+
+inline char *
+vstpeprintf(char *dst, char *end, const char *restrict fmt, va_list ap)
+{
+       int        len;
+       ptrdiff_t  size;
+
+       if (dst == end)
+               return end;
+       if (dst == NULL)
+               return NULL;
+
+       size = end - dst;
+       len = vsnprintf(dst, size, fmt, ap);
+
+       if (len == -1)
+               return NULL;
+       if (len >= size)
+               return end;
+
+       return dst + len;
+}
+
+
+#endif  // !HAVE_STPEPRINTF
+#endif  // include guard
index ab363f549623cfc44519a84c5e0086fe0b2c34c5..a74de9755b6f4ccd8ee4140ae65f751fd140d777 100644 (file)
@@ -61,6 +61,7 @@ libmisc_la_SOURCES = \
        setugid.c \
        setupenv.c \
        shell.c \
+       stpeprintf.c \
        strtoday.c \
        sub.c \
        sulog.c \
diff --git a/libmisc/stpeprintf.c b/libmisc/stpeprintf.c
new file mode 100644 (file)
index 0000000..f3238ea
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * SPDX-FileCopyrightText:  2022 - 2023, Alejandro Colomar <alx@kernel.org>
+ *
+ * SPDX-License-Identifier:  BSD-3-Clause
+ */
+
+
+#include <config.h>
+
+#if !defined(HAVE_STPEPRINTF)
+
+#ident "$Id$"
+
+#include "stpeprintf.h"
+
+#include <stdarg.h>
+
+
+extern inline char *stpeprintf(char *dst, char *end, const char *restrict fmt,
+    ...);
+extern inline char *vstpeprintf(char *dst, char *end, const char *restrict fmt,
+    va_list ap);
+
+
+#endif  // !HAVE_STPEPRINTF