Import from Etherboot 5.4
[people/peper/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 "vsprintf.h"
6
7 #define LONG_SHIFT  ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
8 #define INT_SHIFT   ((int)((sizeof(unsigned int)*CHAR_BIT) - 4))
9 #define SHRT_SHIFT  ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
10 #define CHAR_SHIFT  ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))
11
12 /**************************************************************************
13 PRINTF and friends
14
15         Formats:
16                 %[#]x   - 4 bytes int (8 hex digits, lower case)
17                 %[#]X   - 4 bytes int (8 hex digits, upper case)
18                 %[#]lx  - 8 bytes long (16 hex digits, lower case)
19                 %[#]lX  - 8 bytes long (16 hex digits, upper case)
20                 %[#]hx  - 2 bytes int (4 hex digits, lower case)
21                 %[#]hX  - 2 bytes int (4 hex digits, upper case)
22                 %[#]hhx - 1 byte int (2 hex digits, lower case)
23                 %[#]hhX - 1 byte int (2 hex digits, upper case)
24                         - optional # prefixes 0x or 0X
25                 %d      - decimal int
26                 %c      - char
27                 %s      - string
28                 %@      - Internet address in ddd.ddd.ddd.ddd notation
29                 %!      - Ethernet address in xx:xx:xx:xx:xx:xx notation
30         Note: width specification ignored
31 **************************************************************************/
32 static int vsprintf(char *buf, const char *fmt, va_list args)
33 {
34         char *p, *s;
35         s = buf;
36         for ( ; *fmt != '\0'; ++fmt) {
37                 if (*fmt != '%') {
38                         buf ? *s++ = *fmt : putchar(*fmt);
39                         continue;
40                 }
41                 /* skip width specs */
42                 fmt++;
43                 while (*fmt >= '0' && *fmt <= '9')
44                         fmt++;
45                 if (*fmt == '.')
46                         fmt++;
47                 while (*fmt >= '0' && *fmt <= '9')
48                         fmt++;
49                 if (*fmt == 's') {
50                         for(p = va_arg(args, char *); *p != '\0'; p++) 
51                                 buf ? *s++ = *p : putchar(*p);
52                 }
53                 else {  /* Length of item is bounded */
54                         char tmp[40], *q = tmp;
55                         int alt = 0;
56                         int shift = INT_SHIFT;
57                         if (*fmt == '#') {
58                                 alt = 1;
59                                 fmt++;
60                         }
61                         if (*fmt == 'l') {
62                                 shift = LONG_SHIFT;
63                                 fmt++;
64                         }
65                         else if (*fmt == 'h') {
66                                 shift = SHRT_SHIFT;
67                                 fmt++;
68                                 if (*fmt == 'h') {
69                                         shift = CHAR_SHIFT;
70                                         fmt++;
71                                 }
72                         }
73                         
74                         /*
75                          * Before each format q points to tmp buffer
76                          * After each format q points past end of item
77                          */
78                         if ((*fmt | 0x20) == 'x') {
79                                 /* With x86 gcc, sizeof(long) == sizeof(int) */
80                                 unsigned long h;
81                                 int ncase;
82                                 if (shift > INT_SHIFT) {
83                                         h = va_arg(args, unsigned long);
84                                 } else {
85                                         h = va_arg(args, unsigned int);
86                                 }
87                                 ncase = (*fmt & 0x20);
88                                 if (alt) {
89                                         *q++ = '0';
90                                         *q++ = 'X' | ncase;
91                                 }
92                                 for ( ; shift >= 0; shift -= 4)
93                                         *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
94                         }
95                         else if (*fmt == 'd') {
96                                 char *r;
97                                 long i;
98                                 if (shift > INT_SHIFT) {
99                                         i = va_arg(args, long);
100                                 } else {
101                                         i = va_arg(args, int);
102                                 }
103                                 if (i < 0) {
104                                         *q++ = '-';
105                                         i = -i;
106                                 }
107                                 p = q;          /* save beginning of digits */
108                                 do {
109                                         *q++ = '0' + (i % 10);
110                                         i /= 10;
111                                 } while (i);
112                                 /* reverse digits, stop in middle */
113                                 r = q;          /* don't alter q */
114                                 while (--r > p) {
115                                         i = *r;
116                                         *r = *p;
117                                         *p++ = i;
118                                 }
119                         }
120                         else if (*fmt == '@') {
121                                 unsigned char *r;
122                                 union {
123                                         uint32_t        l;
124                                         unsigned char   c[4];
125                                 } u;
126                                 u.l = va_arg(args, uint32_t);
127                                 for (r = &u.c[0]; r < &u.c[4]; ++r)
128                                         q += sprintf(q, "%d.", *r);
129                                 --q;
130                         }
131                         else if (*fmt == '!') {
132                                 char *r;
133                                 p = va_arg(args, char *);
134                                 for (r = p + ETH_ALEN; p < r; ++p)
135                                         q += sprintf(q, "%hhX:", *p);
136                                 --q;
137                         }
138                         else if (*fmt == 'c')
139                                 *q++ = va_arg(args, int);
140                         else
141                                 *q++ = *fmt;
142                         /* now output the saved string */
143                         for (p = tmp; p < q; ++p)
144                                 buf ? *s++ = *p : putchar(*p);
145                 }
146         }
147         if (buf)
148                 *s = '\0';
149         return (s - buf);
150 }
151
152 int sprintf(char *buf, const char *fmt, ...)
153 {
154         va_list args;
155         int i;
156         va_start(args, fmt);
157         i=vsprintf(buf, fmt, args);
158         va_end(args);
159         return i;
160 }
161
162 void printf(const char *fmt, ...)
163 {
164         va_list args;
165         int i;
166         va_start(args, fmt);
167         i=vsprintf(0, fmt, args);
168         va_end(args);
169 }