From b6fae4fcceb3d3352bf24728045293d694a3e835 Mon Sep 17 00:00:00 2001 From: Hans Ulrich Niedermann Date: Wed, 10 Nov 2021 22:03:31 +0100 Subject: 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 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 --- camlibs/ptp2/ptp.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 9 deletions(-) (limited to 'camlibs/ptp2/ptp.c') 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 #endif +#include #include #include +#include +#include #include #include #ifdef HAVE_UNISTD_H # include #endif +#include "libgphoto2_port/compiletime-assert.h" + #ifdef ENABLE_NLS # include # 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; iParam1)[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*); -- cgit v1.2.1