]> git.ipfire.org Git - thirdparty/bird.git/blame - lib/printf.c
Merge branch 'add-path'
[thirdparty/bird.git] / lib / printf.c
CommitLineData
ecacdfa4
MM
1/*
2 * BIRD Library -- Formatted Output
3 *
4 * (c) 1991, 1992 Lars Wirzenius & Linus Torvalds
5 *
6 * Hacked up for BIRD by Martin Mares <mj@ucw.cz>
53a416d3 7 * Buffer size limitation implemented by Martin Mares.
ecacdfa4
MM
8 */
9
10#include "nest/bird.h"
11#include "string.h"
12
d997534f 13#include <errno.h>
d997534f 14
53ffbff3
OZ
15#include "nest/iface.h"
16
ecacdfa4
MM
17/* we use this so that we can do without the ctype library */
18#define is_digit(c) ((c) >= '0' && (c) <= '9')
19
20static int skip_atoi(const char **s)
21{
22 int i=0;
23
24 while (is_digit(**s))
25 i = i*10 + *((*s)++) - '0';
26 return i;
27}
28
29#define ZEROPAD 1 /* pad with zero */
30#define SIGN 2 /* unsigned/signed long */
31#define PLUS 4 /* show plus */
32#define SPACE 8 /* space if plus */
33#define LEFT 16 /* left justified */
34#define SPECIAL 32 /* 0x */
35#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
36
37#define do_div(n,base) ({ \
38int __res; \
39__res = ((unsigned long) n) % (unsigned) base; \
40n = ((unsigned long) n) / (unsigned) base; \
41__res; })
42
53a416d3
MM
43static char * number(char * str, long num, int base, int size, int precision,
44 int type, int remains)
ecacdfa4
MM
45{
46 char c,sign,tmp[66];
47 const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
48 int i;
49
53a416d3
MM
50 if (size >= 0 && (remains -= size) < 0)
51 return NULL;
ecacdfa4
MM
52 if (type & LARGE)
53 digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
54 if (type & LEFT)
55 type &= ~ZEROPAD;
56 if (base < 2 || base > 36)
57 return 0;
58 c = (type & ZEROPAD) ? '0' : ' ';
59 sign = 0;
60 if (type & SIGN) {
61 if (num < 0) {
62 sign = '-';
63 num = -num;
64 size--;
65 } else if (type & PLUS) {
66 sign = '+';
67 size--;
68 } else if (type & SPACE) {
69 sign = ' ';
70 size--;
71 }
72 }
73 if (type & SPECIAL) {
74 if (base == 16)
75 size -= 2;
76 else if (base == 8)
77 size--;
78 }
79 i = 0;
80 if (num == 0)
81 tmp[i++]='0';
82 else while (num != 0)
83 tmp[i++] = digits[do_div(num,base)];
84 if (i > precision)
85 precision = i;
86 size -= precision;
53a416d3
MM
87 if (size < 0 && -size > remains)
88 return NULL;
ecacdfa4
MM
89 if (!(type&(ZEROPAD+LEFT)))
90 while(size-->0)
91 *str++ = ' ';
92 if (sign)
93 *str++ = sign;
94 if (type & SPECIAL) {
95 if (base==8)
96 *str++ = '0';
97 else if (base==16) {
98 *str++ = '0';
99 *str++ = digits[33];
100 }
101 }
102 if (!(type & LEFT))
103 while (size-- > 0)
104 *str++ = c;
105 while (i < precision--)
106 *str++ = '0';
107 while (i-- > 0)
108 *str++ = tmp[i];
109 while (size-- > 0)
110 *str++ = ' ';
111 return str;
112}
113
7722938d
MM
114/**
115 * bvsnprintf - BIRD's vsnprintf()
116 * @buf: destination buffer
117 * @size: size of the buffer
118 * @fmt: format string
119 * @args: a list of arguments to be formatted
120 *
121 * This functions acts like ordinary sprintf() except that it checks
122 * available space to avoid buffer overflows and it allows some more
30b77304
MM
123 * format specifiers: |%I| for formatting of IP addresses (any non-zero
124 * width is automatically replaced by standard IP address width which
2f6483cd
OZ
125 * depends on whether we use IPv4 or IPv6; |%#I| gives hexadecimal format),
126 * |%R| for Router / Network ID (u32 value printed as IPv4 address)
30b77304 127 * and |%m| resp. |%M| for error messages (uses strerror() to translate @errno code to
7722938d
MM
128 * message text). On the other hand, it doesn't support floating
129 * point numbers.
130 *
131 * Result: number of characters of the output string or -1 if
132 * the buffer space was insufficient.
133 */
53a416d3 134int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
ecacdfa4
MM
135{
136 int len;
137 unsigned long num;
138 int i, base;
2f6483cd 139 u32 x;
53a416d3 140 char *str, *start;
ecacdfa4 141 const char *s;
30b77304 142 char ipbuf[STD_ADDRESS_P_LENGTH+1];
53ffbff3 143 struct iface *iface;
ecacdfa4
MM
144
145 int flags; /* flags to number() */
146
147 int field_width; /* width of output field */
148 int precision; /* min. # of digits for integers; max
149 number of chars for from string */
150 int qualifier; /* 'h', 'l', or 'L' for integer fields */
151
53a416d3 152 for (start=str=buf ; *fmt ; ++fmt, size-=(str-start), start=str) {
ecacdfa4 153 if (*fmt != '%') {
53a416d3
MM
154 if (!size)
155 return -1;
ecacdfa4
MM
156 *str++ = *fmt;
157 continue;
158 }
159
160 /* process flags */
161 flags = 0;
162 repeat:
163 ++fmt; /* this also skips first '%' */
164 switch (*fmt) {
165 case '-': flags |= LEFT; goto repeat;
166 case '+': flags |= PLUS; goto repeat;
167 case ' ': flags |= SPACE; goto repeat;
168 case '#': flags |= SPECIAL; goto repeat;
169 case '0': flags |= ZEROPAD; goto repeat;
170 }
171
172 /* get field width */
173 field_width = -1;
174 if (is_digit(*fmt))
175 field_width = skip_atoi(&fmt);
176 else if (*fmt == '*') {
177 ++fmt;
178 /* it's the next argument */
179 field_width = va_arg(args, int);
180 if (field_width < 0) {
181 field_width = -field_width;
182 flags |= LEFT;
183 }
184 }
185
186 /* get the precision */
187 precision = -1;
188 if (*fmt == '.') {
189 ++fmt;
190 if (is_digit(*fmt))
191 precision = skip_atoi(&fmt);
192 else if (*fmt == '*') {
193 ++fmt;
194 /* it's the next argument */
195 precision = va_arg(args, int);
196 }
197 if (precision < 0)
198 precision = 0;
199 }
200
201 /* get the conversion qualifier */
202 qualifier = -1;
203 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
204 qualifier = *fmt;
205 ++fmt;
206 }
207
208 /* default base */
209 base = 10;
210
53a416d3
MM
211 if (field_width > size)
212 return -1;
ecacdfa4
MM
213 switch (*fmt) {
214 case 'c':
215 if (!(flags & LEFT))
216 while (--field_width > 0)
217 *str++ = ' ';
218 *str++ = (unsigned char) va_arg(args, int);
219 while (--field_width > 0)
220 *str++ = ' ';
221 continue;
222
d997534f
MM
223 case 'm':
224 s = strerror(errno);
225 goto str;
53943a00
MM
226 case 'M':
227 s = strerror(va_arg(args, int));
228 goto str;
ecacdfa4
MM
229 case 's':
230 s = va_arg(args, char *);
231 if (!s)
232 s = "<NULL>";
233
d997534f 234 str:
ecacdfa4
MM
235 len = strlen(s);
236 if (precision >= 0 && len > precision)
237 len = precision;
53a416d3
MM
238 if (len > size)
239 return -1;
ecacdfa4
MM
240
241 if (!(flags & LEFT))
242 while (len < field_width--)
243 *str++ = ' ';
244 for (i = 0; i < len; ++i)
245 *str++ = *s++;
246 while (len < field_width--)
247 *str++ = ' ';
248 continue;
249
250 case 'p':
251 if (field_width == -1) {
252 field_width = 2*sizeof(void *);
253 flags |= ZEROPAD;
254 }
255 str = number(str,
256 (unsigned long) va_arg(args, void *), 16,
53a416d3
MM
257 field_width, precision, flags, size);
258 if (!str)
259 return -1;
ecacdfa4
MM
260 continue;
261
262
263 case 'n':
264 if (qualifier == 'l') {
265 long * ip = va_arg(args, long *);
266 *ip = (str - buf);
267 } else {
268 int * ip = va_arg(args, int *);
269 *ip = (str - buf);
270 }
271 continue;
272
273 /* IP address */
274 case 'I':
275 if (flags & SPECIAL)
30b77304 276 ip_ntox(va_arg(args, ip_addr), ipbuf);
ecacdfa4 277 else {
30b77304 278 ip_ntop(va_arg(args, ip_addr), ipbuf);
1ec52253 279 if (field_width == 1)
30b77304 280 field_width = STD_ADDRESS_P_LENGTH;
ecacdfa4 281 }
30b77304
MM
282 s = ipbuf;
283 goto str;
ecacdfa4 284
53ffbff3
OZ
285 /* Interface scope after link-local IP address */
286 case 'J':
287 iface = va_arg(args, struct iface *);
288 if (!iface)
289 continue;
290 if (!size)
291 return -1;
292
293 *str++ = '%';
294 start++;
295 size--;
296
297 s = iface->name;
298 goto str;
299
2f6483cd
OZ
300 /* Router/Network ID - essentially IPv4 address in u32 value */
301 case 'R':
302 x = va_arg(args, u32);
303 bsprintf(ipbuf, "%d.%d.%d.%d",
304 ((x >> 24) & 0xff),
305 ((x >> 16) & 0xff),
306 ((x >> 8) & 0xff),
307 (x & 0xff));
308 s = ipbuf;
309 goto str;
310
ecacdfa4
MM
311 /* integer number formats - set up the flags and "break" */
312 case 'o':
313 base = 8;
314 break;
315
316 case 'X':
317 flags |= LARGE;
318 case 'x':
319 base = 16;
320 break;
321
322 case 'd':
323 case 'i':
324 flags |= SIGN;
325 case 'u':
326 break;
327
328 default:
53a416d3
MM
329 if (size < 2)
330 return -1;
ecacdfa4
MM
331 if (*fmt != '%')
332 *str++ = '%';
333 if (*fmt)
334 *str++ = *fmt;
335 else
336 --fmt;
337 continue;
338 }
339 if (qualifier == 'l')
340 num = va_arg(args, unsigned long);
341 else if (qualifier == 'h') {
4254dc45 342 num = (unsigned short) va_arg(args, int);
ecacdfa4 343 if (flags & SIGN)
4254dc45 344 num = (short) num;
ecacdfa4
MM
345 } else if (flags & SIGN)
346 num = va_arg(args, int);
347 else
348 num = va_arg(args, unsigned int);
53a416d3
MM
349 str = number(str, num, base, field_width, precision, flags, size);
350 if (!str)
351 return -1;
ecacdfa4 352 }
53a416d3
MM
353 if (!size)
354 return -1;
ecacdfa4
MM
355 *str = '\0';
356 return str-buf;
357}
358
7722938d
MM
359/**
360 * bvsprintf - BIRD's vsprintf()
361 * @buf: buffer
362 * @fmt: format string
363 * @args: a list of arguments to be formatted
364 *
365 * This function is equivalent to bvsnprintf() with an infinite
366 * buffer size. Please use carefully only when you are absolutely
367 * sure the buffer won't overflow.
368 */
53a416d3
MM
369int bvsprintf(char *buf, const char *fmt, va_list args)
370{
371 return bvsnprintf(buf, 1000000000, fmt, args);
372}
373
7722938d
MM
374/**
375 * bsprintf - BIRD's sprintf()
376 * @buf: buffer
377 * @fmt: format string
378 *
379 * This function is equivalent to bvsnprintf() with an infinite
380 * buffer size and variable arguments instead of a &va_list.
381 * Please use carefully only when you are absolutely
382 * sure the buffer won't overflow.
383 */
ecacdfa4
MM
384int bsprintf(char * buf, const char *fmt, ...)
385{
53a416d3
MM
386 va_list args;
387 int i;
ecacdfa4 388
53a416d3
MM
389 va_start(args, fmt);
390 i=bvsnprintf(buf, 1000000000, fmt, args);
391 va_end(args);
392 return i;
393}
394
7722938d
MM
395/**
396 * bsnprintf - BIRD's snprintf()
397 * @buf: buffer
398 * @size: buffer size
399 * @fmt: format string
400 *
401 * This function is equivalent to bsnprintf() with variable arguments instead of a &va_list.
402 */
53a416d3
MM
403int bsnprintf(char * buf, int size, const char *fmt, ...)
404{
405 va_list args;
406 int i;
407
408 va_start(args, fmt);
409 i=bvsnprintf(buf, size, fmt, args);
410 va_end(args);
411 return i;
ecacdfa4 412}
0e175f9f
OZ
413
414int
415buffer_vprint(buffer *buf, const char *fmt, va_list args)
416{
417 int i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
418 buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
419 return i;
420}
421
422int
423buffer_print(buffer *buf, const char *fmt, ...)
424{
425 va_list args;
426 int i;
427
428 va_start(args, fmt);
429 i=bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
430 va_end(args);
431
432 buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
433 return i;
434}
435
436void
437buffer_puts(buffer *buf, const char *str)
438{
439 byte *bp = buf->pos;
440 byte *be = buf->end;
441
442 while (bp < be && *str)
443 *bp++ = *str++;
444
445 if (bp < be)
446 *bp = 0;
447
448 buf->pos = bp;
449}