|
@@ -1,15 +1,35 @@
|
|
1
|
+/*
|
|
2
|
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
|
|
3
|
+ *
|
|
4
|
+ * This program is free software; you can redistribute it and/or
|
|
5
|
+ * modify it under the terms of the GNU General Public License as
|
|
6
|
+ * published by the Free Software Foundation; either version 2 of the
|
|
7
|
+ * License, or any later version.
|
|
8
|
+ *
|
|
9
|
+ * This program is distributed in the hope that it will be useful, but
|
|
10
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12
|
+ * General Public License for more details.
|
|
13
|
+ *
|
|
14
|
+ * You should have received a copy of the GNU General Public License
|
|
15
|
+ * along with this program; if not, write to the Free Software
|
|
16
|
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
17
|
+ */
|
|
18
|
+
|
1
|
19
|
#include <stddef.h>
|
2
|
20
|
#include <stdarg.h>
|
3
|
21
|
#include <console.h>
|
4
|
22
|
#include <errno.h>
|
5
|
23
|
#include <vsprintf.h>
|
6
|
24
|
|
7
|
|
-#define CHAR_LEN 1
|
8
|
|
-#define SHORT_LEN 2
|
9
|
|
-#define INT_LEN 3
|
10
|
|
-#define LONG_LEN 4
|
11
|
|
-#define LONGLONG_LEN 5
|
12
|
|
-#define SIZE_T_LEN 6
|
|
25
|
+/** @file */
|
|
26
|
+
|
|
27
|
+#define CHAR_LEN 0 /**< "hh" length modifier */
|
|
28
|
+#define SHORT_LEN 1 /**< "h" length modifier */
|
|
29
|
+#define INT_LEN 2 /**< no length modifier */
|
|
30
|
+#define LONG_LEN 3 /**< "l" length modifier */
|
|
31
|
+#define LONGLONG_LEN 4 /**< "ll" length modifier */
|
|
32
|
+#define SIZE_T_LEN 5 /**< "z" length modifier */
|
13
|
33
|
|
14
|
34
|
static uint8_t type_sizes[] = {
|
15
|
35
|
[CHAR_LEN] = sizeof ( char ),
|
|
@@ -20,8 +40,6 @@ static uint8_t type_sizes[] = {
|
20
|
40
|
[SIZE_T_LEN] = sizeof ( size_t ),
|
21
|
41
|
};
|
22
|
42
|
|
23
|
|
-/** @file */
|
24
|
|
-
|
25
|
43
|
/**
|
26
|
44
|
* A printf context
|
27
|
45
|
*
|
|
@@ -48,36 +66,80 @@ struct printf_context {
|
48
|
66
|
size_t max_len;
|
49
|
67
|
};
|
50
|
68
|
|
|
69
|
+/**
|
|
70
|
+ * Use lower-case for hexadecimal digits
|
|
71
|
+ *
|
|
72
|
+ * Note that this value is set to 0x20 since that makes for very
|
|
73
|
+ * efficient calculations. (Bitwise-ORing with @c LCASE converts to a
|
|
74
|
+ * lower-case character, for example.)
|
|
75
|
+ */
|
51
|
76
|
#define LCASE 0x20
|
|
77
|
+
|
|
78
|
+/**
|
|
79
|
+ * Use "alternate form"
|
|
80
|
+ *
|
|
81
|
+ * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
|
|
82
|
+ * the number.
|
|
83
|
+ */
|
52
|
84
|
#define ALT_FORM 0x02
|
53
|
85
|
|
54
|
|
-static char * format_hex ( char *buf, unsigned long long num, int width,
|
|
86
|
+/**
|
|
87
|
+ * Format a hexadecimal number
|
|
88
|
+ *
|
|
89
|
+ * @v end End of buffer to contain number
|
|
90
|
+ * @v num Number to format
|
|
91
|
+ * @v width Minimum field width
|
|
92
|
+ * @ret ptr End of buffer
|
|
93
|
+ *
|
|
94
|
+ * Fills a buffer in reverse order with a formatted hexadecimal
|
|
95
|
+ * number. The number will be zero-padded to the specified width.
|
|
96
|
+ * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
|
|
97
|
+ * set.
|
|
98
|
+ *
|
|
99
|
+ * There must be enough space in the buffer to contain the largest
|
|
100
|
+ * number that this function can format.
|
|
101
|
+ */
|
|
102
|
+static char * format_hex ( char *end, unsigned long long num, int width,
|
55
|
103
|
int flags ) {
|
56
|
|
- char *ptr = buf;
|
|
104
|
+ char *ptr = end;
|
57
|
105
|
int case_mod;
|
58
|
106
|
|
59
|
107
|
/* Generate the number */
|
60
|
108
|
case_mod = flags & LCASE;
|
61
|
109
|
do {
|
62
|
|
- *ptr++ = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
|
|
110
|
+ *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
|
63
|
111
|
num >>= 4;
|
64
|
112
|
} while ( num );
|
65
|
113
|
|
66
|
114
|
/* Zero-pad to width */
|
67
|
|
- while ( ( ptr - buf ) < width )
|
68
|
|
- *ptr++ = '0';
|
|
115
|
+ while ( ( end - ptr ) < width )
|
|
116
|
+ *(--ptr) = '0';
|
69
|
117
|
|
70
|
118
|
/* Add "0x" or "0X" if alternate form specified */
|
71
|
119
|
if ( flags & ALT_FORM ) {
|
72
|
|
- *ptr++ = 'X' | case_mod;
|
73
|
|
- *ptr++ = '0';
|
|
120
|
+ *(--ptr) = 'X' | case_mod;
|
|
121
|
+ *(--ptr) = '0';
|
74
|
122
|
}
|
75
|
123
|
|
76
|
124
|
return ptr;
|
77
|
125
|
}
|
78
|
126
|
|
79
|
|
-static char * format_decimal ( char *buf, signed long num, int width ) {
|
80
|
|
- char *ptr = buf;
|
|
127
|
+/**
|
|
128
|
+ * Format a decimal number
|
|
129
|
+ *
|
|
130
|
+ * @v end End of buffer to contain number
|
|
131
|
+ * @v num Number to format
|
|
132
|
+ * @v width Minimum field width
|
|
133
|
+ * @ret ptr End of buffer
|
|
134
|
+ *
|
|
135
|
+ * Fills a buffer in reverse order with a formatted decimal number.
|
|
136
|
+ * The number will be space-padded to the specified width.
|
|
137
|
+ *
|
|
138
|
+ * There must be enough space in the buffer to contain the largest
|
|
139
|
+ * number that this function can format.
|
|
140
|
+ */
|
|
141
|
+static char * format_decimal ( char *end, signed long num, int width ) {
|
|
142
|
+ char *ptr = end;
|
81
|
143
|
int negative = 0;
|
82
|
144
|
|
83
|
145
|
/* Generate the number */
|
|
@@ -86,22 +148,21 @@ static char * format_decimal ( char *buf, signed long num, int width ) {
|
86
|
148
|
num = -num;
|
87
|
149
|
}
|
88
|
150
|
do {
|
89
|
|
- *ptr++ = '0' + ( num % 10 );
|
|
151
|
+ *(--ptr) = '0' + ( num % 10 );
|
90
|
152
|
num /= 10;
|
91
|
153
|
} while ( num );
|
92
|
154
|
|
93
|
155
|
/* Add "-" if necessary */
|
94
|
156
|
if ( negative )
|
95
|
|
- *ptr++ = '-';
|
|
157
|
+ *(--ptr) = '-';
|
96
|
158
|
|
97
|
159
|
/* Space-pad to width */
|
98
|
|
- while ( ( ptr - buf ) < width )
|
99
|
|
- *ptr++ = ' ';
|
|
160
|
+ while ( ( end - ptr ) < width )
|
|
161
|
+ *(--ptr) = ' ';
|
100
|
162
|
|
101
|
163
|
return ptr;
|
102
|
164
|
}
|
103
|
165
|
|
104
|
|
-
|
105
|
166
|
/**
|
106
|
167
|
* Write a formatted string to a printf context
|
107
|
168
|
*
|
|
@@ -114,11 +175,9 @@ int vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
|
114
|
175
|
int flags;
|
115
|
176
|
int width;
|
116
|
177
|
uint8_t *length;
|
117
|
|
- int character;
|
118
|
|
- unsigned long long hex;
|
119
|
|
- signed long decimal;
|
120
|
|
- char num_buf[32];
|
121
|
178
|
char *ptr;
|
|
179
|
+ char tmp_buf[32]; /* 32 is enough for all numerical formats.
|
|
180
|
+ * Insane width fields could overflow this buffer. */
|
122
|
181
|
|
123
|
182
|
/* Initialise context */
|
124
|
183
|
ctx->len = 0;
|
|
@@ -166,22 +225,21 @@ int vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
|
166
|
225
|
}
|
167
|
226
|
}
|
168
|
227
|
/* Process conversion specifier */
|
|
228
|
+ ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
|
|
229
|
+ *ptr = '\0';
|
169
|
230
|
if ( *fmt == 'c' ) {
|
170
|
|
- character = va_arg ( args, unsigned int );
|
171
|
|
- ctx->handler ( ctx, character );
|
|
231
|
+ *(--ptr) = va_arg ( args, unsigned int );
|
172
|
232
|
} else if ( *fmt == 's' ) {
|
173
|
233
|
ptr = va_arg ( args, char * );
|
174
|
|
- for ( ; *ptr ; ptr++ ) {
|
175
|
|
- ctx->handler ( ctx, *ptr );
|
176
|
|
- }
|
177
|
234
|
} else if ( *fmt == 'p' ) {
|
178
|
|
- hex = ( intptr_t ) va_arg ( args, void * );
|
179
|
|
- ptr = format_hex ( num_buf, hex, width,
|
|
235
|
+ intptr_t ptrval;
|
|
236
|
+
|
|
237
|
+ ptrval = ( intptr_t ) va_arg ( args, void * );
|
|
238
|
+ ptr = format_hex ( ptr, ptrval, width,
|
180
|
239
|
( ALT_FORM | LCASE ) );
|
181
|
|
- do {
|
182
|
|
- ctx->handler ( ctx, *(--ptr) );
|
183
|
|
- } while ( ptr != num_buf );
|
184
|
240
|
} else if ( ( *fmt & ~0x20 ) == 'X' ) {
|
|
241
|
+ unsigned long long hex;
|
|
242
|
+
|
185
|
243
|
flags |= ( *fmt & 0x20 ); /* LCASE */
|
186
|
244
|
if ( *length >= sizeof ( unsigned long long ) ) {
|
187
|
245
|
hex = va_arg ( args, unsigned long long );
|
|
@@ -190,22 +248,22 @@ int vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
|
190
|
248
|
} else {
|
191
|
249
|
hex = va_arg ( args, unsigned int );
|
192
|
250
|
}
|
193
|
|
- ptr = format_hex ( num_buf, hex, width, flags );
|
194
|
|
- do {
|
195
|
|
- ctx->handler ( ctx, *(--ptr) );
|
196
|
|
- } while ( ptr != num_buf );
|
|
251
|
+ ptr = format_hex ( ptr, hex, width, flags );
|
197
|
252
|
} else if ( *fmt == 'd' ) {
|
|
253
|
+ signed long decimal;
|
|
254
|
+
|
198
|
255
|
if ( *length >= sizeof ( signed long ) ) {
|
199
|
256
|
decimal = va_arg ( args, signed long );
|
200
|
257
|
} else {
|
201
|
258
|
decimal = va_arg ( args, signed int );
|
202
|
259
|
}
|
203
|
|
- ptr = format_decimal ( num_buf, decimal, width );
|
204
|
|
- do {
|
205
|
|
- ctx->handler ( ctx, *(--ptr) );
|
206
|
|
- } while ( ptr != num_buf );
|
|
260
|
+ ptr = format_decimal ( ptr, decimal, width );
|
207
|
261
|
} else {
|
208
|
|
- ctx->handler ( ctx, *fmt );
|
|
262
|
+ *(--ptr) = *fmt;
|
|
263
|
+ }
|
|
264
|
+ /* Write out conversion result */
|
|
265
|
+ for ( ; *ptr ; ptr++ ) {
|
|
266
|
+ ctx->handler ( ctx, *ptr );
|
209
|
267
|
}
|
210
|
268
|
}
|
211
|
269
|
|