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 
ec_len(CORD_ec x)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.						*/
extract_conv_spec(CORD_pos source,char * buf,int * width,int * prec,int * left,int * long_arg)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;
62     register int saw_number = 0;
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 
CORD_vsprintf(CORD * out,CORD format,va_list args)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) {
234 			    register char c;
235 
236 			    c = (char)va_arg(args, int);
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 
246 			    while ((c = *str++)) {
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;
258             	    va_list vsprintf_args;
259             	    int max_size = 0;
260             	    int res;
261 #		    ifdef __va_copy
262                       __va_copy(vsprintf_args, args);
263 #		    else
264 #		      if defined(__GNUC__) && !defined(__DJGPP__) /* and probably in other cases */
265                         va_copy(vsprintf_args, args);
266 #		      else
267 			vsprintf_args = args;
268 #		      endif
269 #		    endif
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 
323 			while ((c = *buf++)) {
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 
CORD_sprintf(CORD * out,CORD format,...)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 
CORD_fprintf(FILE * f,CORD format,...)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 
CORD_vfprintf(FILE * f,CORD format,va_list args)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 
CORD_printf(CORD format,...)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 
CORD_vprintf(CORD format,va_list args)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 }
397