Correct BSD License clause numbering from 1-2-4 to 1-2-3.
[dragonfly.git] / libexec / rtld-elf / rtld_printf.c
1 /*-
2  * Copyright (c) 1986, 1988, 1991, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  * Copyright (c) 2011 Konstantin Belousov <kib@FreeBSD.org>
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * $FreeBSD$
36  */
37
38 #include <sys/param.h>
39 #include <inttypes.h>
40 #include <stdarg.h>
41 #include <stddef.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include "rtld_printf.h"
45
46 #define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
47
48 #define PRINT_METHOD_SNPRINTF   1
49 #define PRINT_METHOD_WRITE      2
50
51 struct snprintf_arg {
52         int     method;
53         char    *str;
54         char    *buf;
55         size_t  remain;
56         size_t  buf_total;
57         int     fd;
58 };
59
60 static void
61 printf_out(struct snprintf_arg *info)
62 {
63
64         if (info->remain == info->buf_total)
65                 return;
66         write(info->fd, info->buf, info->buf_total - info->remain);
67         info->str = info->buf;
68         info->remain = info->buf_total;
69 }
70
71 static void
72 snprintf_func(int ch, struct snprintf_arg *const info)
73 {
74
75         switch (info->method) {
76         case PRINT_METHOD_SNPRINTF:
77                 if (info->remain >= 2) {
78                         *info->str++ = ch;
79                         info->remain--;
80                 }
81                 break;
82         case PRINT_METHOD_WRITE:
83                 if (info->remain > 0) {
84                         *info->str++ = ch;
85                         info->remain--;
86                 } else
87                         printf_out(info);
88                 break;
89         }
90 }
91
92 static char const hex2ascii_lower[] = "0123456789abcdefghijklmnopqrstuvwxyz";
93 static char const hex2ascii_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
94 #define hex2ascii(hex)  (hex2ascii_lower[hex])
95 #define hex2ascii_upper(hex)    (hex2ascii_upper[hex])
96
97 static __inline int
98 imax(int a, int b)
99 {
100
101         return (a > b ? a : b);
102 }
103
104 static char *
105 ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
106 {
107         char *p, c;
108
109         p = nbuf;
110         *p = '\0';
111         do {
112                 c = upper ? hex2ascii_upper(num % base) :
113                     hex2ascii(num % base);
114                 *++p = c;
115         } while (num /= base);
116         if (lenp)
117                 *lenp = p - nbuf;
118         return (p);
119 }
120
121 static int
122 kvprintf(char const *fmt, struct snprintf_arg *arg, int radix, va_list ap)
123 {
124 #define PCHAR(c) snprintf_func((c), arg)
125         char nbuf[MAXNBUF];
126         const char *p, *percent, *q;
127         u_char *up;
128         int ch, n;
129         uintmax_t num;
130         int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
131         int cflag, hflag, jflag, tflag, zflag;
132         int dwidth, upper;
133         char padc;
134         int stop = 0, retval = 0;
135
136         num = 0;
137
138         if (fmt == NULL)
139                 fmt = "(fmt null)\n";
140
141         if (radix < 2 || radix > 36)
142                 radix = 10;
143
144         for (;;) {
145                 padc = ' ';
146                 width = 0;
147                 while ((ch = (u_char)*fmt++) != '%' || stop) {
148                         if (ch == '\0')
149                                 return (retval);
150                         PCHAR(ch);
151                 }
152                 percent = fmt - 1;
153                 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
154                 sign = 0; dot = 0; dwidth = 0; upper = 0;
155                 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
156 reswitch:       switch (ch = (u_char)*fmt++) {
157                 case '.':
158                         dot = 1;
159                         goto reswitch;
160                 case '#':
161                         sharpflag = 1;
162                         goto reswitch;
163                 case '+':
164                         sign = 1;
165                         goto reswitch;
166                 case '-':
167                         ladjust = 1;
168                         goto reswitch;
169                 case '%':
170                         PCHAR(ch);
171                         break;
172                 case '*':
173                         if (!dot) {
174                                 width = va_arg(ap, int);
175                                 if (width < 0) {
176                                         ladjust = !ladjust;
177                                         width = -width;
178                                 }
179                         } else {
180                                 dwidth = va_arg(ap, int);
181                         }
182                         goto reswitch;
183                 case '0':
184                         if (!dot) {
185                                 padc = '0';
186                                 goto reswitch;
187                         }
188                 case '1': case '2': case '3': case '4':
189                 case '5': case '6': case '7': case '8': case '9':
190                                 for (n = 0;; ++fmt) {
191                                         n = n * 10 + ch - '0';
192                                         ch = *fmt;
193                                         if (ch < '0' || ch > '9')
194                                                 break;
195                                 }
196                         if (dot)
197                                 dwidth = n;
198                         else
199                                 width = n;
200                         goto reswitch;
201                 case 'b':
202                         num = (u_int)va_arg(ap, int);
203                         p = va_arg(ap, char *);
204                         for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
205                                 PCHAR(*q--);
206
207                         if (num == 0)
208                                 break;
209
210                         for (tmp = 0; *p;) {
211                                 n = *p++;
212                                 if (num & (1 << (n - 1))) {
213                                         PCHAR(tmp ? ',' : '<');
214                                         for (; (n = *p) > ' '; ++p)
215                                                 PCHAR(n);
216                                         tmp = 1;
217                                 } else
218                                         for (; *p > ' '; ++p)
219                                                 continue;
220                         }
221                         if (tmp)
222                                 PCHAR('>');
223                         break;
224                 case 'c':
225                         PCHAR(va_arg(ap, int));
226                         break;
227                 case 'D':
228                         up = va_arg(ap, u_char *);
229                         p = va_arg(ap, char *);
230                         if (!width)
231                                 width = 16;
232                         while(width--) {
233                                 PCHAR(hex2ascii(*up >> 4));
234                                 PCHAR(hex2ascii(*up & 0x0f));
235                                 up++;
236                                 if (width)
237                                         for (q=p;*q;q++)
238                                                 PCHAR(*q);
239                         }
240                         break;
241                 case 'd':
242                 case 'i':
243                         base = 10;
244                         sign = 1;
245                         goto handle_sign;
246                 case 'h':
247                         if (hflag) {
248                                 hflag = 0;
249                                 cflag = 1;
250                         } else
251                                 hflag = 1;
252                         goto reswitch;
253                 case 'j':
254                         jflag = 1;
255                         goto reswitch;
256                 case 'l':
257                         if (lflag) {
258                                 lflag = 0;
259                                 qflag = 1;
260                         } else
261                                 lflag = 1;
262                         goto reswitch;
263                 case 'n':
264                         if (jflag)
265                                 *(va_arg(ap, intmax_t *)) = retval;
266                         else if (qflag)
267                                 *(va_arg(ap, quad_t *)) = retval;
268                         else if (lflag)
269                                 *(va_arg(ap, long *)) = retval;
270                         else if (zflag)
271                                 *(va_arg(ap, size_t *)) = retval;
272                         else if (hflag)
273                                 *(va_arg(ap, short *)) = retval;
274                         else if (cflag)
275                                 *(va_arg(ap, char *)) = retval;
276                         else
277                                 *(va_arg(ap, int *)) = retval;
278                         break;
279                 case 'o':
280                         base = 8;
281                         goto handle_nosign;
282                 case 'p':
283                         base = 16;
284                         sharpflag = (width == 0);
285                         sign = 0;
286                         num = (uintptr_t)va_arg(ap, void *);
287                         goto number;
288                 case 'q':
289                         qflag = 1;
290                         goto reswitch;
291                 case 'r':
292                         base = radix;
293                         if (sign)
294                                 goto handle_sign;
295                         goto handle_nosign;
296                 case 's':
297                         p = va_arg(ap, char *);
298                         if (p == NULL)
299                                 p = "(null)";
300                         if (!dot)
301                                 n = strlen (p);
302                         else
303                                 for (n = 0; n < dwidth && p[n]; n++)
304                                         continue;
305
306                         width -= n;
307
308                         if (!ladjust && width > 0)
309                                 while (width--)
310                                         PCHAR(padc);
311                         while (n--)
312                                 PCHAR(*p++);
313                         if (ladjust && width > 0)
314                                 while (width--)
315                                         PCHAR(padc);
316                         break;
317                 case 't':
318                         tflag = 1;
319                         goto reswitch;
320                 case 'u':
321                         base = 10;
322                         goto handle_nosign;
323                 case 'X':
324                         upper = 1;
325                 case 'x':
326                         base = 16;
327                         goto handle_nosign;
328                 case 'y':
329                         base = 16;
330                         sign = 1;
331                         goto handle_sign;
332                 case 'z':
333                         zflag = 1;
334                         goto reswitch;
335 handle_nosign:
336                         sign = 0;
337                         if (jflag)
338                                 num = va_arg(ap, uintmax_t);
339                         else if (qflag)
340                                 num = va_arg(ap, u_quad_t);
341                         else if (tflag)
342                                 num = va_arg(ap, ptrdiff_t);
343                         else if (lflag)
344                                 num = va_arg(ap, u_long);
345                         else if (zflag)
346                                 num = va_arg(ap, size_t);
347                         else if (hflag)
348                                 num = (u_short)va_arg(ap, int);
349                         else if (cflag)
350                                 num = (u_char)va_arg(ap, int);
351                         else
352                                 num = va_arg(ap, u_int);
353                         goto number;
354 handle_sign:
355                         if (jflag)
356                                 num = va_arg(ap, intmax_t);
357                         else if (qflag)
358                                 num = va_arg(ap, quad_t);
359                         else if (tflag)
360                                 num = va_arg(ap, ptrdiff_t);
361                         else if (lflag)
362                                 num = va_arg(ap, long);
363                         else if (zflag)
364                                 num = va_arg(ap, ssize_t);
365                         else if (hflag)
366                                 num = (short)va_arg(ap, int);
367                         else if (cflag)
368                                 num = (char)va_arg(ap, int);
369                         else
370                                 num = va_arg(ap, int);
371 number:
372                         if (sign && (intmax_t)num < 0) {
373                                 neg = 1;
374                                 num = -(intmax_t)num;
375                         }
376                         p = ksprintn(nbuf, num, base, &n, upper);
377                         tmp = 0;
378                         if (sharpflag && num != 0) {
379                                 if (base == 8)
380                                         tmp++;
381                                 else if (base == 16)
382                                         tmp += 2;
383                         }
384                         if (neg)
385                                 tmp++;
386
387                         if (!ladjust && padc == '0')
388                                 dwidth = width - tmp;
389                         width -= tmp + imax(dwidth, n);
390                         dwidth -= n;
391                         if (!ladjust)
392                                 while (width-- > 0)
393                                         PCHAR(' ');
394                         if (neg)
395                                 PCHAR('-');
396                         if (sharpflag && num != 0) {
397                                 if (base == 8) {
398                                         PCHAR('0');
399                                 } else if (base == 16) {
400                                         PCHAR('0');
401                                         PCHAR('x');
402                                 }
403                         }
404                         while (dwidth-- > 0)
405                                 PCHAR('0');
406
407                         while (*p)
408                                 PCHAR(*p--);
409
410                         if (ladjust)
411                                 while (width-- > 0)
412                                         PCHAR(' ');
413
414                         break;
415                 default:
416                         while (percent < fmt)
417                                 PCHAR(*percent++);
418                         /*
419                          * Since we ignore an formatting argument it is no
420                          * longer safe to obey the remaining formatting
421                          * arguments as the arguments will no longer match
422                          * the format specs.
423                          */
424                         stop = 1;
425                         break;
426                 }
427         }
428 #undef PCHAR
429 }
430
431 int
432 rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap)
433 {
434         struct snprintf_arg info;
435         int retval;
436
437         info.method = PRINT_METHOD_SNPRINTF;
438         info.buf = info.str = buf;
439         info.buf_total = info.remain = bufsize;
440         info.fd = -1;
441         retval = kvprintf(fmt, &info, 10, ap);
442         if (info.remain >= 1)
443                 *info.str++ = '\0';
444         return (retval);
445 }
446
447 int
448 rtld_vfdprintf(int fd, const char *fmt, va_list ap)
449 {
450         char buf[512];
451         struct snprintf_arg info;
452         int retval;
453
454         info.method = PRINT_METHOD_WRITE;
455         info.buf = info.str = buf;
456         info.buf_total = info.remain = sizeof(buf);
457         info.fd = fd;
458         retval = kvprintf(fmt, &info, 10, ap);
459         printf_out(&info);
460         return (retval);
461 }
462
463 int
464 rtld_fdprintf(int fd, const char *fmt, ...)
465 {
466         va_list ap;
467         int retval;
468
469         va_start(ap, fmt);
470         retval = rtld_vfdprintf(fd, fmt, ap);
471         va_end(ap);
472         return (retval);
473 }
474
475 void
476 rtld_fdputstr(int fd, const char *str)
477 {
478
479         write(fd, str, strlen(str));
480 }
481
482 void
483 rtld_fdputchar(int fd, int c)
484 {
485         char c1;
486
487         c1 = c;
488         write(fd, &c1, 1);
489 }