]> git.saurik.com Git - apple/libc.git/blobdiff - stdio/FreeBSD/xprintf_int.c
Libc-997.1.1.tar.gz
[apple/libc.git] / stdio / FreeBSD / xprintf_int.c
diff --git a/stdio/FreeBSD/xprintf_int.c b/stdio/FreeBSD/xprintf_int.c
new file mode 100644 (file)
index 0000000..fb64451
--- /dev/null
@@ -0,0 +1,498 @@
+/*-
+ * 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: src/lib/libc/stdio/xprintf_int.c,v 1.2 2005/12/22 14:23:54 cognet Exp $
+ */
+
+#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"
+#include "xprintf_private.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: 39 digits for 128 bit intmax_t plus
+ * 20 grouping characters (which may be multibyte).
+ * Use a bit more for better alignment of stuff.
+ */
+#define        BUF     128
+
+/* misc --------------------------------------------------------------*/
+
+extern const char *__fix_nogrouping(const char *str);
+
+/*
+ * 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, const char *thousep, int thousep_len, 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_len;
+                               memcpy(cp, thousep, thousep_len);
+                               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, const char *thousep, int thousep_len, 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_len;
+                               memcpy(cp, thousep, thousep_len);
+                               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' ---------------------------------------------------------------*/
+
+__private_extern__ int
+__printf_arginfo_int(const struct printf_info *pi, size_t n, int *argt)
+{
+       assert (n > 0);
+       argt[0] = PA_INT;
+#ifdef VECTORS
+       if (pi->is_vec)
+               argt[0] = PA_VECTOR;
+       else
+#endif /* VECTORS */
+       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);
+}
+
+__private_extern__ 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;
+       const char *thousands_sep;      /* locale specific thousands separator */
+       int thousands_sep_len;          /* locale specific thousands separator length */
+       const char *grouping;   /* locale specific numeric grouping rules */
+       uintmax_t uu;
+       int ret;
+
+#ifdef VECTORS
+       if (pi->is_vec) return __xprintf_vector(io, pi, arg);
+#endif /* VECTORS */
+
+       ret = 0;
+       nalt = NULL;
+       digit = __lowercase_hex;
+       ns = '\0';
+       pe = buf + sizeof buf - 1;
+
+       if (pi->group) {
+               thousands_sep = localeconv_l(pi->loc)->thousands_sep;
+               thousands_sep_len = strlen(thousands_sep);
+               grouping = __fix_nogrouping(localeconv_l(pi->loc)->grouping);
+               ngrp = 1;
+       } else {
+               thousands_sep = NULL;
+               thousands_sep_len = 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->signchar;
+
+       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, thousands_sep_len, grouping);
+       else
+               p = __ujtoa(uu, pe, rdx, digit, ngrp, thousands_sep, thousands_sep_len, 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' ---------------------------------------------------------------*/
+
+__private_extern__ int
+__printf_arginfo_ptr(const struct printf_info *pi __unused, size_t n, int *argt)
+{
+
+       assert (n > 0);
+#ifdef VECTORS
+       if (pi->is_vec)
+               argt[0] = PA_VECTOR;
+       else
+#endif /* VECTORS */
+       argt[0] = PA_POINTER;
+       return (1);
+}
+
+__private_extern__ 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;
+
+#ifdef VECTORS
+       if (pi->is_vec) return __xprintf_vector(io, pi, arg);
+#endif /* VECTORS */
+
+       /*-
+        * ``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));
+}