xref: /qemu/tests/tcg/minilib/printf.c (revision abff1abf)
1 /*
2  * Copyright (C) 2015 Virtual Open Systems SAS
3  * Author: Alexander Spyridakis <a.spyridakis@virtualopensystems.com>
4  *
5  * printf based on implementation by Kevin Wolf <kwolf@redhat.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  *
11  * SPDX-License-Identifier: GPL-2.0-only
12  */
13 
14 #include "minilib.h"
15 
16 typedef __builtin_va_list   va_list;
17 #define va_start(ap, X)     __builtin_va_start(ap, X)
18 #define va_arg(ap, type)    __builtin_va_arg(ap, type)
19 #define va_end(ap)          __builtin_va_end(ap)
20 
21 static void print_str(char *s)
22 {
23     while (*s) {
24         __sys_outc(*s++);
25     }
26 }
27 
28 static void print_num(unsigned long long value, int base)
29 {
30     char digits[] = "0123456789abcdef";
31     char buf[32];
32     int i = sizeof(buf) - 2, j;
33 
34     /* Set the buffer to 0. See problem of before. */
35     for (j = 0; j < 32; j++) {
36         buf[j] = 0;
37     }
38 
39     do {
40         buf[i--] = digits[value % base];
41         value /= base;
42     } while (value);
43 
44     print_str(&buf[i + 1]);
45 }
46 
47 void ml_printf(const char *fmt, ...)
48 {
49     va_list ap;
50     char *str;
51     int base;
52     int has_long;
53     int alt_form;
54     unsigned long long val;
55 
56     va_start(ap, fmt);
57 
58     for (; *fmt; fmt++) {
59         if (*fmt != '%') {
60             __sys_outc(*fmt);
61             continue;
62         }
63         fmt++;
64 
65         if (*fmt == '#') {
66             fmt++;
67             alt_form = 1;
68         } else {
69             alt_form = 0;
70         }
71 
72         if (*fmt == 'l') {
73             fmt++;
74             if (*fmt == 'l') {
75                 fmt++;
76                 has_long = 2;
77             } else {
78                 has_long = 1;
79             }
80         } else {
81             has_long = 0;
82         }
83 
84         switch (*fmt) {
85         case 'x':
86         case 'p':
87             base = 16;
88             goto convert_number;
89         case 'd':
90         case 'i':
91         case 'u':
92             base = 10;
93             goto convert_number;
94         case 'o':
95             base = 8;
96             goto convert_number;
97 
98         convert_number:
99             switch (has_long) {
100             case 0:
101                 val = va_arg(ap, unsigned int);
102                 break;
103             case 1:
104                 val = va_arg(ap, unsigned long);
105                 break;
106             case 2:
107                 val = va_arg(ap, unsigned long long);
108                 break;
109             }
110 
111             if (alt_form && base == 16) {
112                 print_str("0x");
113             }
114 
115             print_num(val, base);
116             break;
117 
118         case 's':
119             str = va_arg(ap, char*);
120             print_str(str);
121             break;
122         case 'c':
123             __sys_outc(va_arg(ap, int));
124             break;
125         case '%':
126             __sys_outc(*fmt);
127             break;
128         default:
129             __sys_outc('%');
130             __sys_outc(*fmt);
131             break;
132         }
133     }
134 
135     va_end(ap);
136 }
137