2192974dc546d73173c966e6d57e0fa72f006edc
[litex.git] / litex / soc / misoc / software / libbase / vsnprintf.c
1 /*
2 * MiSoC
3 * Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
4 * Copyright (C) Linux kernel developers
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 3 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <ctype.h>
24
25 /**
26 * vsnprintf - Format a string and place it in a buffer
27 * @buf: The buffer to place the result into
28 * @size: The size of the buffer, including the trailing null space
29 * @fmt: The format string to use
30 * @args: Arguments for the format string
31 *
32 * The return value is the number of characters which would
33 * be generated for the given input, excluding the trailing
34 * '\0', as per ISO C99. If you want to have the exact
35 * number of characters written into @buf as return value
36 * (not including the trailing '\0'), use vscnprintf(). If the
37 * return is greater than or equal to @size, the resulting
38 * string is truncated.
39 *
40 * Call this function if you are already dealing with a va_list.
41 * You probably want snprintf() instead.
42 */
43 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
44 {
45 int len;
46 unsigned long long num;
47 int i, base;
48 char *str, *end, c;
49 const char *s;
50
51 int flags; /* flags to number() */
52
53 int field_width; /* width of output field */
54 int precision; /* min. # of digits for integers; max
55 number of chars for from string */
56 int qualifier; /* 'h', 'l', or 'L' for integer fields */
57 /* 'z' support added 23/7/1999 S.H. */
58 /* 'z' changed to 'Z' --davidm 1/25/99 */
59 /* 't' added for ptrdiff_t */
60
61 /* Reject out-of-range values early. Large positive sizes are
62 used for unknown buffer sizes. */
63 if (unlikely((int) size < 0))
64 return 0;
65
66 str = buf;
67 end = buf + size;
68
69 /* Make sure end is always >= buf */
70 if (end < buf) {
71 end = ((void *)-1);
72 size = end - buf;
73 }
74
75 for (; *fmt ; ++fmt) {
76 if (*fmt != '%') {
77 if (str < end)
78 *str = *fmt;
79 ++str;
80 continue;
81 }
82
83 /* process flags */
84 flags = 0;
85 repeat:
86 ++fmt; /* this also skips first '%' */
87 switch (*fmt) {
88 case '-': flags |= PRINTF_LEFT; goto repeat;
89 case '+': flags |= PRINTF_PLUS; goto repeat;
90 case ' ': flags |= PRINTF_SPACE; goto repeat;
91 case '#': flags |= PRINTF_SPECIAL; goto repeat;
92 case '0': flags |= PRINTF_ZEROPAD; goto repeat;
93 }
94
95 /* get field width */
96 field_width = -1;
97 if (isdigit(*fmt))
98 field_width = skip_atoi(&fmt);
99 else if (*fmt == '*') {
100 ++fmt;
101 /* it's the next argument */
102 field_width = va_arg(args, int);
103 if (field_width < 0) {
104 field_width = -field_width;
105 flags |= PRINTF_LEFT;
106 }
107 }
108
109 /* get the precision */
110 precision = -1;
111 if (*fmt == '.') {
112 ++fmt;
113 if (isdigit(*fmt))
114 precision = skip_atoi(&fmt);
115 else if (*fmt == '*') {
116 ++fmt;
117 /* it's the next argument */
118 precision = va_arg(args, int);
119 }
120 if (precision < 0)
121 precision = 0;
122 }
123
124 /* get the conversion qualifier */
125 qualifier = -1;
126 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
127 *fmt =='Z' || *fmt == 'z' || *fmt == 't') {
128 qualifier = *fmt;
129 ++fmt;
130 if (qualifier == 'l' && *fmt == 'l') {
131 qualifier = 'L';
132 ++fmt;
133 }
134 }
135
136 /* default base */
137 base = 10;
138
139 switch (*fmt) {
140 case 'c':
141 if (!(flags & PRINTF_LEFT)) {
142 while (--field_width > 0) {
143 if (str < end)
144 *str = ' ';
145 ++str;
146 }
147 }
148 c = (unsigned char) va_arg(args, int);
149 if (str < end)
150 *str = c;
151 ++str;
152 while (--field_width > 0) {
153 if (str < end)
154 *str = ' ';
155 ++str;
156 }
157 continue;
158
159 case 's':
160 s = va_arg(args, char *);
161 if (s == NULL)
162 s = "<NULL>";
163
164 len = strnlen(s, precision);
165
166 if (!(flags & PRINTF_LEFT)) {
167 while (len < field_width--) {
168 if (str < end)
169 *str = ' ';
170 ++str;
171 }
172 }
173 for (i = 0; i < len; ++i) {
174 if (str < end)
175 *str = *s;
176 ++str; ++s;
177 }
178 while (len < field_width--) {
179 if (str < end)
180 *str = ' ';
181 ++str;
182 }
183 continue;
184
185 case 'p':
186 if (field_width == -1) {
187 field_width = 2*sizeof(void *);
188 flags |= PRINTF_ZEROPAD;
189 }
190 str = number(str, end,
191 (unsigned long) va_arg(args, void *),
192 16, field_width, precision, flags);
193 continue;
194
195 #ifndef NO_FLOAT
196 case 'g':
197 case 'f': {
198 int m;
199 double f;
200 int integer;
201
202 f = va_arg(args, double);
203 if(f < 0.0) {
204 *str = '-';
205 str++;
206 f = -f;
207 }
208
209 integer = f;
210 if(integer > 0) {
211 m = 1;
212 while(integer > (m*10)) m *= 10;
213 while((m >= 1) && (str < end)) {
214 int n;
215 n = integer/m;
216 *str = '0' + n;
217 str++;
218 f = f - m*n;
219 integer = integer - m*n;
220 m /= 10;
221 }
222 } else if(str < end) {
223 *str = '0';
224 str++;
225 }
226
227 if(str < end) {
228 *str = '.';
229 str++;
230 }
231
232 for(i=0;i<6;i++) {
233 int n;
234
235 f = f*10.0;
236 n = f;
237 f = f - n;
238 if(str >= end) break;
239 *str = '0' + n;
240 str++;
241 }
242
243 continue;
244 }
245 #endif
246
247 case 'n':
248 /* FIXME:
249 * What does C99 say about the overflow case here? */
250 if (qualifier == 'l') {
251 long * ip = va_arg(args, long *);
252 *ip = (str - buf);
253 } else if (qualifier == 'Z' || qualifier == 'z') {
254 size_t * ip = va_arg(args, size_t *);
255 *ip = (str - buf);
256 } else {
257 int * ip = va_arg(args, int *);
258 *ip = (str - buf);
259 }
260 continue;
261
262 case '%':
263 if (str < end)
264 *str = '%';
265 ++str;
266 continue;
267
268 /* integer number formats - set up the flags and "break" */
269 case 'o':
270 base = 8;
271 break;
272
273 case 'X':
274 flags |= PRINTF_LARGE;
275 case 'x':
276 base = 16;
277 break;
278
279 case 'd':
280 case 'i':
281 flags |= PRINTF_SIGN;
282 case 'u':
283 break;
284
285 default:
286 if (str < end)
287 *str = '%';
288 ++str;
289 if (*fmt) {
290 if (str < end)
291 *str = *fmt;
292 ++str;
293 } else {
294 --fmt;
295 }
296 continue;
297 }
298 if (qualifier == 'L')
299 num = va_arg(args, long long);
300 else if (qualifier == 'l') {
301 num = va_arg(args, unsigned long);
302 if (flags & PRINTF_SIGN)
303 num = (signed long) num;
304 } else if (qualifier == 'Z' || qualifier == 'z') {
305 num = va_arg(args, size_t);
306 } else if (qualifier == 't') {
307 num = va_arg(args, ptrdiff_t);
308 } else if (qualifier == 'h') {
309 num = (unsigned short) va_arg(args, int);
310 if (flags & PRINTF_SIGN)
311 num = (signed short) num;
312 } else {
313 num = va_arg(args, unsigned int);
314 if (flags & PRINTF_SIGN)
315 num = (signed int) num;
316 }
317 str = number(str, end, num, base,
318 field_width, precision, flags);
319 }
320 if (size > 0) {
321 if (str < end)
322 *str = '\0';
323 else
324 end[-1] = '\0';
325 }
326 /* the trailing null byte doesn't count towards the total */
327 return str-buf;
328 }