xkbvalidate: Don't rely on GNU extensions

The only reason why I was using _GNU_SOURCE was because of vasprintf(),
so getting rid of that extension should make the source way more
portable.

When using vsnprintf() with a null pointer for the output buffer and a
size of 0, I wasn't quite sure whether this would be undefined
behaviour, so I looked it up in the C11 standard.

In section 7.21.6.5, it explicitly mentions this case, so we're lucky:

If n is zero, nothing is written, and s may be a null pointer.

Additionally, section 7.21.6.12 writes the following about vsnprintf():

The vsnprintf function does not invoke the va_end macro.

So to be sure to avoid undefined behaviour I subsequently added the
corresponding va_end() calls.

With this, the platforms attribute is now "unix", because the program
should now even run on OS X, even though it usually wouldn't be needed.

Signed-off-by: aszlig <aszlig@nix.build>

aszlig 1964b0c1 77e8a127

+20 -5
+2 -2
pkgs/tools/X11/xkbvalidate/default.nix
··· 5 5 meta = { 6 6 description = "NixOS tool to validate X keyboard configuration"; 7 7 license = lib.licenses.mit; 8 - platforms = lib.platforms.linux; 8 + platforms = lib.platforms.unix; 9 9 maintainers = [ lib.maintainers.aszlig ]; 10 10 }; 11 11 } '' 12 12 mkdir -p "$out/bin" 13 - $CC -std=gnu11 -Wall -pedantic -lxkbcommon ${./xkbvalidate.c} \ 13 + $CC -std=c11 -Wall -pedantic -lxkbcommon ${./xkbvalidate.c} \ 14 14 -o "$out/bin/validate" 15 15 ''
+18 -3
pkgs/tools/X11/xkbvalidate/xkbvalidate.c
··· 1 - #define _GNU_SOURCE 2 1 #include <stdarg.h> 3 2 #include <stdbool.h> 4 3 #include <stdio.h> ··· 14 13 static void add_log(struct xkb_context *ctx, enum xkb_log_level level, 15 14 const char *fmt, va_list args) 16 15 { 16 + size_t buflen; 17 + va_list tmpargs; 18 + 17 19 log_buffer_size++; 18 20 19 21 if (log_buffer == NULL) ··· 28 30 return; 29 31 } 30 32 31 - if (vasprintf(&log_buffer[log_buffer_size - 1], fmt, args) == -1) { 33 + /* Unfortunately, vasprintf() is a GNU extension and thus not very 34 + * portable, so let's first get the required buffer size using a dummy 35 + * vsnprintf and afterwards allocate the returned amount of bytes. 36 + * 37 + * We also need to make a copy of the args, because the value of the args 38 + * will be indeterminate after the return. 39 + */ 40 + va_copy(tmpargs, args); 41 + buflen = vsnprintf(NULL, 0, fmt, tmpargs); 42 + va_end(tmpargs); 43 + 44 + log_buffer[log_buffer_size - 1] = malloc(++buflen); 45 + 46 + if (vsnprintf(log_buffer[log_buffer_size - 1], buflen, fmt, args) == -1) { 32 47 perror("log line alloc"); 33 48 log_alloc_success = false; 34 - return; 35 49 } 50 + va_end(args); 36 51 } 37 52 38 53 static void print_logs(void)