summaryrefslogtreecommitdiff
path: root/extra/i2c_pseudo/anprintf.h
blob: c73eb24c6c1e25895614326cd08fabc60984d110 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// SPDX-License-Identifier: GPL-2.0

#include <stdarg.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/string.h>

/*
 * vanprintf - Format a string and place it into a newly allocated buffer.
 * @out: Address of the pointer to place the buffer address into.  Will only be
 *     written to with a successful positive return value.
 * @max_size: If non-negative, the maximum buffer size that this function will
 *     attempt to allocate.  If the formatted string including trailing null
 *     character would not fit, no buffer will be allocated, and an error will
 *     be returned.  (Thus max_size of 0 will always result in an error.)
 * @gfp: GFP flags for kmalloc().
 * @fmt: The format string to use.
 * @ap: Arguments for the format string.
 *
 * Return value meanings:
 *
 *   >=0: A buffer of this size was allocated and its address written to *out.
 *        The caller now owns the buffer and is responsible for freeing it with
 *        kfree().  The final character in the buffer, not counted in this
 *        return value, is the trailing null.  This is the same return value
 *        meaning as snprintf(3).
 *
 *    <0: An error occurred.  Negate the return value for the error number.
 *        @out will not have been written to.  Errors that might come from
 *        snprintf(3) may come from this function as well.  Additionally, the
 *        following errors may occur from this function:
 *
 *        ERANGE: A buffer larger than @max_size would be needed to fit the
 *        formatted string including its trailing null character.
 *
 *        ENOMEM: Allocation of the output buffer failed.
 *
 *        ENOTRECOVERABLE: An unexpected condition occurred.  This may indicate
 *        a bug.
 */
static ssize_t vanprintf(char **out, ssize_t max_size, gfp_t gfp,
	const char *fmt, va_list ap)
{
	int ret;
	ssize_t buf_size;
	char *buf = NULL;
	va_list args1;

	va_copy(args1, ap);
	ret = vsnprintf(NULL, 0, fmt, ap);
	if (ret < 0) {
		pr_err("%s: Formatting failed with error %d.\n", __func__,
			-ret);
		goto fail_before_args1;
	}
	if (max_size >= 0 && ret > max_size) {
		ret = -ERANGE;
		goto fail_before_args1;
	}

	buf_size = ret + 1;
	buf = kmalloc(buf_size, gfp);
	if (buf == NULL) {
		pr_err("%s: kmalloc(%zd, %u) returned NULL\n", __func__,
			buf_size, gfp);
		ret = -ENOMEM;
		goto fail_before_args1;
	}

	ret = vsnprintf(buf, buf_size, fmt, args1);
	va_end(args1);
	if (ret < 0) {
		pr_err("%s: Second formatting pass produced error %d after the first pass succeeded.  This is a bug.\n",
			__func__, -ret);
		goto fail_after_args1;
	}
	if (ret + 1 != buf_size) {
		pr_err("%s: Second formatting pass produced a different formatted output size than the first.  This is a bug.  Will return -ENOTRECOVERABLE.  first_sans_null=%zd second_sans_null=%d\n",
			__func__, buf_size - 1, ret);
		ret = -ENOTRECOVERABLE;
		goto fail_after_args1;
	}

	*out = buf;
	return ret;

 fail_before_args1:
	va_end(args1);
 fail_after_args1:
	kfree(buf);
	if (ret >= 0) {
		pr_err("%s: Jumped to failure cleanup section with non-negative return value %d set.  This is a bug.  Will return -ENOTRECOVERABLE instead.\n",
			__func__, ret);
		ret = -ENOTRECOVERABLE;
	}
	return ret;
}

/*
 * anprintf - Format a string and place it into a newly allocated buffer.
 * @out: Address of the pointer to place the buffer address into.  Will only be
 *     written to with a successful positive return value.
 * @max_size: If non-negative, the maximum buffer size that this function will
 *     attempt to allocate.  If the formatted string including trailing null
 *     character would not fit, no buffer will be allocated, and an error will
 *     be returned.  (Thus max_size of 0 will always result in an error.)
 * @gfp: GFP flags for kmalloc().
 * @fmt: The format string to use.
 * @...: Arguments for the format string.
 *
 * Return value meanings:
 *
 *   >=0: A buffer of this size was allocated and its address written to *out.
 *        The caller now owns the buffer and is responsible for freeing it with
 *        kfree().  The final character in the buffer, not counted in this
 *        return value, is the trailing null.  This is the same return value
 *        meaning as snprintf(3).
 *
 *    <0: An error occurred.  Negate the return value for the error number.
 *        @out will not have been written to.  Errors that might come from
 *        snprintf(3) may come from this function as well.  Additionally, the
 *        following errors may occur from this function:
 *
 *        ERANGE: A buffer larger than @max_size would be needed to fit the
 *        formatted string including its trailing null character.
 *
 *        ENOMEM: Allocation of the output buffer failed.
 *
 *        ENOTRECOVERABLE: An unexpected condition occurred.  This may indicate
 *        a bug.
 */
static ssize_t anprintf(char **out, ssize_t max_size, gfp_t gfp,
	const char *fmt, ...)
{
	ssize_t ret;
	va_list args;

	va_start(args, fmt);
	ret = vanprintf(out, max_size, gfp, fmt, args);
	va_end(args);
	return ret;
}