1*388550b0Srillig /* $NetBSD: vsnprintf_ss.c,v 1.14 2022/04/19 20:32:16 rillig Exp $ */
2abca035cSchristos
3abca035cSchristos /*-
4abca035cSchristos * Copyright (c) 1990, 1993
5abca035cSchristos * The Regents of the University of California. All rights reserved.
6abca035cSchristos *
7abca035cSchristos * This code is derived from software contributed to Berkeley by
8abca035cSchristos * Chris Torek.
9abca035cSchristos *
10abca035cSchristos * Redistribution and use in source and binary forms, with or without
11abca035cSchristos * modification, are permitted provided that the following conditions
12abca035cSchristos * are met:
13abca035cSchristos * 1. Redistributions of source code must retain the above copyright
14abca035cSchristos * notice, this list of conditions and the following disclaimer.
15abca035cSchristos * 2. Redistributions in binary form must reproduce the above copyright
16abca035cSchristos * notice, this list of conditions and the following disclaimer in the
17abca035cSchristos * documentation and/or other materials provided with the distribution.
18abca035cSchristos * 3. Neither the name of the University nor the names of its contributors
19abca035cSchristos * may be used to endorse or promote products derived from this software
20abca035cSchristos * without specific prior written permission.
21abca035cSchristos *
22abca035cSchristos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23abca035cSchristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24abca035cSchristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25abca035cSchristos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26abca035cSchristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27abca035cSchristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28abca035cSchristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29abca035cSchristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30abca035cSchristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31abca035cSchristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32abca035cSchristos * SUCH DAMAGE.
33abca035cSchristos */
34abca035cSchristos
35abca035cSchristos #include <sys/cdefs.h>
36abca035cSchristos #if defined(LIBC_SCCS) && !defined(lint)
37abca035cSchristos #if 0
38abca035cSchristos static char sccsid[] = "@(#)vsnprintf.c 8.1 (Berkeley) 6/4/93";
39abca035cSchristos #else
40*388550b0Srillig __RCSID("$NetBSD: vsnprintf_ss.c,v 1.14 2022/04/19 20:32:16 rillig Exp $");
41abca035cSchristos #endif
42abca035cSchristos #endif /* LIBC_SCCS and not lint */
43abca035cSchristos
44abca035cSchristos #include "namespace.h"
45abca035cSchristos
46aa781c37Schristos #include <sys/types.h>
47aa781c37Schristos #include <inttypes.h>
48abca035cSchristos #include <assert.h>
49abca035cSchristos #include <stdio.h>
50aa781c37Schristos #include <errno.h>
51aa781c37Schristos #include <stdarg.h>
52aa781c37Schristos #include <string.h>
53abca035cSchristos #include "reentrant.h"
544ca73ce8Schristos #include "extern.h"
55abca035cSchristos #include "local.h"
56abca035cSchristos
57abca035cSchristos #ifdef __weak_alias
__weak_alias(vsnprintf_ss,_vsnprintf_ss)58abca035cSchristos __weak_alias(vsnprintf_ss,_vsnprintf_ss)
59abca035cSchristos #endif
60abca035cSchristos
61aa781c37Schristos /*
62aa781c37Schristos * vsnprintf_ss: scaled down version of printf(3).
63aa781c37Schristos *
64aa781c37Schristos * this version based on vfprintf() from libc which was derived from
65aa781c37Schristos * software contributed to Berkeley by Chris Torek.
66aa781c37Schristos *
67aa781c37Schristos */
68abca035cSchristos
69aa781c37Schristos /*
70aa781c37Schristos * macros for converting digits to letters and vice versa
71aa781c37Schristos */
72aa781c37Schristos #define to_digit(c) ((c) - '0')
73aa781c37Schristos #define is_digit(c) ((unsigned)to_digit(c) <= 9)
74aa781c37Schristos #define to_char(n) (char)((n) + '0')
75aa781c37Schristos
76aa781c37Schristos /*
77aa781c37Schristos * flags used during conversion.
78aa781c37Schristos */
79aa781c37Schristos #define ALT 0x001 /* alternate form */
80aa781c37Schristos #define HEXPREFIX 0x002 /* add 0x or 0X prefix */
81aa781c37Schristos #define LADJUST 0x004 /* left adjustment */
82aa781c37Schristos #define LONGDBL 0x008 /* long double; unimplemented */
83aa781c37Schristos #define LONGINT 0x010 /* long integer */
84aa781c37Schristos #define QUADINT 0x020 /* quad integer */
85aa781c37Schristos #define SHORTINT 0x040 /* short integer */
86aa781c37Schristos #define MAXINT 0x080 /* intmax_t */
87aa781c37Schristos #define PTRINT 0x100 /* intptr_t */
88aa781c37Schristos #define SIZEINT 0x200 /* size_t */
89aa781c37Schristos #define ZEROPAD 0x400 /* zero (as opposed to blank) pad */
90aa781c37Schristos #define FPT 0x800 /* Floating point number */
91aa781c37Schristos
92aa781c37Schristos /*
93aa781c37Schristos * To extend shorts properly, we need both signed and unsigned
94aa781c37Schristos * argument extraction methods.
95aa781c37Schristos */
96aa781c37Schristos #define SARG() \
97aa781c37Schristos (flags&MAXINT ? va_arg(ap, intmax_t) : \
98aa781c37Schristos flags&PTRINT ? va_arg(ap, intptr_t) : \
99aa781c37Schristos flags&SIZEINT ? va_arg(ap, ssize_t) : /* XXX */ \
100aa781c37Schristos flags&QUADINT ? va_arg(ap, quad_t) : \
101aa781c37Schristos flags&LONGINT ? va_arg(ap, long) : \
102aa781c37Schristos flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
103aa781c37Schristos (long)va_arg(ap, int))
104aa781c37Schristos #define UARG() \
105aa781c37Schristos (flags&MAXINT ? va_arg(ap, uintmax_t) : \
106aa781c37Schristos flags&PTRINT ? va_arg(ap, uintptr_t) : \
107aa781c37Schristos flags&SIZEINT ? va_arg(ap, size_t) : \
108aa781c37Schristos flags&QUADINT ? va_arg(ap, u_quad_t) : \
109aa781c37Schristos flags&LONGINT ? va_arg(ap, u_long) : \
110aa781c37Schristos flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
111aa781c37Schristos (u_long)va_arg(ap, u_int))
112aa781c37Schristos
113aa781c37Schristos #define PUTCHAR(C) do { \
114aa781c37Schristos if (sbuf < tailp) \
115aa781c37Schristos *sbuf++ = (C); \
116*388550b0Srillig } while (0)
117aa781c37Schristos
118aa781c37Schristos int
1193eb244d8Sjoerg vsnprintf_ss(char *sbuf, size_t slen, const char *fmt0, va_list ap)
120aa781c37Schristos {
121aa781c37Schristos const char *fmt; /* format string */
122aa781c37Schristos int ch; /* character from fmt */
123aa781c37Schristos int n; /* handy integer (short term usage) */
124aa781c37Schristos char *cp; /* handy char pointer (short term usage) */
125aa781c37Schristos int flags; /* flags as above */
126aa781c37Schristos int ret; /* return value accumulator */
127aa781c37Schristos int width; /* width from format (%8d), or 0 */
128aa781c37Schristos int prec; /* precision from format (%.3d), or -1 */
129aa781c37Schristos char sign; /* sign prefix (' ', '+', '-', or \0) */
130aa781c37Schristos
131aa781c37Schristos u_quad_t _uquad; /* integer arguments %[diouxX] */
132aa781c37Schristos enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
133aa781c37Schristos int dprec; /* a copy of prec if [diouxX], 0 otherwise */
134aa781c37Schristos int realsz; /* field size expanded by dprec */
135aa781c37Schristos int size; /* size of converted field or string */
136aa781c37Schristos const char *xdigs; /* digits for [xX] conversion */
137aa781c37Schristos char bf[128]; /* space for %c, %[diouxX] */
138aa781c37Schristos char *tailp; /* tail pointer for snprintf */
139c5e820caSchristos size_t len;
140aa781c37Schristos
141aa781c37Schristos static const char xdigs_lower[16] = "0123456789abcdef";
142aa781c37Schristos static const char xdigs_upper[16] = "0123456789ABCDEF";
143aa781c37Schristos
1445d7be274Schristos _DIAGASSERT(slen == 0 || sbuf != NULL);
14572e43063Schristos _DIAGASSERT(fmt0 != NULL);
146abca035cSchristos
1473a55b338Schristos if (slen > INT_MAX) {
1483a55b338Schristos errno = EOVERFLOW;
149526d9427Schristos return -1;
150cca9be70Schristos }
151cca9be70Schristos
152aa781c37Schristos tailp = sbuf + slen;
153aa781c37Schristos
154aa781c37Schristos cp = NULL; /* XXX: shutup gcc */
155aa781c37Schristos size = 0; /* XXX: shutup gcc */
156aa781c37Schristos
157aa781c37Schristos fmt = fmt0;
158aa781c37Schristos ret = 0;
159aa781c37Schristos
160aa781c37Schristos xdigs = NULL; /* XXX: shut up gcc warning */
161aa781c37Schristos
162aa781c37Schristos /*
163aa781c37Schristos * Scan the format for conversions (`%' character).
164aa781c37Schristos */
165aa781c37Schristos for (;;) {
166aa781c37Schristos while (*fmt != '%' && *fmt) {
167aa781c37Schristos ret++;
168b56987f3Schristos PUTCHAR(*fmt);
169b56987f3Schristos fmt++;
170aa781c37Schristos }
171aa781c37Schristos if (*fmt == 0)
172aa781c37Schristos goto done;
173aa781c37Schristos
174aa781c37Schristos fmt++; /* skip over '%' */
175aa781c37Schristos
176aa781c37Schristos flags = 0;
177aa781c37Schristos dprec = 0;
178aa781c37Schristos width = 0;
179aa781c37Schristos prec = -1;
180aa781c37Schristos sign = '\0';
181aa781c37Schristos
182aa781c37Schristos rflag: ch = *fmt++;
183aa781c37Schristos reswitch: switch (ch) {
184aa781c37Schristos case ' ':
185aa781c37Schristos /*
186aa781c37Schristos * ``If the space and + flags both appear, the space
187aa781c37Schristos * flag will be ignored.''
188aa781c37Schristos * -- ANSI X3J11
189aa781c37Schristos */
190aa781c37Schristos if (!sign)
191aa781c37Schristos sign = ' ';
192aa781c37Schristos goto rflag;
193aa781c37Schristos case '#':
194aa781c37Schristos flags |= ALT;
195aa781c37Schristos goto rflag;
196aa781c37Schristos case '*':
197aa781c37Schristos /*
198aa781c37Schristos * ``A negative field width argument is taken as a
199aa781c37Schristos * - flag followed by a positive field width.''
200aa781c37Schristos * -- ANSI X3J11
201aa781c37Schristos * They don't exclude field widths read from args.
202aa781c37Schristos */
203aa781c37Schristos if ((width = va_arg(ap, int)) >= 0)
204aa781c37Schristos goto rflag;
205aa781c37Schristos width = -width;
206aa781c37Schristos /* FALLTHROUGH */
207aa781c37Schristos case '-':
208aa781c37Schristos flags |= LADJUST;
209aa781c37Schristos goto rflag;
210aa781c37Schristos case '+':
211aa781c37Schristos sign = '+';
212aa781c37Schristos goto rflag;
213aa781c37Schristos case '.':
214aa781c37Schristos if ((ch = *fmt++) == '*') {
215aa781c37Schristos n = va_arg(ap, int);
216aa781c37Schristos prec = n < 0 ? -1 : n;
217aa781c37Schristos goto rflag;
218aa781c37Schristos }
219aa781c37Schristos n = 0;
220aa781c37Schristos while (is_digit(ch)) {
221aa781c37Schristos n = 10 * n + to_digit(ch);
222aa781c37Schristos ch = *fmt++;
223aa781c37Schristos }
224aa781c37Schristos prec = n < 0 ? -1 : n;
225aa781c37Schristos goto reswitch;
226aa781c37Schristos case '0':
227aa781c37Schristos /*
228aa781c37Schristos * ``Note that 0 is taken as a flag, not as the
229aa781c37Schristos * beginning of a field width.''
230aa781c37Schristos * -- ANSI X3J11
231aa781c37Schristos */
232aa781c37Schristos flags |= ZEROPAD;
233aa781c37Schristos goto rflag;
234aa781c37Schristos case '1': case '2': case '3': case '4':
235aa781c37Schristos case '5': case '6': case '7': case '8': case '9':
236aa781c37Schristos n = 0;
237aa781c37Schristos do {
238aa781c37Schristos n = 10 * n + to_digit(ch);
239aa781c37Schristos ch = *fmt++;
240aa781c37Schristos } while (is_digit(ch));
241aa781c37Schristos width = n;
242aa781c37Schristos goto reswitch;
243aa781c37Schristos case 'h':
244aa781c37Schristos flags |= SHORTINT;
245aa781c37Schristos goto rflag;
246aa781c37Schristos case 'j':
247aa781c37Schristos flags |= MAXINT;
248aa781c37Schristos goto rflag;
249aa781c37Schristos case 'l':
250aa781c37Schristos if (*fmt == 'l') {
251aa781c37Schristos fmt++;
252aa781c37Schristos flags |= QUADINT;
253aa781c37Schristos } else {
254aa781c37Schristos flags |= LONGINT;
255aa781c37Schristos }
256aa781c37Schristos goto rflag;
257aa781c37Schristos case 'q':
258aa781c37Schristos flags |= QUADINT;
259aa781c37Schristos goto rflag;
260aa781c37Schristos case 't':
261aa781c37Schristos flags |= PTRINT;
262aa781c37Schristos goto rflag;
263aa781c37Schristos case 'z':
264aa781c37Schristos flags |= SIZEINT;
265aa781c37Schristos goto rflag;
266aa781c37Schristos case 'c':
267aa781c37Schristos *(cp = bf) = va_arg(ap, int);
268aa781c37Schristos size = 1;
269aa781c37Schristos sign = '\0';
270aa781c37Schristos break;
271aa781c37Schristos case 'D':
272aa781c37Schristos flags |= LONGINT;
273aa781c37Schristos /*FALLTHROUGH*/
274aa781c37Schristos case 'd':
275aa781c37Schristos case 'i':
276aa781c37Schristos _uquad = SARG();
277aa781c37Schristos if ((quad_t)_uquad < 0) {
278aa781c37Schristos _uquad = -_uquad;
279aa781c37Schristos sign = '-';
280aa781c37Schristos }
281aa781c37Schristos base = DEC;
282aa781c37Schristos goto number;
283aa781c37Schristos case 'n':
284aa781c37Schristos if (flags & MAXINT)
285aa781c37Schristos *va_arg(ap, intmax_t *) = ret;
286aa781c37Schristos else if (flags & PTRINT)
287aa781c37Schristos *va_arg(ap, intptr_t *) = ret;
288aa781c37Schristos else if (flags & SIZEINT)
289aa781c37Schristos *va_arg(ap, ssize_t *) = ret;
290aa781c37Schristos else if (flags & QUADINT)
291aa781c37Schristos *va_arg(ap, quad_t *) = ret;
292aa781c37Schristos else if (flags & LONGINT)
293aa781c37Schristos *va_arg(ap, long *) = ret;
294aa781c37Schristos else if (flags & SHORTINT)
295aa781c37Schristos *va_arg(ap, short *) = ret;
296aa781c37Schristos else
297aa781c37Schristos *va_arg(ap, int *) = ret;
298aa781c37Schristos continue; /* no output */
299aa781c37Schristos case 'O':
300aa781c37Schristos flags |= LONGINT;
301aa781c37Schristos /*FALLTHROUGH*/
302aa781c37Schristos case 'o':
303aa781c37Schristos _uquad = UARG();
304aa781c37Schristos base = OCT;
305aa781c37Schristos goto nosign;
306aa781c37Schristos case 'p':
307aa781c37Schristos /*
308aa781c37Schristos * ``The argument shall be a pointer to void. The
309aa781c37Schristos * value of the pointer is converted to a sequence
310aa781c37Schristos * of printable characters, in an implementation-
311aa781c37Schristos * defined manner.''
312aa781c37Schristos * -- ANSI X3J11
313aa781c37Schristos */
314aa781c37Schristos /* NOSTRICT */
315aa781c37Schristos _uquad = (u_long)va_arg(ap, void *);
316aa781c37Schristos base = HEX;
317aa781c37Schristos xdigs = xdigs_lower;
318aa781c37Schristos flags |= HEXPREFIX;
319aa781c37Schristos ch = 'x';
320aa781c37Schristos goto nosign;
321aa781c37Schristos case 's':
322aa781c37Schristos if ((cp = va_arg(ap, char *)) == NULL)
323aa781c37Schristos /*XXXUNCONST*/
324aa781c37Schristos cp = __UNCONST("(null)");
325aa781c37Schristos if (prec >= 0) {
326aa781c37Schristos /*
327aa781c37Schristos * can't use strlen; can only look for the
328aa781c37Schristos * NUL in the first `prec' characters, and
329aa781c37Schristos * strlen() will go further.
330aa781c37Schristos */
331cfbb35edSchristos char *p = memchr(cp, 0, (size_t)prec);
332aa781c37Schristos
333aa781c37Schristos if (p != NULL) {
334c5e820caSchristos _DIAGASSERT(__type_fit(int, p - cp));
335c5e820caSchristos size = (int)(p - cp);
336aa781c37Schristos if (size > prec)
337aa781c37Schristos size = prec;
338aa781c37Schristos } else
339aa781c37Schristos size = prec;
340c5e820caSchristos } else {
341c5e820caSchristos len = strlen(cp);
342c5e820caSchristos _DIAGASSERT(__type_fit(int, len));
343c5e820caSchristos size = (int)len;
344c5e820caSchristos }
345aa781c37Schristos sign = '\0';
346aa781c37Schristos break;
347aa781c37Schristos case 'U':
348aa781c37Schristos flags |= LONGINT;
349aa781c37Schristos /*FALLTHROUGH*/
350aa781c37Schristos case 'u':
351aa781c37Schristos _uquad = UARG();
352aa781c37Schristos base = DEC;
353aa781c37Schristos goto nosign;
354aa781c37Schristos case 'X':
355aa781c37Schristos xdigs = xdigs_upper;
356aa781c37Schristos goto hex;
357aa781c37Schristos case 'x':
358aa781c37Schristos xdigs = xdigs_lower;
359aa781c37Schristos hex: _uquad = UARG();
360aa781c37Schristos base = HEX;
361aa781c37Schristos /* leading 0x/X only if non-zero */
362aa781c37Schristos if (flags & ALT && _uquad != 0)
363aa781c37Schristos flags |= HEXPREFIX;
364aa781c37Schristos
365aa781c37Schristos /* unsigned conversions */
366aa781c37Schristos nosign: sign = '\0';
367aa781c37Schristos /*
368aa781c37Schristos * ``... diouXx conversions ... if a precision is
369aa781c37Schristos * specified, the 0 flag will be ignored.''
370aa781c37Schristos * -- ANSI X3J11
371aa781c37Schristos */
372aa781c37Schristos number: if ((dprec = prec) >= 0)
373aa781c37Schristos flags &= ~ZEROPAD;
374aa781c37Schristos
375aa781c37Schristos /*
376aa781c37Schristos * ``The result of converting a zero value with an
377aa781c37Schristos * explicit precision of zero is no characters.''
378aa781c37Schristos * -- ANSI X3J11
379aa781c37Schristos */
380aa781c37Schristos cp = bf + sizeof(bf);
381aa781c37Schristos if (_uquad != 0 || prec != 0) {
382aa781c37Schristos /*
383aa781c37Schristos * Unsigned mod is hard, and unsigned mod
384aa781c37Schristos * by a constant is easier than that by
385aa781c37Schristos * a variable; hence this switch.
386aa781c37Schristos */
387aa781c37Schristos switch (base) {
388aa781c37Schristos case OCT:
389aa781c37Schristos do {
390aa781c37Schristos *--cp = to_char(_uquad & 7);
391aa781c37Schristos _uquad >>= 3;
392aa781c37Schristos } while (_uquad);
393aa781c37Schristos /* handle octal leading 0 */
394aa781c37Schristos if (flags & ALT && *cp != '0')
395aa781c37Schristos *--cp = '0';
396aa781c37Schristos break;
397aa781c37Schristos
398aa781c37Schristos case DEC:
399aa781c37Schristos /* many numbers are 1 digit */
400aa781c37Schristos while (_uquad >= 10) {
401aa781c37Schristos *--cp = to_char(_uquad % 10);
402aa781c37Schristos _uquad /= 10;
403aa781c37Schristos }
404aa781c37Schristos *--cp = to_char(_uquad);
405aa781c37Schristos break;
406aa781c37Schristos
407aa781c37Schristos case HEX:
408aa781c37Schristos do {
409cfbb35edSchristos *--cp = xdigs[(size_t)_uquad & 15];
410aa781c37Schristos _uquad >>= 4;
411aa781c37Schristos } while (_uquad);
412aa781c37Schristos break;
413aa781c37Schristos
414aa781c37Schristos default:
415aa781c37Schristos /*XXXUNCONST*/
416aa781c37Schristos cp = __UNCONST("bug bad base");
417c5e820caSchristos len = strlen(cp);
418c5e820caSchristos _DIAGASSERT(__type_fit(int, len));
419c5e820caSchristos size = (int)len;
420aa781c37Schristos goto skipsize;
421aa781c37Schristos }
422aa781c37Schristos }
423c5e820caSchristos _DIAGASSERT(__type_fit(int, bf + sizeof(bf) - cp));
424c5e820caSchristos size = (int)(bf + sizeof(bf) - cp);
425aa781c37Schristos skipsize:
426aa781c37Schristos break;
427aa781c37Schristos default: /* "%?" prints ?, unless ? is NUL */
428aa781c37Schristos if (ch == '\0')
429aa781c37Schristos goto done;
430aa781c37Schristos /* pretend it was %c with argument ch */
431aa781c37Schristos cp = bf;
432aa781c37Schristos *cp = ch;
433aa781c37Schristos size = 1;
434aa781c37Schristos sign = '\0';
435aa781c37Schristos break;
436abca035cSchristos }
437abca035cSchristos
438aa781c37Schristos /*
439aa781c37Schristos * All reasonable formats wind up here. At this point, `cp'
440aa781c37Schristos * points to a string which (if not flags&LADJUST) should be
441aa781c37Schristos * padded out to `width' places. If flags&ZEROPAD, it should
442aa781c37Schristos * first be prefixed by any sign or other prefix; otherwise,
443aa781c37Schristos * it should be blank padded before the prefix is emitted.
444aa781c37Schristos * After any left-hand padding and prefixing, emit zeroes
445aa781c37Schristos * required by a decimal [diouxX] precision, then print the
446aa781c37Schristos * string proper, then emit zeroes required by any leftover
447aa781c37Schristos * floating precision; finally, if LADJUST, pad with blanks.
448aa781c37Schristos *
449aa781c37Schristos * Compute actual size, so we know how much to pad.
450aa781c37Schristos * size excludes decimal prec; realsz includes it.
451aa781c37Schristos */
452aa781c37Schristos realsz = dprec > size ? dprec : size;
453aa781c37Schristos if (sign)
454aa781c37Schristos realsz++;
455aa781c37Schristos else if (flags & HEXPREFIX)
456aa781c37Schristos realsz+= 2;
457aa781c37Schristos
458aa781c37Schristos /* adjust ret */
459aa781c37Schristos ret += width > realsz ? width : realsz;
460aa781c37Schristos
461aa781c37Schristos /* right-adjusting blank padding */
462aa781c37Schristos if ((flags & (LADJUST|ZEROPAD)) == 0) {
463aa781c37Schristos n = width - realsz;
464aa781c37Schristos while (n-- > 0)
465aa781c37Schristos PUTCHAR(' ');
466abca035cSchristos }
467aa781c37Schristos
468aa781c37Schristos /* prefix */
469aa781c37Schristos if (sign) {
470aa781c37Schristos PUTCHAR(sign);
471aa781c37Schristos } else if (flags & HEXPREFIX) {
472aa781c37Schristos PUTCHAR('0');
473aa781c37Schristos PUTCHAR(ch);
474aa781c37Schristos }
475aa781c37Schristos
476aa781c37Schristos /* right-adjusting zero padding */
477aa781c37Schristos if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) {
478aa781c37Schristos n = width - realsz;
479aa781c37Schristos while (n-- > 0)
480aa781c37Schristos PUTCHAR('0');
481aa781c37Schristos }
482aa781c37Schristos
483aa781c37Schristos /* leading zeroes from decimal precision */
484aa781c37Schristos n = dprec - size;
485aa781c37Schristos while (n-- > 0)
486aa781c37Schristos PUTCHAR('0');
487aa781c37Schristos
488aa781c37Schristos /* the string or number proper */
489aa781c37Schristos while (size--)
490aa781c37Schristos PUTCHAR(*cp++);
491aa781c37Schristos /* left-adjusting padding (always blank) */
492aa781c37Schristos if (flags & LADJUST) {
493aa781c37Schristos n = width - realsz;
494aa781c37Schristos while (n-- > 0)
495aa781c37Schristos PUTCHAR(' ');
496aa781c37Schristos }
497aa781c37Schristos }
498aa781c37Schristos
499aa781c37Schristos done:
500aa781c37Schristos if (sbuf == tailp)
501aa781c37Schristos sbuf[-1] = '\0';
502aa781c37Schristos else
503aa781c37Schristos *sbuf = '\0';
504526d9427Schristos return ret;
505aa781c37Schristos /* NOTREACHED */
506abca035cSchristos }
507