diff options
Diffstat (limited to 'lib/libc/stdio/xprintf_int.c')
-rw-r--r-- | lib/libc/stdio/xprintf_int.c | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/lib/libc/stdio/xprintf_int.c b/lib/libc/stdio/xprintf_int.c new file mode 100644 index 0000000..f006b54 --- /dev/null +++ b/lib/libc/stdio/xprintf_int.c @@ -0,0 +1,471 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <namespace.h> +#include <err.h> +#include <sys/types.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <locale.h> +#include <stdint.h> +#include <assert.h> +#include <namespace.h> +#include <string.h> +#include <wchar.h> +#include <un-namespace.h> + +#include "printf.h" + +/* private stuff -----------------------------------------------------*/ + +union arg { + int intarg; + u_int uintarg; + long longarg; + u_long ulongarg; + intmax_t intmaxarg; + uintmax_t uintmaxarg; +}; + +/* + * Macros for converting digits to letters and vice versa + */ +#define to_char(n) ((n) + '0') + +/* various globals ---------------------------------------------------*/ + +/* + * The size of the buffer we use for integer conversions. + * Technically, we would need the most space for base 10 + * conversions with thousands' grouping characters between + * each pair of digits: 60 digits for 128 bit intmax_t. + * Use a bit more for better alignment of stuff. + */ +#define BUF 64 + +/* misc --------------------------------------------------------------*/ + +/* + * Convert an unsigned long to ASCII for printf purposes, returning + * a pointer to the first character of the string representation. + * Octal numbers can be forced to have a leading zero; hex numbers + * use the given digits. + */ +static char * +__ultoa(u_long val, char *endp, int base, const char *xdigs, + int needgrp, char thousep, const char *grp) +{ + char *cp = endp; + long sval; + int ndig; + + /* + * Handle the three cases separately, in the hope of getting + * better/faster code. + */ + switch (base) { + case 10: + if (val < 10) { /* many numbers are 1 digit */ + *--cp = to_char(val); + return (cp); + } + ndig = 0; + /* + * On many machines, unsigned arithmetic is harder than + * signed arithmetic, so we do at most one unsigned mod and + * divide; this is sufficient to reduce the range of + * the incoming value to where signed arithmetic works. + */ + if (val > LONG_MAX) { + *--cp = to_char(val % 10); + ndig++; + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + ndig++; + /* + * If (*grp == CHAR_MAX) then no more grouping + * should be performed. + */ + if (needgrp && ndig == *grp && *grp != CHAR_MAX + && sval > 9) { + *--cp = thousep; + ndig = 0; + /* + * If (*(grp+1) == '\0') then we have to + * use *grp character (last grouping rule) + * for all next cases + */ + if (*(grp+1) != '\0') + grp++; + } + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: /* oops */ + assert(base == 16); + } + return (cp); +} + + +/* Identical to __ultoa, but for intmax_t. */ +static char * +__ujtoa(uintmax_t val, char *endp, int base, const char *xdigs, + int needgrp, char thousep, const char *grp) +{ + char *cp = endp; + intmax_t sval; + int ndig; + + switch (base) { + case 10: + if (val < 10) { + *--cp = to_char(val % 10); + return (cp); + } + ndig = 0; + if (val > INTMAX_MAX) { + *--cp = to_char(val % 10); + ndig++; + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + ndig++; + /* + * If (*grp == CHAR_MAX) then no more grouping + * should be performed. + */ + if (needgrp && *grp != CHAR_MAX && ndig == *grp + && sval > 9) { + *--cp = thousep; + ndig = 0; + /* + * If (*(grp+1) == '\0') then we have to + * use *grp character (last grouping rule) + * for all next cases + */ + if (*(grp+1) != '\0') + grp++; + } + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: + abort(); + } + return (cp); +} + + +/* 'd' ---------------------------------------------------------------*/ + +int +__printf_arginfo_int(const struct printf_info *pi, size_t n, int *argt) +{ + assert (n > 0); + argt[0] = PA_INT; + if (pi->is_ptrdiff) + argt[0] |= PA_FLAG_PTRDIFF; + else if (pi->is_size) + argt[0] |= PA_FLAG_SIZE; + else if (pi->is_long) + argt[0] |= PA_FLAG_LONG; + else if (pi->is_intmax) + argt[0] |= PA_FLAG_INTMAX; + else if (pi->is_quad) + argt[0] |= PA_FLAG_QUAD; + else if (pi->is_long_double) + argt[0] |= PA_FLAG_LONG_LONG; + else if (pi->is_short) + argt[0] |= PA_FLAG_SHORT; + else if (pi->is_char) + argt[0] = PA_CHAR; + return (1); +} + +int +__printf_render_int(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + const union arg *argp; + char buf[BUF]; + char *p, *pe; + char ns, l; + int rdx, sign, zext, ngrp; + const char *nalt, *digit; + char thousands_sep; /* locale specific thousands separator */ + const char *grouping; /* locale specific numeric grouping rules */ + uintmax_t uu; + int ret; + + ret = 0; + nalt = NULL; + digit = __lowercase_hex; + ns = '\0'; + pe = buf + sizeof buf - 1; + + if (pi->group) { + thousands_sep = *(localeconv()->thousands_sep); + grouping = localeconv()->grouping; + ngrp = 1; + } else { + thousands_sep = 0; + grouping = NULL; + ngrp = 0; + } + + switch(pi->spec) { + case 'd': + case 'i': + rdx = 10; + sign = 1; + break; + case 'X': + digit = __uppercase_hex; + /*FALLTHOUGH*/ + case 'x': + rdx = 16; + sign = 0; + break; + case 'u': + case 'U': + rdx = 10; + sign = 0; + break; + case 'o': + case 'O': + rdx = 8; + sign = 0; + break; + default: + fprintf(stderr, "pi->spec = '%c'\n", pi->spec); + assert(1 == 0); + } + argp = arg[0]; + + if (sign) + ns = pi->showsign; + + if (pi->is_long_double || pi->is_quad || pi->is_intmax || + pi->is_size || pi->is_ptrdiff) { + if (sign && argp->intmaxarg < 0) { + uu = -argp->intmaxarg; + ns = '-'; + } else + uu = argp->uintmaxarg; + } else if (pi->is_long) { + if (sign && argp->longarg < 0) { + uu = (u_long)-argp->longarg; + ns = '-'; + } else + uu = argp->ulongarg; + } else if (pi->is_short) { + if (sign && (short)argp->intarg < 0) { + uu = -(short)argp->intarg; + ns = '-'; + } else + uu = (unsigned short)argp->uintarg; + } else if (pi->is_char) { + if (sign && (signed char)argp->intarg < 0) { + uu = -(signed char)argp->intarg; + ns = '-'; + } else + uu = (unsigned char)argp->uintarg; + } else { + if (sign && argp->intarg < 0) { + uu = (unsigned)-argp->intarg; + ns = '-'; + } else + uu = argp->uintarg; + } + if (uu <= ULONG_MAX) + p = __ultoa(uu, pe, rdx, digit, ngrp, thousands_sep, grouping); + else + p = __ujtoa(uu, pe, rdx, digit, ngrp, thousands_sep, grouping); + + l = 0; + if (uu == 0) { + /*- + * ``The result of converting a zero value with an + * explicit precision of zero is no characters.'' + * -- ANSI X3J11 + * + * ``The C Standard is clear enough as is. The call + * printf("%#.0o", 0) should print 0.'' + * -- Defect Report #151 + */ + ; + if (pi->prec == 0 && !(pi->alt && rdx == 8)) + p = pe; + } else if (pi->alt) { + if (rdx == 8) + *--p = '0'; + if (rdx == 16) { + if (pi->spec == 'x') + nalt = "0x"; + else + nalt = "0X"; + l += 2; + } + } + l += pe - p; + if (ns) + l++; + + /*- + * ``... diouXx conversions ... if a precision is + * specified, the 0 flag will be ignored.'' + * -- ANSI X3J11 + */ + if (pi->prec > (pe - p)) + zext = pi->prec - (pe - p); + else if (pi->prec != -1) + zext = 0; + else if (pi->pad == '0' && pi->width > l && !pi->left) + zext = pi->width - l; + else + zext = 0; + + l += zext; + + while (zext > 0 && p > buf) { + *--p = '0'; + zext--; + } + + if (l < BUF) { + if (ns) { + *--p = ns; + } else if (nalt != NULL) { + *--p = nalt[1]; + *--p = nalt[0]; + } + if (pi->width > (pe - p) && !pi->left) { + l = pi->width - (pe - p); + while (l > 0 && p > buf) { + *--p = ' '; + l--; + } + if (l) + ret += __printf_pad(io, l, 0); + } + } else { + if (!pi->left && pi->width > l) + ret += __printf_pad(io, pi->width - l, 0); + if (ns != '\0') + ret += __printf_puts(io, &ns, 1); + else if (nalt != NULL) + ret += __printf_puts(io, nalt, 2); + if (zext > 0) + ret += __printf_pad(io, zext, 1); + } + + ret += __printf_puts(io, p, pe - p); + if (pi->width > ret && pi->left) + ret += __printf_pad(io, pi->width - ret, 0); + __printf_flush(io); + return (ret); +} + +/* 'p' ---------------------------------------------------------------*/ + +int +__printf_arginfo_ptr(const struct printf_info *pi __unused, size_t n, int *argt) +{ + + assert (n > 0); + argt[0] = PA_POINTER; + return (1); +} + +int +__printf_render_ptr(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) +{ + struct printf_info p2; + uintmax_t u; + const void *p; + + /*- + * ``The argument shall be a pointer to void. The + * value of the pointer is converted to a sequence + * of printable characters, in an implementation- + * defined manner.'' + * -- ANSI X3J11 + */ + u = (uintmax_t)(uintptr_t) *((void **)arg[0]); + p2 = *pi; + + p2.spec = 'x'; + p2.alt = 1; + p2.is_long_double = 1; + p = &u; + return (__printf_render_int(io, &p2, &p)); +} |