summaryrefslogtreecommitdiff
path: root/camlibs/ptp2/ptp.c
diff options
context:
space:
mode:
authorHans Ulrich Niedermann <hun@n-dimensional.de>2021-11-10 22:03:31 +0100
committerHans Ulrich Niedermann <hun@n-dimensional.de>2021-11-12 00:54:39 +0100
commitb6fae4fcceb3d3352bf24728045293d694a3e835 (patch)
treeb37c3f391e32e60bf8b9a2823d2d79ea3dbac2c0 /camlibs/ptp2/ptp.c
parent8527242022916573d38f8f86fb84fce47c450088 (diff)
downloadlibgphoto2-b6fae4fcceb3d3352bf24728045293d694a3e835.tar.gz
ptp2/ptp.c: Rewrite PTP_CNT_INIT to build with pure C99
This rewrites the variadic macro PTP_CNT_INIT() and the ptp_init_container() function such that they compile with the strictest compiler available: clang -std=c99 -pedantic -Wall -Wextra -Weverything This is achieved by moving the code argument to be the first of the variable arguments, and adapting the parameter counting and argument handling inside ptp_init_container() accordingly. As the code in ptp_init_container() which handles the va_args and puts them into PTPContainer Param<N> fields relies on a particular memory layout of the PTPContainer struct, this adds compile time assertions which check the memory layout is the one the code expects. Closes: https://github.com/gphoto/libgphoto2/pull/740
Diffstat (limited to 'camlibs/ptp2/ptp.c')
-rw-r--r--camlibs/ptp2/ptp.c70
1 files changed, 61 insertions, 9 deletions
diff --git a/camlibs/ptp2/ptp.c b/camlibs/ptp2/ptp.c
index 47adce4bf..85f8470fc 100644
--- a/camlibs/ptp2/ptp.c
+++ b/camlibs/ptp2/ptp.c
@@ -30,14 +30,19 @@
# include <libxml/parser.h>
#endif
+#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <stdint.h>
+#include <limits.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
+#include "libgphoto2_port/compiletime-assert.h"
+
#ifdef ENABLE_NLS
# include <libintl.h>
# undef _
@@ -59,27 +64,74 @@
#define CHECK_PTP_RC(RESULT) do { uint16_t r = (RESULT); if (r != PTP_RC_OK) return r; } while(0)
-static inline void
-ptp_init_container(PTPContainer* ptp, uint16_t code, int n_param, ...)
+
+/* Initialize a PTPContainer struct. For the usage, see the PTP_CNT_INIT() macro below. */
+static inline
+void ptp_init_container(PTPContainer* ptp, int n_param, ...)
{
va_list args;
int i;
memset(ptp, 0, sizeof(*ptp));
- ptp->Code = code;
+ va_start(args, n_param);
+ /* verify the int value cannot have cut off the uint16_t value */
+ COMPILETIME_ASSERT(INT_MAX >= UINT16_MAX);
+ /* note that uint16_t can be promoted to an int, so va_arg
+ * needs to be called with the int type */
+ ptp->Code = (uint16_t)(va_arg(args, int));
ptp->Nparam = n_param;
- va_start(args, n_param);
- for (i=0; i<n_param; ++i)
+ /* Make certain the memory layout of PTPContainer matches what
+ * the for loop code below expects.
+ */
+ COMPILETIME_ASSERT((offsetof(PTPContainer, Param1) + sizeof(uint32_t))
+ == offsetof(PTPContainer, Param2));
+ COMPILETIME_ASSERT((offsetof(PTPContainer, Param2) + sizeof(uint32_t))
+ == offsetof(PTPContainer, Param3));
+ COMPILETIME_ASSERT((offsetof(PTPContainer, Param3) + sizeof(uint32_t))
+ == offsetof(PTPContainer, Param4));
+ COMPILETIME_ASSERT((offsetof(PTPContainer, Param4) + sizeof(uint32_t))
+ == offsetof(PTPContainer, Param5));
+
+ COMPILETIME_ASSERT((offsetof(PTPContainer, Param1) + 5*sizeof(uint32_t))
+ == offsetof(PTPContainer, Nparam));
+
+ /* Silently ignore parameters past Param5 */
+ for (i=0; (i<n_param) && (i<5); ++i) {
(&ptp->Param1)[i] = va_arg(args, uint32_t);
+ }
va_end(args);
}
-#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
-#define NARGS(...) NARGS_SEQ(-1, ##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
-#define PTP_CNT_INIT(PTP, CODE, ...) \
- ptp_init_container(&PTP, CODE, NARGS(__VA_ARGS__), ##__VA_ARGS__)
+#define N_PARAM_SEQ(DUMMY, \
+ Pa, Pb, Pc, Pd, Pe, \
+ P0, P1, P2, P3, P4, P5, \
+ N, ...) \
+ N
+
+
+#define N_PARAM(...) \
+ N_PARAM_SEQ(__VA_ARGS__, \
+ "a", "b", "c", "d", "e", \
+ 5U, 4U, 3U, 2U, 1U, 0U, NULL)
+
+
+/* Usage: PTP_CNT_INIT(PTP, CODE[, PARAM...])
+ *
+ * Together with ptp_init_container(), this basically is a shortcut to
+ * initializing a PTPContainer struct, but automatically counts the
+ * number of PTPContainer parameters (members Param1 through Param5)
+ * set.
+ *
+ * The valid number of parameters is 0 through 5.
+ */
+#define PTP_CNT_INIT(PTP, ...) \
+ do { \
+ const int n_param = N_PARAM(-666, __VA_ARGS__); \
+ ptp_init_container(&PTP, n_param, __VA_ARGS__); \
+ } while (0)
+
static uint16_t ptp_exit_recv_memory_handler (PTPDataHandler*,unsigned char**,unsigned long*);
static uint16_t ptp_init_recv_memory_handler(PTPDataHandler*);