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

# 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 errno=saved_errno;
270 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 i = va_arg(args, int);
337 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 u = va_arg(args, int);
387 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 errno = 0;
416 s = strerror(saved_errno);
417 if (errno == EINVAL) {
418 sprintf(errno_buffer,
419 "errno %d out of range",
420 saved_errno);
421 s = errno_buffer;
422 }
423 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 }