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 |
tdb |
1.2 |
char ukcprog_formf_sccsid[] = "$Id: formf.c,v 1.1 2002/03/08 14:37:29 tdb Exp $ UKC"; |
11 |
tdb |
1.1 |
|
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, short); |
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, short); |
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 |
|
|
} |