FreeCalypso > hg > ffs-editor
comparison src/libsys/sprintf/vspcore.c @ 0:92470e5d0b9e
src: partial import from FC Selenite
| author | Mychaela Falconia <falcon@freecalypso.org> |
|---|---|
| date | Fri, 15 May 2020 01:28:16 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:92470e5d0b9e |
|---|---|
| 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 } |
