1*61046927SAndroid Build Coastguard Worker //
2*61046927SAndroid Build Coastguard Worker // Copyright 2020 Serge Martin
3*61046927SAndroid Build Coastguard Worker //
4*61046927SAndroid Build Coastguard Worker // Permission is hereby granted, free of charge, to any person obtaining a
5*61046927SAndroid Build Coastguard Worker // copy of this software and associated documentation files (the "Software"),
6*61046927SAndroid Build Coastguard Worker // to deal in the Software without restriction, including without limitation
7*61046927SAndroid Build Coastguard Worker // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*61046927SAndroid Build Coastguard Worker // and/or sell copies of the Software, and to permit persons to whom the
9*61046927SAndroid Build Coastguard Worker // Software is furnished to do so, subject to the following conditions:
10*61046927SAndroid Build Coastguard Worker //
11*61046927SAndroid Build Coastguard Worker // The above copyright notice and this permission notice shall be included in
12*61046927SAndroid Build Coastguard Worker // all copies or substantial portions of the Software.
13*61046927SAndroid Build Coastguard Worker //
14*61046927SAndroid Build Coastguard Worker // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*61046927SAndroid Build Coastguard Worker // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*61046927SAndroid Build Coastguard Worker // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17*61046927SAndroid Build Coastguard Worker // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18*61046927SAndroid Build Coastguard Worker // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19*61046927SAndroid Build Coastguard Worker // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20*61046927SAndroid Build Coastguard Worker // OTHER DEALINGS IN THE SOFTWARE.
21*61046927SAndroid Build Coastguard Worker //
22*61046927SAndroid Build Coastguard Worker // Extract from Serge's printf clover code by airlied.
23*61046927SAndroid Build Coastguard Worker
24*61046927SAndroid Build Coastguard Worker #include <assert.h>
25*61046927SAndroid Build Coastguard Worker #include <stdarg.h>
26*61046927SAndroid Build Coastguard Worker #include <stdbool.h>
27*61046927SAndroid Build Coastguard Worker #include <stdlib.h>
28*61046927SAndroid Build Coastguard Worker #include <string.h>
29*61046927SAndroid Build Coastguard Worker
30*61046927SAndroid Build Coastguard Worker #include "macros.h"
31*61046927SAndroid Build Coastguard Worker #include "strndup.h"
32*61046927SAndroid Build Coastguard Worker #include "u_math.h"
33*61046927SAndroid Build Coastguard Worker #include "u_printf.h"
34*61046927SAndroid Build Coastguard Worker
35*61046927SAndroid Build Coastguard Worker /* Some versions of MinGW are missing _vscprintf's declaration, although they
36*61046927SAndroid Build Coastguard Worker * still provide the symbol in the import library. */
37*61046927SAndroid Build Coastguard Worker #ifdef __MINGW32__
38*61046927SAndroid Build Coastguard Worker _CRTIMP int _vscprintf(const char *format, va_list argptr);
39*61046927SAndroid Build Coastguard Worker #endif
40*61046927SAndroid Build Coastguard Worker
41*61046927SAndroid Build Coastguard Worker const char*
util_printf_prev_tok(const char * str)42*61046927SAndroid Build Coastguard Worker util_printf_prev_tok(const char *str)
43*61046927SAndroid Build Coastguard Worker {
44*61046927SAndroid Build Coastguard Worker while (*str != '%')
45*61046927SAndroid Build Coastguard Worker str--;
46*61046927SAndroid Build Coastguard Worker return str;
47*61046927SAndroid Build Coastguard Worker }
48*61046927SAndroid Build Coastguard Worker
util_printf_next_spec_pos(const char * str,size_t pos)49*61046927SAndroid Build Coastguard Worker size_t util_printf_next_spec_pos(const char *str, size_t pos)
50*61046927SAndroid Build Coastguard Worker {
51*61046927SAndroid Build Coastguard Worker if (str == NULL)
52*61046927SAndroid Build Coastguard Worker return -1;
53*61046927SAndroid Build Coastguard Worker
54*61046927SAndroid Build Coastguard Worker const char *str_found = str + pos;
55*61046927SAndroid Build Coastguard Worker do {
56*61046927SAndroid Build Coastguard Worker str_found = strchr(str_found, '%');
57*61046927SAndroid Build Coastguard Worker if (str_found == NULL)
58*61046927SAndroid Build Coastguard Worker return -1;
59*61046927SAndroid Build Coastguard Worker
60*61046927SAndroid Build Coastguard Worker ++str_found;
61*61046927SAndroid Build Coastguard Worker if (*str_found == '%') {
62*61046927SAndroid Build Coastguard Worker ++str_found;
63*61046927SAndroid Build Coastguard Worker continue;
64*61046927SAndroid Build Coastguard Worker }
65*61046927SAndroid Build Coastguard Worker
66*61046927SAndroid Build Coastguard Worker char *spec_pos = strpbrk(str_found, "cdieEfFgGaAosuxXp%");
67*61046927SAndroid Build Coastguard Worker if (spec_pos == NULL) {
68*61046927SAndroid Build Coastguard Worker return -1;
69*61046927SAndroid Build Coastguard Worker } else if (*spec_pos == '%') {
70*61046927SAndroid Build Coastguard Worker str_found = spec_pos;
71*61046927SAndroid Build Coastguard Worker } else {
72*61046927SAndroid Build Coastguard Worker return spec_pos - str;
73*61046927SAndroid Build Coastguard Worker }
74*61046927SAndroid Build Coastguard Worker } while (1);
75*61046927SAndroid Build Coastguard Worker }
76*61046927SAndroid Build Coastguard Worker
u_printf_length(const char * fmt,va_list untouched_args)77*61046927SAndroid Build Coastguard Worker size_t u_printf_length(const char *fmt, va_list untouched_args)
78*61046927SAndroid Build Coastguard Worker {
79*61046927SAndroid Build Coastguard Worker int size;
80*61046927SAndroid Build Coastguard Worker char junk;
81*61046927SAndroid Build Coastguard Worker
82*61046927SAndroid Build Coastguard Worker /* Make a copy of the va_list so the original caller can still use it */
83*61046927SAndroid Build Coastguard Worker va_list args;
84*61046927SAndroid Build Coastguard Worker va_copy(args, untouched_args);
85*61046927SAndroid Build Coastguard Worker
86*61046927SAndroid Build Coastguard Worker #ifdef _WIN32
87*61046927SAndroid Build Coastguard Worker /* We need to use _vcsprintf to calculate the size as vsnprintf returns -1
88*61046927SAndroid Build Coastguard Worker * if the number of characters to write is greater than count.
89*61046927SAndroid Build Coastguard Worker */
90*61046927SAndroid Build Coastguard Worker size = _vscprintf(fmt, args);
91*61046927SAndroid Build Coastguard Worker (void)junk;
92*61046927SAndroid Build Coastguard Worker #else
93*61046927SAndroid Build Coastguard Worker size = vsnprintf(&junk, 1, fmt, args);
94*61046927SAndroid Build Coastguard Worker #endif
95*61046927SAndroid Build Coastguard Worker assert(size >= 0);
96*61046927SAndroid Build Coastguard Worker
97*61046927SAndroid Build Coastguard Worker va_end(args);
98*61046927SAndroid Build Coastguard Worker
99*61046927SAndroid Build Coastguard Worker return size;
100*61046927SAndroid Build Coastguard Worker }
101*61046927SAndroid Build Coastguard Worker
102*61046927SAndroid Build Coastguard Worker /**
103*61046927SAndroid Build Coastguard Worker * Used to print plain format strings without arguments as some post-processing
104*61046927SAndroid Build Coastguard Worker * will be required:
105*61046927SAndroid Build Coastguard Worker * - %% needs to be printed as %
106*61046927SAndroid Build Coastguard Worker */
107*61046927SAndroid Build Coastguard Worker static void
u_printf_plain_sized(FILE * out,const char * format,size_t len)108*61046927SAndroid Build Coastguard Worker u_printf_plain_sized(FILE *out, const char* format, size_t len)
109*61046927SAndroid Build Coastguard Worker {
110*61046927SAndroid Build Coastguard Worker bool found = false;
111*61046927SAndroid Build Coastguard Worker size_t last = 0;
112*61046927SAndroid Build Coastguard Worker
113*61046927SAndroid Build Coastguard Worker for (size_t i = 0; i < len; i++) {
114*61046927SAndroid Build Coastguard Worker if (!found && format[i] == '%') {
115*61046927SAndroid Build Coastguard Worker found = true;
116*61046927SAndroid Build Coastguard Worker } else if (found && format[i] == '%') {
117*61046927SAndroid Build Coastguard Worker /* print one character less so we only print a single % */
118*61046927SAndroid Build Coastguard Worker fwrite(format + last, i - last - 1, 1, out);
119*61046927SAndroid Build Coastguard Worker
120*61046927SAndroid Build Coastguard Worker last = i;
121*61046927SAndroid Build Coastguard Worker found = false;
122*61046927SAndroid Build Coastguard Worker } else {
123*61046927SAndroid Build Coastguard Worker /* We should never end up here with an actual format token */
124*61046927SAndroid Build Coastguard Worker assert(!found);
125*61046927SAndroid Build Coastguard Worker found = false;
126*61046927SAndroid Build Coastguard Worker }
127*61046927SAndroid Build Coastguard Worker }
128*61046927SAndroid Build Coastguard Worker
129*61046927SAndroid Build Coastguard Worker fwrite(format + last, len - last, 1, out);
130*61046927SAndroid Build Coastguard Worker }
131*61046927SAndroid Build Coastguard Worker
132*61046927SAndroid Build Coastguard Worker static void
u_printf_plain(FILE * out,const char * format)133*61046927SAndroid Build Coastguard Worker u_printf_plain(FILE *out, const char* format)
134*61046927SAndroid Build Coastguard Worker {
135*61046927SAndroid Build Coastguard Worker u_printf_plain_sized(out, format, strlen(format));
136*61046927SAndroid Build Coastguard Worker }
137*61046927SAndroid Build Coastguard Worker
138*61046927SAndroid Build Coastguard Worker static void
u_printf_impl(FILE * out,const char * buffer,size_t buffer_size,const u_printf_info * info,const u_printf_info ** info_ptr,unsigned info_size)139*61046927SAndroid Build Coastguard Worker u_printf_impl(FILE *out, const char *buffer, size_t buffer_size,
140*61046927SAndroid Build Coastguard Worker const u_printf_info *info,
141*61046927SAndroid Build Coastguard Worker const u_printf_info **info_ptr,
142*61046927SAndroid Build Coastguard Worker unsigned info_size)
143*61046927SAndroid Build Coastguard Worker {
144*61046927SAndroid Build Coastguard Worker for (size_t buf_pos = 0; buf_pos < buffer_size;) {
145*61046927SAndroid Build Coastguard Worker uint32_t fmt_idx = *(uint32_t*)&buffer[buf_pos];
146*61046927SAndroid Build Coastguard Worker
147*61046927SAndroid Build Coastguard Worker /* the idx is 1 based */
148*61046927SAndroid Build Coastguard Worker assert(fmt_idx > 0);
149*61046927SAndroid Build Coastguard Worker fmt_idx -= 1;
150*61046927SAndroid Build Coastguard Worker
151*61046927SAndroid Build Coastguard Worker /* The API allows more arguments than the format uses */
152*61046927SAndroid Build Coastguard Worker if (fmt_idx >= info_size)
153*61046927SAndroid Build Coastguard Worker return;
154*61046927SAndroid Build Coastguard Worker
155*61046927SAndroid Build Coastguard Worker const u_printf_info *fmt = info != NULL ?
156*61046927SAndroid Build Coastguard Worker &info[fmt_idx] : info_ptr[fmt_idx];
157*61046927SAndroid Build Coastguard Worker const char *format = fmt->strings;
158*61046927SAndroid Build Coastguard Worker buf_pos += sizeof(fmt_idx);
159*61046927SAndroid Build Coastguard Worker
160*61046927SAndroid Build Coastguard Worker if (!fmt->num_args) {
161*61046927SAndroid Build Coastguard Worker u_printf_plain(out, format);
162*61046927SAndroid Build Coastguard Worker continue;
163*61046927SAndroid Build Coastguard Worker }
164*61046927SAndroid Build Coastguard Worker
165*61046927SAndroid Build Coastguard Worker for (int i = 0; i < fmt->num_args; i++) {
166*61046927SAndroid Build Coastguard Worker int arg_size = fmt->arg_sizes[i];
167*61046927SAndroid Build Coastguard Worker size_t spec_pos = util_printf_next_spec_pos(format, 0);
168*61046927SAndroid Build Coastguard Worker
169*61046927SAndroid Build Coastguard Worker /* If we hit an unused argument we skip all remaining ones */
170*61046927SAndroid Build Coastguard Worker if (spec_pos == -1)
171*61046927SAndroid Build Coastguard Worker break;
172*61046927SAndroid Build Coastguard Worker
173*61046927SAndroid Build Coastguard Worker const char *token = util_printf_prev_tok(&format[spec_pos]);
174*61046927SAndroid Build Coastguard Worker const char *next_format = &format[spec_pos + 1];
175*61046927SAndroid Build Coastguard Worker
176*61046927SAndroid Build Coastguard Worker /* print the part before the format token */
177*61046927SAndroid Build Coastguard Worker if (token != format)
178*61046927SAndroid Build Coastguard Worker u_printf_plain_sized(out, format, token - format);
179*61046927SAndroid Build Coastguard Worker
180*61046927SAndroid Build Coastguard Worker char *print_str = strndup(token, next_format - token);
181*61046927SAndroid Build Coastguard Worker /* rebase spec_pos so we can use it with print_str */
182*61046927SAndroid Build Coastguard Worker spec_pos += format - token;
183*61046927SAndroid Build Coastguard Worker
184*61046927SAndroid Build Coastguard Worker /* print the formatted part */
185*61046927SAndroid Build Coastguard Worker if (print_str[spec_pos] == 's') {
186*61046927SAndroid Build Coastguard Worker uint64_t idx;
187*61046927SAndroid Build Coastguard Worker memcpy(&idx, &buffer[buf_pos], 8);
188*61046927SAndroid Build Coastguard Worker fprintf(out, print_str, &fmt->strings[idx]);
189*61046927SAndroid Build Coastguard Worker
190*61046927SAndroid Build Coastguard Worker /* Never pass a 'n' spec to the host printf */
191*61046927SAndroid Build Coastguard Worker } else if (print_str[spec_pos] != 'n') {
192*61046927SAndroid Build Coastguard Worker char *vec_pos = strchr(print_str, 'v');
193*61046927SAndroid Build Coastguard Worker char *mod_pos = strpbrk(print_str, "hl");
194*61046927SAndroid Build Coastguard Worker
195*61046927SAndroid Build Coastguard Worker int component_count = 1;
196*61046927SAndroid Build Coastguard Worker if (vec_pos != NULL) {
197*61046927SAndroid Build Coastguard Worker /* non vector part of the format */
198*61046927SAndroid Build Coastguard Worker size_t base = mod_pos ? mod_pos - print_str : spec_pos;
199*61046927SAndroid Build Coastguard Worker size_t l = base - (vec_pos - print_str) - 1;
200*61046927SAndroid Build Coastguard Worker char *vec = strndup(&vec_pos[1], l);
201*61046927SAndroid Build Coastguard Worker component_count = atoi(vec);
202*61046927SAndroid Build Coastguard Worker free(vec);
203*61046927SAndroid Build Coastguard Worker
204*61046927SAndroid Build Coastguard Worker /* remove the vector and precision stuff */
205*61046927SAndroid Build Coastguard Worker memmove(&print_str[vec_pos - print_str], &print_str[spec_pos], 2);
206*61046927SAndroid Build Coastguard Worker }
207*61046927SAndroid Build Coastguard Worker
208*61046927SAndroid Build Coastguard Worker /* in fact vec3 are vec4 */
209*61046927SAndroid Build Coastguard Worker int men_components = component_count == 3 ? 4 : component_count;
210*61046927SAndroid Build Coastguard Worker size_t elmt_size = arg_size / men_components;
211*61046927SAndroid Build Coastguard Worker bool is_float = strpbrk(print_str, "fFeEgGaA") != NULL;
212*61046927SAndroid Build Coastguard Worker
213*61046927SAndroid Build Coastguard Worker for (int i = 0; i < component_count; i++) {
214*61046927SAndroid Build Coastguard Worker size_t elmt_buf_pos = buf_pos + i * elmt_size;
215*61046927SAndroid Build Coastguard Worker switch (elmt_size) {
216*61046927SAndroid Build Coastguard Worker case 1: {
217*61046927SAndroid Build Coastguard Worker uint8_t v;
218*61046927SAndroid Build Coastguard Worker memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
219*61046927SAndroid Build Coastguard Worker fprintf(out, print_str, v);
220*61046927SAndroid Build Coastguard Worker break;
221*61046927SAndroid Build Coastguard Worker }
222*61046927SAndroid Build Coastguard Worker case 2: {
223*61046927SAndroid Build Coastguard Worker uint16_t v;
224*61046927SAndroid Build Coastguard Worker memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
225*61046927SAndroid Build Coastguard Worker fprintf(out, print_str, v);
226*61046927SAndroid Build Coastguard Worker break;
227*61046927SAndroid Build Coastguard Worker }
228*61046927SAndroid Build Coastguard Worker case 4: {
229*61046927SAndroid Build Coastguard Worker if (is_float) {
230*61046927SAndroid Build Coastguard Worker float v;
231*61046927SAndroid Build Coastguard Worker memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
232*61046927SAndroid Build Coastguard Worker fprintf(out, print_str, v);
233*61046927SAndroid Build Coastguard Worker } else {
234*61046927SAndroid Build Coastguard Worker uint32_t v;
235*61046927SAndroid Build Coastguard Worker memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
236*61046927SAndroid Build Coastguard Worker fprintf(out, print_str, v);
237*61046927SAndroid Build Coastguard Worker }
238*61046927SAndroid Build Coastguard Worker break;
239*61046927SAndroid Build Coastguard Worker }
240*61046927SAndroid Build Coastguard Worker case 8: {
241*61046927SAndroid Build Coastguard Worker if (is_float) {
242*61046927SAndroid Build Coastguard Worker double v;
243*61046927SAndroid Build Coastguard Worker memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
244*61046927SAndroid Build Coastguard Worker fprintf(out, print_str, v);
245*61046927SAndroid Build Coastguard Worker } else {
246*61046927SAndroid Build Coastguard Worker uint64_t v;
247*61046927SAndroid Build Coastguard Worker memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
248*61046927SAndroid Build Coastguard Worker fprintf(out, print_str, v);
249*61046927SAndroid Build Coastguard Worker }
250*61046927SAndroid Build Coastguard Worker break;
251*61046927SAndroid Build Coastguard Worker }
252*61046927SAndroid Build Coastguard Worker default:
253*61046927SAndroid Build Coastguard Worker assert(false);
254*61046927SAndroid Build Coastguard Worker break;
255*61046927SAndroid Build Coastguard Worker }
256*61046927SAndroid Build Coastguard Worker
257*61046927SAndroid Build Coastguard Worker if (i < component_count - 1)
258*61046927SAndroid Build Coastguard Worker fprintf(out, ",");
259*61046927SAndroid Build Coastguard Worker }
260*61046927SAndroid Build Coastguard Worker }
261*61046927SAndroid Build Coastguard Worker
262*61046927SAndroid Build Coastguard Worker /* rebase format */
263*61046927SAndroid Build Coastguard Worker format = next_format;
264*61046927SAndroid Build Coastguard Worker free(print_str);
265*61046927SAndroid Build Coastguard Worker
266*61046927SAndroid Build Coastguard Worker buf_pos += arg_size;
267*61046927SAndroid Build Coastguard Worker buf_pos = align_uintptr(buf_pos, 4);
268*61046927SAndroid Build Coastguard Worker }
269*61046927SAndroid Build Coastguard Worker
270*61046927SAndroid Build Coastguard Worker /* print remaining */
271*61046927SAndroid Build Coastguard Worker u_printf_plain(out, format);
272*61046927SAndroid Build Coastguard Worker }
273*61046927SAndroid Build Coastguard Worker }
274*61046927SAndroid Build Coastguard Worker
u_printf(FILE * out,const char * buffer,size_t buffer_size,const u_printf_info * info,unsigned info_size)275*61046927SAndroid Build Coastguard Worker void u_printf(FILE *out, const char *buffer, size_t buffer_size,
276*61046927SAndroid Build Coastguard Worker const u_printf_info *info, unsigned info_size)
277*61046927SAndroid Build Coastguard Worker {
278*61046927SAndroid Build Coastguard Worker u_printf_impl(out, buffer, buffer_size, info, NULL, info_size);
279*61046927SAndroid Build Coastguard Worker }
280*61046927SAndroid Build Coastguard Worker
u_printf_ptr(FILE * out,const char * buffer,size_t buffer_size,const u_printf_info ** info,unsigned info_size)281*61046927SAndroid Build Coastguard Worker void u_printf_ptr(FILE *out, const char *buffer, size_t buffer_size,
282*61046927SAndroid Build Coastguard Worker const u_printf_info **info, unsigned info_size)
283*61046927SAndroid Build Coastguard Worker {
284*61046927SAndroid Build Coastguard Worker u_printf_impl(out, buffer, buffer_size, NULL, info, info_size);
285*61046927SAndroid Build Coastguard Worker }
286