cordprnt.c raw
1 /*
2 * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved.
3 *
4 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
5 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
6 *
7 * Permission is hereby granted to use or copy this program
8 * for any purpose, provided the above notices are retained on all copies.
9 * Permission to modify the code and to distribute modified code is granted,
10 * provided the above notices are retained, and a notice that the code was
11 * modified is included with the above copyright notice.
12 */
13
14 /*
15 * A `sprintf` implementation that understands cords. This is probably
16 * not terribly portable. It assumes an ANSI platform `stdarg.h` file.
17 * It further assumes that I can make copies of `va_list` variables,
18 * and read arguments repeatedly by applying `va_arg` to the copies.
19 * This could be avoided at some performance cost.
20 * We also assume that unsigned and signed integers of various kinds
21 * have the same sizes, and can be cast back and forth.
22 * We assume that `void *` and `char *` have the same size.
23 * All this cruft is needed because we want to rely on the underlying
24 * `sprintf` implementation whenever possible.
25 */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include "gc/gc.h"
32
33 #include <stdarg.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #ifndef CORD_BUILD
38 # define CORD_BUILD
39 #endif
40 #ifndef CORD_DONT_DECLARE_OOM_FN
41 /* Avoid `CORD_oom_fn` symbol redefinition by MinGW. */
42 # define CORD_DONT_DECLARE_OOM_FN
43 #endif
44 #include "gc/cord.h"
45 #include "gc/ec.h"
46
47 /* Maximum length of a single conversion specification. */
48 #define CONV_SPEC_LEN 50
49
50 /* Maximum length of any conversion with the default width and precision. */
51 #define CONV_RESULT_LEN 50
52
53 #if defined(CPPCHECK)
54 # define MACRO_BLKSTMT_BEGIN {
55 # define MACRO_BLKSTMT_END }
56 #else
57 # define MACRO_BLKSTMT_BEGIN do {
58 # define MACRO_BLKSTMT_END \
59 } \
60 while (0)
61 #endif
62
63 #define OUT_OF_MEMORY \
64 MACRO_BLKSTMT_BEGIN \
65 CORD__call_oom_fn(); \
66 fprintf(stderr, "Out of memory\n"); \
67 abort(); \
68 MACRO_BLKSTMT_END
69
70 static size_t
71 ec_len(CORD_ec x)
72 {
73 return CORD_len(x[0].ec_cord) + (size_t)(x[0].ec_bufptr - x[0].ec_buf);
74 }
75
76 /* Possible non-numeric precision values. */
77 #define NONE -1
78 #define VARIABLE -2
79
80 /*
81 * Copy the conversion specification from `CORD_pos` into the
82 * buffer `buf`. Return a negative value on error.
83 * `source` initially points one past the leading "%" symbol;
84 * it is left-pointing at the conversion type. Assign width and
85 * precision fields to `*width` and `*prec`, respectively. If the
86 * width or precision is specified as "*", then the corresponding
87 * variable is assigned. Set `*left` to 1 if left adjustment flag
88 * is present. Set `*long_arg` to 1 if `long` flag ("l" or "L") is
89 * present, or to -1 if "h" is present, or to 2 if "z" is present.
90 */
91 static int
92 extract_conv_spec(CORD_pos source, char *buf, int *width, int *prec, int *left,
93 int *long_arg)
94 {
95 int result = 0;
96 int current_number = 0;
97 int saw_period = 0;
98 int saw_number = 0;
99 int chars_so_far = 0;
100 char current;
101
102 *width = NONE;
103 buf[chars_so_far++] = '%';
104 while (CORD_pos_valid(source)) {
105 if (chars_so_far >= CONV_SPEC_LEN)
106 return -1;
107 current = CORD_pos_fetch(source);
108 buf[chars_so_far++] = current;
109 switch (current) {
110 case '*':
111 saw_number = 1;
112 current_number = VARIABLE;
113 break;
114 case '0':
115 if (!saw_number) {
116 /* Zero-fill flag; ignore it. */
117 break;
118 }
119 current_number *= 10;
120 break;
121 case '1':
122 case '2':
123 case '3':
124 case '4':
125 case '5':
126 case '6':
127 case '7':
128 case '8':
129 case '9':
130 saw_number = 1;
131 current_number *= 10;
132 current_number += current - '0';
133 break;
134 case '.':
135 saw_period = 1;
136 if (saw_number) {
137 *width = current_number;
138 saw_number = 0;
139 }
140 current_number = 0;
141 break;
142 case 'l':
143 case 'L':
144 *long_arg = 1;
145 current_number = 0;
146 break;
147 case 'z':
148 *long_arg = 2;
149 current_number = 0;
150 break;
151 case 'h':
152 *long_arg = -1;
153 current_number = 0;
154 break;
155 case ' ':
156 case '+':
157 case '#':
158 current_number = 0;
159 break;
160 case '-':
161 *left = 1;
162 current_number = 0;
163 break;
164 case 'd':
165 case 'i':
166 case 'o':
167 case 'u':
168 case 'x':
169 case 'X':
170 case 'f':
171 case 'e':
172 case 'E':
173 case 'g':
174 case 'G':
175 case 'c':
176 case 'C':
177 case 's':
178 case 'S':
179 case 'p':
180 case 'n':
181 case 'r':
182 goto done;
183 default:
184 return -1;
185 }
186 CORD_next(source);
187 }
188 return -1;
189 done:
190 if (saw_number) {
191 if (saw_period) {
192 *prec = current_number;
193 } else {
194 *prec = NONE;
195 *width = current_number;
196 }
197 } else {
198 *prec = NONE;
199 }
200 buf[chars_so_far] = '\0';
201 return result;
202 }
203
204 #if defined(__DJGPP__) || defined(__STRICT_ANSI__)
205 /* `vsnprintf` is missing in DJGPP (v2.0.3). */
206 # define GC_VSNPRINTF(buf, bufsz, format, args) vsprintf(buf, format, args)
207 #elif defined(_MSC_VER)
208 # if defined(_WIN32_WCE)
209 /* `_vsnprintf` is deprecated in WinCE. */
210 # define GC_VSNPRINTF StringCchVPrintfA
211 # else
212 # define GC_VSNPRINTF _vsnprintf
213 # endif
214 #else
215 # define GC_VSNPRINTF vsnprintf
216 #endif
217
218 int
219 CORD_vsprintf(CORD *out, CORD format, va_list args)
220 {
221 CORD_ec result;
222 int count;
223 char current;
224 CORD_pos pos;
225 char conv_spec[CONV_SPEC_LEN + 1];
226
227 #if defined(CPPCHECK)
228 memset(pos, '\0', sizeof(CORD_pos));
229 #endif
230 CORD_ec_init(result);
231 for (CORD_set_pos(pos, format, 0); CORD_pos_valid(pos); CORD_next(pos)) {
232 current = CORD_pos_fetch(pos);
233 if (current == '%') {
234 CORD_next(pos);
235 if (!CORD_pos_valid(pos))
236 return -1;
237 current = CORD_pos_fetch(pos);
238 if (current == '%') {
239 CORD_ec_append(result, current);
240 } else {
241 int width, prec;
242 int left_adj = 0;
243 int long_arg = 0;
244 CORD arg;
245 size_t len;
246
247 if (extract_conv_spec(pos, conv_spec, &width, &prec, &left_adj,
248 &long_arg)
249 < 0) {
250 return -1;
251 }
252 current = CORD_pos_fetch(pos);
253 switch (current) {
254 case 'n':
255 /* Assign the length to next argument. */
256 if (long_arg == 0) {
257 unsigned *pos_ptr = va_arg(args, unsigned *);
258 *pos_ptr = (unsigned)ec_len(result);
259 } else if (long_arg == 2) {
260 size_t *pos_ptr = va_arg(args, size_t *);
261 *pos_ptr = ec_len(result);
262 } else if (long_arg > 0) {
263 unsigned long *pos_ptr = va_arg(args, unsigned long *);
264 *pos_ptr = (unsigned long)ec_len(result);
265 } else {
266 unsigned short *pos_ptr = va_arg(args, unsigned short *);
267 *pos_ptr = (unsigned short)ec_len(result);
268 }
269 goto done;
270 case 'r':
271 /* Append the cord and padding, if any. */
272 if (width == VARIABLE)
273 width = va_arg(args, int);
274 if (prec == VARIABLE)
275 prec = va_arg(args, int);
276 arg = va_arg(args, CORD);
277 len = CORD_len(arg);
278 if (prec != NONE && len > (unsigned)prec) {
279 if (prec < 0)
280 return -1;
281 arg = CORD_substr(arg, 0, (unsigned)prec);
282 len = (unsigned)prec;
283 }
284 if (width != NONE && len < (unsigned)width) {
285 char *blanks = (char *)GC_MALLOC_ATOMIC((unsigned)width - len + 1);
286
287 if (NULL == blanks)
288 OUT_OF_MEMORY;
289 memset(blanks, ' ', (unsigned)width - len);
290 blanks[(unsigned)width - len] = '\0';
291 if (left_adj) {
292 arg = CORD_cat(arg, blanks);
293 } else {
294 arg = CORD_cat(blanks, arg);
295 }
296 }
297 CORD_ec_append_cord(result, arg);
298 goto done;
299 case 'c':
300 if (width == NONE && prec == NONE) {
301 char c;
302
303 c = (char)va_arg(args, int);
304 CORD_ec_append(result, c);
305 goto done;
306 }
307 break;
308 case 's':
309 if (width == NONE && prec == NONE) {
310 const char *str = va_arg(args, char *);
311 char c;
312
313 while ((c = *str++) != '\0') {
314 CORD_ec_append(result, c);
315 }
316 goto done;
317 }
318 break;
319 default:
320 break;
321 }
322 /* Use standard `sprintf` to perform the conversion. */
323 {
324 char *buf;
325 va_list vsprintf_args;
326 int max_size = 0;
327 int res = 0;
328
329 #if defined(CPPCHECK)
330 va_copy(vsprintf_args, args);
331 #elif defined(__va_copy)
332 __va_copy(vsprintf_args, args);
333 #elif defined(__GNUC__) && !defined(__DJGPP__) \
334 && !defined(__EMX__) /*< and probably in other cases */
335 va_copy(vsprintf_args, args);
336 #else
337 vsprintf_args = args;
338 #endif
339 if (width == VARIABLE)
340 width = va_arg(args, int);
341 if (prec == VARIABLE)
342 prec = va_arg(args, int);
343 if (width != NONE)
344 max_size = width;
345 if (prec != NONE && prec > max_size)
346 max_size = prec;
347 max_size += CONV_RESULT_LEN;
348 if (max_size >= CORD_BUFSZ) {
349 buf = (char *)GC_MALLOC_ATOMIC((unsigned)max_size + 1);
350 if (NULL == buf)
351 OUT_OF_MEMORY;
352 } else {
353 if (CORD_BUFSZ - (result[0].ec_bufptr - result[0].ec_buf)
354 < max_size) {
355 CORD_ec_flush_buf(result);
356 }
357 buf = result[0].ec_bufptr;
358 }
359 switch (current) {
360 case 'd':
361 case 'i':
362 case 'o':
363 case 'u':
364 case 'x':
365 case 'X':
366 case 'c':
367 if (long_arg <= 0) {
368 (void)va_arg(args, int);
369 } else if (long_arg == 2) {
370 (void)va_arg(args, size_t);
371 } else /* `long_arg == 1` */ {
372 (void)va_arg(args, long);
373 }
374 break;
375 case 's':
376 case 'p':
377 (void)va_arg(args, char *);
378 break;
379 case 'f':
380 case 'e':
381 case 'E':
382 case 'g':
383 case 'G':
384 (void)va_arg(args, double);
385 break;
386 default:
387 res = -1;
388 }
389 if (0 == res)
390 res = GC_VSNPRINTF(buf, (unsigned)max_size + 1, conv_spec,
391 vsprintf_args);
392 #if defined(CPPCHECK) || defined(__va_copy) \
393 || (defined(__GNUC__) && !defined(__DJGPP__) && !defined(__EMX__))
394 va_end(vsprintf_args);
395 #endif
396 len = (unsigned)res;
397 if ((GC_uintptr_t)len == (GC_uintptr_t)buf) {
398 /* Old-style `vsprintf`. */
399 len = strlen(buf);
400 } else if (res < 0) {
401 return -1;
402 }
403 if (buf != result[0].ec_bufptr) {
404 char c;
405
406 while ((c = *buf++) != '\0') {
407 CORD_ec_append(result, c);
408 }
409 } else {
410 result[0].ec_bufptr = buf + len;
411 }
412 }
413 done:;
414 }
415 } else {
416 CORD_ec_append(result, current);
417 }
418 }
419 count = (int)ec_len(result);
420 *out = CORD_balance(CORD_ec_to_cord(result));
421 return count;
422 }
423
424 int
425 CORD_sprintf(CORD *out, CORD format, ...)
426 {
427 va_list args;
428 int result;
429
430 va_start(args, format);
431 result = CORD_vsprintf(out, format, args);
432 va_end(args);
433 return result;
434 }
435
436 int
437 CORD_fprintf(FILE *f, CORD format, ...)
438 {
439 va_list args;
440 int result;
441 CORD out = CORD_EMPTY; /*< initialized to prevent a compiler warning */
442
443 va_start(args, format);
444 result = CORD_vsprintf(&out, format, args);
445 va_end(args);
446 if (result > 0)
447 CORD_put(out, f);
448 return result;
449 }
450
451 int
452 CORD_vfprintf(FILE *f, CORD format, va_list args)
453 {
454 int result;
455 CORD out = CORD_EMPTY;
456
457 result = CORD_vsprintf(&out, format, args);
458 if (result > 0)
459 CORD_put(out, f);
460 return result;
461 }
462
463 int
464 CORD_printf(CORD format, ...)
465 {
466 va_list args;
467 int result;
468 CORD out = CORD_EMPTY;
469
470 va_start(args, format);
471 result = CORD_vsprintf(&out, format, args);
472 va_end(args);
473 if (result > 0)
474 CORD_put(out, stdout);
475 return result;
476 }
477
478 int
479 CORD_vprintf(CORD format, va_list args)
480 {
481 int result;
482 CORD out = CORD_EMPTY;
483
484 result = CORD_vsprintf(&out, format, args);
485 if (result > 0)
486 CORD_put(out, stdout);
487 return result;
488 }
489