diff options
author | Hans Ulrich Niedermann <hun@n-dimensional.de> | 2021-11-10 22:03:31 +0100 |
---|---|---|
committer | Hans Ulrich Niedermann <hun@n-dimensional.de> | 2021-11-12 00:54:39 +0100 |
commit | b6fae4fcceb3d3352bf24728045293d694a3e835 (patch) | |
tree | b37c3f391e32e60bf8b9a2823d2d79ea3dbac2c0 /camlibs/ptp2/ptp.c | |
parent | 8527242022916573d38f8f86fb84fce47c450088 (diff) | |
download | libgphoto2-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.c | 70 |
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*); |