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