/src/mupdf/source/fitz/printf.c
Line | Count | Source |
1 | | // Copyright (C) 2004-2025 Artifex Software, Inc. |
2 | | // |
3 | | // This file is part of MuPDF. |
4 | | // |
5 | | // MuPDF is free software: you can redistribute it and/or modify it under the |
6 | | // terms of the GNU Affero General Public License as published by the Free |
7 | | // Software Foundation, either version 3 of the License, or (at your option) |
8 | | // any later version. |
9 | | // |
10 | | // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY |
11 | | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | | // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
13 | | // details. |
14 | | // |
15 | | // You should have received a copy of the GNU Affero General Public License |
16 | | // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> |
17 | | // |
18 | | // Alternative licensing terms are available from the licensor. |
19 | | // For commercial licensing, see <https://www.artifex.com/> or contact |
20 | | // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
21 | | // CA 94129, USA, for further information. |
22 | | |
23 | | #include "mupdf/fitz.h" |
24 | | |
25 | | #include <float.h> |
26 | | #include <math.h> |
27 | | #include <stdarg.h> |
28 | | #include <stdio.h> |
29 | | |
30 | | #ifdef _MSC_VER |
31 | | #if _MSC_VER < 1500 /* MSVC 2008 */ |
32 | | int snprintf(char *s, size_t n, const char *fmt, ...) |
33 | | { |
34 | | int r; |
35 | | va_list ap; |
36 | | va_start(ap, fmt); |
37 | | r = vsprintf(s, fmt, ap); |
38 | | va_end(ap); |
39 | | return r; |
40 | | } |
41 | | #else if _MSC_VER < 1900 /* MSVC 2015 */ |
42 | | #define snprintf _snprintf |
43 | | #endif |
44 | | #endif |
45 | | |
46 | | static const char *fz_hex_digits = "0123456789abcdef"; |
47 | | static const char *fz_hex_digits_UC = "0123456789ABCDEF"; |
48 | | |
49 | | struct fmtbuf |
50 | | { |
51 | | fz_context *ctx; |
52 | | void *user; |
53 | | void (*emit)(fz_context *ctx, void *user, int c); |
54 | | }; |
55 | | |
56 | | static inline void fmtputc(struct fmtbuf *out, int c) |
57 | 6.83M | { |
58 | 6.83M | out->emit(out->ctx, out->user, c); |
59 | 6.83M | } |
60 | | |
61 | | static inline void fmtputrune(struct fmtbuf *out, int c) |
62 | 0 | { |
63 | 0 | char buf[10]; |
64 | 0 | int i, n; |
65 | 0 | if (c < 128) |
66 | 0 | fmtputc(out, c); |
67 | 0 | else |
68 | 0 | { |
69 | 0 | n = fz_runetochar(buf, c); |
70 | 0 | for (i = 0; i < n; ++i) |
71 | 0 | fmtputc(out, buf[i]); |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | /* |
76 | | * Convert float to shortest possible string that won't lose precision, except: |
77 | | * NaN to 0, +Inf to FLT_MAX, -Inf to -FLT_MAX. |
78 | | */ |
79 | | static void fmtfloat(struct fmtbuf *out, float f) |
80 | 3.30k | { |
81 | 3.30k | char digits[40], *s = digits; |
82 | 3.30k | int exp, ndigits, point; |
83 | | |
84 | 3.30k | if (isnan(f)) f = 0; |
85 | 3.30k | if (isinf(f)) f = f < 0 ? -FLT_MAX : FLT_MAX; |
86 | | |
87 | 3.30k | if (signbit(f)) |
88 | 0 | fmtputc(out, '-'); |
89 | | |
90 | 3.30k | if (f == 0) |
91 | 560 | { |
92 | 560 | fmtputc(out, '0'); |
93 | 560 | return; |
94 | 560 | } |
95 | | |
96 | 2.74k | ndigits = fz_grisu(f, digits, &exp); |
97 | 2.74k | point = exp + ndigits; |
98 | | |
99 | 2.74k | if (point <= 0) |
100 | 104 | { |
101 | 104 | fmtputc(out, '.'); |
102 | 104 | while (point++ < 0) |
103 | 0 | fmtputc(out, '0'); |
104 | 420 | while (ndigits-- > 0) |
105 | 316 | fmtputc(out, *s++); |
106 | 104 | } |
107 | | |
108 | 2.64k | else |
109 | 2.64k | { |
110 | 14.9k | while (ndigits-- > 0) |
111 | 12.2k | { |
112 | 12.2k | fmtputc(out, *s++); |
113 | 12.2k | if (--point == 0 && ndigits > 0) |
114 | 1.51k | fmtputc(out, '.'); |
115 | 12.2k | } |
116 | 2.69k | while (point-- > 0) |
117 | 51 | fmtputc(out, '0'); |
118 | 2.64k | } |
119 | 2.74k | } |
120 | | |
121 | | static void fmtfloat_e(struct fmtbuf *out, double f, int w, int p) |
122 | 0 | { |
123 | 0 | char buf[100], *s = buf; |
124 | 0 | snprintf(buf, sizeof buf, "%*.*e", w, p, f); |
125 | 0 | while (*s) |
126 | 0 | fmtputc(out, *s++); |
127 | 0 | } |
128 | | |
129 | | static void fmtfloat_f(struct fmtbuf *out, double f, int w, int p) |
130 | 0 | { |
131 | 0 | char buf[100], *s = buf; |
132 | 0 | snprintf(buf, sizeof buf, "%*.*f", w, p, f); |
133 | 0 | while (*s) |
134 | 0 | fmtputc(out, *s++); |
135 | 0 | } |
136 | | |
137 | | static void fmtuint32(struct fmtbuf *out, unsigned int a, int s, int z, int w, int base, int q) |
138 | 85.3k | { |
139 | 85.3k | char buf[40]; |
140 | 85.3k | int i; |
141 | 85.3k | const char *hex_digits = fz_hex_digits; |
142 | | |
143 | 85.3k | if (base < 0) |
144 | 0 | { |
145 | 0 | base = -base; |
146 | 0 | hex_digits = fz_hex_digits_UC; |
147 | 0 | } |
148 | | |
149 | 85.3k | i = 0; |
150 | 85.3k | if (a == 0) |
151 | 23 | buf[i++] = '0'; |
152 | 458k | while (a) { |
153 | 373k | buf[i++] = hex_digits[a % base]; |
154 | 373k | a /= base; |
155 | 373k | } |
156 | 85.3k | if (s) { |
157 | 2 | if (z == '0') |
158 | 0 | while (i < w - 1) |
159 | 0 | buf[i++] = z; |
160 | 2 | buf[i++] = s; |
161 | 2 | } |
162 | 85.3k | while (i < w) |
163 | 0 | buf[i++] = z; |
164 | 458k | while (i > 0) |
165 | 373k | { |
166 | 373k | fmtputc(out, buf[--i]); |
167 | 373k | if (q && i != 0 && i % 3 == 0) |
168 | 0 | fmtputc(out, q); |
169 | 373k | } |
170 | 85.3k | } |
171 | | |
172 | | static void fmtuint64(struct fmtbuf *out, uint64_t a, int s, int z, int w, int base, int q) |
173 | 3 | { |
174 | 3 | char buf[80]; |
175 | 3 | int i; |
176 | 3 | const char *hex_digits = fz_hex_digits; |
177 | | |
178 | 3 | if (base < 0) |
179 | 0 | { |
180 | 0 | base = -base; |
181 | 0 | hex_digits = fz_hex_digits_UC; |
182 | 0 | } |
183 | | |
184 | 3 | i = 0; |
185 | 3 | if (a == 0) |
186 | 0 | buf[i++] = '0'; |
187 | 45 | while (a) { |
188 | 42 | buf[i++] = hex_digits[a % base]; |
189 | 42 | a /= base; |
190 | 42 | } |
191 | 3 | if (s) { |
192 | 0 | if (z == '0') |
193 | 0 | while (i < w - 1) |
194 | 0 | buf[i++] = z; |
195 | 0 | buf[i++] = s; |
196 | 0 | } |
197 | 3 | while (i < w) |
198 | 0 | buf[i++] = z; |
199 | 45 | while (i > 0) |
200 | 42 | { |
201 | 42 | fmtputc(out, buf[--i]); |
202 | 42 | if (q && i != 0 && i % 3 == 0) |
203 | 0 | fmtputc(out, q); |
204 | 42 | } |
205 | 3 | } |
206 | | |
207 | | static void fmtint32(struct fmtbuf *out, int value, int s, int z, int w, int base, int q) |
208 | 85.3k | { |
209 | 85.3k | unsigned int a; |
210 | | |
211 | 85.3k | if (value < 0) |
212 | 2 | { |
213 | 2 | s = '-'; |
214 | 2 | a = -value; |
215 | 2 | } |
216 | 85.3k | else if (s) |
217 | 0 | { |
218 | 0 | s = '+'; |
219 | 0 | a = value; |
220 | 0 | } |
221 | 85.3k | else |
222 | 85.3k | { |
223 | 85.3k | s = 0; |
224 | 85.3k | a = value; |
225 | 85.3k | } |
226 | 85.3k | fmtuint32(out, a, s, z, w, base, q); |
227 | 85.3k | } |
228 | | |
229 | | static void fmtint64(struct fmtbuf *out, int64_t value, int s, int z, int w, int base, int q) |
230 | 3 | { |
231 | 3 | uint64_t a; |
232 | | |
233 | 3 | if (value < 0) |
234 | 0 | { |
235 | 0 | s = '-'; |
236 | 0 | a = -value; |
237 | 0 | } |
238 | 3 | else if (s) |
239 | 0 | { |
240 | 0 | s = '+'; |
241 | 0 | a = value; |
242 | 0 | } |
243 | 3 | else |
244 | 3 | { |
245 | 3 | s = 0; |
246 | 3 | a = value; |
247 | 3 | } |
248 | 3 | fmtuint64(out, a, s, z, w, base, q); |
249 | 3 | } |
250 | | |
251 | | static void fmtquote(struct fmtbuf *out, const char *s, int sq, int eq, int verbatim) |
252 | 0 | { |
253 | 0 | int i, n, c; |
254 | 0 | fmtputc(out, sq); |
255 | 0 | while (*s != 0) { |
256 | 0 | n = fz_chartorune(&c, s); |
257 | 0 | switch (c) { |
258 | 0 | default: |
259 | 0 | if (c < 32) { |
260 | 0 | fmtputc(out, '\\'); |
261 | 0 | fmtputc(out, 'x'); |
262 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]); |
263 | 0 | fmtputc(out, "0123456789ABCDEF"[(c)&15]); |
264 | 0 | } else if (c > 127) { |
265 | 0 | if (verbatim) |
266 | 0 | { |
267 | 0 | for (i = 0; i < n; ++i) |
268 | 0 | fmtputc(out, s[i]); |
269 | 0 | } |
270 | 0 | else if (c <= 0xffff) |
271 | 0 | { |
272 | 0 | fmtputc(out, '\\'); |
273 | 0 | fmtputc(out, 'u'); |
274 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>12)&15]); |
275 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>8)&15]); |
276 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]); |
277 | 0 | fmtputc(out, "0123456789ABCDEF"[(c)&15]); |
278 | 0 | } |
279 | 0 | else |
280 | 0 | { |
281 | | /* Use a surrogate pair */ |
282 | 0 | int hi = 0xd800 + ((c - 0x10000) >> 10); |
283 | 0 | int lo = 0xdc00 + ((c - 0x10000) & 0x3ff); |
284 | 0 | fmtputc(out, '\\'); |
285 | 0 | fmtputc(out, 'u'); |
286 | 0 | fmtputc(out, "0123456789ABCDEF"[(hi>>12)&15]); |
287 | 0 | fmtputc(out, "0123456789ABCDEF"[(hi>>8)&15]); |
288 | 0 | fmtputc(out, "0123456789ABCDEF"[(hi>>4)&15]); |
289 | 0 | fmtputc(out, "0123456789ABCDEF"[(hi)&15]); |
290 | 0 | fmtputc(out, '\\'); |
291 | 0 | fmtputc(out, 'u'); |
292 | 0 | fmtputc(out, "0123456789ABCDEF"[(lo>>12)&15]); |
293 | 0 | fmtputc(out, "0123456789ABCDEF"[(lo>>8)&15]); |
294 | 0 | fmtputc(out, "0123456789ABCDEF"[(lo>>4)&15]); |
295 | 0 | fmtputc(out, "0123456789ABCDEF"[(lo)&15]); |
296 | 0 | } |
297 | 0 | } else { |
298 | 0 | if (c == sq || c == eq) |
299 | 0 | fmtputc(out, '\\'); |
300 | 0 | fmtputc(out, c); |
301 | 0 | } |
302 | 0 | break; |
303 | 0 | case '\\': fmtputc(out, '\\'); fmtputc(out, '\\'); break; |
304 | 0 | case '\b': fmtputc(out, '\\'); fmtputc(out, 'b'); break; |
305 | 0 | case '\f': fmtputc(out, '\\'); fmtputc(out, 'f'); break; |
306 | 0 | case '\n': fmtputc(out, '\\'); fmtputc(out, 'n'); break; |
307 | 0 | case '\r': fmtputc(out, '\\'); fmtputc(out, 'r'); break; |
308 | 0 | case '\t': fmtputc(out, '\\'); fmtputc(out, 't'); break; |
309 | 0 | } |
310 | 0 | s += n; |
311 | 0 | } |
312 | 0 | fmtputc(out, eq); |
313 | 0 | } |
314 | | |
315 | | static void fmtquote_pdf(struct fmtbuf *out, const char *s, int sq, int eq) |
316 | 0 | { |
317 | 0 | int c; |
318 | 0 | fmtputc(out, sq); |
319 | 0 | while ((c = (unsigned char)*s++) != 0) { |
320 | 0 | switch (c) { |
321 | 0 | default: |
322 | 0 | if (c < 32 || c > 127) { |
323 | 0 | fmtputc(out, '\\'); |
324 | 0 | if (sq == '(') |
325 | 0 | { |
326 | 0 | fmtputc(out, '0' + ((c >> 6) & 7)); |
327 | 0 | fmtputc(out, '0' + ((c >> 3) & 7)); |
328 | 0 | fmtputc(out, '0' + ((c) & 7)); |
329 | 0 | } |
330 | 0 | else |
331 | 0 | { |
332 | 0 | fmtputc(out, 'x'); |
333 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]); |
334 | 0 | fmtputc(out, "0123456789ABCDEF"[(c)&15]); |
335 | 0 | } |
336 | 0 | } else { |
337 | 0 | if (c == sq || c == eq) |
338 | 0 | fmtputc(out, '\\'); |
339 | 0 | fmtputc(out, c); |
340 | 0 | } |
341 | 0 | break; |
342 | 0 | case '\\': fmtputc(out, '\\'); fmtputc(out, '\\'); break; |
343 | 0 | case '\b': fmtputc(out, '\\'); fmtputc(out, 'b'); break; |
344 | 0 | case '\f': fmtputc(out, '\\'); fmtputc(out, 'f'); break; |
345 | 0 | case '\n': fmtputc(out, '\\'); fmtputc(out, 'n'); break; |
346 | 0 | case '\r': fmtputc(out, '\\'); fmtputc(out, 'r'); break; |
347 | 0 | case '\t': fmtputc(out, '\\'); fmtputc(out, 't'); break; |
348 | 0 | } |
349 | 0 | } |
350 | 0 | fmtputc(out, eq); |
351 | 0 | } |
352 | | |
353 | | int |
354 | | fz_is_valid_xml_char(int c) |
355 | 0 | { |
356 | 0 | if (c == 9 || c == 10 || c == 13) |
357 | 0 | return 1; |
358 | 0 | if (c < 32) |
359 | 0 | return 0; |
360 | 0 | if (c < 0xd800) |
361 | 0 | return 1; |
362 | 0 | if (c < 0xe000) |
363 | 0 | return 0; |
364 | 0 | if (c <= 0xfffd) |
365 | 0 | return 1; |
366 | 0 | if (c < 0x10000) |
367 | 0 | return 0; |
368 | 0 | if (c <= 0x10FFFF) |
369 | 0 | return 1; |
370 | 0 | return 0; |
371 | 0 | } |
372 | | |
373 | | int |
374 | | fz_is_valid_xml_string(const char *s) |
375 | 0 | { |
376 | 0 | int c, n; |
377 | 0 | while (*s != 0) { |
378 | 0 | n = fz_chartorune(&c, s); |
379 | 0 | if (!fz_is_valid_xml_char(c)) |
380 | 0 | return 0; |
381 | 0 | s += n; |
382 | 0 | } |
383 | 0 | return 1; |
384 | 0 | } |
385 | | |
386 | | int |
387 | | fz_range_limit_xml_char(int c) |
388 | 0 | { |
389 | 0 | if (fz_is_valid_xml_char(c)) |
390 | 0 | return c; |
391 | 0 | return 0xFFFD; |
392 | 0 | } |
393 | | |
394 | | static void fmtquote_xml(struct fmtbuf *out, const char *s) |
395 | 0 | { |
396 | 0 | int c, n; |
397 | 0 | fmtputc(out, '"'); |
398 | 0 | while (*s != 0) { |
399 | 0 | n = fz_chartorune(&c, s); |
400 | 0 | switch (c) { |
401 | 0 | case '"': |
402 | 0 | fmtputc(out, '&'); |
403 | 0 | fmtputc(out, 'q'); |
404 | 0 | fmtputc(out, 'u'); |
405 | 0 | fmtputc(out, 'o'); |
406 | 0 | fmtputc(out, 't'); |
407 | 0 | fmtputc(out, ';'); |
408 | 0 | break; |
409 | 0 | case '&': |
410 | 0 | fmtputc(out, '&'); |
411 | 0 | fmtputc(out, 'a'); |
412 | 0 | fmtputc(out, 'm'); |
413 | 0 | fmtputc(out, 'p'); |
414 | 0 | fmtputc(out, ';'); |
415 | 0 | break; |
416 | 0 | case '<': |
417 | 0 | fmtputc(out, '&'); |
418 | 0 | fmtputc(out, 'l'); |
419 | 0 | fmtputc(out, 't'); |
420 | 0 | fmtputc(out, ';'); |
421 | 0 | break; |
422 | 0 | case '>': |
423 | 0 | fmtputc(out, '&'); |
424 | 0 | fmtputc(out, 'g'); |
425 | 0 | fmtputc(out, 't'); |
426 | 0 | fmtputc(out, ';'); |
427 | 0 | break; |
428 | 0 | default: |
429 | 0 | c = fz_range_limit_xml_char(c); |
430 | 0 | if (c < 32 || c >= 127) |
431 | 0 | { |
432 | 0 | fmtputc(out, '&'); |
433 | 0 | fmtputc(out, '#'); |
434 | 0 | fmtputc(out, 'x'); |
435 | 0 | if (c > 65535) |
436 | 0 | { |
437 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>20)&15]); |
438 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>16)&15]); |
439 | 0 | } |
440 | 0 | if (c > 255) |
441 | 0 | { |
442 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>12)&15]); |
443 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>8)&15]); |
444 | 0 | } |
445 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]); |
446 | 0 | fmtputc(out, "0123456789ABCDEF"[(c)&15]); |
447 | 0 | fmtputc(out, ';'); |
448 | 0 | } |
449 | 0 | else |
450 | 0 | fmtputc(out, c); |
451 | 0 | break; |
452 | 0 | } |
453 | 0 | s += n; |
454 | 0 | } |
455 | 0 | fmtputc(out, '"'); |
456 | 0 | } |
457 | | |
458 | | static void fmtquote_hex(struct fmtbuf *out, const char *s) |
459 | 0 | { |
460 | 0 | int c; |
461 | 0 | fmtputc(out, '"'); |
462 | 0 | while ((c = *s++) != 0) |
463 | 0 | { |
464 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]); |
465 | 0 | fmtputc(out, "0123456789ABCDEF"[(c)&15]); |
466 | 0 | } |
467 | 0 | fmtputc(out, '"'); |
468 | 0 | } |
469 | | |
470 | | static void fmtname(struct fmtbuf *out, const char *s) |
471 | 0 | { |
472 | 0 | int c; |
473 | 0 | fmtputc(out, '/'); |
474 | 0 | while ((c = *s++) != 0) { |
475 | 0 | if (c <= 32 || c == '/' || c == '#') { |
476 | 0 | fmtputc(out, '#'); |
477 | 0 | fmtputc(out, "0123456789ABCDEF"[(c>>4)&15]); |
478 | 0 | fmtputc(out, "0123456789ABCDEF"[(c)&15]); |
479 | 0 | } else { |
480 | 0 | fmtputc(out, c); |
481 | 0 | } |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | | void |
486 | | fz_format_string(fz_context *ctx, void *user, void (*emit)(fz_context *ctx, void *user, int c), const char *fmt, va_list args) |
487 | 209k | { |
488 | 209k | struct fmtbuf out; |
489 | 209k | int c, s, z, p, ps, w, q, i; |
490 | 209k | int32_t i32; |
491 | 209k | int64_t i64; |
492 | 209k | const char *str; |
493 | 209k | const wchar_t *wstr; |
494 | 209k | size_t bits; |
495 | | |
496 | 209k | out.ctx = ctx; |
497 | 209k | out.user = user; |
498 | 209k | out.emit = emit; |
499 | | |
500 | 5.27M | while ((c = *fmt++) != 0) |
501 | 5.06M | { |
502 | 5.06M | if (c == '%') |
503 | 183k | { |
504 | 183k | q = 0; |
505 | 183k | s = 0; |
506 | 183k | z = ' '; |
507 | | |
508 | | /* flags */ |
509 | 183k | while ((c = *fmt++) != 0) |
510 | 183k | { |
511 | | /* plus sign */ |
512 | 183k | if (c == '+') |
513 | 0 | s = 1; |
514 | | /* space sign */ |
515 | 183k | else if (c == ' ') |
516 | 0 | s = ' '; |
517 | | /* zero padding */ |
518 | 183k | else if (c == '0') |
519 | 0 | z = '0'; |
520 | | /* comma separators */ |
521 | 183k | else if (c == '\'') |
522 | 0 | q = '\''; |
523 | 183k | else if (c == ',') |
524 | 0 | q = ','; |
525 | 183k | else if (c == '_') |
526 | 0 | q = '_'; |
527 | | /* TODO: '-' to left justify */ |
528 | 183k | else |
529 | 183k | break; |
530 | 183k | } |
531 | 183k | if (c == 0) |
532 | 0 | break; |
533 | | |
534 | | /* width */ |
535 | 183k | w = 0; |
536 | 183k | if (c == '*') { |
537 | 0 | c = *fmt++; |
538 | 0 | w = va_arg(args, int); |
539 | 183k | } else { |
540 | 183k | while (c >= '0' && c <= '9') { |
541 | 0 | w = w * 10 + c - '0'; |
542 | 0 | c = *fmt++; |
543 | 0 | } |
544 | 183k | } |
545 | 183k | if (c == 0) |
546 | 0 | break; |
547 | | |
548 | | /* precision */ |
549 | 183k | p = 6; |
550 | 183k | ps = -1; |
551 | 183k | if (c == '.') { |
552 | 14 | c = *fmt++; |
553 | 14 | if (c == 0) |
554 | 0 | break; |
555 | 14 | if (c == '*') { |
556 | 14 | c = *fmt++; |
557 | 14 | p = va_arg(args, int); |
558 | 14 | } else { |
559 | 0 | if (c >= '0' && c <= '9') |
560 | 0 | p = 0; |
561 | 0 | while (c >= '0' && c <= '9') { |
562 | 0 | p = p * 10 + c - '0'; |
563 | 0 | c = *fmt++; |
564 | 0 | } |
565 | 0 | } |
566 | 14 | ps = p; |
567 | 14 | } |
568 | 183k | if (c == 0) |
569 | 0 | break; |
570 | | |
571 | | /* lengths */ |
572 | 183k | bits = 0; |
573 | 183k | if (c == 'l') { |
574 | 3 | c = *fmt++; |
575 | 3 | bits = sizeof(int64_t) * 8; |
576 | 3 | if (c == 0) |
577 | 0 | break; |
578 | 3 | } |
579 | 183k | if (c == 't') { |
580 | 0 | c = *fmt++; |
581 | 0 | bits = sizeof(ptrdiff_t) * 8; |
582 | 0 | if (c == 0) |
583 | 0 | break; |
584 | 0 | } |
585 | 183k | if (c == 'z') { |
586 | 0 | c = *fmt++; |
587 | 0 | bits = sizeof(size_t) * 8; |
588 | 0 | if (c == 0) |
589 | 0 | break; |
590 | 0 | } |
591 | | |
592 | 183k | switch (c) { |
593 | 0 | default: |
594 | 0 | fmtputc(&out, '%'); |
595 | 0 | fmtputc(&out, c); |
596 | 0 | break; |
597 | 0 | case '%': |
598 | 0 | fmtputc(&out, '%'); |
599 | 0 | break; |
600 | | |
601 | 0 | case 'M': |
602 | 0 | { |
603 | 0 | fz_matrix *matrix = va_arg(args, fz_matrix*); |
604 | 0 | fmtfloat(&out, matrix->a); fmtputc(&out, ' '); |
605 | 0 | fmtfloat(&out, matrix->b); fmtputc(&out, ' '); |
606 | 0 | fmtfloat(&out, matrix->c); fmtputc(&out, ' '); |
607 | 0 | fmtfloat(&out, matrix->d); fmtputc(&out, ' '); |
608 | 0 | fmtfloat(&out, matrix->e); fmtputc(&out, ' '); |
609 | 0 | fmtfloat(&out, matrix->f); |
610 | 0 | } |
611 | 0 | break; |
612 | 0 | case 'R': |
613 | 0 | { |
614 | 0 | fz_rect *rect = va_arg(args, fz_rect*); |
615 | 0 | fmtfloat(&out, rect->x0); fmtputc(&out, ' '); |
616 | 0 | fmtfloat(&out, rect->y0); fmtputc(&out, ' '); |
617 | 0 | fmtfloat(&out, rect->x1); fmtputc(&out, ' '); |
618 | 0 | fmtfloat(&out, rect->y1); |
619 | 0 | } |
620 | 0 | break; |
621 | 0 | case 'P': |
622 | 0 | { |
623 | 0 | fz_point *point = va_arg(args, fz_point*); |
624 | 0 | fmtfloat(&out, point->x); fmtputc(&out, ' '); |
625 | 0 | fmtfloat(&out, point->y); |
626 | 0 | } |
627 | 0 | break; |
628 | | |
629 | 0 | case 'C': /* unicode char */ |
630 | 0 | c = va_arg(args, int); |
631 | 0 | fmtputrune(&out, c); |
632 | 0 | break; |
633 | 452 | case 'c': |
634 | 452 | c = va_arg(args, int); |
635 | 452 | fmtputc(&out, c); |
636 | 452 | break; |
637 | 0 | case 'e': |
638 | 0 | fmtfloat_e(&out, va_arg(args, double), w, p); |
639 | 0 | break; |
640 | 0 | case 'f': |
641 | 0 | fmtfloat_f(&out, va_arg(args, double), w, p); |
642 | 0 | break; |
643 | 3.30k | case 'g': |
644 | 3.30k | fmtfloat(&out, va_arg(args, double)); |
645 | 3.30k | break; |
646 | | |
647 | 0 | case 'p': |
648 | 0 | bits = 8 * sizeof(void *); |
649 | 0 | z = '0'; |
650 | 0 | fmtputc(&out, '0'); |
651 | 0 | fmtputc(&out, 'x'); |
652 | 0 | q = 0; |
653 | | /* fallthrough */ |
654 | 0 | case 'x': |
655 | 0 | if (bits == 64) |
656 | 0 | { |
657 | 0 | i64 = va_arg(args, int64_t); |
658 | 0 | fmtuint64(&out, i64, 0, z, w, 16, q); |
659 | 0 | } |
660 | 0 | else |
661 | 0 | { |
662 | 0 | i32 = va_arg(args, int); |
663 | 0 | fmtuint32(&out, i32, 0, z, w, 16, q); |
664 | 0 | } |
665 | 0 | break; |
666 | 0 | case 'X': |
667 | 0 | if (bits == 64) |
668 | 0 | { |
669 | 0 | i64 = va_arg(args, int64_t); |
670 | 0 | fmtuint64(&out, i64, 0, z, w, -16, q); |
671 | 0 | } |
672 | 0 | else |
673 | 0 | { |
674 | 0 | i32 = va_arg(args, int); |
675 | 0 | fmtuint32(&out, i32, 0, z, w, -16, q); |
676 | 0 | } |
677 | 0 | break; |
678 | 85.3k | case 'd': |
679 | 85.3k | case 'i': |
680 | 85.3k | if (bits == 64) |
681 | 3 | { |
682 | 3 | i64 = va_arg(args, int64_t); |
683 | 3 | fmtint64(&out, i64, s, z, w, 10, q); |
684 | 3 | } |
685 | 85.3k | else |
686 | 85.3k | { |
687 | 85.3k | i32 = va_arg(args, int); |
688 | 85.3k | fmtint32(&out, i32, s, z, w, 10, q); |
689 | 85.3k | } |
690 | 85.3k | break; |
691 | 0 | case 'u': |
692 | 0 | if (bits == 64) |
693 | 0 | { |
694 | 0 | i64 = va_arg(args, int64_t); |
695 | 0 | fmtuint64(&out, i64, 0, z, w, 10, q); |
696 | 0 | } |
697 | 0 | else |
698 | 0 | { |
699 | 0 | i32 = va_arg(args, int); |
700 | 0 | fmtuint32(&out, i32, 0, z, w, 10, q); |
701 | 0 | } |
702 | 0 | break; |
703 | | |
704 | 94.1k | case 's': |
705 | 94.1k | if (bits > 0) |
706 | 0 | { |
707 | 0 | wstr = va_arg(args, const wchar_t*); |
708 | 0 | if (!wstr) |
709 | 0 | wstr = L"(null)"; |
710 | 0 | if (ps >= 0) |
711 | 0 | { |
712 | 0 | for (i=0; i < ps && ((c = *wstr++) != 0); ++i) |
713 | 0 | fmtputrune(&out, c); |
714 | 0 | } |
715 | 0 | else |
716 | 0 | { |
717 | 0 | while ((c = *wstr++) != 0) |
718 | 0 | fmtputrune(&out, c); |
719 | 0 | } |
720 | 0 | } |
721 | 94.1k | else |
722 | 94.1k | { |
723 | 94.1k | str = va_arg(args, const char*); |
724 | 94.1k | if (!str) |
725 | 0 | str = "(null)"; |
726 | 94.1k | if (ps >= 0) |
727 | 14 | { |
728 | 468 | for (i=0; i < ps && ((c = *str++) != 0); ++i) |
729 | 454 | fmtputc(&out, c); |
730 | 14 | } |
731 | 94.0k | else |
732 | 94.0k | { |
733 | 1.65M | while ((c = *str++) != 0) |
734 | 1.55M | fmtputc(&out, c); |
735 | 94.0k | } |
736 | 94.1k | } |
737 | 94.1k | break; |
738 | 0 | case 'Q': /* quoted string (with verbatim unicode) */ |
739 | 0 | str = va_arg(args, const char*); |
740 | 0 | if (!str) str = ""; |
741 | 0 | fmtquote(&out, str, '"', '"', 1); |
742 | 0 | break; |
743 | 0 | case 'q': /* quoted string */ |
744 | 0 | str = va_arg(args, const char*); |
745 | 0 | if (!str) str = ""; |
746 | 0 | fmtquote(&out, str, '"', '"', 0); |
747 | 0 | break; |
748 | 0 | case '<': /* quoted string for xml */ |
749 | 0 | str = va_arg(args, const char*); |
750 | 0 | if (!str) str = ""; |
751 | 0 | fmtquote_xml(&out, str); |
752 | 0 | break; |
753 | 0 | case '>': /* hex string */ |
754 | 0 | str = va_arg(args, const char*); |
755 | 0 | if (!str) str = ""; |
756 | 0 | fmtquote_hex(&out, str); |
757 | 0 | break; |
758 | 0 | case '(': /* pdf string */ |
759 | 0 | str = va_arg(args, const char*); |
760 | 0 | if (!str) str = ""; |
761 | 0 | fmtquote_pdf(&out, str, '(', ')'); |
762 | 0 | break; |
763 | 0 | case 'n': /* pdf name */ |
764 | 0 | str = va_arg(args, const char*); |
765 | 0 | if (!str) str = ""; |
766 | 0 | fmtname(&out, str); |
767 | 0 | break; |
768 | 183k | } |
769 | 183k | } |
770 | 4.88M | else |
771 | 4.88M | { |
772 | 4.88M | fmtputc(&out, c); |
773 | 4.88M | } |
774 | 5.06M | } |
775 | 209k | } |
776 | | |
777 | | struct snprintf_buffer |
778 | | { |
779 | | char *p; |
780 | | size_t s, n; |
781 | | }; |
782 | | |
783 | | static void snprintf_emit(fz_context *ctx, void *out_, int c) |
784 | 6.80M | { |
785 | 6.80M | struct snprintf_buffer *out = out_; |
786 | 6.80M | if (out->n < out->s) |
787 | 6.80M | out->p[out->n] = c; |
788 | 6.80M | ++(out->n); |
789 | 6.80M | } |
790 | | |
791 | | size_t |
792 | | fz_vsnprintf(char *buffer, size_t space, const char *fmt, va_list args) |
793 | 199k | { |
794 | 199k | struct snprintf_buffer out; |
795 | 199k | out.p = buffer; |
796 | 199k | out.s = space > 0 ? space - 1 : 0; |
797 | 199k | out.n = 0; |
798 | | |
799 | | /* Note: using a NULL context is safe here */ |
800 | 199k | fz_format_string(NULL, &out, snprintf_emit, fmt, args); |
801 | 199k | if (space > 0) |
802 | 199k | out.p[out.n < space ? out.n : space - 1] = '\0'; |
803 | | |
804 | 199k | return out.n; |
805 | 199k | } |
806 | | |
807 | | size_t |
808 | | fz_snprintf(char *buffer, size_t space, const char *fmt, ...) |
809 | 8.81k | { |
810 | 8.81k | va_list ap; |
811 | 8.81k | struct snprintf_buffer out; |
812 | 8.81k | out.p = buffer; |
813 | 8.81k | out.s = space > 0 ? space - 1 : 0; |
814 | 8.81k | out.n = 0; |
815 | | |
816 | 8.81k | va_start(ap, fmt); |
817 | | /* Note: using a NULL context is safe here */ |
818 | 8.81k | fz_format_string(NULL, &out, snprintf_emit, fmt, ap); |
819 | 8.81k | if (space > 0) |
820 | 8.81k | out.p[out.n < space ? out.n : space - 1] = '\0'; |
821 | 8.81k | va_end(ap); |
822 | | |
823 | 8.81k | return out.n; |
824 | 8.81k | } |
825 | | |
826 | | char * |
827 | | fz_asprintf(fz_context *ctx, const char *fmt, ...) |
828 | 0 | { |
829 | 0 | size_t len; |
830 | 0 | char *mem; |
831 | 0 | va_list ap; |
832 | 0 | va_start(ap, fmt); |
833 | 0 | len = fz_vsnprintf(NULL, 0, fmt, ap); |
834 | 0 | va_end(ap); |
835 | 0 | mem = Memento_label(fz_malloc(ctx, len+1), "asprintf"); |
836 | 0 | va_start(ap, fmt); |
837 | 0 | fz_vsnprintf(mem, len+1, fmt, ap); |
838 | | va_end(ap); |
839 | 0 | return mem; |
840 | 0 | } |