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