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