ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libukcprog/src/formf.c
Revision: 1.4
Committed: Thu Aug 21 21:53:42 2003 UTC (20 years, 8 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBUKCPROG_1_0_2, LIBUKCPROG_1_0_1, HEAD
Changes since 1.3: +1 -0 lines
Log Message:
Further to bug #8 - restore errno at the end of the function.

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 tdb 1.4 errno=saved_errno;
270 tdb 1.1 return buf; /* end of format string */
271     }
272    
273     /* flags, in any order */
274     for (;;) {
275     if (*++fmt == '\0')
276     panic("confused format flags in formf");
277    
278     if (*fmt == '-')
279     left_justify = TRUE;
280    
281     else if (*fmt == '+')
282     print_sign = TRUE;
283    
284     else if (*fmt == '0')
285     zero_pad = TRUE;
286    
287     else if (*fmt == '#')
288     alternate_form = TRUE;
289    
290     else
291     break; /* end of flags */
292    
293     }
294    
295     /* minimum field width */
296     if (*fmt == '*') {
297     min_field_width = va_arg(args, int);
298     ++fmt;
299     } else
300     while (isdigit(*fmt)) {
301     min_field_width *= 10;
302     min_field_width += *fmt - '0';
303     ++fmt;
304     }
305    
306     if (*fmt == '.') {
307    
308     /* precision */
309     if (*++fmt == '*') {
310     precision_given = TRUE;
311     precision = va_arg(args, int);
312     ++fmt;
313     } else if (isdigit(*fmt)) {
314     precision_given = TRUE;
315     precision = 0;
316     do {
317     precision *= 10;
318     precision += *fmt - '0';
319     ++fmt;
320     } while (isdigit(*fmt));
321     }
322     }
323    
324     if (strchr("hlL", *fmt) != NULL)
325     modifier = *fmt++;
326    
327     want_uppercase = TRUE;
328     alternate_prefix = "0X";
329     alternate_size = 2;
330     is_negative = FALSE;
331    
332     switch (*fmt) {
333     case 'd':
334     case 'i':
335     if (modifier == 'h')
336 tdb 1.2 i = va_arg(args, int);
337 tdb 1.1 else if (modifier == 'l')
338     i = va_arg(args, long);
339     else
340     i = va_arg(args, int);
341     is_negative = i < 0;
342     u = is_negative ? -i : i;
343     s = long_to_ascii(u, 10, want_uppercase);
344     break;
345    
346     case 'o':
347     base = 8;
348     alternate_prefix = "0";
349     alternate_size = 1;
350     goto gencase;
351     case 'p':
352     alternate_prefix = "0x";
353     alternate_size = 2;
354     want_uppercase = FALSE;
355     base = 16;
356     print_sign = FALSE;
357     u = (unsigned long)va_arg(args, char *);
358     s = long_to_ascii(u, base, want_uppercase);
359     #ifdef MSDOS
360     /* The traditional format for pointers on
361     * MSDOS is segment:offset. NOTE: we depend
362     * on long_to_ascii() returning a buffer with
363     * space before the beginning.
364     */
365     if ((len = strlen(s)) <= 8) {
366     s -= 8 - len;
367     memset(s, '0', 8 - len);
368    
369     --s;
370     memmove(s, s + 1, 4);
371     s[4] = ':';
372     }
373     #endif
374     break;
375     case 'x':
376     alternate_prefix = "0x";
377     alternate_size = 2;
378     want_uppercase = FALSE;
379     /* fall through */;
380     case 'X':
381     base = 16;
382     goto gencase;
383     case 'u':
384     base = 10;
385     gencase: if (modifier == 'h')
386 tdb 1.2 u = va_arg(args, int);
387 tdb 1.1 else if (modifier == 'l')
388     u = va_arg(args, long);
389     else
390     u = va_arg(args, int);
391     s = long_to_ascii(u, base, want_uppercase);
392     break;
393    
394     case 'c':
395     str[0] = (char)va_arg(args, int); /* promoted char */
396     str[1] = '\0';
397     s = str;
398     print_sign = FALSE;
399     break;
400    
401     case '%':
402     str[0] = '%';
403     str[1] = '\0';
404     s = str;
405     print_sign = FALSE;
406     break;
407    
408     case 's':
409     s = va_arg(args, char *);
410     if (s == NULL)
411     panic("null pointer for %s in formf");
412     print_sign = FALSE;
413     break;
414     case 'm':
415 tdb 1.3 errno = 0;
416     s = strerror(saved_errno);
417     if (errno == EINVAL) {
418 tdb 1.1 sprintf(errno_buffer,
419     "errno %d out of range",
420     saved_errno);
421     s = errno_buffer;
422 tdb 1.3 }
423 tdb 1.1 print_sign = FALSE;
424     break;
425     #ifdef VMS
426     case 'M':
427     s = vms_error_text(va_arg(args, unsigned));
428     print_sign = FALSE;
429     break;
430     #endif /* VMS */
431     case 'f':
432     d = va_arg(args, double);
433     is_negative = d < 0.0;
434     if (is_negative)
435     d = -d;
436     if (!precision_given)
437     precision = 6; /* from K&R */
438     s = float_to_ascii(d, precision);
439     break;
440     case 'e':
441     want_uppercase = FALSE; /* fall through */
442     case 'E':
443     d = va_arg(args, double);
444     is_negative = d < 0.0;
445     if (is_negative)
446     d = -d;
447     if (!precision_given)
448     precision = 6; /* from K&R */
449     s = efloat_to_ascii(d, precision,
450     want_uppercase);
451     break;
452     case 'g':
453     want_uppercase = FALSE; /* fall through */
454     case 'G':
455     d = va_arg(args, double);
456     is_negative = d < 0.0;
457     if (is_negative)
458     d = -d;
459     if (!precision_given)
460     precision = 6; /* from K&R */
461     s = gfloat_to_ascii(d, precision,
462     want_uppercase);
463     break;
464     case 'n':
465     ip = va_arg(args, int *);
466     *ip = pos;
467    
468     ++fmt; /* step over format character */
469     continue;
470    
471     default:
472     panic("illegal format in formf");
473     s = NULL; /* to satisfy gcc */
474     break;
475     }
476    
477     len = strlen(s);
478    
479     /* truncate strings if requested */
480     if ((*fmt == 's' || *fmt == 'm')
481     && precision_given && precision < len)
482     len = precision;
483    
484     if (!left_justify) {
485     const char *fillch_str = zero_pad ? "0" : " ";
486    
487     if ((is_negative || print_sign) && zero_pad) {
488     concat(&buf, &pos, &lim, &from_malloc,
489     is_negative ? "-" : "+", 1);
490     is_negative = print_sign = FALSE;
491     --min_field_width;
492     }
493    
494     if (alternate_form && alternate_prefix != NULL) {
495     concat(&buf, &pos, &lim, &from_malloc,
496     alternate_prefix, alternate_size);
497     min_field_width -= alternate_size;
498     alternate_form = FALSE;
499     }
500    
501     while (len < min_field_width) {
502     concat(&buf, &pos, &lim, &from_malloc,
503     fillch_str, 1);
504     --min_field_width;
505     }
506     }
507    
508     if (is_negative || print_sign) {
509     concat(&buf, &pos, &lim, &from_malloc,
510     is_negative ? "-" : "+", 1);
511     --min_field_width;
512     }
513    
514     if (alternate_form && alternate_prefix != NULL) {
515     concat(&buf, &pos, &lim, &from_malloc,
516     alternate_prefix, alternate_size);
517     min_field_width -= alternate_size;
518     }
519    
520    
521     concat(&buf, &pos, &lim, &from_malloc, s, len);
522     min_field_width -= len;
523    
524     if (left_justify) {
525     while (min_field_width > 0) {
526     concat(&buf, &pos, &lim, &from_malloc, " ", 1);
527     --min_field_width;
528     }
529     }
530    
531     ++fmt; /* step over format character */
532     }
533     }