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