-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprintf_vector.c
More file actions
246 lines (219 loc) · 9 KB
/
printf_vector.c
File metadata and controls
246 lines (219 loc) · 9 KB
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/* Copyright 2019 Rob Johnson <rtjohnsocs42@gmail.com> */
/* BSD License. */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <printf.h>
#include <printf_vector.h>
/******************************
* printf extension for arrays
******************************/
static int pa2size(const struct printf_info *info, int pa)
{
if (pa & PA_FLAG_PTR)
return sizeof(void *);
switch (pa & (~PA_FLAG_MASK)) {
case PA_INT:
switch (pa & PA_FLAG_MASK) {
case 0: return sizeof(int);
case PA_FLAG_LONG_LONG: return sizeof(long long);
case PA_FLAG_LONG: return sizeof(long);
case PA_FLAG_SHORT: return sizeof(short);
default: return -1;
}
case PA_CHAR: return sizeof(char);
case PA_WCHAR: return sizeof(wchar_t);
case PA_STRING: return sizeof(char *);
case PA_WSTRING: return sizeof(wchar_t *);
case PA_POINTER: return sizeof(void *);
case PA_FLOAT: return sizeof(float);
case PA_DOUBLE:
switch (pa & PA_FLAG_MASK) {
case 0: return info->is_short ? sizeof(float) : sizeof(double);
case PA_FLAG_LONG_DOUBLE: return sizeof(long double);
default: return -1;
}
default: return -1;
}
return -1;
}
#define FPRINTF_AS_TYPE(type, ptr) \
fprintf(stream, \
fmt, \
(fn) ? \
((type (*)(type, void *, void **, void *, int, int, int))(fn))(*(type *)(ptr), fnarg, extra_args, array, i, width, prec) : \
*(type *)(ptr), \
extra_args[0], extra_args[1], extra_args[2], extra_args[3], \
extra_args[4], extra_args[5], extra_args[6], extra_args[7], \
extra_args[8], extra_args[9], extra_args[10], extra_args[11], \
extra_args[12], extra_args[13], extra_args[14], \
*(type *)(ptr), \
i, \
array, \
width, \
prec, \
fnarg)
static int print_vector_elt(FILE *stream, char *fmt, const struct printf_info *info, int pa,
void *extra_args[15],
void *array, void *eltp, int i, int width, int prec, void *fn, void *fnarg)
{
if (pa & PA_FLAG_PTR) {
if (info->alt)
return FPRINTF_AS_TYPE(void *, &eltp);
else
return FPRINTF_AS_TYPE(void *, eltp);
}
switch (pa & (~PA_FLAG_MASK)) {
case PA_INT:
switch (pa & PA_FLAG_MASK) {
case 0: return FPRINTF_AS_TYPE(int, eltp);
case PA_FLAG_LONG_LONG: return FPRINTF_AS_TYPE(long long, eltp);
case PA_FLAG_LONG: return FPRINTF_AS_TYPE(long, eltp);
case PA_FLAG_SHORT: return FPRINTF_AS_TYPE(short, eltp);
default: return -1;
}
case PA_CHAR: return FPRINTF_AS_TYPE(char, eltp);
case PA_WCHAR: return FPRINTF_AS_TYPE(wchar_t, eltp);
case PA_STRING: return FPRINTF_AS_TYPE(char *, eltp);
case PA_WSTRING: return FPRINTF_AS_TYPE(wchar_t *, eltp);
case PA_POINTER:
if (info->alt)
return FPRINTF_AS_TYPE(void *, &eltp);
else
return FPRINTF_AS_TYPE(void *, eltp);
case PA_FLOAT: return FPRINTF_AS_TYPE(float, eltp);
case PA_DOUBLE:
switch (pa & PA_FLAG_MASK) {
case 0: return info->is_short ?
FPRINTF_AS_TYPE(float, eltp) :
FPRINTF_AS_TYPE(double, eltp);
case PA_FLAG_LONG_DOUBLE: return FPRINTF_AS_TYPE(long double, eltp);
default: return -1;
}
default: return -1;
}
return -1;
}
#define MAX_EXTRA_ARGS (15)
static int nextra_args(const struct printf_info *info)
{
int args = 0;
if (info->left)
args += 8;
if (info->space)
args += 4;
if (info->showsign)
args += 2;
if (info->group)
args += 1;
return args;
}
/* Returns an string explaining the error if there is one, or NULL if it is valid. */
static const char * is_valid_argtype_array(int elt_must_be_pointer, int nextras, int nargs, int argtypes[22])
{
/*
A valid array is of the form
{ elt_type, PA_POINTER, ..., PA_POINTER, elt_type, PA_INT, PA_POINTER, PA_INT, PA_INT, PA_POINTER };
|-----MAX_EXTRA_ARGS------|
*/
/* static const int extra_args_array[MAX_EXTRA_ARGS] = { PA_POINTER, PA_POINTER, PA_POINTER, PA_POINTER, */
/* PA_POINTER, PA_POINTER, PA_POINTER, PA_POINTER, */
/* PA_POINTER, PA_POINTER, PA_POINTER, PA_POINTER, */
/* PA_POINTER, PA_POINTER, PA_POINTER, PA_POINTER, }; */
if (nargs < 0)
return "Per-element format takes a negative number of arguments?!?";
if (nargs > 22)
return "Per-element format requires more than the maximum supported number of arguments (22)";
if (elt_must_be_pointer && argtypes[0] != PA_POINTER)
return "Per-element format does not expect element to be a pointer, but you specified the '#' option to the 'V' format specififer";
//if (memcmp(&argtypes[1], extra_args_array, nextras * sizeof(argtypes[0])))
// return "Per-element format does not expect all extra args to be pointers";
if (nargs > MAX_EXTRA_ARGS + 1 && argtypes[MAX_EXTRA_ARGS + 1] != argtypes[0])
return "The 17th argument must be the same type as the first argument.";
if (nargs > MAX_EXTRA_ARGS + 2 && (argtypes[MAX_EXTRA_ARGS + 2] & ~PA_FLAG_MASK) != PA_INT)
return "The 18th argument must be of type int (the index of the current element in the array).";
if (nargs > MAX_EXTRA_ARGS + 3 && argtypes[MAX_EXTRA_ARGS + 3] != PA_POINTER)
return "The 19th argument must be a pointer type (the array).";
if (nargs > MAX_EXTRA_ARGS + 4 && (argtypes[MAX_EXTRA_ARGS + 4] & ~PA_FLAG_MASK) != PA_INT)
return "The 20th argument must be an int (the number of items in the array).";
if (nargs > MAX_EXTRA_ARGS + 5 && (argtypes[MAX_EXTRA_ARGS + 5] & ~PA_FLAG_MASK) != PA_INT)
return "The 21st argument must be an int (the precision arg of the 'V' specifier).";
if (nargs > MAX_EXTRA_ARGS + 6 && argtypes[MAX_EXTRA_ARGS + 5] != PA_POINTER)
return "The 22nd argument must be a pointer type (the fnarg optional argument or NULL).";
return NULL;
}
int printf_vector(FILE *stream,
const struct printf_info *info,
const void * const *args)
{
int i;
int nchars = 0;
int nargs;
int argtype[MAX_EXTRA_ARGS + 7];
void *extra_args[MAX_EXTRA_ARGS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, };
int nextras = 0;
int eltsz;
int nelts;
char *eltfmt;
char *delim;
void *array;
void *fn = NULL;
void *fnarg = NULL;
int next_arg = 0;
const char *error_string;
nelts = info->width;
nextras = nextra_args(info);
/* Get our args, including optional extra args to be passed through to the per-element format */
array = *(void **)args[0];
eltfmt = *(char **)args[1];
delim = *(char **)args[2];
if (!info->i18n) {
next_arg = 3;
} else {
fn = *(void **)args[3];
fnarg = *(void **)args[4];
next_arg = 5;
}
for (i = 0; i < nextras; i++)
extra_args[i] = *(void **)args[next_arg++];
/* Now parse the per-element format and check that it is consistent with the options we were given */
nargs = parse_printf_format(eltfmt, MAX_EXTRA_ARGS + 7, argtype);
error_string = is_valid_argtype_array(info->alt, nextras, nargs, argtype);
if (error_string) {
return fprintf(stream, "ERRROR: printf_vector: %s", error_string);
}
/* Compute element size (and possibly override it) from per-element format */
eltsz = pa2size(info, argtype[0]);
if (info->prec > 0)
eltsz = info->prec;
void * eltp = array;
for (i = 0; i < nelts - 1; i++) {
nchars += print_vector_elt(stream, eltfmt, info, argtype[0], extra_args, array, eltp, i, info->width, info->prec, fn, fnarg);
nchars += print_vector_elt(stream, delim, info, argtype[0], extra_args, array, eltp, i, info->width, info->prec, fn, fnarg);
eltp += eltsz;
}
if (nelts > 0)
nchars += print_vector_elt(stream, eltfmt, info, argtype[0], extra_args, array, eltp, i, info->width, info->prec, fn, fnarg);
return nchars;
}
int printf_vector_arginfo_size (const struct printf_info *info,
size_t n,
int *argtypes,
int *size)
{
static const int myargtypes[20] = { PA_POINTER, PA_STRING, PA_STRING, // Required
PA_POINTER, PA_POINTER, // 'I' fn and arg
// Optional additional args to per-element and delimiter formats
PA_POINTER, PA_POINTER, PA_POINTER, PA_POINTER,
PA_POINTER, PA_POINTER, PA_POINTER, PA_POINTER,
PA_POINTER, PA_POINTER, PA_POINTER, PA_POINTER,
PA_POINTER, PA_POINTER, PA_POINTER,
};
int args = 3;
if (info->i18n)
args += 2;
args += nextra_args(info);
memcpy(argtypes, myargtypes, (20 < n ? 20 : n) * sizeof(argtypes[0]));
return args;
}