|
@@ -1,213 +1,322 @@
|
|
1
|
+#include <stddef.h>
|
1
|
2
|
#include <stdarg.h>
|
2
|
|
-#include <gpxe/if_ether.h> /* for ETH_ALEN */
|
3
|
|
-#include "limits.h" /* for CHAR_BIT */
|
4
|
|
-#include "console.h"
|
5
|
|
-#include "errno.h"
|
6
|
|
-#include "vsprintf.h"
|
7
|
|
-
|
8
|
|
-#define LONGLONG_SHIFT ((int)((sizeof(unsigned long long)*CHAR_BIT) - 4))
|
9
|
|
-#define LONG_SHIFT ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
|
10
|
|
-#define INT_SHIFT ((int)((sizeof(unsigned int)*CHAR_BIT) - 4))
|
11
|
|
-#define SHRT_SHIFT ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
|
12
|
|
-#define CHAR_SHIFT ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))
|
|
3
|
+#include <console.h>
|
|
4
|
+#include <errno.h>
|
|
5
|
+#include <vsprintf.h>
|
|
6
|
+
|
|
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
|
|
13
|
+
|
|
14
|
+static uint8_t type_sizes[] = {
|
|
15
|
+ [CHAR_LEN] = sizeof ( char ),
|
|
16
|
+ [SHORT_LEN] = sizeof ( short ),
|
|
17
|
+ [INT_LEN] = sizeof ( int ),
|
|
18
|
+ [LONG_LEN] = sizeof ( long ),
|
|
19
|
+ [LONGLONG_LEN] = sizeof ( long long ),
|
|
20
|
+ [SIZE_T_LEN] = sizeof ( size_t ),
|
|
21
|
+};
|
13
|
22
|
|
14
|
23
|
/** @file */
|
15
|
24
|
|
16
|
25
|
/**
|
17
|
|
- * Write a formatted string to a buffer.
|
|
26
|
+ * A printf context
|
18
|
27
|
*
|
19
|
|
- * @v buf Buffer into which to write the string, or NULL
|
|
28
|
+ * Contexts are used in order to be able to share code between
|
|
29
|
+ * vprintf() and vsnprintf(), without requiring the allocation of a
|
|
30
|
+ * buffer for vprintf().
|
|
31
|
+ */
|
|
32
|
+struct printf_context {
|
|
33
|
+ /**
|
|
34
|
+ * Character handler
|
|
35
|
+ *
|
|
36
|
+ * @v ctx Context
|
|
37
|
+ * @v c Character
|
|
38
|
+ *
|
|
39
|
+ * This method is called for each character written to the
|
|
40
|
+ * formatted string. It must increment @len.
|
|
41
|
+ */
|
|
42
|
+ void ( * handler ) ( struct printf_context *ctx, unsigned int c );
|
|
43
|
+ /** Length of formatted string */
|
|
44
|
+ size_t len;
|
|
45
|
+ /** Buffer for formatted string (used by printf_sputc()) */
|
|
46
|
+ char *buf;
|
|
47
|
+ /** Buffer length (used by printf_sputc()) */
|
|
48
|
+ size_t max_len;
|
|
49
|
+};
|
|
50
|
+
|
|
51
|
+#define LCASE 0x20
|
|
52
|
+#define ALT_FORM 0x02
|
|
53
|
+
|
|
54
|
+static char * format_hex ( char *buf, unsigned long long num, int width,
|
|
55
|
+ int flags ) {
|
|
56
|
+ char *ptr = buf;
|
|
57
|
+ int case_mod;
|
|
58
|
+
|
|
59
|
+ /* Generate the number */
|
|
60
|
+ case_mod = flags & LCASE;
|
|
61
|
+ do {
|
|
62
|
+ *ptr++ = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
|
|
63
|
+ num >>= 4;
|
|
64
|
+ } while ( num );
|
|
65
|
+
|
|
66
|
+ /* Zero-pad to width */
|
|
67
|
+ while ( ( ptr - buf ) < width )
|
|
68
|
+ *ptr++ = '0';
|
|
69
|
+
|
|
70
|
+ /* Add "0x" or "0X" if alternate form specified */
|
|
71
|
+ if ( flags & ALT_FORM ) {
|
|
72
|
+ *ptr++ = 'X' | case_mod;
|
|
73
|
+ *ptr++ = '0';
|
|
74
|
+ }
|
|
75
|
+
|
|
76
|
+ return ptr;
|
|
77
|
+}
|
|
78
|
+
|
|
79
|
+static char * format_decimal ( char *buf, signed long num, int width ) {
|
|
80
|
+ char *ptr = buf;
|
|
81
|
+ int negative = 0;
|
|
82
|
+
|
|
83
|
+ /* Generate the number */
|
|
84
|
+ if ( num < 0 ) {
|
|
85
|
+ negative = 1;
|
|
86
|
+ num = -num;
|
|
87
|
+ }
|
|
88
|
+ do {
|
|
89
|
+ *ptr++ = '0' + ( num % 10 );
|
|
90
|
+ num /= 10;
|
|
91
|
+ } while ( num );
|
|
92
|
+
|
|
93
|
+ /* Add "-" if necessary */
|
|
94
|
+ if ( negative )
|
|
95
|
+ *ptr++ = '-';
|
|
96
|
+
|
|
97
|
+ /* Space-pad to width */
|
|
98
|
+ while ( ( ptr - buf ) < width )
|
|
99
|
+ *ptr++ = ' ';
|
|
100
|
+
|
|
101
|
+ return ptr;
|
|
102
|
+}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+/**
|
|
106
|
+ * Write a formatted string to a printf context
|
|
107
|
+ *
|
|
108
|
+ * @v ctx Context
|
20
|
109
|
* @v fmt Format string
|
21
|
110
|
* @v args Arguments corresponding to the format string
|
22
|
|
- * @ret len Length of string written to buffer (if buf != NULL)
|
23
|
|
- * @ret 0 (if buf == NULL)
|
24
|
|
- * @err None -
|
25
|
|
- *
|
26
|
|
- * If #buf==NULL, then the string will be written to the console
|
27
|
|
- * directly using putchar().
|
28
|
|
- *
|
|
111
|
+ * @ret len Length of formatted string
|
29
|
112
|
*/
|
30
|
|
-static int vsprintf(char *buf, const char *fmt, va_list args)
|
31
|
|
-{
|
32
|
|
- const char *p;
|
33
|
|
- char *s;
|
34
|
|
- s = buf;
|
35
|
|
- for ( ; *fmt != '\0'; ++fmt) {
|
36
|
|
- if (*fmt != '%') {
|
37
|
|
- buf ? *s++ = *fmt : putchar(*fmt);
|
|
113
|
+int vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
|
|
114
|
+ int flags;
|
|
115
|
+ int width;
|
|
116
|
+ uint8_t *length;
|
|
117
|
+ int character;
|
|
118
|
+ unsigned long long hex;
|
|
119
|
+ signed long decimal;
|
|
120
|
+ char num_buf[32];
|
|
121
|
+ char *ptr;
|
|
122
|
+
|
|
123
|
+ /* Initialise context */
|
|
124
|
+ ctx->len = 0;
|
|
125
|
+
|
|
126
|
+ for ( ; *fmt ; fmt++ ) {
|
|
127
|
+ /* Pass through ordinary characters */
|
|
128
|
+ if ( *fmt != '%' ) {
|
|
129
|
+ ctx->handler ( ctx, *fmt );
|
38
|
130
|
continue;
|
39
|
131
|
}
|
40
|
|
- /* skip width specs */
|
41
|
132
|
fmt++;
|
42
|
|
- while (*fmt >= '0' && *fmt <= '9')
|
43
|
|
- fmt++;
|
44
|
|
- if (*fmt == '.')
|
45
|
|
- fmt++;
|
46
|
|
- while (*fmt >= '0' && *fmt <= '9')
|
47
|
|
- fmt++;
|
48
|
|
- if (*fmt == 's') {
|
49
|
|
- for(p = va_arg(args, char *); *p != '\0'; p++)
|
50
|
|
- buf ? *s++ = *p : putchar(*p);
|
51
|
|
- } else if (*fmt == 'm') {
|
52
|
|
- for(p = strerror(errno); *p != '\0'; p++)
|
53
|
|
- buf ? *s++ = *p : putchar(*p);
|
54
|
|
- } else { /* Length of item is bounded */
|
55
|
|
- char tmp[40], *q = tmp;
|
56
|
|
- int alt = 0;
|
57
|
|
- int shift = INT_SHIFT;
|
58
|
|
- if (*fmt == '#') {
|
59
|
|
- alt = 1;
|
60
|
|
- fmt++;
|
61
|
|
- }
|
62
|
|
- if (*fmt == 'l') {
|
63
|
|
- shift = LONG_SHIFT;
|
64
|
|
- fmt++;
|
|
133
|
+ /* Process flag characters */
|
|
134
|
+ flags = 0;
|
|
135
|
+ for ( ; ; fmt++ ) {
|
|
136
|
+ if ( *fmt == '#' ) {
|
|
137
|
+ flags |= ALT_FORM;
|
|
138
|
+ } else if ( *fmt == '0' ) {
|
|
139
|
+ /* We always 0-pad hex and space-pad decimal */
|
|
140
|
+ } else {
|
|
141
|
+ /* End of flag characters */
|
|
142
|
+ break;
|
65
|
143
|
}
|
66
|
|
- if (*fmt == 'l') {
|
67
|
|
- shift = LONGLONG_SHIFT;
|
68
|
|
- fmt++;
|
69
|
|
- }
|
70
|
|
- if (*fmt == 'h') {
|
71
|
|
- shift = SHRT_SHIFT;
|
72
|
|
- fmt++;
|
73
|
|
- if (*fmt == 'h') {
|
74
|
|
- shift = CHAR_SHIFT;
|
75
|
|
- fmt++;
|
76
|
|
- }
|
|
144
|
+ }
|
|
145
|
+ /* Process field width */
|
|
146
|
+ width = 0;
|
|
147
|
+ for ( ; ; fmt++ ) {
|
|
148
|
+ if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
|
|
149
|
+ width = ( width * 10 ) + ( *fmt - '0' );
|
|
150
|
+ } else {
|
|
151
|
+ break;
|
77
|
152
|
}
|
78
|
|
-
|
79
|
|
- /*
|
80
|
|
- * Before each format q points to tmp buffer
|
81
|
|
- * After each format q points past end of item
|
82
|
|
- */
|
83
|
|
- if ((*fmt | 0x20) == 'x') {
|
84
|
|
- /* With x86 gcc, sizeof(long) == sizeof(int) */
|
85
|
|
- unsigned long h;
|
86
|
|
- int ncase;
|
87
|
|
- if (shift > LONG_SHIFT) {
|
88
|
|
- h = va_arg(args, unsigned long long);
|
89
|
|
- } else if (shift > INT_SHIFT) {
|
90
|
|
- h = va_arg(args, unsigned long);
|
91
|
|
- } else {
|
92
|
|
- h = va_arg(args, unsigned int);
|
93
|
|
- }
|
94
|
|
- ncase = (*fmt & 0x20);
|
95
|
|
- if (alt) {
|
96
|
|
- *q++ = '0';
|
97
|
|
- *q++ = 'X' | ncase;
|
98
|
|
- }
|
99
|
|
- for ( ; shift >= 0; shift -= 4)
|
100
|
|
- *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
|
|
153
|
+ }
|
|
154
|
+ /* We don't do floating point */
|
|
155
|
+ /* Process length modifier */
|
|
156
|
+ length = &type_sizes[INT_LEN];
|
|
157
|
+ for ( ; ; fmt++ ) {
|
|
158
|
+ if ( *fmt == 'h' ) {
|
|
159
|
+ length--;
|
|
160
|
+ } else if ( *fmt == 'l' ) {
|
|
161
|
+ length++;
|
|
162
|
+ } else if ( *fmt == 'z' ) {
|
|
163
|
+ length = &type_sizes[SIZE_T_LEN];
|
|
164
|
+ } else {
|
|
165
|
+ break;
|
101
|
166
|
}
|
102
|
|
- else if (*fmt == 'd') {
|
103
|
|
- char *r, *t;
|
104
|
|
- long i;
|
105
|
|
- if (shift > LONG_SHIFT) {
|
106
|
|
- i = va_arg(args, long long);
|
107
|
|
- } else if (shift > INT_SHIFT) {
|
108
|
|
- i = va_arg(args, long);
|
109
|
|
- } else {
|
110
|
|
- i = va_arg(args, int);
|
111
|
|
- }
|
112
|
|
- if (i < 0) {
|
113
|
|
- *q++ = '-';
|
114
|
|
- i = -i;
|
115
|
|
- }
|
116
|
|
- t = q; /* save beginning of digits */
|
117
|
|
- do {
|
118
|
|
- *q++ = '0' + (i % 10);
|
119
|
|
- i /= 10;
|
120
|
|
- } while (i);
|
121
|
|
- /* reverse digits, stop in middle */
|
122
|
|
- r = q; /* don't alter q */
|
123
|
|
- while (--r > t) {
|
124
|
|
- i = *r;
|
125
|
|
- *r = *t;
|
126
|
|
- *t++ = i;
|
127
|
|
- }
|
|
167
|
+ }
|
|
168
|
+ /* Process conversion specifier */
|
|
169
|
+ if ( *fmt == 'c' ) {
|
|
170
|
+ character = va_arg ( args, unsigned int );
|
|
171
|
+ ctx->handler ( ctx, character );
|
|
172
|
+ } else if ( *fmt == 's' ) {
|
|
173
|
+ ptr = va_arg ( args, char * );
|
|
174
|
+ for ( ; *ptr ; ptr++ ) {
|
|
175
|
+ ctx->handler ( ctx, *ptr );
|
128
|
176
|
}
|
129
|
|
- else if (*fmt == '@') {
|
130
|
|
- unsigned char *r;
|
131
|
|
- union {
|
132
|
|
- uint32_t l;
|
133
|
|
- unsigned char c[4];
|
134
|
|
- } u;
|
135
|
|
- u.l = va_arg(args, uint32_t);
|
136
|
|
- for (r = &u.c[0]; r < &u.c[4]; ++r)
|
137
|
|
- q += sprintf(q, "%d.", *r);
|
138
|
|
- --q;
|
|
177
|
+ } else if ( *fmt == 'p' ) {
|
|
178
|
+ hex = ( intptr_t ) va_arg ( args, void * );
|
|
179
|
+ ptr = format_hex ( num_buf, hex, width,
|
|
180
|
+ ( ALT_FORM | LCASE ) );
|
|
181
|
+ do {
|
|
182
|
+ ctx->handler ( ctx, *(--ptr) );
|
|
183
|
+ } while ( ptr != num_buf );
|
|
184
|
+ } else if ( ( *fmt & ~0x20 ) == 'X' ) {
|
|
185
|
+ flags |= ( *fmt & 0x20 ); /* LCASE */
|
|
186
|
+ if ( *length >= sizeof ( unsigned long long ) ) {
|
|
187
|
+ hex = va_arg ( args, unsigned long long );
|
|
188
|
+ } else if ( *length >= sizeof ( unsigned long ) ) {
|
|
189
|
+ hex = va_arg ( args, unsigned long );
|
|
190
|
+ } else {
|
|
191
|
+ hex = va_arg ( args, unsigned int );
|
139
|
192
|
}
|
140
|
|
- else if (*fmt == '!') {
|
141
|
|
- const char *r;
|
142
|
|
- p = va_arg(args, char *);
|
143
|
|
- for (r = p + ETH_ALEN; p < r; ++p)
|
144
|
|
- q += sprintf(q, "%hhX:", *p);
|
145
|
|
- --q;
|
|
193
|
+ ptr = format_hex ( num_buf, hex, width, flags );
|
|
194
|
+ do {
|
|
195
|
+ ctx->handler ( ctx, *(--ptr) );
|
|
196
|
+ } while ( ptr != num_buf );
|
|
197
|
+ } else if ( *fmt == 'd' ) {
|
|
198
|
+ if ( *length >= sizeof ( signed long ) ) {
|
|
199
|
+ decimal = va_arg ( args, signed long );
|
|
200
|
+ } else {
|
|
201
|
+ decimal = va_arg ( args, signed int );
|
146
|
202
|
}
|
147
|
|
- else if (*fmt == 'c')
|
148
|
|
- *q++ = va_arg(args, int);
|
149
|
|
- else
|
150
|
|
- *q++ = *fmt;
|
151
|
|
- /* now output the saved string */
|
152
|
|
- for (p = tmp; p < q; ++p)
|
153
|
|
- buf ? *s++ = *p : putchar(*p);
|
|
203
|
+ ptr = format_decimal ( num_buf, decimal, width );
|
|
204
|
+ do {
|
|
205
|
+ ctx->handler ( ctx, *(--ptr) );
|
|
206
|
+ } while ( ptr != num_buf );
|
|
207
|
+ } else {
|
|
208
|
+ ctx->handler ( ctx, *fmt );
|
154
|
209
|
}
|
155
|
210
|
}
|
156
|
|
- if (buf)
|
157
|
|
- *s = '\0';
|
158
|
|
- return (s - buf);
|
|
211
|
+
|
|
212
|
+ return ctx->len;
|
159
|
213
|
}
|
160
|
214
|
|
161
|
215
|
/**
|
162
|
|
- * Write a formatted string to a buffer.
|
|
216
|
+ * Write character to buffer
|
163
|
217
|
*
|
164
|
|
- * @v buf Buffer into which to write the string, or NULL
|
165
|
|
- * @v fmt Format string
|
166
|
|
- * @v ... Arguments corresponding to the format string
|
167
|
|
- * @ret len Length of string written to buffer (if buf != NULL)
|
168
|
|
- * @ret 0 (if buf == NULL)
|
169
|
|
- * @err None -
|
|
218
|
+ * @v ctx Context
|
|
219
|
+ * @v c Character
|
|
220
|
+ */
|
|
221
|
+static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
|
|
222
|
+ if ( ++ctx->len < ctx->max_len )
|
|
223
|
+ ctx->buf[ctx->len-1] = c;
|
|
224
|
+}
|
|
225
|
+
|
|
226
|
+/**
|
|
227
|
+ * Write a formatted string to a buffer
|
170
|
228
|
*
|
171
|
|
- * If #buf==NULL, then the string will be written to the console
|
172
|
|
- * directly using putchar().
|
|
229
|
+ * @v buf Buffer into which to write the string
|
|
230
|
+ * @v size Size of buffer
|
|
231
|
+ * @v fmt Format string
|
|
232
|
+ * @v args Arguments corresponding to the format string
|
|
233
|
+ * @ret len Length of formatted string
|
173
|
234
|
*
|
|
235
|
+ * If the buffer is too small to contain the string, the returned
|
|
236
|
+ * length is the length that would have been written had enough space
|
|
237
|
+ * been available.
|
174
|
238
|
*/
|
175
|
|
-int sprintf(char *buf, const char *fmt, ...)
|
176
|
|
-{
|
177
|
|
- va_list args;
|
178
|
|
- int i;
|
179
|
|
- va_start(args, fmt);
|
180
|
|
- i=vsprintf(buf, fmt, args);
|
181
|
|
- va_end(args);
|
182
|
|
- return i;
|
|
239
|
+int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
|
|
240
|
+ struct printf_context ctx;
|
|
241
|
+ int len;
|
|
242
|
+
|
|
243
|
+ /* Ensure last byte is NUL if a size is specified. This
|
|
244
|
+ * catches the case of the buffer being too small, in which
|
|
245
|
+ * case a trailing NUL would not otherwise be added.
|
|
246
|
+ */
|
|
247
|
+ if ( size != PRINTF_NO_LENGTH )
|
|
248
|
+ buf[size-1] = '\0';
|
|
249
|
+
|
|
250
|
+ /* Hand off to vcprintf */
|
|
251
|
+ ctx.handler = printf_sputc;
|
|
252
|
+ ctx.buf = buf;
|
|
253
|
+ ctx.max_len = size;
|
|
254
|
+ len = vcprintf ( &ctx, fmt, args );
|
|
255
|
+
|
|
256
|
+ /* Add trailing NUL */
|
|
257
|
+ printf_sputc ( &ctx, '\0' );
|
|
258
|
+
|
|
259
|
+ return len;
|
183
|
260
|
}
|
184
|
261
|
|
185
|
|
-#warning "Remove this buffer-overflow-in-waiting at some point"
|
|
262
|
+/**
|
|
263
|
+ * Write a formatted string to a buffer
|
|
264
|
+ *
|
|
265
|
+ * @v buf Buffer into which to write the string
|
|
266
|
+ * @v size Size of buffer
|
|
267
|
+ * @v fmt Format string
|
|
268
|
+ * @v ... Arguments corresponding to the format string
|
|
269
|
+ * @ret len Length of formatted string
|
|
270
|
+ */
|
186
|
271
|
int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
|
187
|
272
|
va_list args;
|
188
|
273
|
int i;
|
189
|
274
|
|
190
|
275
|
va_start ( args, fmt );
|
191
|
|
- i = vsprintf ( buf, fmt, args );
|
|
276
|
+ i = vsnprintf ( buf, size, fmt, args );
|
192
|
277
|
va_end ( args );
|
193
|
278
|
return i;
|
194
|
279
|
}
|
195
|
280
|
|
|
281
|
+/**
|
|
282
|
+ * Write character to console
|
|
283
|
+ *
|
|
284
|
+ * @v ctx Context
|
|
285
|
+ * @v c Character
|
|
286
|
+ */
|
|
287
|
+static void printf_putchar ( struct printf_context *ctx, unsigned int c ) {
|
|
288
|
+ ++ctx->len;
|
|
289
|
+ putchar ( c );
|
|
290
|
+}
|
|
291
|
+
|
|
292
|
+/**
|
|
293
|
+ * Write a formatted string to the console
|
|
294
|
+ *
|
|
295
|
+ * @v fmt Format string
|
|
296
|
+ * @v args Arguments corresponding to the format string
|
|
297
|
+ * @ret len Length of formatted string
|
|
298
|
+ */
|
|
299
|
+int vprintf ( const char *fmt, va_list args ) {
|
|
300
|
+ struct printf_context ctx;
|
|
301
|
+
|
|
302
|
+ /* Hand off to vcprintf */
|
|
303
|
+ ctx.handler = printf_putchar;
|
|
304
|
+ return vcprintf ( &ctx, fmt, args );
|
|
305
|
+}
|
|
306
|
+
|
196
|
307
|
/**
|
197
|
308
|
* Write a formatted string to the console.
|
198
|
309
|
*
|
199
|
310
|
* @v fmt Format string
|
200
|
311
|
* @v ... Arguments corresponding to the format string
|
201
|
|
- * @ret None -
|
202
|
|
- * @err None -
|
203
|
|
- *
|
|
312
|
+ * @ret len Length of formatted string
|
204
|
313
|
*/
|
205
|
|
-int printf(const char *fmt, ...)
|
206
|
|
-{
|
|
314
|
+int printf ( const char *fmt, ... ) {
|
207
|
315
|
va_list args;
|
208
|
316
|
int i;
|
209
|
|
- va_start(args, fmt);
|
210
|
|
- i=vsprintf(0, fmt, args);
|
211
|
|
- va_end(args);
|
|
317
|
+
|
|
318
|
+ va_start ( args, fmt );
|
|
319
|
+ i = vprintf ( fmt, args );
|
|
320
|
+ va_end ( args );
|
212
|
321
|
return i;
|
213
|
322
|
}
|