]>
Commit | Line | Data |
---|---|---|
73ffefd0 TT |
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 | /* An sprintf implementation that understands cords. This is probably */ | |
14 | /* not terribly portable. It assumes an ANSI stdarg.h. It further */ | |
15 | /* assumes that I can make copies of va_list variables, and read */ | |
16 | /* arguments repeatedly by applyting va_arg to the copies. This */ | |
17 | /* could be avoided at some performance cost. */ | |
18 | /* We also assume that unsigned and signed integers of various kinds */ | |
19 | /* have the same sizes, and can be cast back and forth. */ | |
20 | /* We assume that void * and char * have the same size. */ | |
21 | /* All this cruft is needed because we want to rely on the underlying */ | |
22 | /* sprintf implementation whenever possible. */ | |
23 | /* Boehm, September 21, 1995 6:00 pm PDT */ | |
24 | ||
25 | #include "cord.h" | |
26 | #include "ec.h" | |
27 | #include <stdio.h> | |
28 | #include <stdarg.h> | |
29 | #include <string.h> | |
30 | #include "gc.h" | |
31 | ||
32 | #define CONV_SPEC_LEN 50 /* Maximum length of a single */ | |
33 | /* conversion specification. */ | |
34 | #define CONV_RESULT_LEN 50 /* Maximum length of any */ | |
35 | /* conversion with default */ | |
36 | /* width and prec. */ | |
37 | ||
38 | ||
39 | static int ec_len(CORD_ec x) | |
40 | { | |
41 | return(CORD_len(x[0].ec_cord) + (x[0].ec_bufptr - x[0].ec_buf)); | |
42 | } | |
43 | ||
44 | /* Possible nonumeric precision values. */ | |
45 | # define NONE -1 | |
46 | # define VARIABLE -2 | |
47 | /* Copy the conversion specification from CORD_pos into the buffer buf */ | |
48 | /* Return negative on error. */ | |
49 | /* Source initially points one past the leading %. */ | |
50 | /* It is left pointing at the conversion type. */ | |
51 | /* Assign field width and precision to *width and *prec. */ | |
52 | /* If width or prec is *, VARIABLE is assigned. */ | |
53 | /* Set *left to 1 if left adjustment flag is present. */ | |
54 | /* Set *long_arg to 1 if long flag ('l' or 'L') is present, or to */ | |
55 | /* -1 if 'h' is present. */ | |
56 | static int extract_conv_spec(CORD_pos source, char *buf, | |
57 | int * width, int *prec, int *left, int * long_arg) | |
58 | { | |
59 | register int result = 0; | |
60 | register int current_number = 0; | |
61 | register int saw_period = 0; | |
54f28c21 | 62 | register int saw_number = 0; |
73ffefd0 TT |
63 | register int chars_so_far = 0; |
64 | register char current; | |
65 | ||
66 | *width = NONE; | |
67 | buf[chars_so_far++] = '%'; | |
68 | while(CORD_pos_valid(source)) { | |
69 | if (chars_so_far >= CONV_SPEC_LEN) return(-1); | |
70 | current = CORD_pos_fetch(source); | |
71 | buf[chars_so_far++] = current; | |
72 | switch(current) { | |
73 | case '*': | |
74 | saw_number = 1; | |
75 | current_number = VARIABLE; | |
76 | break; | |
77 | case '0': | |
78 | if (!saw_number) { | |
79 | /* Zero fill flag; ignore */ | |
80 | break; | |
81 | } /* otherwise fall through: */ | |
82 | case '1': | |
83 | case '2': | |
84 | case '3': | |
85 | case '4': | |
86 | case '5': | |
87 | case '6': | |
88 | case '7': | |
89 | case '8': | |
90 | case '9': | |
91 | saw_number = 1; | |
92 | current_number *= 10; | |
93 | current_number += current - '0'; | |
94 | break; | |
95 | case '.': | |
96 | saw_period = 1; | |
97 | if(saw_number) { | |
98 | *width = current_number; | |
99 | saw_number = 0; | |
100 | } | |
101 | current_number = 0; | |
102 | break; | |
103 | case 'l': | |
104 | case 'L': | |
105 | *long_arg = 1; | |
106 | current_number = 0; | |
107 | break; | |
108 | case 'h': | |
109 | *long_arg = -1; | |
110 | current_number = 0; | |
111 | break; | |
112 | case ' ': | |
113 | case '+': | |
114 | case '#': | |
115 | current_number = 0; | |
116 | break; | |
117 | case '-': | |
118 | *left = 1; | |
119 | current_number = 0; | |
120 | break; | |
121 | case 'd': | |
122 | case 'i': | |
123 | case 'o': | |
124 | case 'u': | |
125 | case 'x': | |
126 | case 'X': | |
127 | case 'f': | |
128 | case 'e': | |
129 | case 'E': | |
130 | case 'g': | |
131 | case 'G': | |
132 | case 'c': | |
133 | case 'C': | |
134 | case 's': | |
135 | case 'S': | |
136 | case 'p': | |
137 | case 'n': | |
138 | case 'r': | |
139 | goto done; | |
140 | default: | |
141 | return(-1); | |
142 | } | |
143 | CORD_next(source); | |
144 | } | |
145 | return(-1); | |
146 | done: | |
147 | if (saw_number) { | |
148 | if (saw_period) { | |
149 | *prec = current_number; | |
150 | } else { | |
151 | *prec = NONE; | |
152 | *width = current_number; | |
153 | } | |
154 | } else { | |
155 | *prec = NONE; | |
156 | } | |
157 | buf[chars_so_far] = '\0'; | |
158 | return(result); | |
159 | } | |
160 | ||
161 | int CORD_vsprintf(CORD * out, CORD format, va_list args) | |
162 | { | |
163 | CORD_ec result; | |
164 | register int count; | |
165 | register char current; | |
166 | CORD_pos pos; | |
167 | char conv_spec[CONV_SPEC_LEN + 1]; | |
168 | ||
169 | CORD_ec_init(result); | |
170 | for (CORD_set_pos(pos, format, 0); CORD_pos_valid(pos); CORD_next(pos)) { | |
171 | current = CORD_pos_fetch(pos); | |
172 | if (current == '%') { | |
173 | CORD_next(pos); | |
174 | if (!CORD_pos_valid(pos)) return(-1); | |
175 | current = CORD_pos_fetch(pos); | |
176 | if (current == '%') { | |
177 | CORD_ec_append(result, current); | |
178 | } else { | |
179 | int width, prec; | |
180 | int left_adj = 0; | |
181 | int long_arg = 0; | |
182 | CORD arg; | |
183 | size_t len; | |
184 | ||
185 | if (extract_conv_spec(pos, conv_spec, | |
186 | &width, &prec, | |
187 | &left_adj, &long_arg) < 0) { | |
188 | return(-1); | |
189 | } | |
190 | current = CORD_pos_fetch(pos); | |
191 | switch(current) { | |
192 | case 'n': | |
193 | /* Assign length to next arg */ | |
194 | if (long_arg == 0) { | |
195 | int * pos_ptr; | |
196 | pos_ptr = va_arg(args, int *); | |
197 | *pos_ptr = ec_len(result); | |
198 | } else if (long_arg > 0) { | |
199 | long * pos_ptr; | |
200 | pos_ptr = va_arg(args, long *); | |
201 | *pos_ptr = ec_len(result); | |
202 | } else { | |
203 | short * pos_ptr; | |
204 | pos_ptr = va_arg(args, short *); | |
205 | *pos_ptr = ec_len(result); | |
206 | } | |
207 | goto done; | |
208 | case 'r': | |
209 | /* Append cord and any padding */ | |
210 | if (width == VARIABLE) width = va_arg(args, int); | |
211 | if (prec == VARIABLE) prec = va_arg(args, int); | |
212 | arg = va_arg(args, CORD); | |
213 | len = CORD_len(arg); | |
214 | if (prec != NONE && len > prec) { | |
215 | if (prec < 0) return(-1); | |
216 | arg = CORD_substr(arg, 0, prec); | |
217 | len = prec; | |
218 | } | |
219 | if (width != NONE && len < width) { | |
220 | char * blanks = GC_MALLOC_ATOMIC(width-len+1); | |
221 | ||
222 | memset(blanks, ' ', width-len); | |
223 | blanks[width-len] = '\0'; | |
224 | if (left_adj) { | |
225 | arg = CORD_cat(arg, blanks); | |
226 | } else { | |
227 | arg = CORD_cat(blanks, arg); | |
228 | } | |
229 | } | |
230 | CORD_ec_append_cord(result, arg); | |
231 | goto done; | |
232 | case 'c': | |
233 | if (width == NONE && prec == NONE) { | |
56ba54b4 | 234 | register char c; |
73ffefd0 | 235 | |
30c3de1f | 236 | c = (char)va_arg(args, int); |
73ffefd0 TT |
237 | CORD_ec_append(result, c); |
238 | goto done; | |
239 | } | |
240 | break; | |
241 | case 's': | |
242 | if (width == NONE && prec == NONE) { | |
243 | char * str = va_arg(args, char *); | |
244 | register char c; | |
245 | ||
54f28c21 | 246 | while ((c = *str++)) { |
73ffefd0 TT |
247 | CORD_ec_append(result, c); |
248 | } | |
249 | goto done; | |
250 | } | |
251 | break; | |
252 | default: | |
253 | break; | |
254 | } | |
255 | /* Use standard sprintf to perform conversion */ | |
256 | { | |
257 | register char * buf; | |
30c3de1f | 258 | va_list vsprintf_args; |
73ffefd0 TT |
259 | int max_size = 0; |
260 | int res; | |
30c3de1f JS |
261 | # ifdef __va_copy |
262 | __va_copy(vsprintf_args, args); | |
263 | # else | |
4109fe85 | 264 | # if defined(__GNUC__) && !defined(__DJGPP__) /* and probably in other cases */ |
30c3de1f JS |
265 | va_copy(vsprintf_args, args); |
266 | # else | |
267 | vsprintf_args = args; | |
268 | # endif | |
269 | # endif | |
73ffefd0 TT |
270 | if (width == VARIABLE) width = va_arg(args, int); |
271 | if (prec == VARIABLE) prec = va_arg(args, int); | |
272 | if (width != NONE) max_size = width; | |
273 | if (prec != NONE && prec > max_size) max_size = prec; | |
274 | max_size += CONV_RESULT_LEN; | |
275 | if (max_size >= CORD_BUFSZ) { | |
276 | buf = GC_MALLOC_ATOMIC(max_size + 1); | |
277 | } else { | |
278 | if (CORD_BUFSZ - (result[0].ec_bufptr-result[0].ec_buf) | |
279 | < max_size) { | |
280 | CORD_ec_flush_buf(result); | |
281 | } | |
282 | buf = result[0].ec_bufptr; | |
283 | } | |
284 | switch(current) { | |
285 | case 'd': | |
286 | case 'i': | |
287 | case 'o': | |
288 | case 'u': | |
289 | case 'x': | |
290 | case 'X': | |
291 | case 'c': | |
292 | if (long_arg <= 0) { | |
293 | (void) va_arg(args, int); | |
294 | } else if (long_arg > 0) { | |
295 | (void) va_arg(args, long); | |
296 | } | |
297 | break; | |
298 | case 's': | |
299 | case 'p': | |
300 | (void) va_arg(args, char *); | |
301 | break; | |
302 | case 'f': | |
303 | case 'e': | |
304 | case 'E': | |
305 | case 'g': | |
306 | case 'G': | |
307 | (void) va_arg(args, double); | |
308 | break; | |
309 | default: | |
310 | return(-1); | |
311 | } | |
312 | res = vsprintf(buf, conv_spec, vsprintf_args); | |
313 | len = (size_t)res; | |
314 | if ((char *)(GC_word)res == buf) { | |
315 | /* old style vsprintf */ | |
316 | len = strlen(buf); | |
317 | } else if (res < 0) { | |
318 | return(-1); | |
319 | } | |
320 | if (buf != result[0].ec_bufptr) { | |
321 | register char c; | |
322 | ||
54f28c21 | 323 | while ((c = *buf++)) { |
73ffefd0 TT |
324 | CORD_ec_append(result, c); |
325 | } | |
326 | } else { | |
327 | result[0].ec_bufptr = buf + len; | |
328 | } | |
329 | } | |
330 | done:; | |
331 | } | |
332 | } else { | |
333 | CORD_ec_append(result, current); | |
334 | } | |
335 | } | |
336 | count = ec_len(result); | |
337 | *out = CORD_balance(CORD_ec_to_cord(result)); | |
338 | return(count); | |
339 | } | |
340 | ||
341 | int CORD_sprintf(CORD * out, CORD format, ...) | |
342 | { | |
343 | va_list args; | |
344 | int result; | |
345 | ||
346 | va_start(args, format); | |
347 | result = CORD_vsprintf(out, format, args); | |
348 | va_end(args); | |
349 | return(result); | |
350 | } | |
351 | ||
352 | int CORD_fprintf(FILE * f, CORD format, ...) | |
353 | { | |
354 | va_list args; | |
355 | int result; | |
356 | CORD out; | |
357 | ||
358 | va_start(args, format); | |
359 | result = CORD_vsprintf(&out, format, args); | |
360 | va_end(args); | |
361 | if (result > 0) CORD_put(out, f); | |
362 | return(result); | |
363 | } | |
364 | ||
365 | int CORD_vfprintf(FILE * f, CORD format, va_list args) | |
366 | { | |
367 | int result; | |
368 | CORD out; | |
369 | ||
370 | result = CORD_vsprintf(&out, format, args); | |
371 | if (result > 0) CORD_put(out, f); | |
372 | return(result); | |
373 | } | |
374 | ||
375 | int CORD_printf(CORD format, ...) | |
376 | { | |
377 | va_list args; | |
378 | int result; | |
379 | CORD out; | |
380 | ||
381 | va_start(args, format); | |
382 | result = CORD_vsprintf(&out, format, args); | |
383 | va_end(args); | |
384 | if (result > 0) CORD_put(out, stdout); | |
385 | return(result); | |
386 | } | |
387 | ||
388 | int CORD_vprintf(CORD format, va_list args) | |
389 | { | |
390 | int result; | |
391 | CORD out; | |
392 | ||
393 | result = CORD_vsprintf(&out, format, args); | |
394 | if (result > 0) CORD_put(out, stdout); | |
395 | return(result); | |
396 | } |