667560f2592cbc0ab54ed89562956858f9fc2df1
[fw/sdcc] / support / gc / cord / cordprnt.c
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;
62     register int saw_number;
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) {
234                             register char c = va_arg(args, char);
235
236                             CORD_ec_append(result, c);
237                             goto done;
238                         }
239                         break;
240                     case 's':
241                         if (width == NONE && prec == NONE) {
242                             char * str = va_arg(args, char *);
243                             register char c;
244
245                             while (c = *str++) {
246                                 CORD_ec_append(result, c);
247                             }
248                             goto done;
249                         }
250                         break;
251                     default:
252                         break;
253                 }
254                 /* Use standard sprintf to perform conversion */
255                 {
256                     register char * buf;
257                     va_list vsprintf_args = args;
258                         /* The above does not appear to be sanctioned   */
259                         /* by the ANSI C standard.                      */
260                     int max_size = 0;
261                     int res;
262                         
263                     if (width == VARIABLE) width = va_arg(args, int);
264                     if (prec == VARIABLE) prec = va_arg(args, int);
265                     if (width != NONE) max_size = width;
266                     if (prec != NONE && prec > max_size) max_size = prec;
267                     max_size += CONV_RESULT_LEN;
268                     if (max_size >= CORD_BUFSZ) {
269                         buf = GC_MALLOC_ATOMIC(max_size + 1);
270                     } else {
271                         if (CORD_BUFSZ - (result[0].ec_bufptr-result[0].ec_buf)
272                             < max_size) {
273                             CORD_ec_flush_buf(result);
274                         }
275                         buf = result[0].ec_bufptr;
276                     }
277                     switch(current) {
278                         case 'd':
279                         case 'i':
280                         case 'o':
281                         case 'u':
282                         case 'x':
283                         case 'X':
284                         case 'c':
285                             if (long_arg <= 0) {
286                               (void) va_arg(args, int);
287                             } else if (long_arg > 0) {
288                               (void) va_arg(args, long);
289                             }
290                             break;
291                         case 's':
292                         case 'p':
293                             (void) va_arg(args, char *);
294                             break;
295                         case 'f':
296                         case 'e':
297                         case 'E':
298                         case 'g':
299                         case 'G':
300                             (void) va_arg(args, double);
301                             break;
302                         default:
303                             return(-1);
304                     }
305                     res = vsprintf(buf, conv_spec, vsprintf_args);
306                     len = (size_t)res;
307                     if ((char *)(GC_word)res == buf) {
308                         /* old style vsprintf */
309                         len = strlen(buf);
310                     } else if (res < 0) {
311                         return(-1);
312                     }
313                     if (buf != result[0].ec_bufptr) {
314                         register char c;
315
316                         while (c = *buf++) {
317                             CORD_ec_append(result, c);
318                         }
319                     } else {
320                         result[0].ec_bufptr = buf + len;
321                     }
322                 }
323               done:;
324             }
325         } else {
326             CORD_ec_append(result, current);
327         }
328     }
329     count = ec_len(result);
330     *out = CORD_balance(CORD_ec_to_cord(result));
331     return(count);
332 }
333
334 int CORD_sprintf(CORD * out, CORD format, ...)
335 {
336     va_list args;
337     int result;
338     
339     va_start(args, format);
340     result = CORD_vsprintf(out, format, args);
341     va_end(args);
342     return(result);
343 }
344
345 int CORD_fprintf(FILE * f, CORD format, ...)
346 {
347     va_list args;
348     int result;
349     CORD out;
350     
351     va_start(args, format);
352     result = CORD_vsprintf(&out, format, args);
353     va_end(args);
354     if (result > 0) CORD_put(out, f);
355     return(result);
356 }
357
358 int CORD_vfprintf(FILE * f, CORD format, va_list args)
359 {
360     int result;
361     CORD out;
362     
363     result = CORD_vsprintf(&out, format, args);
364     if (result > 0) CORD_put(out, f);
365     return(result);
366 }
367
368 int CORD_printf(CORD format, ...)
369 {
370     va_list args;
371     int result;
372     CORD out;
373     
374     va_start(args, format);
375     result = CORD_vsprintf(&out, format, args);
376     va_end(args);
377     if (result > 0) CORD_put(out, stdout);
378     return(result);
379 }
380
381 int CORD_vprintf(CORD format, va_list args)
382 {
383     int result;
384     CORD out;
385     
386     result = CORD_vsprintf(&out, format, args);
387     if (result > 0) CORD_put(out, stdout);
388     return(result);
389 }