comparison src/libsys/sprintf/vspcore.c @ 86:425ab6d987f3

src/libsys: pieced together from Citrine
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 20 Jul 2018 20:36:19 +0000
parents
children
comparison
equal deleted inserted replaced
85:4ef6808ea50e 86:425ab6d987f3
1 /*
2 * Embedded [v]sprintf() implementation by Mychaela Falconia,
3 * loosely based on the 4.3BSD-Tahoe version.
4 *
5 * This module contains the core of the vsprintf() function, which may
6 * be either used directly by user code or called by the sprintf()
7 * trivial wrapper.
8 */
9
10 #include <sys/types.h>
11 #include <ctype.h>
12 #include <stdarg.h>
13 #include "defs.h"
14
15 extern u_char * _sprintf_integer(u_char *op, int width, int flags, int sign,
16 unsigned number, int base, int prec);
17 extern u_char * _sprintf_percent_f(u_char *op, int width, int flags, int sign,
18 double number, int prec);
19
20 u_char *
21 _sprintf_field(u_char *op, int width, int flags, int sign,
22 u_char *body, int size, int dprec, int fpprec)
23 {
24 int fieldsz; /* field size expanded by sign, etc */
25 int realsz; /* field size expanded by decimal precision */
26 int n; /* scratch */
27
28 /*
29 * All reasonable formats wind up here. At this point,
30 * `body' points to a string which (if not flags&LADJUST)
31 * should be padded out to `width' places. If
32 * flags&ZEROPAD, it should first be prefixed by any
33 * sign or other prefix; otherwise, it should be blank
34 * padded before the prefix is emitted. After any
35 * left-hand padding and prefixing, emit zeroes
36 * required by a decimal [diouxX] precision, then print
37 * the string proper, then emit zeroes required by any
38 * leftover floating precision; finally, if LADJUST,
39 * pad with blanks.
40 */
41
42 /*
43 * compute actual size, so we know how much to pad
44 * fieldsz excludes decimal prec; realsz includes it
45 */
46 fieldsz = size + fpprec;
47 if (sign)
48 fieldsz++;
49 if (flags & HEXPREFIX)
50 fieldsz += 2;
51 realsz = dprec > fieldsz ? dprec : fieldsz;
52
53 /* right-adjusting blank padding */
54 if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
55 for (n = realsz; n < width; n++)
56 *op++ = ' ';
57 /* prefix */
58 if (sign)
59 *op++ = sign;
60 if (flags & HEXPREFIX) {
61 *op++ = '0';
62 *op++ = (flags & UPPERCASE) ? 'X' : 'x';
63 }
64 /* right-adjusting zero padding */
65 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
66 for (n = realsz; n < width; n++)
67 *op++ = '0';
68 /* leading zeroes from decimal precision */
69 for (n = fieldsz; n < dprec; n++)
70 *op++ = '0';
71
72 /* the string or number proper */
73 bcopy(body, op, size);
74 op += size;
75 /* trailing f.p. zeroes */
76 while (--fpprec >= 0)
77 *op++ = '0';
78 /* left-adjusting padding (always blank) */
79 if (flags & LADJUST)
80 for (n = realsz; n < width; n++)
81 *op++ = ' ';
82
83 return(op);
84 }
85
86 int
87 vsprintf(buf0, fmt0, argp)
88 u_char *buf0, *fmt0;
89 va_list argp;
90 {
91 u_char *op; /* output buffer working ptr */
92 u_char *fmt; /* format string working ptr */
93 int ch; /* character from fmt */
94 int n; /* scratch integer */
95 char *t; /* scratch pointer */
96 int flags; /* flags as above */
97 int prec; /* precision from format (%.3d), or -1 */
98 int width; /* width from format (%8d), or 0 */
99 char sign; /* sign prefix (' ', '+', '-', or \0) */
100 unsigned un; /* unsigned number for conversion */
101
102 op = buf0;
103 for (fmt = fmt0; ; ++fmt) {
104 for (; (ch = *fmt) && ch != '%'; ++fmt)
105 *op++ = ch;
106 if (!ch) {
107 out: *op = '\0';
108 return (op - buf0);
109 }
110
111 flags = 0; width = 0;
112 prec = -1;
113 sign = '\0';
114
115 rflag: switch (*++fmt) {
116 case ' ':
117 /*
118 * ``If the space and + flags both appear, the space
119 * flag will be ignored.''
120 * -- ANSI X3J11
121 */
122 if (!sign)
123 sign = ' ';
124 goto rflag;
125 case '#':
126 flags |= ALT;
127 goto rflag;
128 case '*':
129 /*
130 * ``A negative field width argument is taken as a
131 * - flag followed by a positive field width.''
132 * -- ANSI X3J11
133 * They don't exclude field widths read from args.
134 */
135 if ((width = va_arg(argp, int)) >= 0)
136 goto rflag;
137 width = -width;
138 /* FALLTHROUGH */
139 case '-':
140 flags |= LADJUST;
141 goto rflag;
142 case '+':
143 sign = '+';
144 goto rflag;
145 case '.':
146 if (*++fmt == '*')
147 n = va_arg(argp, int);
148 else {
149 n = 0;
150 while (isascii(*fmt) && isdigit(*fmt))
151 n = 10 * n + todigit(*fmt++);
152 --fmt;
153 }
154 prec = n < 0 ? -1 : n;
155 goto rflag;
156 case '0':
157 /*
158 * ``Note that 0 is taken as a flag, not as the
159 * beginning of a field width.''
160 * -- ANSI X3J11
161 */
162 flags |= ZEROPAD;
163 goto rflag;
164 case '1': case '2': case '3': case '4':
165 case '5': case '6': case '7': case '8': case '9':
166 n = 0;
167 do {
168 n = 10 * n + todigit(*fmt);
169 } while (isascii(*++fmt) && isdigit(*fmt));
170 width = n;
171 --fmt;
172 goto rflag;
173 case 'L':
174 flags |= LONGDBL;
175 goto rflag;
176 case 'h':
177 flags |= SHORTINT;
178 goto rflag;
179 case 'l':
180 flags |= LONGINT;
181 goto rflag;
182 case 'c':
183 /*
184 * XXX reusing a variable of type char
185 * for an unrelated purpose
186 */
187 sign = (char) va_arg(argp, int);
188 op = _sprintf_field(op, width, flags, 0, &sign, 1,
189 0, 0);
190 break;
191 case 'D':
192 flags |= LONGINT;
193 /*FALLTHROUGH*/
194 case 'd':
195 case 'i':
196 n = va_arg(argp, int);
197 if (n < 0) {
198 un = -n;
199 sign = '-';
200 } else
201 un = n;
202 op = _sprintf_integer(op, width, flags, sign, un, 10,
203 prec);
204 break;
205 case 'f':
206 op = _sprintf_percent_f(op, width, flags, sign,
207 va_arg(argp, double), prec);
208 break;
209 case 'n':
210 n = op - buf0;
211 if (flags & LONGINT)
212 *va_arg(argp, long *) = n;
213 else if (flags & SHORTINT)
214 *va_arg(argp, short *) = n;
215 else
216 *va_arg(argp, int *) = n;
217 break;
218 case 'O':
219 flags |= LONGINT;
220 /*FALLTHROUGH*/
221 case 'o':
222 un = va_arg(argp, unsigned);
223 op = _sprintf_integer(op, width, flags, 0, un, 8, prec);
224 break;
225 case 'p':
226 /*
227 * ``The argument shall be a pointer to void. The
228 * value of the pointer is converted to a sequence
229 * of printable characters, in an implementation-
230 * defined manner.''
231 * -- ANSI X3J11
232 */
233 /* NOSTRICT */
234 un = (unsigned)va_arg(argp, void *);
235 op = _sprintf_integer(op, width, flags | HEXPREFIX, 0,
236 un, 16, prec);
237 break;
238 case 's':
239 if (!(t = va_arg(argp, char *)))
240 t = "(null)";
241 if (prec >= 0) {
242 /*
243 * can't use strlen; can only look for the
244 * NUL in the first `prec' characters, and
245 * strlen() will go further.
246 */
247 char *p, *memchr();
248
249 if (p = memchr(t, 0, prec)) {
250 n = p - t;
251 if (n > prec)
252 n = prec;
253 } else
254 n = prec;
255 } else
256 n = strlen(t);
257 op = _sprintf_field(op, width, flags, 0, t, n, 0, 0);
258 break;
259 case 'U':
260 flags |= LONGINT;
261 /*FALLTHROUGH*/
262 case 'u':
263 un = va_arg(argp, unsigned);
264 op = _sprintf_integer(op, width, flags, 0, un, 10,
265 prec);
266 break;
267 case 'X':
268 flags |= UPPERCASE;
269 /* FALLTHROUGH */
270 case 'x':
271 un = va_arg(argp, unsigned);
272 /* leading 0x/X only if non-zero */
273 if (flags & ALT && un != 0)
274 flags |= HEXPREFIX;
275 op = _sprintf_integer(op, width, flags, 0, un, 16,
276 prec);
277 break;
278 case '\0': /* "%?" prints ?, unless ? is NULL */
279 goto out;
280 default:
281 *op++ = *fmt;
282 }
283 }
284 /* NOTREACHED */
285 }