source: bootcd/isolinux/syslinux-6.03/gpxe/src/core/vsprintf.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 10.1 KB
Line 
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
19FILE_LICENCE ( GPL2_OR_LATER );
20
21#include <stddef.h>
22#include <stdarg.h>
23#include <stdio.h>
24#include <console.h>
25#include <errno.h>
26#include <gpxe/vsprintf.h>
27
28/** @file */
29
30#define CHAR_LEN        0       /**< "hh" length modifier */
31#define SHORT_LEN       1       /**< "h" length modifier */
32#define INT_LEN         2       /**< no length modifier */
33#define LONG_LEN        3       /**< "l" length modifier */
34#define LONGLONG_LEN    4       /**< "ll" length modifier */
35#define SIZE_T_LEN      5       /**< "z" length modifier */
36
37static uint8_t type_sizes[] = {
38        [CHAR_LEN]      = sizeof ( char ),
39        [SHORT_LEN]     = sizeof ( short ),
40        [INT_LEN]       = sizeof ( int ),
41        [LONG_LEN]      = sizeof ( long ),
42        [LONGLONG_LEN]  = sizeof ( long long ),
43        [SIZE_T_LEN]    = sizeof ( size_t ),
44};
45
46/**
47 * Use lower-case for hexadecimal digits
48 *
49 * Note that this value is set to 0x20 since that makes for very
50 * efficient calculations.  (Bitwise-ORing with @c LCASE converts to a
51 * lower-case character, for example.)
52 */
53#define LCASE 0x20
54
55/**
56 * Use "alternate form"
57 *
58 * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
59 * the number.
60 */
61#define ALT_FORM 0x02
62
63/**
64 * Format a hexadecimal number
65 *
66 * @v end               End of buffer to contain number
67 * @v num               Number to format
68 * @v width             Minimum field width
69 * @ret ptr             End of buffer
70 *
71 * Fills a buffer in reverse order with a formatted hexadecimal
72 * number.  The number will be zero-padded to the specified width.
73 * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
74 * set.
75 *
76 * There must be enough space in the buffer to contain the largest
77 * number that this function can format.
78 */
79static char * format_hex ( char *end, unsigned long long num, int width,
80                           int flags ) {
81        char *ptr = end;
82        int case_mod;
83
84        /* Generate the number */
85        case_mod = flags & LCASE;
86        do {
87                *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
88                num >>= 4;
89        } while ( num );
90
91        /* Zero-pad to width */
92        while ( ( end - ptr ) < width )
93                *(--ptr) = '0';
94
95        /* Add "0x" or "0X" if alternate form specified */
96        if ( flags & ALT_FORM ) {
97                *(--ptr) = 'X' | case_mod;
98                *(--ptr) = '0';
99        }
100
101        return ptr;
102}
103
104/**
105 * Format a decimal number
106 *
107 * @v end               End of buffer to contain number
108 * @v num               Number to format
109 * @v width             Minimum field width
110 * @ret ptr             End of buffer
111 *
112 * Fills a buffer in reverse order with a formatted decimal number.
113 * The number will be space-padded to the specified width.
114 *
115 * There must be enough space in the buffer to contain the largest
116 * number that this function can format.
117 */
118static char * format_decimal ( char *end, signed long num, int width ) {
119        char *ptr = end;
120        int negative = 0;
121
122        /* Generate the number */
123        if ( num < 0 ) {
124                negative = 1;
125                num = -num;
126        }
127        do {
128                *(--ptr) = '0' + ( num % 10 );
129                num /= 10;
130        } while ( num );
131
132        /* Add "-" if necessary */
133        if ( negative )
134                *(--ptr) = '-';
135
136        /* Space-pad to width */
137        while ( ( end - ptr ) < width )
138                *(--ptr) = ' ';
139
140        return ptr;
141}
142
143/**
144 * Print character via a printf context
145 *
146 * @v ctx               Context
147 * @v c                 Character
148 *
149 * Call's the printf_context::handler() method and increments
150 * printf_context::len.
151 */
152static inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
153        ctx->handler ( ctx, c );
154        ++ctx->len;
155}
156
157/**
158 * Write a formatted string to a printf context
159 *
160 * @v ctx               Context
161 * @v fmt               Format string
162 * @v args              Arguments corresponding to the format string
163 * @ret len             Length of formatted string
164 */
165size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
166        int flags;
167        int width;
168        uint8_t *length;
169        char *ptr;
170        char tmp_buf[32]; /* 32 is enough for all numerical formats.
171                           * Insane width fields could overflow this buffer. */
172
173        /* Initialise context */
174        ctx->len = 0;
175
176        for ( ; *fmt ; fmt++ ) {
177                /* Pass through ordinary characters */
178                if ( *fmt != '%' ) {
179                        cputchar ( ctx, *fmt );
180                        continue;
181                }
182                fmt++;
183                /* Process flag characters */
184                flags = 0;
185                for ( ; ; fmt++ ) {
186                        if ( *fmt == '#' ) {
187                                flags |= ALT_FORM;
188                        } else if ( *fmt == '0' ) {
189                                /* We always 0-pad hex and space-pad decimal */
190                        } else {
191                                /* End of flag characters */
192                                break;
193                        }
194                }
195                /* Process field width */
196                width = 0;
197                for ( ; ; fmt++ ) {
198                        if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
199                                width = ( width * 10 ) + ( *fmt - '0' );
200                        } else {
201                                break;
202                        }
203                }
204                /* We don't do floating point */
205                /* Process length modifier */
206                length = &type_sizes[INT_LEN];
207                for ( ; ; fmt++ ) {
208                        if ( *fmt == 'h' ) {
209                                length--;
210                        } else if ( *fmt == 'l' ) {
211                                length++;
212                        } else if ( *fmt == 'z' ) {
213                                length = &type_sizes[SIZE_T_LEN];
214                        } else {
215                                break;
216                        }
217                }
218                /* Process conversion specifier */
219                ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
220                *ptr = '\0';
221                if ( *fmt == 'c' ) {
222                        cputchar ( ctx, va_arg ( args, unsigned int ) );
223                } else if ( *fmt == 's' ) {
224                        ptr = va_arg ( args, char * );
225                        if ( ! ptr )
226                                ptr = "<NULL>";
227                } else if ( *fmt == 'p' ) {
228                        intptr_t ptrval;
229
230                        ptrval = ( intptr_t ) va_arg ( args, void * );
231                        ptr = format_hex ( ptr, ptrval, width,
232                                           ( ALT_FORM | LCASE ) );
233                } else if ( ( *fmt & ~0x20 ) == 'X' ) {
234                        unsigned long long hex;
235
236                        flags |= ( *fmt & 0x20 ); /* LCASE */
237                        if ( *length >= sizeof ( unsigned long long ) ) {
238                                hex = va_arg ( args, unsigned long long );
239                        } else if ( *length >= sizeof ( unsigned long ) ) {
240                                hex = va_arg ( args, unsigned long );
241                        } else {
242                                hex = va_arg ( args, unsigned int );
243                        }
244                        ptr = format_hex ( ptr, hex, width, flags );
245                } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
246                        signed long decimal;
247
248                        if ( *length >= sizeof ( signed long ) ) {
249                                decimal = va_arg ( args, signed long );
250                        } else {
251                                decimal = va_arg ( args, signed int );
252                        }
253                        ptr = format_decimal ( ptr, decimal, width );
254                } else {
255                        *(--ptr) = *fmt;
256                }
257                /* Write out conversion result */
258                for ( ; *ptr ; ptr++ ) {
259                        cputchar ( ctx, *ptr );
260                }
261        }
262
263        return ctx->len;
264}
265
266/** Context used by vsnprintf() and friends */
267struct sputc_context {
268        struct printf_context ctx;
269        /** Buffer for formatted string (used by printf_sputc()) */
270        char *buf;
271        /** Buffer length (used by printf_sputc()) */
272        size_t max_len;
273};
274
275/**
276 * Write character to buffer
277 *
278 * @v ctx               Context
279 * @v c                 Character
280 */
281static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
282        struct sputc_context * sctx =
283                container_of ( ctx, struct sputc_context, ctx );
284
285        if ( ctx->len < sctx->max_len )
286                sctx->buf[ctx->len] = c;
287}
288
289/**
290 * Write a formatted string to a buffer
291 *
292 * @v buf               Buffer into which to write the string
293 * @v size              Size of buffer
294 * @v fmt               Format string
295 * @v args              Arguments corresponding to the format string
296 * @ret len             Length of formatted string
297 *
298 * If the buffer is too small to contain the string, the returned
299 * length is the length that would have been written had enough space
300 * been available.
301 */
302int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
303        struct sputc_context sctx;
304        size_t len;
305        size_t end;
306
307        /* Hand off to vcprintf */
308        sctx.ctx.handler = printf_sputc;
309        sctx.buf = buf;
310        sctx.max_len = size;
311        len = vcprintf ( &sctx.ctx, fmt, args );
312
313        /* Add trailing NUL */
314        if ( size ) {
315                end = size - 1;
316                if ( len < end )
317                        end = len;
318                buf[end] = '\0';
319        }
320
321        return len;
322}
323
324/**
325 * Write a formatted string to a buffer
326 *
327 * @v buf               Buffer into which to write the string
328 * @v size              Size of buffer
329 * @v fmt               Format string
330 * @v ...               Arguments corresponding to the format string
331 * @ret len             Length of formatted string
332 */
333int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
334        va_list args;
335        int i;
336
337        va_start ( args, fmt );
338        i = vsnprintf ( buf, size, fmt, args );
339        va_end ( args );
340        return i;
341}
342
343/**
344 * Version of vsnprintf() that accepts a signed buffer size
345 *
346 * @v buf               Buffer into which to write the string
347 * @v size              Size of buffer
348 * @v fmt               Format string
349 * @v args              Arguments corresponding to the format string
350 * @ret len             Length of formatted string
351 */
352int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
353
354        /* Treat negative buffer size as zero buffer size */
355        if ( ssize < 0 )
356                ssize = 0;
357
358        /* Hand off to vsnprintf */
359        return vsnprintf ( buf, ssize, fmt, args );
360}
361
362/**
363 * Version of vsnprintf() that accepts a signed buffer size
364 *
365 * @v buf               Buffer into which to write the string
366 * @v size              Size of buffer
367 * @v fmt               Format string
368 * @v ...               Arguments corresponding to the format string
369 * @ret len             Length of formatted string
370 */
371int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
372        va_list args;
373        int len;
374
375        /* Hand off to vssnprintf */
376        va_start ( args, fmt );
377        len = vssnprintf ( buf, ssize, fmt, args );
378        va_end ( args );
379        return len;
380}
381
382/**
383 * Write character to console
384 *
385 * @v ctx               Context
386 * @v c                 Character
387 */
388static void printf_putchar ( struct printf_context *ctx __unused,
389                             unsigned int c ) {
390        putchar ( c );
391}
392
393/**
394 * Write a formatted string to the console
395 *
396 * @v fmt               Format string
397 * @v args              Arguments corresponding to the format string
398 * @ret len             Length of formatted string
399 */
400int vprintf ( const char *fmt, va_list args ) {
401        struct printf_context ctx;
402
403        /* Hand off to vcprintf */
404        ctx.handler = printf_putchar;   
405        return vcprintf ( &ctx, fmt, args );   
406}
407
408/**
409 * Write a formatted string to the console.
410 *
411 * @v fmt               Format string
412 * @v ...               Arguments corresponding to the format string
413 * @ret len             Length of formatted string
414 */
415int printf ( const char *fmt, ... ) {
416        va_list args;
417        int i;
418
419        va_start ( args, fmt );
420        i = vprintf ( fmt, args );
421        va_end ( args );
422        return i;
423}
Note: See TracBrowser for help on using the repository browser.