Blame SOURCES/safe-printf.patch

df3356
From 05d657339a80405900f8a066e41f08c121755cbf Mon Sep 17 00:00:00 2001
df3356
From: Stef Walter <stefw@redhat.com>
df3356
Date: Fri, 3 Jan 2014 16:37:12 +0100
df3356
Subject: [PATCH] sssd: Use safe printf when formatting full_name_format
df3356
 strings
df3356
df3356
Since sssd.conf uses such printf formats in its code, and it's
df3356
completely bogus to be passing user provided input to printf
df3356
directly...
df3356
---
df3356
 service/Makefile.am      |   1 +
df3356
 service/realm-sssd.c     |  52 ++++---
df3356
 service/safe-printf.c    | 348 +++++++++++++++++++++++++++++++++++++++++++++++
df3356
 service/safe-printf.h    |  45 ++++++
df3356
 tests/Makefile.am        |   6 +
df3356
 tests/test-safe-printf.c | 238 ++++++++++++++++++++++++++++++++
df3356
 7 files changed, 662 insertions(+), 29 deletions(-)
df3356
 create mode 100644 service/safe-printf.c
df3356
 create mode 100644 service/safe-printf.h
df3356
 create mode 100644 tests/test-safe-printf.c
df3356
df3356
diff --git a/service/Makefile.am b/service/Makefile.am
df3356
index 37743a7..603ccbe 100644
df3356
--- a/service/Makefile.am
df3356
+++ b/service/Makefile.am
df3356
@@ -59,6 +59,7 @@ realmd_SOURCES = \
df3356
 	realm-sssd-config.c realm-sssd-config.h \
df3356
 	realm-sssd-ipa.c realm-sssd-ipa.h \
df3356
 	realm-usleep-async.c realm-usleep-async.h \
df3356
+	safe-printf.c safe-printf.h \
df3356
 	$(NULL)
df3356
 
df3356
 realmd_CFLAGS = \
df3356
diff --git a/service/realm-sssd.c b/service/realm-sssd.c
df3356
index b904514..a4863d4 100644
df3356
--- a/service/realm-sssd.c
df3356
+++ b/service/realm-sssd.c
df3356
@@ -25,6 +25,7 @@
df3356
 #include "realm-service.h"
df3356
 #include "realm-sssd.h"
df3356
 #include "realm-sssd-config.h"
df3356
+#include "safe-printf.h"
df3356
 
df3356
 #include <glib/gstdio.h>
df3356
 #include <glib/gi18n.h>
df3356
@@ -315,38 +316,13 @@ update_domain (RealmSssd *self)
df3356
 	g_free (domain);
df3356
 }
df3356
 
df3356
-static gchar *
df3356
-build_login_format (const gchar *format,
df3356
-                    ...) G_GNUC_PRINTF (1, 2);
df3356
-
df3356
-static gchar *
df3356
-build_login_format (const gchar *format,
df3356
-                    ...)
df3356
-{
df3356
-	gchar *result;
df3356
-	va_list va;
df3356
-
df3356
-	/* This function exists mostly to get around gcc warnings */
df3356
-
df3356
-	if (format == NULL)
df3356
-		format = "%1$s@%2$s";
df3356
-
df3356
-	va_start (va, format);
df3356
-	result = g_strdup_vprintf (format, va);
df3356
-	va_end (va);
df3356
-
df3356
-	return result;
df3356
-}
df3356
-
df3356
-#pragma GCC diagnostic push
df3356
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
df3356
-
df3356
 static void
df3356
 update_login_formats (RealmSssd *self)
df3356
 {
df3356
 	RealmKerberos *kerberos = REALM_KERBEROS (self);
df3356
 	gchar *login_formats[2] = { NULL, NULL };
df3356
 	gchar *format = NULL;
df3356
+	gchar *domain_name;
df3356
 	gboolean qualify;
df3356
 
df3356
 	if (self->pv->section == NULL) {
df3356
@@ -366,10 +342,28 @@ update_login_formats (RealmSssd *self)
df3356
 	/* Setup the login formats */
df3356
 	format = realm_ini_config_get (self->pv->config, self->pv->section, "full_name_format");
df3356
 
df3356
-	/* Here we place a '%s' in the place of the user in the format */
df3356
-	login_formats[0] = build_login_format (format, "%U", self->pv->domain);
df3356
-	realm_kerberos_set_login_formats (kerberos, (const gchar **)login_formats);
df3356
+	/* The full domain name */
df3356
+	domain_name = calc_domain (self);
df3356
+
df3356
+	/*
df3356
+	 * In theory we should be discovering the short name or flat name as sssd
df3356
+	 * calls it. We configured it as the sssd.conf 'domains' name, so we just
df3356
+	 * use that. Eventually we want to have a way to query sssd for that.
df3356
+	 */
df3356
+
df3356
+	/*
df3356
+	 * Here we place a '%U' in the place of the user in the format, and
df3356
+	 * fill in the domain appropriately. sssd uses snprintf for this, which
df3356
+	 * is risky and very compex to do right with positional arguments.
df3356
+	 *
df3356
+	 * We only replace the arguments documented in sssd.conf, as well as
df3356
+	 * other non-field printf replacements.
df3356
+	 */
df3356
+
df3356
+	if (safe_asprintf (login_formats, format, "%U", domain_name ? domain_name : "", self->pv->domain, NULL) >= 0)
df3356
+		realm_kerberos_set_login_formats (kerberos, (const gchar **)login_formats);
df3356
 	g_free (login_formats[0]);
df3356
+	g_free (domain_name);
df3356
 	g_free (format);
df3356
 }
df3356
 
df3356
diff --git a/service/safe-printf.c b/service/safe-printf.c
df3356
new file mode 100644
df3356
index 0000000..069c915
df3356
--- /dev/null
df3356
+++ b/service/safe-printf.c
df3356
@@ -0,0 +1,348 @@
df3356
+/* realmd -- Realm configuration service
df3356
+ *
df3356
+ * Copyright 2013 Red Hat Inc
df3356
+ *
df3356
+ * This program is free software: you can redistribute it and/or modify
df3356
+ * it under the terms of the GNU Lesser General Public License as published
df3356
+ * by the Free Software Foundation; either version 2 of the licence or (at
df3356
+ * your option) any later version.
df3356
+ *
df3356
+ * See the included COPYING file for more information.
df3356
+ *
df3356
+ * Author: Stef Walter <stefw@redhat.com>
df3356
+ */
df3356
+
df3356
+#include "config.h"
df3356
+
df3356
+#include "safe-printf.h"
df3356
+
df3356
+#include <stdarg.h>
df3356
+#include <string.h>
df3356
+
df3356
+#ifndef MIN
df3356
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
df3356
+#endif
df3356
+
df3356
+#ifndef MAX
df3356
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
df3356
+#endif
df3356
+
df3356
+static void
df3356
+safe_padding (int count,
df3356
+              int *total,
df3356
+              void (* callback) (void *, const char *, size_t),
df3356
+              void *data)
df3356
+{
df3356
+	char eight[] = "        ";
df3356
+	int num;
df3356
+
df3356
+	while (count > 0) {
df3356
+		num = MIN (count, 8);
df3356
+		callback (data, eight, num);
df3356
+		count -= num;
df3356
+		*total += num;
df3356
+	}
df3356
+}
df3356
+
df3356
+static void
df3356
+dummy_callback (void *data,
df3356
+                const char *piece,
df3356
+                size_t len)
df3356
+{
df3356
+
df3356
+}
df3356
+
df3356
+int
df3356
+safe_printf_cb (void (* callback) (void *, const char *, size_t),
df3356
+                void *data,
df3356
+                const char *format,
df3356
+                const char * const args[],
df3356
+                int num_args)
df3356
+{
df3356
+	int at_arg = 0;
df3356
+	const char *cp;
df3356
+	int precision;
df3356
+	int width;
df3356
+	int len;
df3356
+	const char *value;
df3356
+	int total;
df3356
+	int left;
df3356
+
df3356
+	if (!callback)
df3356
+		callback = dummy_callback;
df3356
+
df3356
+	total = 0;
df3356
+	cp = format;
df3356
+
df3356
+	while (*cp) {
df3356
+
df3356
+		/* Piece of raw string */
df3356
+		if (*cp != '%') {
df3356
+			len = strcspn (cp, "%");
df3356
+			callback (data, cp, len);
df3356
+			total += len;
df3356
+			cp += len;
df3356
+			continue;
df3356
+		}
df3356
+
df3356
+		cp++;
df3356
+
df3356
+		/* An literal percent sign? */
df3356
+		if (*cp == '%') {
df3356
+			callback (data, "%", 1);
df3356
+			total++;
df3356
+			cp++;
df3356
+			continue;
df3356
+		}
df3356
+
df3356
+		value = NULL;
df3356
+		left = 0;
df3356
+		precision = -1;
df3356
+		width = -1;
df3356
+
df3356
+		/* Test for positional argument.  */
df3356
+		if (*cp >= '0' && *cp <= '9') {
df3356
+			/* Look-ahead parsing, otherwise skipped */
df3356
+			if (cp[strspn (cp, "0123456789")] == '$') {
df3356
+				unsigned int n = 0;
df3356
+				for (; *cp >= '0' && *cp <= '9'; cp++)
df3356
+					n = 10 * n + (*cp - '0');
df3356
+				/* Positional argument 0 is invalid. */
df3356
+				if (n == 0)
df3356
+					return -1;
df3356
+				/* Positional argument N too high */
df3356
+				if (n > num_args)
df3356
+					return -1;
df3356
+				value = args[n - 1];
df3356
+				cp++; /* $ */
df3356
+			}
df3356
+		}
df3356
+
df3356
+		/* Read the supported flags. */
df3356
+		for (; ; cp++) {
df3356
+			if (*cp == '-')
df3356
+				left = 1;
df3356
+			/* Supported but ignored */
df3356
+			else if (*cp != ' ')
df3356
+				break;
df3356
+		}
df3356
+
df3356
+		/* Parse the width. */
df3356
+		if (*cp >= '0' && *cp <= '9') {
df3356
+			width = 0;
df3356
+			for (; *cp >= '0' && *cp <= '9'; cp++)
df3356
+				width = 10 * width + (*cp - '0');
df3356
+		}
df3356
+
df3356
+		/* Parse the precision. */
df3356
+		if (*cp == '.') {
df3356
+			precision = 0;
df3356
+			for (cp++; *cp >= '0' && *cp <= '9'; cp++)
df3356
+				precision = 10 * precision + (*cp - '0');
df3356
+		}
df3356
+
df3356
+		/* Read the conversion character.  */
df3356
+		switch (*cp++) {
df3356
+		case 's':
df3356
+			/* Non-positional argument */
df3356
+			if (value == NULL) {
df3356
+				/* Too many arguments used */
df3356
+				if (at_arg == num_args)
df3356
+					return -1;
df3356
+				value = args[at_arg++];
df3356
+			}
df3356
+			break;
df3356
+
df3356
+		/* No other conversion characters are supported */
df3356
+		default:
df3356
+			return -1;
df3356
+		}
df3356
+
df3356
+		/* How many characters are we printing? */
df3356
+		len = strlen (value);
df3356
+		if (precision >= 0)
df3356
+			len = MIN (precision, len);
df3356
+
df3356
+		/* Do we need padding? */
df3356
+		safe_padding (left ? 0 : width - len, &total, callback, data);
df3356
+
df3356
+		/* The actual data */;
df3356
+		callback (data, value, len);
df3356
+		total += len;
df3356
+
df3356
+		/* Do we need padding? */
df3356
+		safe_padding (left ? width - len : 0, &total, callback, data);
df3356
+	}
df3356
+
df3356
+	return total;
df3356
+}
df3356
+
df3356
+struct sprintf_ctx {
df3356
+	char *data;
df3356
+	size_t length;
df3356
+	size_t alloc;
df3356
+};
df3356
+
df3356
+static void
df3356
+asprintf_callback (void *data,
df3356
+                   const char *piece,
df3356
+                   size_t length)
df3356
+{
df3356
+	struct sprintf_ctx *cx = data;
df3356
+	void *mem;
df3356
+
df3356
+	if (!cx->data)
df3356
+		return;
df3356
+
df3356
+	/* Reallocate if necessary */
df3356
+	if (cx->length + length + 1 > cx->alloc) {
df3356
+		cx->alloc += MAX (length + 1, 1024);
df3356
+		mem = realloc (cx->data, cx->alloc);
df3356
+		if (mem == NULL) {
df3356
+			free (cx->data);
df3356
+			cx->data = NULL;
df3356
+			return;
df3356
+		}
df3356
+		cx->data = mem;
df3356
+	}
df3356
+
df3356
+	memcpy (cx->data + cx->length, piece, length);
df3356
+	cx->length += length;
df3356
+	cx->data[cx->length] = 0;
df3356
+}
df3356
+
df3356
+static const char **
df3356
+valist_to_args (va_list va,
df3356
+                int *num_args)
df3356
+{
df3356
+	int alo_args;
df3356
+	const char **args;
df3356
+	const char *arg;
df3356
+	void *mem;
df3356
+
df3356
+	*num_args = alo_args = 0;
df3356
+	args = NULL;
df3356
+
df3356
+	for (;;) {
df3356
+		arg = va_arg (va, const char *);
df3356
+		if (arg == NULL)
df3356
+			break;
df3356
+		if (*num_args == alo_args) {
df3356
+			alo_args += 8;
df3356
+			mem = realloc (args, sizeof (const char *) * alo_args);
df3356
+			if (!mem) {
df3356
+				free (args);
df3356
+				return NULL;
df3356
+			}
df3356
+			args = mem;
df3356
+		}
df3356
+		args[(*num_args)++] = arg;
df3356
+	}
df3356
+
df3356
+	return args;
df3356
+}
df3356
+
df3356
+int
df3356
+safe_asprintf (char **strp,
df3356
+               const char *format,
df3356
+               ...)
df3356
+{
df3356
+	struct sprintf_ctx cx;
df3356
+	const char **args;
df3356
+	int num_args;
df3356
+	va_list va;
df3356
+	int ret;
df3356
+	int i;
df3356
+
df3356
+	va_start (va, format);
df3356
+	args = valist_to_args (va, &num_args);
df3356
+	va_end (va);
df3356
+
df3356
+	if (args == NULL)
df3356
+		return -1;
df3356
+
df3356
+	/* Preallocate a pretty good guess */
df3356
+	cx.alloc = strlen (format) + 1;
df3356
+	for (i = 0; i < num_args; i++)
df3356
+		cx.alloc += strlen (args[i]);
df3356
+
df3356
+	cx.data = malloc (cx.alloc);
df3356
+	if (!cx.data) {
df3356
+		free (args);
df3356
+		return -1;
df3356
+	}
df3356
+
df3356
+	cx.data[0] = '\0';
df3356
+	cx.length = 0;
df3356
+
df3356
+	ret = safe_printf_cb (asprintf_callback, &cx, format, args, num_args);
df3356
+	if (cx.data == NULL)
df3356
+		ret = -1;
df3356
+	if (ret < 0)
df3356
+		free (cx.data);
df3356
+	else
df3356
+		*strp = cx.data;
df3356
+
df3356
+	free (args);
df3356
+	return ret;
df3356
+}
df3356
+
df3356
+static void
df3356
+snprintf_callback (void *data,
df3356
+                   const char *piece,
df3356
+                   size_t length)
df3356
+{
df3356
+	struct sprintf_ctx *cx = data;
df3356
+
df3356
+	/* Don't copy if too much data */
df3356
+	if (cx->length > cx->alloc)
df3356
+		length = 0;
df3356
+	else if (cx->length + length > cx->alloc)
df3356
+		length = cx->alloc - cx->length;
df3356
+	else
df3356
+		length = length;
df3356
+
df3356
+	if (length > 0)
df3356
+		memcpy (cx->data + cx->length, piece, length);
df3356
+
df3356
+	/* Null termination happens later */
df3356
+	cx->length += length;
df3356
+}
df3356
+
df3356
+int
df3356
+safe_snprintf (char *str,
df3356
+               size_t len,
df3356
+               const char *format,
df3356
+               ...)
df3356
+{
df3356
+	struct sprintf_ctx cx;
df3356
+	int num_args;
df3356
+	va_list va;
df3356
+	const char **args;
df3356
+	int ret;
df3356
+
df3356
+	cx.data = str;
df3356
+	cx.length = 0;
df3356
+	cx.alloc = len;
df3356
+
df3356
+	va_start (va, format);
df3356
+	args = valist_to_args (va, &num_args);
df3356
+	va_end (va);
df3356
+
df3356
+	if (args == NULL)
df3356
+		return -1;
df3356
+
df3356
+	if (len)
df3356
+		cx.data[0] = '\0';
df3356
+
df3356
+	ret = safe_printf_cb (snprintf_callback, &cx, format, args, num_args);
df3356
+	if (ret < 0)
df3356
+		return ret;
df3356
+
df3356
+	/* Null terminate appropriately */
df3356
+	if (len > 0)
df3356
+		cx.data[MIN(cx.length, len - 1)] = '\0';
df3356
+
df3356
+	free (args);
df3356
+	return ret;
df3356
+}
df3356
diff --git a/service/safe-printf.h b/service/safe-printf.h
df3356
new file mode 100644
df3356
index 0000000..8881b91
df3356
--- /dev/null
df3356
+++ b/service/safe-printf.h
df3356
@@ -0,0 +1,45 @@
df3356
+/* realmd -- Realm configuration service
df3356
+ *
df3356
+ * Copyright 2013 Red Hat Inc
df3356
+ *
df3356
+ * This program is free software: you can redistribute it and/or modify
df3356
+ * it under the terms of the GNU Lesser General Public License as published
df3356
+ * by the Free Software Foundation; either version 2 of the licence or (at
df3356
+ * your option) any later version.
df3356
+ *
df3356
+ * See the included COPYING file for more information.
df3356
+ *
df3356
+ * Author: Stef Walter <stefw@redhat.com>
df3356
+ */
df3356
+
df3356
+#include "config.h"
df3356
+
df3356
+#ifndef __SAFE_PRINTF_H__
df3356
+#define __SAFE_PRINTF_H__
df3356
+
df3356
+#include <stdlib.h>
df3356
+
df3356
+#ifndef GNUC_NULL_TERMINATED
df3356
+#if __GNUC__ >= 4
df3356
+#define GNUC_NULL_TERMINATED __attribute__((__sentinel__))
df3356
+#else
df3356
+#define GNUC_NULL_TERMINATED
df3356
+#endif
df3356
+#endif
df3356
+
df3356
+int        safe_asprintf     (char **strp,
df3356
+                              const char *format,
df3356
+                              ...) GNUC_NULL_TERMINATED;
df3356
+
df3356
+int        safe_snprintf     (char *str,
df3356
+                              size_t len,
df3356
+                              const char *format,
df3356
+                              ...) GNUC_NULL_TERMINATED;
df3356
+
df3356
+int        safe_printf_cb    (void (* callback) (void *data, const char *piece, size_t len),
df3356
+                              void *data,
df3356
+                              const char *format,
df3356
+                              const char * const args[],
df3356
+                              int num_args);
df3356
+
df3356
+#endif /* __SAFE_PRINTF_H__ */
df3356
diff --git a/tests/Makefile.am b/tests/Makefile.am
df3356
index f6d400b..d26c762 100644
df3356
--- a/tests/Makefile.am
df3356
+++ b/tests/Makefile.am
df3356
@@ -17,6 +17,7 @@ LDADD = \
df3356
 TEST_PROGS = \
df3356
 	test-ini-config \
df3356
 	test-sssd-config \
df3356
+	test-safe-printf \
df3356
 	test-login-name \
df3356
 	test-samba-ou-format \
df3356
 	test-settings \
df3356
@@ -43,6 +44,11 @@ test_sssd_config_SOURCES = \
df3356
 	$(top_srcdir)/service/realm-settings.c \
df3356
 	$(NULL)
df3356
 
df3356
+test_safe_printf_SOURCES = \
df3356
+	test-safe-printf.c \
df3356
+	$(top_srcdir)/service/safe-printf.c \
df3356
+	$(NULL)
df3356
+
df3356
 test_login_name_SOURCES = \
df3356
 	test-login-name.c \
df3356
 	$(top_srcdir)/service/realm-login-name.c \
df3356
diff --git a/tests/test-safe-printf.c b/tests/test-safe-printf.c
df3356
new file mode 100644
df3356
index 0000000..fa337ac
df3356
--- /dev/null
df3356
+++ b/tests/test-safe-printf.c
df3356
@@ -0,0 +1,238 @@
df3356
+/* realmd -- Realm configuration service
df3356
+ *
df3356
+ * Copyright 2012 Red Hat Inc
df3356
+ *
df3356
+ * This program is free software: you can redistribute it and/or modify
df3356
+ * it under the terms of the GNU Lesser General Public License as published
df3356
+ * by the Free Software Foundation; either version 2 of the licence or (at
df3356
+ * your option) any later version.
df3356
+ *
df3356
+ * See the included COPYING file for more information.
df3356
+ *
df3356
+ * Author: Stef Walter <stefw@gnome.org>
df3356
+ */
df3356
+
df3356
+#include "config.h"
df3356
+
df3356
+#include "service/safe-printf.h"
df3356
+
df3356
+#include <glib.h>
df3356
+
df3356
+#include <string.h>
df3356
+
df3356
+typedef struct {
df3356
+	const gchar *format;
df3356
+	const gchar *args[8];
df3356
+	const gchar *result;
df3356
+} Fixture;
df3356
+
df3356
+static void
df3356
+callback (void *data,
df3356
+          const char *piece,
df3356
+          size_t len)
df3356
+{
df3356
+	g_string_append_len (data, piece, len);
df3356
+}
df3356
+
df3356
+static void
df3356
+test_safe_printf (gconstpointer user_data)
df3356
+{
df3356
+	const Fixture *fixture = user_data;
df3356
+	GString *out;
df3356
+	int num_args;
df3356
+	int ret;
df3356
+
df3356
+	for (num_args = 0; fixture->args[num_args] != NULL; )
df3356
+		num_args++;
df3356
+
df3356
+	out = g_string_new ("");
df3356
+	ret = safe_printf_cb (callback, out, fixture->format, (const gchar **)fixture->args, num_args);
df3356
+	if (fixture->result) {
df3356
+		g_assert_cmpint (ret, >=, 0);
df3356
+		g_assert_cmpstr (out->str, ==, fixture->result);
df3356
+		g_assert_cmpint (ret, ==, out->len);
df3356
+	} else {
df3356
+		g_assert_cmpint (ret, <, 0);
df3356
+	}
df3356
+
df3356
+	g_string_free (out, TRUE);
df3356
+}
df3356
+
df3356
+static const Fixture fixtures[] = {
df3356
+	{
df3356
+	  /* Just a bog standard string */
df3356
+	  "%s", { "blah", NULL, },
df3356
+	  "blah"
df3356
+	},
df3356
+	{
df3356
+	  /* Empty to print */
df3356
+	  "%s", { "", NULL, },
df3356
+	  ""
df3356
+	},
df3356
+	{
df3356
+	  /* Nothing to print */
df3356
+	  "", { "blah", NULL, },
df3356
+	  ""
df3356
+	},
df3356
+	{
df3356
+	  /* Width right aligned */
df3356
+	  "%8s", { "blah", NULL, },
df3356
+	  "    blah"
df3356
+	},
df3356
+	{
df3356
+	  /* Width left aligned */
df3356
+	  "whoop %-8s doo", { "dee", NULL, },
df3356
+	  "whoop dee      doo"
df3356
+	},
df3356
+	{
df3356
+	  /* Width space aligned (ignored) */
df3356
+	  "whoop % 8s doo", { "dee", NULL, },
df3356
+	  "whoop      dee doo"
df3356
+	},
df3356
+	{
df3356
+	  /* Width left space aligned (ignored) */
df3356
+	  "whoop % -8s doo", { "dee", NULL, },
df3356
+	  "whoop dee      doo"
df3356
+	},
df3356
+	{
df3356
+	  /* Precision 1 digit */
df3356
+	  "whoop %.3s doo", { "deedle-dee", NULL, },
df3356
+	  "whoop dee doo"
df3356
+	},
df3356
+	{
df3356
+	  /* Precision, N digits */
df3356
+	  "whoop %.10s doo", { "deedle-dee-deedle-do-deedle-dum", NULL, },
df3356
+	  "whoop deedle-dee doo"
df3356
+	},
df3356
+	{
df3356
+	  /* Precision, zero digits */
df3356
+	  "whoop %.s doo", { "deedle", NULL, },
df3356
+	  "whoop  doo"
df3356
+	},
df3356
+	{
df3356
+	  /* Multiple simple arguments */
df3356
+	  "space %s %s", { "man", "dances", NULL, },
df3356
+	  "space man dances"
df3356
+	},
df3356
+	{
df3356
+	  /* Literal percent */
df3356
+	  "100%% of space folk dance", { NULL, },
df3356
+	  "100% of space folk dance"
df3356
+	},
df3356
+	{
df3356
+	  /* Multiple simple arguments */
df3356
+	  "space %2$s %1$s", { "dances", "man", NULL, },
df3356
+	  "space man dances"
df3356
+	},
df3356
+	{
df3356
+	  /* Skipping an argument (not supported by standard printf) */
df3356
+	  "space %2$s dances", { "dances", "man", NULL, },
df3356
+	  "space man dances"
df3356
+	},
df3356
+
df3356
+	/* Failures start here */
df3356
+
df3356
+	{
df3356
+	  /* Unsupported conversion */
df3356
+	  "%x", { "blah", NULL, },
df3356
+	  NULL
df3356
+	},
df3356
+	{
df3356
+	  /* Bad positional argument */
df3356
+	  "space %55$s dances", { "dances", "man", NULL, },
df3356
+	  NULL
df3356
+	},
df3356
+	{
df3356
+	  /* Zero positional argument */
df3356
+	  "space %0$s dances", { "dances", "man", NULL, },
df3356
+	  NULL
df3356
+	},
df3356
+	{
df3356
+	  /* Too many args used */
df3356
+	  "%s %s dances", { "space", NULL, },
df3356
+	  NULL
df3356
+	},
df3356
+
df3356
+};
df3356
+
df3356
+static void
df3356
+test_safe_snprintf (void)
df3356
+{
df3356
+	char buffer[8];
df3356
+	int ret;
df3356
+
df3356
+	ret = safe_snprintf (buffer, 8, "%s", "space", "man", NULL);
df3356
+	g_assert_cmpint (ret, ==, 5);
df3356
+	g_assert_cmpstr (buffer, ==, "space");
df3356
+
df3356
+	ret = safe_snprintf (buffer, 8, "", "space", "man", NULL);
df3356
+	g_assert_cmpint (ret, ==, 0);
df3356
+	g_assert_cmpstr (buffer, ==, "");
df3356
+
df3356
+	ret = safe_snprintf (buffer, 8, "the %s %s dances away", "space", "man", NULL);
df3356
+	g_assert_cmpint (ret, ==, 25);
df3356
+	g_assert_cmpstr (buffer, ==, "the spa");
df3356
+
df3356
+	ret = safe_snprintf (buffer, 8, "%5$s", NULL);
df3356
+	g_assert_cmpint (ret, <, 0);
df3356
+}
df3356
+
df3356
+static void
df3356
+test_safe_asprintf (void)
df3356
+{
df3356
+	char *buffer;
df3356
+	int ret;
df3356
+
df3356
+	ret = safe_asprintf (&buffer, "%s", "space", "man", NULL);
df3356
+	g_assert_cmpint (ret, ==, 5);
df3356
+	g_assert_cmpstr (buffer, ==, "space");
df3356
+	free (buffer);
df3356
+
df3356
+	ret = safe_asprintf (&buffer, "", "space", "man", NULL);
df3356
+	g_assert_cmpint (ret, ==, 0);
df3356
+	g_assert_cmpstr (buffer, ==, "");
df3356
+	free (buffer);
df3356
+
df3356
+	ret = safe_asprintf (&buffer, "the %s %s dances away", "space", "man", NULL);
df3356
+	g_assert_cmpint (ret, ==, 25);
df3356
+	g_assert_cmpstr (buffer, ==, "the space man dances away");
df3356
+	free (buffer);
df3356
+
df3356
+	ret = safe_asprintf (&buffer, "%1$s %1$s %1$s %1$s %1$s %1$s", "space man", NULL);
df3356
+	g_assert_cmpint (ret, ==, 59);
df3356
+	g_assert_cmpstr (buffer, ==, "space man space man space man space man space man space man");
df3356
+	free (buffer);
df3356
+
df3356
+	ret = safe_asprintf (&buffer, "%5$s", NULL);
df3356
+	g_assert_cmpint (ret, <, 0);
df3356
+}
df3356
+
df3356
+int
df3356
+main (int argc,
df3356
+      char **argv)
df3356
+{
df3356
+	gchar *escaped;
df3356
+	gchar *name;
df3356
+	gint i;
df3356
+
df3356
+	g_test_init (&argc, &argv, NULL);
df3356
+	g_set_prgname ("test-safe-printf");
df3356
+
df3356
+	for (i = 0; i < G_N_ELEMENTS (fixtures); i++) {
df3356
+		if (g_str_equal (fixtures[i].format, ""))
df3356
+			escaped = g_strdup ("_empty_");
df3356
+		else
df3356
+			escaped = g_strdup (fixtures[i].format);
df3356
+		g_strdelimit (escaped, " =\\/", '_');
df3356
+		name = g_strdup_printf ("/realmd/safe-printf/%s", escaped);
df3356
+		g_free (escaped);
df3356
+
df3356
+		g_test_add_data_func (name, fixtures + i, test_safe_printf);
df3356
+		g_free (name);
df3356
+	}
df3356
+
df3356
+	g_test_add_func ("/realmd/safe-snprintf", test_safe_snprintf);
df3356
+	g_test_add_func ("/realmd/safe-asprintf", test_safe_asprintf);
df3356
+
df3356
+	return g_test_run ();
df3356
+}
df3356
-- 
df3356
1.8.4.2
df3356