Doxygenation
[people/lynusvaz/gpxe.git] / src / core / vsprintf.c
1 #include <stdarg.h>
2 #include "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 LONG_SHIFT  ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
9 #define INT_SHIFT   ((int)((sizeof(unsigned int)*CHAR_BIT) - 4))
10 #define SHRT_SHIFT  ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
11 #define CHAR_SHIFT  ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))
12
13 /** @file
14  *
15  * printf and friends.
16  *
17  * Etherboot's printf() functions understand the following format
18  * specifiers:
19  *
20  *      - Hexadecimal integers
21  *              - @c %[#]x      - 4 bytes int (8 hex digits, lower case)
22  *              - @c %[#]X      - 4 bytes int (8 hex digits, upper case)
23  *              - @c %[#]lx     - 8 bytes long (16 hex digits, lower case)
24  *              - @c %[#]lX     - 8 bytes long (16 hex digits, upper case)
25  *              - @c %[#]hx     - 2 bytes int (4 hex digits, lower case)
26  *              - @c %[#]hX     - 2 bytes int (4 hex digits, upper case)
27  *              - @c %[#]hhx    - 1 byte int (2 hex digits, lower case)
28  *              - @c %[#]hhX    - 1 byte int (2 hex digits, upper case)
29  *              .
30  *              If the optional # prefix is specified, the output will
31  *              be prefixed with 0x (or 0X).
32  *
33  *      - Other integers
34  *              - @c %d         - decimal int
35  *      .
36  *      Note that any width specification (e.g. the @c 02 in @c %02x)
37  *      will be accepted but ignored.
38  *
39  *      - Strings and characters
40  *              - @c %c         - char
41  *              - @c %s         - string
42  *              - @c %m         - error message text (i.e. strerror(errno))
43  *
44  *      - Etherboot-specific specifiers
45  *              - @c %@         - IP in ddd.ddd.ddd.ddd notation
46  *              - @c %!         - MAC address in xx:xx:xx:xx:xx:xx notation
47  *
48  */
49
50 /**
51  * Write a formatted string to a buffer.
52  *
53  * @v buf               Buffer into which to write the string, or NULL
54  * @v fmt               Format string
55  * @v args              Arguments corresponding to the format string
56  * @ret len             Length of string written to buffer (if buf != NULL)
57  * @ret 0               (if buf == NULL)
58  * @err None
59  *
60  * If @c buf==NULL, then the string will be written to the console
61  * directly using putchar().
62  *
63  */
64 static int vsprintf(char *buf, const char *fmt, va_list args)
65 {
66         const char *p;
67         char *s;
68         s = buf;
69         for ( ; *fmt != '\0'; ++fmt) {
70                 if (*fmt != '%') {
71                         buf ? *s++ = *fmt : putchar(*fmt);
72                         continue;
73                 }
74                 /* skip width specs */
75                 fmt++;
76                 while (*fmt >= '0' && *fmt <= '9')
77                         fmt++;
78                 if (*fmt == '.')
79                         fmt++;
80                 while (*fmt >= '0' && *fmt <= '9')
81                         fmt++;
82                 if (*fmt == 's') {
83                         for(p = va_arg(args, char *); *p != '\0'; p++) 
84                                 buf ? *s++ = *p : putchar(*p);
85                 } else if (*fmt == 'm') {
86                         for(p = strerror(errno); *p != '\0'; p++)
87                                 buf ? *s++ = *p : putchar(*p);
88                 } else {        /* Length of item is bounded */
89                         char tmp[40], *q = tmp;
90                         int alt = 0;
91                         int shift = INT_SHIFT;
92                         if (*fmt == '#') {
93                                 alt = 1;
94                                 fmt++;
95                         }
96                         if (*fmt == 'l') {
97                                 shift = LONG_SHIFT;
98                                 fmt++;
99                         }
100                         else if (*fmt == 'h') {
101                                 shift = SHRT_SHIFT;
102                                 fmt++;
103                                 if (*fmt == 'h') {
104                                         shift = CHAR_SHIFT;
105                                         fmt++;
106                                 }
107                         }
108                         
109                         /*
110                          * Before each format q points to tmp buffer
111                          * After each format q points past end of item
112                          */
113                         if ((*fmt | 0x20) == 'x') {
114                                 /* With x86 gcc, sizeof(long) == sizeof(int) */
115                                 unsigned long h;
116                                 int ncase;
117                                 if (shift > INT_SHIFT) {
118                                         h = va_arg(args, unsigned long);
119                                 } else {
120                                         h = va_arg(args, unsigned int);
121                                 }
122                                 ncase = (*fmt & 0x20);
123                                 if (alt) {
124                                         *q++ = '0';
125                                         *q++ = 'X' | ncase;
126                                 }
127                                 for ( ; shift >= 0; shift -= 4)
128                                         *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
129                         }
130                         else if (*fmt == 'd') {
131                                 char *r, *t;
132                                 long i;
133                                 if (shift > INT_SHIFT) {
134                                         i = va_arg(args, long);
135                                 } else {
136                                         i = va_arg(args, int);
137                                 }
138                                 if (i < 0) {
139                                         *q++ = '-';
140                                         i = -i;
141                                 }
142                                 t = q;          /* save beginning of digits */
143                                 do {
144                                         *q++ = '0' + (i % 10);
145                                         i /= 10;
146                                 } while (i);
147                                 /* reverse digits, stop in middle */
148                                 r = q;          /* don't alter q */
149                                 while (--r > t) {
150                                         i = *r;
151                                         *r = *t;
152                                         *t++ = i;
153                                 }
154                         }
155                         else if (*fmt == '@') {
156                                 unsigned char *r;
157                                 union {
158                                         uint32_t        l;
159                                         unsigned char   c[4];
160                                 } u;
161                                 u.l = va_arg(args, uint32_t);
162                                 for (r = &u.c[0]; r < &u.c[4]; ++r)
163                                         q += sprintf(q, "%d.", *r);
164                                 --q;
165                         }
166                         else if (*fmt == '!') {
167                                 const char *r;
168                                 p = va_arg(args, char *);
169                                 for (r = p + ETH_ALEN; p < r; ++p)
170                                         q += sprintf(q, "%hhX:", *p);
171                                 --q;
172                         }
173                         else if (*fmt == 'c')
174                                 *q++ = va_arg(args, int);
175                         else
176                                 *q++ = *fmt;
177                         /* now output the saved string */
178                         for (p = tmp; p < q; ++p)
179                                 buf ? *s++ = *p : putchar(*p);
180                 }
181         }
182         if (buf)
183                 *s = '\0';
184         return (s - buf);
185 }
186
187 /**
188  * Write a formatted string to a buffer.
189  *
190  * @v buf               Buffer into which to write the string, or NULL
191  * @v fmt               Format string
192  * @v ...               Arguments corresponding to the format string
193  * @ret len             Length of string written to buffer (if buf != NULL)
194  * @ret 0               (if buf == NULL)
195  * @err None
196  *
197  * If @c buf==NULL, then the string will be written to the console
198  * directly using putchar().
199  *
200  */
201 int sprintf(char *buf, const char *fmt, ...)
202 {
203         va_list args;
204         int i;
205         va_start(args, fmt);
206         i=vsprintf(buf, fmt, args);
207         va_end(args);
208         return i;
209 }
210
211 /**
212  * Write a formatted string to the console.
213  *
214  * @v fmt               Format string
215  * @v ...               Arguments corresponding to the format string
216  * @ret None
217  * @err None
218  *
219  */
220 void printf(const char *fmt, ...)
221 {
222         va_list args;
223         int i;
224         va_start(args, fmt);
225         i=vsprintf(0, fmt, args);
226         va_end(args);
227 }