blob: c4d49ce8dc432845ebd20da3615779ba0d1797a1 [file] [log] [blame]
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01001#include "dlog.h"
2
3#include <stdbool.h>
4#include <stddef.h>
5#include <stdarg.h>
6
7#include "arch.h"
8#include "spinlock.h"
9#include "std.h"
10
11#define FLAG_SPACE 0x01
12#define FLAG_ZERO 0x02
13#define FLAG_MINUS 0x04
14#define FLAG_PLUS 0x08
15#define FLAG_ALT 0x10
16#define FLAG_UPPER 0x20
17#define FLAG_NEG 0x40
18
19/*
20 * Prints a raw string to the debug log and returns its length.
21 */
22static size_t print_raw_string(const char *str)
23{
24 const char *c = str;
25 while (*c != '\0')
26 arch_putchar(*c++);
27 return c - str;
28}
29
30/*
31 * Prints a formatted string to the debug log. The format includes a minimum
32 * width, the fill character, and flags (whether to align to left or right).
33 *
34 * str is the full string, while suffix is a pointer within str that indicates
35 * where the suffix begins. This is used when printing right-aligned numbers
36 * with a zero fill; for example, -10 with width 4 should be padded to -010,
37 * so suffix would point to index one of the "-10" string .
38 */
39static void print_string(const char *str, const char *suffix, size_t width,
40 int flags, char fill)
41{
42 size_t len = suffix - str;
43
44 /* Print the string up to the beginning of the suffix. */
45 while (str != suffix)
46 arch_putchar(*str++);
47
48 if (flags & FLAG_MINUS) {
49 /* Left-aligned. Print suffix, then print padding if needed. */
50 len += print_raw_string(suffix);
51 while (len < width) {
52 arch_putchar(' ');
53 len++;
54 }
55 return;
56 }
57
58 /* Fill until we reach the desired length. */
59 len += strlen(suffix);
60 while (len < width) {
61 arch_putchar(fill);
62 len++;
63 }
64
65 /* Now print the rest of the string. */
66 print_raw_string(suffix);
67}
68
69/*
70 * Prints a number to the debug log. The caller specifies the base, its minimum
71 * width and printf-style flags.
72 */
73static void print_num(size_t v, size_t base, size_t width, int flags)
74{
75 static const char *digits_lower = "0123456789abcdefx";
76 static const char *digits_upper = "0123456789ABCDEFX";
77 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
78 char buf[51];
79 char *ptr = buf + sizeof(buf) - 1;
80 char *num;
81 *ptr = '\0';
82 do {
83 --ptr;
84 *ptr = d[v % base];
85 v /= base;
86 } while (v);
87
88 /* Num stores where the actual number begins. */
89 num = ptr;
90
91 /* Add prefix if requested. */
92 if (flags & FLAG_ALT) {
93 switch (base) {
94 case 16:
95 ptr -= 2;
96 ptr[0] = '0';
97 ptr[1] = d[16];
98 break;
99
100 case 8:
101 ptr--;
102 *ptr = '0';
103 break;
104 }
105 }
106
107 /* Add sign if requested. */
108 if (flags & FLAG_NEG)
109 *--ptr = '-';
110 else if (flags & FLAG_PLUS)
111 *--ptr = '+';
112 else if (flags & FLAG_SPACE)
113 *--ptr = ' ';
114
115 if (flags & FLAG_ZERO)
116 print_string(ptr, num, width, flags, '0');
117 else
118 print_string(ptr, ptr, width, flags, ' ');
119}
120
121/*
122 * Parses the optional flags field of a printf-style format. It returns the spot
123 * on the string where a non-flag character was found.
124 */
125static const char *parse_flags(const char *p, int *flags)
126{
127 for (;;) {
128 switch (*p) {
129 case ' ':
130 *flags |= FLAG_SPACE;
131 break;
132
133 case '0':
134 *flags |= FLAG_ZERO;
135 break;
136
137 case '-':
138 *flags |= FLAG_MINUS;
139 break;
140
141 case '+':
142 *flags |= FLAG_PLUS;
143
144 case '#':
145 *flags |= FLAG_ALT;
146 break;
147
148 default:
149 return p;
150 }
151 p++;
152 }
153}
154
155/*
156 * Prints the given format string to the debug log.
157 */
158void dlog(const char *str, ...)
159{
160 static struct spinlock sl = SPINLOCK_INIT;
161 const char *p;
162 va_list args;
163 size_t w;
164 int flags;
165 char buf[2];
166
167 va_start(args, str);
168
169 sl_lock(&sl);
170
171 for (p = str; *p; p++) {
172 switch (*p) {
173 default:
174 arch_putchar(*p);
175 break;
176
177 case '%':
178 /* Read optional flags. */
179 flags = 0;
180 p = parse_flags(p + 1, &flags) - 1;
181
182 /* Read the minimum width, if one is specified. */
183 w = 0;
184 while (p[1] >= '0' && p[1] <= '9') {
185 w = (w * 10) + (p[1] - '0');
186 p++;
187 }
188
189 /* Read minimum width from arguments. */
190 if (w == 0 && p[1] == '*') {
191 int v = va_arg(args, int);
192 if (v >= 0) {
193 w = v;
194 } else {
195 w = -v;
196 flags |= FLAG_MINUS;
197 }
198 p++;
199 }
200
201 /* Handle the format specifier. */
202 switch (p[1]) {
203 case 's':
204 {
205 char *str = va_arg(args, char *);
206 print_string(str, str, w, flags, ' ');
207 }
208 p++;
209 break;
210
211 case 'd':
212 case 'i':
213 {
214 int v = va_arg(args, int);
215 if (v < 0) {
216 flags |= FLAG_NEG;
217 v = -v;
218 }
219
220 print_num((size_t)v, 10, w, flags);
221 }
222 p++;
223 break;
224
225 case 'X':
226 flags |= FLAG_UPPER;
227 print_num(va_arg(args, size_t), 16, w, flags);
228 break;
229
230 case 'p':
231 print_num(va_arg(args, size_t), 16,
232 sizeof(size_t) * 2, FLAG_ZERO);
233 p++;
234 break;
235
236 case 'x':
237 print_num(va_arg(args, size_t), 16, w, flags);
238 p++;
239 break;
240
241 case 'u':
242 print_num(va_arg(args, size_t), 10, w, flags);
243 p++;
244 break;
245
246 case 'o':
247 print_num(va_arg(args, size_t), 8, w, flags);
248 p++;
249 break;
250
251 case 'c':
252 buf[1] = 0;
253 buf[0] = va_arg(args, int);
254 print_string(buf, buf, w, flags, ' ');
255 p++;
256 break;
257
258 case '%':
259 break;
260
261 default:
262 arch_putchar('%');
263 }
264
265 break;
266 }
267 }
268
269 sl_unlock(&sl);
270
271 va_end(args);
272}