xref: /dragonfly/lib/libc/stdio/xprintf_int.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1 /*-
2  * Copyright (c) 2005 Poul-Henning Kamp
3  * Copyright (c) 1990, 1993
4  *        The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Chris Torek.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD: src/lib/libc/stdio/xprintf_int.c,v 1.2 2005/12/22 14:23:54 cognet Exp $
34  */
35 
36 #include "namespace.h"
37 #include <err.h>
38 #include <sys/types.h>
39 #include <stddef.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <limits.h>
43 #include <locale.h>
44 #include <stdint.h>
45 #include <assert.h>
46 #include <string.h>
47 #include <wchar.h>
48 #include "un-namespace.h"
49 
50 #include "printf.h"
51 
52 /* private stuff -----------------------------------------------------*/
53 
54 union arg {
55           int                           intarg;
56           u_int                         uintarg;
57           long                          longarg;
58           u_long                        ulongarg;
59           intmax_t            intmaxarg;
60           uintmax_t                     uintmaxarg;
61 };
62 
63 /*
64  * Macros for converting digits to letters and vice versa
65  */
66 #define   to_char(n)          ((n) + '0')
67 
68 /* various globals ---------------------------------------------------*/
69 
70 /*
71  * The size of the buffer we use for integer conversions.
72  * Technically, we would need the most space for base 10
73  * conversions with thousands' grouping characters between
74  * each pair of digits: 60 digits for 128 bit intmax_t.
75  * Use a bit more for better alignment of stuff.
76  */
77 #define   BUF       64
78 
79 /* misc --------------------------------------------------------------*/
80 
81 /*
82  * Convert an unsigned long to ASCII for printf purposes, returning
83  * a pointer to the first character of the string representation.
84  * Octal numbers can be forced to have a leading zero; hex numbers
85  * use the given digits.
86  */
87 static char *
__ultoa(u_long val,char * endp,int base,const char * xdigs,int needgrp,char thousep,const char * grp)88 __ultoa(u_long val, char *endp, int base, const char *xdigs,
89           int needgrp, char thousep, const char *grp)
90 {
91           char *cp = endp;
92           long sval;
93           int ndig;
94 
95           /*
96            * Handle the three cases separately, in the hope of getting
97            * better/faster code.
98            */
99           switch (base) {
100           case 10:
101                     if (val < 10) {     /* many numbers are 1 digit */
102                               *--cp = to_char(val);
103                               return (cp);
104                     }
105                     ndig = 0;
106                     /*
107                      * On many machines, unsigned arithmetic is harder than
108                      * signed arithmetic, so we do at most one unsigned mod and
109                      * divide; this is sufficient to reduce the range of
110                      * the incoming value to where signed arithmetic works.
111                      */
112                     if (val > LONG_MAX) {
113                               *--cp = to_char(val % 10);
114                               ndig++;
115                               sval = val / 10;
116                     } else {
117                               sval = val;
118                     }
119                     do {
120                               *--cp = to_char(sval % 10);
121                               ndig++;
122                               /*
123                                * If (*grp == CHAR_MAX) then no more grouping
124                                * should be performed.
125                                */
126                               if (needgrp && ndig == *grp && *grp != CHAR_MAX
127                                                   && sval > 9) {
128                                         *--cp = thousep;
129                                         ndig = 0;
130                                         /*
131                                          * If (*(grp+1) == '\0') then we have to
132                                          * use *grp character (last grouping rule)
133                                          * for all next cases
134                                          */
135                                         if (*(grp+1) != '\0')
136                                                   grp++;
137                               }
138                               sval /= 10;
139                     } while (sval != 0);
140                     break;
141 
142           case 8:
143                     do {
144                               *--cp = to_char(val & 7);
145                               val >>= 3;
146                     } while (val);
147                     break;
148 
149           case 16:
150                     do {
151                               *--cp = xdigs[val & 15];
152                               val >>= 4;
153                     } while (val);
154                     break;
155 
156           default:                      /* oops */
157                     assert(base == 16);
158           }
159           return (cp);
160 }
161 
162 
163 /* Identical to __ultoa, but for intmax_t. */
164 static char *
__ujtoa(uintmax_t val,char * endp,int base,const char * xdigs,int needgrp,char thousep,const char * grp)165 __ujtoa(uintmax_t val, char *endp, int base, const char *xdigs,
166           int needgrp, char thousep, const char *grp)
167 {
168           char *cp = endp;
169           intmax_t sval;
170           int ndig;
171 
172           switch (base) {
173           case 10:
174                     if (val < 10) {
175                               *--cp = to_char(val % 10);
176                               return (cp);
177                     }
178                     ndig = 0;
179                     if (val > INTMAX_MAX) {
180                               *--cp = to_char(val % 10);
181                               ndig++;
182                               sval = val / 10;
183                     } else {
184                               sval = val;
185                     }
186                     do {
187                               *--cp = to_char(sval % 10);
188                               ndig++;
189                               /*
190                                * If (*grp == CHAR_MAX) then no more grouping
191                                * should be performed.
192                                */
193                               if (needgrp && *grp != CHAR_MAX && ndig == *grp
194                                                   && sval > 9) {
195                                         *--cp = thousep;
196                                         ndig = 0;
197                                         /*
198                                          * If (*(grp+1) == '\0') then we have to
199                                          * use *grp character (last grouping rule)
200                                          * for all next cases
201                                          */
202                                         if (*(grp+1) != '\0')
203                                                   grp++;
204                               }
205                               sval /= 10;
206                     } while (sval != 0);
207                     break;
208 
209           case 8:
210                     do {
211                               *--cp = to_char(val & 7);
212                               val >>= 3;
213                     } while (val);
214                     break;
215 
216           case 16:
217                     do {
218                               *--cp = xdigs[val & 15];
219                               val >>= 4;
220                     } while (val);
221                     break;
222 
223           default:
224                     abort();
225           }
226           return (cp);
227 }
228 
229 
230 /* 'd' ---------------------------------------------------------------*/
231 
232 int
__printf_arginfo_int(const struct printf_info * pi,size_t n,int * argt)233 __printf_arginfo_int(const struct printf_info *pi, size_t n, int *argt)
234 {
235           assert (n > 0);
236           argt[0] = PA_INT;
237           if (pi->is_ptrdiff)
238                     argt[0] |= PA_FLAG_PTRDIFF;
239           else if (pi->is_size)
240                     argt[0] |= PA_FLAG_SIZE;
241           else if (pi->is_long)
242                     argt[0] |= PA_FLAG_LONG;
243           else if (pi->is_intmax)
244                     argt[0] |= PA_FLAG_INTMAX;
245           else if (pi->is_quad)
246                     argt[0] |= PA_FLAG_QUAD;
247           else if (pi->is_long_double)
248                     argt[0] |= PA_FLAG_LONG_LONG;
249           else if (pi->is_short)
250                     argt[0] |= PA_FLAG_SHORT;
251           else if (pi->is_char)
252                     argt[0] = PA_CHAR;
253           return (1);
254 }
255 
256 int
__printf_render_int(struct __printf_io * io,const struct printf_info * pi,const void * const * arg)257 __printf_render_int(struct __printf_io *io, const struct printf_info *pi,
258                         const void *const *arg)
259 {
260           const union arg *argp;
261           char buf[BUF];
262           char *p, *pe;
263           char ns, l;
264           int rdx, sign, zext, ngrp;
265           const char *nalt, *digit;
266           char thousands_sep; /* locale specific thousands separator */
267           const char *grouping;         /* locale specific numeric grouping rules */
268           uintmax_t uu;
269           int ret;
270 
271           ret = 0;
272           nalt = NULL;
273           digit = __lowercase_hex;
274           ns = '\0';
275           pe = buf + sizeof buf - 1;
276 
277           if (pi->group) {
278                     thousands_sep = *(localeconv()->thousands_sep);
279                     grouping = localeconv()->grouping;
280                     ngrp = 1;
281           } else {
282                     thousands_sep = 0;
283                     grouping = NULL;
284                     ngrp = 0;
285           }
286 
287           switch(pi->spec) {
288           case 'd':
289           case 'i':
290                     rdx = 10;
291                     sign = 1;
292                     break;
293           case 'X':
294                     digit = __uppercase_hex;
295                     /*FALLTHOUGH*/
296           case 'x':
297                     rdx = 16;
298                     sign = 0;
299                     break;
300           case 'u':
301           case 'U':
302                     rdx = 10;
303                     sign = 0;
304                     break;
305           case 'o':
306           case 'O':
307                     rdx = 8;
308                     sign = 0;
309                     break;
310           default:
311                     fprintf(stderr, "pi->spec = '%c'\n", pi->spec);
312                     assert(1 == 0);
313           }
314           argp = arg[0];
315 
316           if (sign)
317                     ns = pi->showsign;
318 
319           if (pi->is_long_double || pi->is_quad || pi->is_intmax ||
320               pi->is_size || pi->is_ptrdiff) {
321                     if (sign && argp->intmaxarg < 0) {
322                               uu = -argp->intmaxarg;
323                               ns = '-';
324                     } else {
325                               uu = argp->uintmaxarg;
326                     }
327           } else if (pi->is_long) {
328                     if (sign && argp->longarg < 0) {
329                               uu = (u_long)-argp->longarg;
330                               ns = '-';
331                     } else {
332                               uu = argp->ulongarg;
333                     }
334           } else if (pi->is_short) {
335                     if (sign && (short)argp->intarg < 0) {
336                               uu = -(short)argp->intarg;
337                               ns = '-';
338                     } else {
339                               uu = (unsigned short)argp->uintarg;
340                     }
341           } else if (pi->is_char) {
342                     if (sign && (signed char)argp->intarg < 0) {
343                               uu = -(signed char)argp->intarg;
344                               ns = '-';
345                     } else {
346                               uu = (unsigned char)argp->uintarg;
347                     }
348           } else {
349                     if (sign && argp->intarg < 0) {
350                               uu = (unsigned)-argp->intarg;
351                               ns = '-';
352                     } else {
353                               uu = argp->uintarg;
354                     }
355           }
356           if (uu <= ULONG_MAX)
357                     p = __ultoa(uu, pe, rdx, digit, ngrp, thousands_sep, grouping);
358           else
359                     p = __ujtoa(uu, pe, rdx, digit, ngrp, thousands_sep, grouping);
360 
361           l = 0;
362           if (uu == 0) {
363                     /*-
364                      * ``The result of converting a zero value with an
365                      * explicit precision of zero is no characters.''
366                      *      -- ANSI X3J11
367                      *
368                      * ``The C Standard is clear enough as is.  The call
369                      * printf("%#.0o", 0) should print 0.''
370                      *      -- Defect Report #151
371                      */
372                     if (pi->prec == 0 && !(pi->alt && rdx == 8))
373                               p = pe;
374           } else if (pi->alt) {
375                     if (rdx == 8)
376                               *--p = '0';
377                     if (rdx == 16) {
378                               if (pi->spec == 'x')
379                                         nalt = "0x";
380                               else
381                                         nalt = "0X";
382                               l += 2;
383                     }
384           }
385           l += pe - p;
386           if (ns)
387                     l++;
388 
389           /*-
390            * ``... diouXx conversions ... if a precision is
391            * specified, the 0 flag will be ignored.''
392            *      -- ANSI X3J11
393            */
394           if (pi->prec > (pe - p))
395                     zext = pi->prec - (pe - p);
396           else if (pi->prec != -1)
397                     zext = 0;
398           else if (pi->pad == '0' && pi->width > l && !pi->left)
399                     zext = pi->width - l;
400           else
401                     zext = 0;
402 
403           l += zext;
404 
405           while (zext > 0 && p > buf) {
406                     *--p = '0';
407                     zext--;
408           }
409 
410           if (l < BUF) {
411                     if (ns) {
412                               *--p = ns;
413                     } else if (nalt != NULL) {
414                               *--p = nalt[1];
415                               *--p = nalt[0];
416                     }
417                     if (pi->width > (pe - p) && !pi->left) {
418                               l = pi->width - (pe - p);
419                               while (l > 0 && p > buf) {
420                                         *--p = ' ';
421                                         l--;
422                               }
423                               if (l)
424                                         ret += __printf_pad(io, l, 0);
425                     }
426           } else {
427                     if (!pi->left && pi->width > l)
428                               ret += __printf_pad(io, pi->width - l, 0);
429                     if (ns != '\0')
430                               ret += __printf_puts(io, &ns, 1);
431                     else if (nalt != NULL)
432                               ret += __printf_puts(io, nalt, 2);
433                     if (zext > 0)
434                               ret += __printf_pad(io, zext, 1);
435           }
436 
437           ret += __printf_puts(io, p, pe - p);
438           if (pi->width > ret && pi->left)
439                     ret += __printf_pad(io, pi->width - ret, 0);
440           __printf_flush(io);
441           return (ret);
442 }
443 
444 /* 'p' ---------------------------------------------------------------*/
445 
446 int
__printf_arginfo_ptr(const struct printf_info * pi __unused,size_t n,int * argt)447 __printf_arginfo_ptr(const struct printf_info *pi __unused, size_t n, int *argt)
448 {
449 
450           assert (n > 0);
451           argt[0] = PA_POINTER;
452           return (1);
453 }
454 
455 int
__printf_render_ptr(struct __printf_io * io,const struct printf_info * pi,const void * const * arg)456 __printf_render_ptr(struct __printf_io *io, const struct printf_info *pi,
457                         const void *const *arg)
458 {
459           struct printf_info p2;
460           uintmax_t u;
461           const void *p;
462 
463           /*-
464            * ``The argument shall be a pointer to void.  The
465            * value of the pointer is converted to a sequence
466            * of printable characters, in an implementation-
467            * defined manner.''
468            *      -- ANSI X3J11
469            */
470           u = (uintmax_t)(uintptr_t) *((void **)arg[0]);
471           p2 = *pi;
472 
473           p2.spec = 'x';
474           p2.alt = 1;
475           p2.is_long_double = 1;
476           p = &u;
477           return (__printf_render_int(io, &p2, &p));
478 }
479