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