b9aadc3587b00989f11f48547c373cc54722dc65
[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  * 4. 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 <ctype.h>
40 #include <inttypes.h>
41 #include <stdarg.h>
42 #include <stddef.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include "rtld_printf.h"
46
47 #define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
48
49 #define PRINT_METHOD_SNPRINTF   1
50 #define PRINT_METHOD_WRITE      2
51
52 struct snprintf_arg {
53         int     method;
54         char    *str;
55         char    *buf;
56         size_t  remain;
57         size_t  buf_total;
58         int     fd;
59 };
60
61 static void
62 printf_out(struct snprintf_arg *info)
63 {
64
65         if (info->remain == info->buf_total)
66                 return;
67         write(info->fd, info->buf, info->buf_total - info->remain);
68         info->str = info->buf;
69         info->remain = info->buf_total;
70 }
71
72 static void
73 snprintf_func(int ch, struct snprintf_arg *const info)
74 {
75
76         switch (info->method) {
77         case PRINT_METHOD_SNPRINTF:
78                 if (info->remain >= 2) {
79                         *info->str++ = ch;
80                         info->remain--;
81                 }
82                 break;
83         case PRINT_METHOD_WRITE:
84                 if (info->remain > 0) {
85                         *info->str++ = ch;
86                         info->remain--;
87                 } else
88                         printf_out(info);
89                 break;
90         }
91 }
92
93 static char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
94 #define hex2ascii(hex)  (hex2ascii_data[hex])
95
96 static __inline int
97 imax(int a, int b)
98 {
99
100         return (a > b ? a : b);
101 }
102
103 static char *
104 ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
105 {
106         char *p, c;
107
108         p = nbuf;
109         *p = '\0';
110         do {
111                 c = hex2ascii(num % base);
112                 *++p = upper ? toupper(c) : c;
113         } while (num /= base);
114         if (lenp)
115                 *lenp = p - nbuf;
116         return (p);
117 }
118
119 static int
120 kvprintf(char const *fmt, struct snprintf_arg *arg, int radix, va_list ap)
121 {
122 #define PCHAR(c) snprintf_func((c), arg)
123         char nbuf[MAXNBUF];
124         const char *p, *percent, *q;
125         u_char *up;
126         int ch, n;
127         uintmax_t num;
128         int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
129         int cflag, hflag, jflag, tflag, zflag;
130         int dwidth, upper;
131         char padc;
132         int stop = 0, retval = 0;
133
134         num = 0;
135
136         if (fmt == NULL)
137                 fmt = "(fmt null)\n";
138
139         if (radix < 2 || radix > 36)
140                 radix = 10;
141
142         for (;;) {
143                 padc = ' ';
144                 width = 0;
145                 while ((ch = (u_char)*fmt++) != '%' || stop) {
146                         if (ch == '\0')
147                                 return (retval);
148                         PCHAR(ch);
149                 }
150                 percent = fmt - 1;
151                 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
152                 sign = 0; dot = 0; dwidth = 0; upper = 0;
153                 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
154 reswitch:       switch (ch = (u_char)*fmt++) {
155                 case '.':
156                         dot = 1;
157                         goto reswitch;
158                 case '#':
159                         sharpflag = 1;
160                         goto reswitch;
161                 case '+':
162                         sign = 1;
163                         goto reswitch;
164                 case '-':
165                         ladjust = 1;
166                         goto reswitch;
167                 case '%':
168                         PCHAR(ch);
169                         break;
170                 case '*':
171                         if (!dot) {
172                                 width = va_arg(ap, int);
173                                 if (width < 0) {
174                                         ladjust = !ladjust;
175                                         width = -width;
176                                 }
177                         } else {
178                                 dwidth = va_arg(ap, int);
179                         }
180                         goto reswitch;
181                 case '0':
182                         if (!dot) {
183                                 padc = '0';
184                                 goto reswitch;
185                         }
186                 case '1': case '2': case '3': case '4':
187                 case '5': case '6': case '7': case '8': case '9':
188                                 for (n = 0;; ++fmt) {
189                                         n = n * 10 + ch - '0';
190                                         ch = *fmt;
191                                         if (ch < '0' || ch > '9')
192                                                 break;
193                                 }
194                         if (dot)
195                                 dwidth = n;
196                         else
197                                 width = n;
198                         goto reswitch;
199                 case 'b':
200                         num = (u_int)va_arg(ap, int);
201                         p = va_arg(ap, char *);
202                         for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
203                                 PCHAR(*q--);
204
205                         if (num == 0)
206                                 break;
207
208                         for (tmp = 0; *p;) {
209                                 n = *p++;
210                                 if (num & (1 << (n - 1))) {
211                                         PCHAR(tmp ? ',' : '<');
212                                         for (; (n = *p) > ' '; ++p)
213                                                 PCHAR(n);
214                                         tmp = 1;
215                                 } else
216                                         for (; *p > ' '; ++p)
217                                                 continue;
218                         }
219                         if (tmp)
220                                 PCHAR('>');
221                         break;
222                 case 'c':
223                         PCHAR(va_arg(ap, int));
224                         break;
225                 case 'D':
226                         up = va_arg(ap, u_char *);
227                         p = va_arg(ap, char *);
228                         if (!width)
229                                 width = 16;
230                         while(width--) {
231                                 PCHAR(hex2ascii(*up >> 4));
232                                 PCHAR(hex2ascii(*up & 0x0f));
233                                 up++;
234                                 if (width)
235                                         for (q=p;*q;q++)
236                                                 PCHAR(*q);
237                         }
238                         break;
239                 case 'd':
240                 case 'i':
241                         base = 10;
242                         sign = 1;
243                         goto handle_sign;
244                 case 'h':
245                         if (hflag) {
246                                 hflag = 0;
247                                 cflag = 1;
248                         } else
249                                 hflag = 1;
250                         goto reswitch;
251                 case 'j':
252                         jflag = 1;
253                         goto reswitch;
254                 case 'l':
255                         if (lflag) {
256                                 lflag = 0;
257                                 qflag = 1;
258                         } else
259                                 lflag = 1;
260                         goto reswitch;
261                 case 'n':
262                         if (jflag)
263                                 *(va_arg(ap, intmax_t *)) = retval;
264                         else if (qflag)
265                                 *(va_arg(ap, quad_t *)) = retval;
266                         else if (lflag)
267                                 *(va_arg(ap, long *)) = retval;
268                         else if (zflag)
269                                 *(va_arg(ap, size_t *)) = retval;
270                         else if (hflag)
271                                 *(va_arg(ap, short *)) = retval;
272                         else if (cflag)
273                                 *(va_arg(ap, char *)) = retval;
274                         else
275                                 *(va_arg(ap, int *)) = retval;
276                         break;
277                 case 'o':
278                         base = 8;
279                         goto handle_nosign;
280                 case 'p':
281                         base = 16;
282                         sharpflag = (width == 0);
283                         sign = 0;
284                         num = (uintptr_t)va_arg(ap, void *);
285                         goto number;
286                 case 'q':
287                         qflag = 1;
288                         goto reswitch;
289                 case 'r':
290                         base = radix;
291                         if (sign)
292                                 goto handle_sign;
293                         goto handle_nosign;
294                 case 's':
295                         p = va_arg(ap, char *);
296                         if (p == NULL)
297                                 p = "(null)";
298                         if (!dot)
299                                 n = strlen (p);
300                         else
301                                 for (n = 0; n < dwidth && p[n]; n++)
302                                         continue;
303
304                         width -= n;
305
306                         if (!ladjust && width > 0)
307                                 while (width--)
308                                         PCHAR(padc);
309                         while (n--)
310                                 PCHAR(*p++);
311                         if (ladjust && width > 0)
312                                 while (width--)
313                                         PCHAR(padc);
314                         break;
315                 case 't':
316                         tflag = 1;
317                         goto reswitch;
318                 case 'u':
319                         base = 10;
320                         goto handle_nosign;
321                 case 'X':
322                         upper = 1;
323                 case 'x':
324                         base = 16;
325                         goto handle_nosign;
326                 case 'y':
327                         base = 16;
328                         sign = 1;
329                         goto handle_sign;
330                 case 'z':
331                         zflag = 1;
332                         goto reswitch;
333 handle_nosign:
334                         sign = 0;
335                         if (jflag)
336                                 num = va_arg(ap, uintmax_t);
337                         else if (qflag)
338                                 num = va_arg(ap, u_quad_t);
339                         else if (tflag)
340                                 num = va_arg(ap, ptrdiff_t);
341                         else if (lflag)
342                                 num = va_arg(ap, u_long);
343                         else if (zflag)
344                                 num = va_arg(ap, size_t);
345                         else if (hflag)
346                                 num = (u_short)va_arg(ap, int);
347                         else if (cflag)
348                                 num = (u_char)va_arg(ap, int);
349                         else
350                                 num = va_arg(ap, u_int);
351                         goto number;
352 handle_sign:
353                         if (jflag)
354                                 num = va_arg(ap, intmax_t);
355                         else if (qflag)
356                                 num = va_arg(ap, quad_t);
357                         else if (tflag)
358                                 num = va_arg(ap, ptrdiff_t);
359                         else if (lflag)
360                                 num = va_arg(ap, long);
361                         else if (zflag)
362                                 num = va_arg(ap, ssize_t);
363                         else if (hflag)
364                                 num = (short)va_arg(ap, int);
365                         else if (cflag)
366                                 num = (char)va_arg(ap, int);
367                         else
368                                 num = va_arg(ap, int);
369 number:
370                         if (sign && (intmax_t)num < 0) {
371                                 neg = 1;
372                                 num = -(intmax_t)num;
373                         }
374                         p = ksprintn(nbuf, num, base, &n, upper);
375                         tmp = 0;
376                         if (sharpflag && num != 0) {
377                                 if (base == 8)
378                                         tmp++;
379                                 else if (base == 16)
380                                         tmp += 2;
381                         }
382                         if (neg)
383                                 tmp++;
384
385                         if (!ladjust && padc == '0')
386                                 dwidth = width - tmp;
387                         width -= tmp + imax(dwidth, n);
388                         dwidth -= n;
389                         if (!ladjust)
390                                 while (width-- > 0)
391                                         PCHAR(' ');
392                         if (neg)
393                                 PCHAR('-');
394                         if (sharpflag && num != 0) {
395                                 if (base == 8) {
396                                         PCHAR('0');
397                                 } else if (base == 16) {
398                                         PCHAR('0');
399                                         PCHAR('x');
400                                 }
401                         }
402                         while (dwidth-- > 0)
403                                 PCHAR('0');
404
405                         while (*p)
406                                 PCHAR(*p--);
407
408                         if (ladjust)
409                                 while (width-- > 0)
410                                         PCHAR(' ');
411
412                         break;
413                 default:
414                         while (percent < fmt)
415                                 PCHAR(*percent++);
416                         /*
417                          * Since we ignore an formatting argument it is no
418                          * longer safe to obey the remaining formatting
419                          * arguments as the arguments will no longer match
420                          * the format specs.
421                          */
422                         stop = 1;
423                         break;
424                 }
425         }
426 #undef PCHAR
427 }
428
429 int
430 rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap)
431 {
432         struct snprintf_arg info;
433         int retval;
434
435         info.method = PRINT_METHOD_SNPRINTF;
436         info.buf = info.str = buf;
437         info.buf_total = info.remain = bufsize;
438         info.fd = -1;
439         retval = kvprintf(fmt, &info, 10, ap);
440         if (info.remain >= 1)
441                 *info.str++ = '\0';
442         return (retval);
443 }
444
445 int
446 rtld_vfdprintf(int fd, const char *fmt, va_list ap)
447 {
448         char buf[512];
449         struct snprintf_arg info;
450         int retval;
451
452         info.method = PRINT_METHOD_WRITE;
453         info.buf = info.str = buf;
454         info.buf_total = info.remain = sizeof(buf);
455         info.fd = fd;
456         retval = kvprintf(fmt, &info, 10, ap);
457         printf_out(&info);
458         return (retval);
459 }
460
461 int
462 rtld_fdprintf(int fd, const char *fmt, ...)
463 {
464         va_list ap;
465         int retval;
466
467         va_start(ap, fmt);
468         retval = rtld_vfdprintf(fd, fmt, ap);
469         va_end(ap);
470         return (retval);
471 }
472
473 void
474 rtld_fdputstr(int fd, const char *str)
475 {
476
477         write(fd, str, strlen(str));
478 }
479
480 void
481 rtld_fdputchar(int fd, int c)
482 {
483         char c1;
484
485         c1 = c;
486         write(fd, &c1, 1);
487 }