ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libukcprog/src/formf.c
Revision: 1.2
Committed: Thu Aug 21 21:27:39 2003 UTC (20 years, 8 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.1: +2 -2 lines
Log Message:
Fix bug #9 using patch given by Adam Sampson.
"handle shorts correctly in varargs"

File Contents

# User Rev Content
1 tdb 1.1 /* formf.c -- formatted strings and error messages */
2    
3     /* Copyright 1992 Godfrey Paul, University of Kent at Canterbury.
4     *
5     * You can do what you like with this source code as long as
6     * you don't try to make money out of it and you include an
7     * unaltered copy of this message (including the copyright).
8     */
9    
10     char ukcprog_formf_sccsid[] = "$Id: formf.c,v 1.25 1999/07/15 11:10:21 djb1 Exp $ UKC";
11    
12     #ifdef __STDC__
13     #include <stdarg.h>
14     #else
15     #include <varargs.h>
16     #endif
17    
18     #include <stdio.h>
19     #include <ctype.h>
20     #include <errno.h>
21     #include <stdlib.h>
22     #include <string.h>
23     #ifndef __STDC__
24     #include <memory.h>
25     #endif
26    
27     #include "ukcprog.h"
28    
29     #ifndef MSDOS
30     #ifndef __FreeBSD__
31     #ifndef linux
32     extern int sys_nerr, errno;
33     extern char *sys_errlist[];
34     #endif /* !linux */
35     #endif /* !__FreeBSD__ */
36     #endif /* !MSDOS */
37    
38    
39     /* forward declarations */
40     static void new_buffer PROTO((char ** p_buf, int *p_lim, bool *p_from_malloc));
41     static void concat PROTO((char **p_buf, int *p_pos, int* p_lim,
42     bool *p_from_malloc, const char *str, int len));
43     static char *long_to_ascii PROTO((unsigned long unum, int base,
44     bool want_uppercase));
45     static char *float_to_ascii PROTO((double d, int precision));
46     static char *efloat_to_ascii PROTO((double d, int precision,
47     bool want_uppercase));
48     static char *gfloat_to_ascii PROTO((double d, int precision,
49     bool want_uppercase));
50    
51     #define MAX_NUM_LEN 40 /* buffer sizes used in numeric conversions */
52     #define MAX_FLOAT_LEN 128
53    
54    
55     /*
56     * new_buffer()
57     * Reallocate the given buffer to twice its current size. *p_lim is the
58     * last usable character in the buffer. The buffer passed in might
59     * not have been allocated by malloc(); this is indicated by *p_from_malloc.
60     */
61     static void
62     new_buffer(p_buf, p_lim, p_from_malloc)
63     char **p_buf;
64     int *p_lim;
65     bool *p_from_malloc;
66     {
67     char *new;
68     size_t size;
69    
70     size = *p_lim + 1;
71    
72     if (*p_from_malloc)
73     new = realloc(*p_buf, size * 2);
74    
75     else {
76     if ((new = malloc(size * 2)) != NULL)
77     memcpy(new, *p_buf, size);
78     *p_from_malloc = TRUE;
79     }
80    
81     if (new == NULL)
82     panic("malloc returned NULL in new_buffer");
83    
84     *p_buf = new;
85     *p_lim = (size * 2) - 1;
86     }
87    
88    
89     /*
90     * concat()
91     * Add exactly len bytes of str to the buffer, expanding it with
92     * new_buffer() if we need extra space. Str must be at least len
93     * bytes long.
94     */
95     static void
96     concat(p_buf, p_pos, p_lim, p_from_malloc, str, len)
97     char **p_buf;
98     int *p_pos, *p_lim;
99     bool *p_from_malloc;
100     const char *str;
101     int len;
102     {
103     while (*p_lim - *p_pos < len)
104     new_buffer(p_buf, p_lim, p_from_malloc);
105    
106     memcpy(&(*p_buf)[*p_pos], str, (size_t)len);
107     *p_pos += len;
108     }
109    
110    
111     static char *
112     long_to_ascii(unum, base, want_uppercase)
113     unsigned long unum;
114     int base;
115     bool want_uppercase;
116     {
117     static char nbuf[MAX_NUM_LEN + 1];
118     char *s;
119     const char *digs;
120    
121     s = nbuf + MAX_NUM_LEN;
122    
123     digs = want_uppercase ? "0123456789ABCDEF" : "0123456789abcdef";
124     do {
125     *--s = digs[unum % base];
126     unum /= base;
127     } while (unum != 0);
128    
129    
130     return s;
131     }
132    
133    
134     static char *
135     float_to_ascii(d, precision)
136     double d;
137     int precision;
138     {
139     static char buf[MAX_FLOAT_LEN];
140    
141     sprintf(buf, "%.*f", precision, d);
142     return buf;
143     }
144    
145    
146     static char *
147     efloat_to_ascii(d, precision, want_uppercase)
148     double d;
149     int precision;
150     bool want_uppercase;
151     {
152     static char buf[MAX_FLOAT_LEN];
153     const char *format;
154    
155     if (want_uppercase)
156     format = "%.*E";
157     else
158     format = "%.*e";
159    
160     sprintf(buf, format, precision, d);
161     return buf;
162     }
163    
164    
165    
166     static char *
167     gfloat_to_ascii(d, precision, want_uppercase)
168     double d;
169     int precision;
170     bool want_uppercase;
171     {
172     static char buf[MAX_FLOAT_LEN];
173     const char *format;
174    
175     if (want_uppercase)
176     format = "%.*G";
177     else
178     format = "%.*g";
179    
180     sprintf(buf, format, precision, d);
181     return buf;
182     }
183    
184     #ifdef VMS
185     static const char *
186     vms_error_text(unsigned long msgid)
187     {
188     unsigned long status;
189     unsigned short msglen;
190     static char buf[256 + 1];
191     static struct dsc$descriptor_s msg = {
192     sizeof buf - 1, /* length */
193     DSC$K_DTYPE_T, /* data type (8 bit chars) */
194     DSC$K_CLASS_S, /* class (fixed length) */
195     buf
196     };
197    
198     status = SYS$GETMSG(msgid, &msglen, &msg, 0, 0);
199     if (status != SS$_NORMAL && status != SS$_MSGNOTFND)
200     sprintf(buf, "Unknown VMS message ID 0x%x", msgid);
201     else
202     buf[msglen] = '\0';
203    
204     return buf;
205     }
206     #endif /* VMS */
207    
208     /*
209     * formf()
210     * Returns a pointer to a string formatted from the arguments given.
211     * If the string has been obtained from malloc(), the return value will
212     * be different to the buffer pased in; it is the caller's responsibility
213     * to free() this when no longer required.
214     */
215     char *
216     formf(buffer_space, buffer_size, format, args)
217     char *buffer_space;
218     int buffer_size;
219     const char *format;
220     va_list args;
221     {
222     bool left_justify, print_sign, zero_pad, alternate_form;
223     bool is_negative, precision_given, want_uppercase, from_malloc;
224     int min_field_width, precision, modifier, base, pos, lim, len;
225     const char *fmt, *alternate_prefix, *s;
226     char *buf, str[2], errno_buffer[42];
227     int alternate_size, saved_errno;
228     unsigned long u;
229     long i;
230     int *ip;
231     double d;
232    
233     saved_errno = errno; /* for use later in %m format */
234    
235     /*
236     * The formatted string is prepared in a buffer pointed to by buf.
237     * This starts out pointing to a buffer passed in as an argument,
238     * but if this fills, a new one is obtained from malloc().
239     * Pos is the index of the position for the *next* character
240     * in the output string, and lim is the index of the last
241     * usable character.
242     */
243     from_malloc = FALSE;
244     buf = buffer_space;
245     lim = buffer_size - 1;
246     pos = 0;
247     fmt = format;
248    
249     for (;;) {
250     min_field_width = 0;
251     precision_given = FALSE;
252     precision = 0;
253     modifier = 0;
254     left_justify = FALSE;
255     print_sign = FALSE;
256     zero_pad = FALSE;
257     alternate_form = FALSE;
258    
259     /* Find next argument for conversion */
260     while (*fmt != '\0' && *fmt != '%') {
261     if (pos == lim)
262     new_buffer(&buf, &lim, &from_malloc);
263    
264     buf[pos++] = *fmt++;
265     }
266    
267     if (*fmt == '\0') {
268     buf[pos] = '\0';
269     return buf; /* end of format string */
270     }
271    
272     /* flags, in any order */
273     for (;;) {
274     if (*++fmt == '\0')
275     panic("confused format flags in formf");
276    
277     if (*fmt == '-')
278     left_justify = TRUE;
279    
280     else if (*fmt == '+')
281     print_sign = TRUE;
282    
283     else if (*fmt == '0')
284     zero_pad = TRUE;
285    
286     else if (*fmt == '#')
287     alternate_form = TRUE;
288    
289     else
290     break; /* end of flags */
291    
292     }
293    
294     /* minimum field width */
295     if (*fmt == '*') {
296     min_field_width = va_arg(args, int);
297     ++fmt;
298     } else
299     while (isdigit(*fmt)) {
300     min_field_width *= 10;
301     min_field_width += *fmt - '0';
302     ++fmt;
303     }
304    
305     if (*fmt == '.') {
306    
307     /* precision */
308     if (*++fmt == '*') {
309     precision_given = TRUE;
310     precision = va_arg(args, int);
311     ++fmt;
312     } else if (isdigit(*fmt)) {
313     precision_given = TRUE;
314     precision = 0;
315     do {
316     precision *= 10;
317     precision += *fmt - '0';
318     ++fmt;
319     } while (isdigit(*fmt));
320     }
321     }
322    
323     if (strchr("hlL", *fmt) != NULL)
324     modifier = *fmt++;
325    
326     want_uppercase = TRUE;
327     alternate_prefix = "0X";
328     alternate_size = 2;
329     is_negative = FALSE;
330    
331     switch (*fmt) {
332     case 'd':
333     case 'i':
334     if (modifier == 'h')
335 tdb 1.2 i = va_arg(args, int);
336 tdb 1.1 else if (modifier == 'l')
337     i = va_arg(args, long);
338     else
339     i = va_arg(args, int);
340     is_negative = i < 0;
341     u = is_negative ? -i : i;
342     s = long_to_ascii(u, 10, want_uppercase);
343     break;
344    
345     case 'o':
346     base = 8;
347     alternate_prefix = "0";
348     alternate_size = 1;
349     goto gencase;
350     case 'p':
351     alternate_prefix = "0x";
352     alternate_size = 2;
353     want_uppercase = FALSE;
354     base = 16;
355     print_sign = FALSE;
356     u = (unsigned long)va_arg(args, char *);
357     s = long_to_ascii(u, base, want_uppercase);
358     #ifdef MSDOS
359     /* The traditional format for pointers on
360     * MSDOS is segment:offset. NOTE: we depend
361     * on long_to_ascii() returning a buffer with
362     * space before the beginning.
363     */
364     if ((len = strlen(s)) <= 8) {
365     s -= 8 - len;
366     memset(s, '0', 8 - len);
367    
368     --s;
369     memmove(s, s + 1, 4);
370     s[4] = ':';
371     }
372     #endif
373     break;
374     case 'x':
375     alternate_prefix = "0x";
376     alternate_size = 2;
377     want_uppercase = FALSE;
378     /* fall through */;
379     case 'X':
380     base = 16;
381     goto gencase;
382     case 'u':
383     base = 10;
384     gencase: if (modifier == 'h')
385 tdb 1.2 u = va_arg(args, int);
386 tdb 1.1 else if (modifier == 'l')
387     u = va_arg(args, long);
388     else
389     u = va_arg(args, int);
390     s = long_to_ascii(u, base, want_uppercase);
391     break;
392    
393     case 'c':
394     str[0] = (char)va_arg(args, int); /* promoted char */
395     str[1] = '\0';
396     s = str;
397     print_sign = FALSE;
398     break;
399    
400     case '%':
401     str[0] = '%';
402     str[1] = '\0';
403     s = str;
404     print_sign = FALSE;
405     break;
406    
407     case 's':
408     s = va_arg(args, char *);
409     if (s == NULL)
410     panic("null pointer for %s in formf");
411     print_sign = FALSE;
412     break;
413     case 'm':
414     if (saved_errno < 0 || saved_errno > sys_nerr ||
415     *sys_errlist[saved_errno] == '\0') {
416     sprintf(errno_buffer,
417     "errno %d out of range",
418     saved_errno);
419     s = errno_buffer;
420     } else
421     s = sys_errlist[saved_errno];
422     print_sign = FALSE;
423     break;
424     #ifdef VMS
425     case 'M':
426     s = vms_error_text(va_arg(args, unsigned));
427     print_sign = FALSE;
428     break;
429     #endif /* VMS */
430     case 'f':
431     d = va_arg(args, double);
432     is_negative = d < 0.0;
433     if (is_negative)
434     d = -d;
435     if (!precision_given)
436     precision = 6; /* from K&R */
437     s = float_to_ascii(d, precision);
438     break;
439     case 'e':
440     want_uppercase = FALSE; /* fall through */
441     case 'E':
442     d = va_arg(args, double);
443     is_negative = d < 0.0;
444     if (is_negative)
445     d = -d;
446     if (!precision_given)
447     precision = 6; /* from K&R */
448     s = efloat_to_ascii(d, precision,
449     want_uppercase);
450     break;
451     case 'g':
452     want_uppercase = FALSE; /* fall through */
453     case 'G':
454     d = va_arg(args, double);
455     is_negative = d < 0.0;
456     if (is_negative)
457     d = -d;
458     if (!precision_given)
459     precision = 6; /* from K&R */
460     s = gfloat_to_ascii(d, precision,
461     want_uppercase);
462     break;
463     case 'n':
464     ip = va_arg(args, int *);
465     *ip = pos;
466    
467     ++fmt; /* step over format character */
468     continue;
469    
470     default:
471     panic("illegal format in formf");
472     s = NULL; /* to satisfy gcc */
473     break;
474     }
475    
476     len = strlen(s);
477    
478     /* truncate strings if requested */
479     if ((*fmt == 's' || *fmt == 'm')
480     && precision_given && precision < len)
481     len = precision;
482    
483     if (!left_justify) {
484     const char *fillch_str = zero_pad ? "0" : " ";
485    
486     if ((is_negative || print_sign) && zero_pad) {
487     concat(&buf, &pos, &lim, &from_malloc,
488     is_negative ? "-" : "+", 1);
489     is_negative = print_sign = FALSE;
490     --min_field_width;
491     }
492    
493     if (alternate_form && alternate_prefix != NULL) {
494     concat(&buf, &pos, &lim, &from_malloc,
495     alternate_prefix, alternate_size);
496     min_field_width -= alternate_size;
497     alternate_form = FALSE;
498     }
499    
500     while (len < min_field_width) {
501     concat(&buf, &pos, &lim, &from_malloc,
502     fillch_str, 1);
503     --min_field_width;
504     }
505     }
506    
507     if (is_negative || print_sign) {
508     concat(&buf, &pos, &lim, &from_malloc,
509     is_negative ? "-" : "+", 1);
510     --min_field_width;
511     }
512    
513     if (alternate_form && alternate_prefix != NULL) {
514     concat(&buf, &pos, &lim, &from_malloc,
515     alternate_prefix, alternate_size);
516     min_field_width -= alternate_size;
517     }
518    
519    
520     concat(&buf, &pos, &lim, &from_malloc, s, len);
521     min_field_width -= len;
522    
523     if (left_justify) {
524     while (min_field_width > 0) {
525     concat(&buf, &pos, &lim, &from_malloc, " ", 1);
526     --min_field_width;
527     }
528     }
529    
530     ++fmt; /* step over format character */
531     }
532     }