]> git.ipfire.org Git - thirdparty/bird.git/blob - lib/printf.c
Integrated IP functions.
[thirdparty/bird.git] / lib / printf.c
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>
7 * Buffer size limitation implemented by Martin Mares.
8 */
9
10 #include "nest/bird.h"
11 #include "string.h"
12
13 #include <errno.h>
14
15 #include "nest/iface.h"
16
17 /* we use this so that we can do without the ctype library */
18 #define is_digit(c) ((c) >= '0' && (c) <= '9')
19
20 static 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) ({ \
38 int __res; \
39 __res = ((unsigned long) n) % (unsigned) base; \
40 n = ((unsigned long) n) / (unsigned) base; \
41 __res; })
42
43 static char * number(char * str, long num, int base, int size, int precision,
44 int type, int remains)
45 {
46 char c,sign,tmp[66];
47 const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
48 int i;
49
50 if (size >= 0 && (remains -= size) < 0)
51 return NULL;
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;
87 if (size < 0 && -size > remains)
88 return NULL;
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
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
123 * format specifiers: |%I| for formatting of IP addresses (any non-zero
124 * width is automatically replaced by standard IP address width which
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)
127 * and |%m| resp. |%M| for error messages (uses strerror() to translate @errno code to
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 */
134 int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
135 {
136 int len;
137 unsigned long num;
138 int i, base;
139 u32 x;
140 char *str, *start;
141 const char *s;
142 char ipbuf[STD_ADDRESS_P_LENGTH+1];
143 struct iface *iface;
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
152 for (start=str=buf ; *fmt ; ++fmt, size-=(str-start), start=str) {
153 if (*fmt != '%') {
154 if (!size)
155 return -1;
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
211 if (field_width > size)
212 return -1;
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
223 case 'm':
224 if (flags & SPECIAL) {
225 if (!errno)
226 continue;
227 if (size < 2)
228 return -1;
229 *str++ = ':';
230 *str++ = ' ';
231 start += 2;
232 size -= 2;
233 }
234 s = strerror(errno);
235 goto str;
236 case 'M':
237 s = strerror(va_arg(args, int));
238 goto str;
239 case 's':
240 s = va_arg(args, char *);
241 if (!s)
242 s = "<NULL>";
243
244 str:
245 len = strlen(s);
246 if (precision >= 0 && len > precision)
247 len = precision;
248 if (len > size)
249 return -1;
250
251 if (!(flags & LEFT))
252 while (len < field_width--)
253 *str++ = ' ';
254 for (i = 0; i < len; ++i)
255 *str++ = *s++;
256 while (len < field_width--)
257 *str++ = ' ';
258 continue;
259
260 case 'p':
261 if (field_width == -1) {
262 field_width = 2*sizeof(void *);
263 flags |= ZEROPAD;
264 }
265 str = number(str,
266 (unsigned long) va_arg(args, void *), 16,
267 field_width, precision, flags, size);
268 if (!str)
269 return -1;
270 continue;
271
272
273 case 'n':
274 if (qualifier == 'l') {
275 long * ip = va_arg(args, long *);
276 *ip = (str - buf);
277 } else {
278 int * ip = va_arg(args, int *);
279 *ip = (str - buf);
280 }
281 continue;
282
283 /* IP address */
284 case 'I':
285 if (flags & SPECIAL)
286 ipa_ntox(va_arg(args, ip_addr), ipbuf);
287 else {
288 ipa_ntop(va_arg(args, ip_addr), ipbuf);
289 if (field_width == 1)
290 field_width = STD_ADDRESS_P_LENGTH;
291 }
292 s = ipbuf;
293 goto str;
294
295 /* Interface scope after link-local IP address */
296 case 'J':
297 iface = va_arg(args, struct iface *);
298 if (!iface)
299 continue;
300 if (!size)
301 return -1;
302
303 *str++ = '%';
304 start++;
305 size--;
306
307 s = iface->name;
308 goto str;
309
310 /* Router/Network ID - essentially IPv4 address in u32 value */
311 case 'R':
312 x = va_arg(args, u32);
313 bsprintf(ipbuf, "%d.%d.%d.%d",
314 ((x >> 24) & 0xff),
315 ((x >> 16) & 0xff),
316 ((x >> 8) & 0xff),
317 (x & 0xff));
318 s = ipbuf;
319 goto str;
320
321 /* integer number formats - set up the flags and "break" */
322 case 'o':
323 base = 8;
324 break;
325
326 case 'X':
327 flags |= LARGE;
328 case 'x':
329 base = 16;
330 break;
331
332 case 'd':
333 case 'i':
334 flags |= SIGN;
335 case 'u':
336 break;
337
338 default:
339 if (size < 2)
340 return -1;
341 if (*fmt != '%')
342 *str++ = '%';
343 if (*fmt)
344 *str++ = *fmt;
345 else
346 --fmt;
347 continue;
348 }
349 if (qualifier == 'l')
350 num = va_arg(args, unsigned long);
351 else if (qualifier == 'h') {
352 num = (unsigned short) va_arg(args, int);
353 if (flags & SIGN)
354 num = (short) num;
355 } else if (flags & SIGN)
356 num = va_arg(args, int);
357 else
358 num = va_arg(args, unsigned int);
359 str = number(str, num, base, field_width, precision, flags, size);
360 if (!str)
361 return -1;
362 }
363 if (!size)
364 return -1;
365 *str = '\0';
366 return str-buf;
367 }
368
369 /**
370 * bvsprintf - BIRD's vsprintf()
371 * @buf: buffer
372 * @fmt: format string
373 * @args: a list of arguments to be formatted
374 *
375 * This function is equivalent to bvsnprintf() with an infinite
376 * buffer size. Please use carefully only when you are absolutely
377 * sure the buffer won't overflow.
378 */
379 int bvsprintf(char *buf, const char *fmt, va_list args)
380 {
381 return bvsnprintf(buf, 1000000000, fmt, args);
382 }
383
384 /**
385 * bsprintf - BIRD's sprintf()
386 * @buf: buffer
387 * @fmt: format string
388 *
389 * This function is equivalent to bvsnprintf() with an infinite
390 * buffer size and variable arguments instead of a &va_list.
391 * Please use carefully only when you are absolutely
392 * sure the buffer won't overflow.
393 */
394 int bsprintf(char * buf, const char *fmt, ...)
395 {
396 va_list args;
397 int i;
398
399 va_start(args, fmt);
400 i=bvsnprintf(buf, 1000000000, fmt, args);
401 va_end(args);
402 return i;
403 }
404
405 /**
406 * bsnprintf - BIRD's snprintf()
407 * @buf: buffer
408 * @size: buffer size
409 * @fmt: format string
410 *
411 * This function is equivalent to bsnprintf() with variable arguments instead of a &va_list.
412 */
413 int bsnprintf(char * buf, int size, const char *fmt, ...)
414 {
415 va_list args;
416 int i;
417
418 va_start(args, fmt);
419 i=bvsnprintf(buf, size, fmt, args);
420 va_end(args);
421 return i;
422 }
423
424 int
425 buffer_vprint(buffer *buf, const char *fmt, va_list args)
426 {
427 int i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
428 buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
429 return i;
430 }
431
432 int
433 buffer_print(buffer *buf, const char *fmt, ...)
434 {
435 va_list args;
436 int i;
437
438 va_start(args, fmt);
439 i=bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
440 va_end(args);
441
442 buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
443 return i;
444 }
445
446 void
447 buffer_puts(buffer *buf, const char *str)
448 {
449 byte *bp = buf->pos;
450 byte *be = buf->end;
451
452 while (bp < be && *str)
453 *bp++ = *str++;
454
455 if (bp < be)
456 *bp = 0;
457
458 buf->pos = bp;
459 }