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 (21 years, 4 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

# Content
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 i = va_arg(args, int);
336 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 u = va_arg(args, int);
386 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 }