]> sourceware.org Git - systemtap.git/blob - runtime/vsprintf.c
2007-01-31 Martin Hunt <hunt@redhat.com>
[systemtap.git] / runtime / vsprintf.c
1 /* -*- linux-c -*-
2 * vsprintf.c
3 * Copyright (C) 2006 Red Hat Inc.
4 * Based on code from the Linux kernel
5 * Copyright (C) 1991, 1992 Linus Torvalds
6 *
7 * This file is part of systemtap, and is free software. You can
8 * redistribute it and/or modify it under the terms of the GNU General
9 * Public License (GPL); either version 2, or (at your option) any
10 * later version.
11 */
12 #ifndef _VSPRINTF_C_
13 #define _VSPRINTF_C_
14
15 enum endian {STP_NATIVE=0, STP_LITTLE, STP_BIG};
16 static enum endian _stp_endian = STP_NATIVE;
17
18 static int skip_atoi(const char **s)
19 {
20 int i=0;
21 while (isdigit(**s))
22 i = i*10 + *((*s)++) - '0';
23 return i;
24 }
25
26 enum print_flag {STP_ZEROPAD=1, STP_SIGN=2, STP_PLUS=4, STP_SPACE=8, STP_LEFT=16, STP_SPECIAL=32, STP_LARGE=64};
27
28 static char * number(char * buf, char * end, uint64_t num, int base, int size, int precision, enum print_flag type)
29 {
30 char c,sign,tmp[66];
31 const char *digits;
32 static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
33 static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
34 int i;
35
36 digits = (type & STP_LARGE) ? large_digits : small_digits;
37 if (type & STP_LEFT)
38 type &= ~STP_ZEROPAD;
39 if (base < 2 || base > 36)
40 return NULL;
41 c = (type & STP_ZEROPAD) ? '0' : ' ';
42 sign = 0;
43 if (type & STP_SIGN) {
44 if ((int64_t) num < 0) {
45 sign = '-';
46 num = - (int64_t) num;
47 size--;
48 } else if (type & STP_PLUS) {
49 sign = '+';
50 size--;
51 } else if (type & STP_SPACE) {
52 sign = ' ';
53 size--;
54 }
55 }
56 if (type & STP_SPECIAL) {
57 if (base == 16)
58 size -= 2;
59 else if (base == 8)
60 size--;
61 }
62 i = 0;
63 if (num == 0)
64 tmp[i++]='0';
65 else while (num != 0)
66 tmp[i++] = digits[do_div(num,base)];
67 if (i > precision)
68 precision = i;
69 size -= precision;
70 if (!(type&(STP_ZEROPAD+STP_LEFT))) {
71 while(size-->0) {
72 if (buf <= end)
73 *buf = ' ';
74 ++buf;
75 }
76 }
77 if (sign) {
78 if (buf <= end)
79 *buf = sign;
80 ++buf;
81 }
82 if (type & STP_SPECIAL) {
83 if (base==8) {
84 if (buf <= end)
85 *buf = '0';
86 ++buf;
87 } else if (base==16) {
88 if (buf <= end)
89 *buf = '0';
90 ++buf;
91 if (buf <= end)
92 *buf = digits[33];
93 ++buf;
94 }
95 }
96 if (!(type & STP_LEFT)) {
97 while (size-- > 0) {
98 if (buf <= end)
99 *buf = c;
100 ++buf;
101 }
102 }
103 while (i < precision--) {
104 if (buf <= end)
105 *buf = '0';
106 ++buf;
107 }
108 while (i-- > 0) {
109 if (buf <= end)
110 *buf = tmp[i];
111 ++buf;
112 }
113 while (size-- > 0) {
114 if (buf <= end)
115 *buf = ' ';
116 ++buf;
117 }
118 return buf;
119 }
120
121 int _stp_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
122 {
123 int len;
124 uint64_t num;
125 int i, base;
126 char *str, *end, c;
127 const char *s;
128 enum print_flag flags; /* flags to number() */
129 int field_width; /* width of output field */
130 int precision; /* min. # of digits for integers; max
131 number of chars for from string */
132 int qualifier; /* 'h', 'l', or 'L' for integer fields */
133 char *write_len_ptr = NULL;
134 int write_len_width = 0;
135
136 /* Reject out-of-range values early */
137 if (unlikely((int) size < 0))
138 return 0;
139
140 str = buf;
141 end = buf + size - 1;
142
143 for (; *fmt ; ++fmt) {
144 if (*fmt != '%') {
145 if (str <= end)
146 *str = *fmt;
147 ++str;
148 continue;
149 }
150
151 /* process flags */
152 flags = 0;
153 repeat:
154 ++fmt; /* this also skips first '%' */
155 switch (*fmt) {
156 case '-': flags |= STP_LEFT; goto repeat;
157 case '+': flags |= STP_PLUS; goto repeat;
158 case ' ': flags |= STP_SPACE; goto repeat;
159 case '#': flags |= STP_SPECIAL; goto repeat;
160 case '0': flags |= STP_ZEROPAD; goto repeat;
161 }
162
163 /* get field width */
164 field_width = -1;
165 if (isdigit(*fmt))
166 field_width = skip_atoi(&fmt);
167 else if (*fmt == '*') {
168 ++fmt;
169 /* it's the next argument */
170 field_width = va_arg(args, int);
171 if (field_width < 0) {
172 field_width = -field_width;
173 flags |= STP_LEFT;
174 }
175 }
176
177 /* get the precision */
178 precision = -1;
179 if (*fmt == '.') {
180 ++fmt;
181 if (isdigit(*fmt))
182 precision = skip_atoi(&fmt);
183 else if (*fmt == '*') {
184 ++fmt;
185 /* it's the next argument */
186 precision = va_arg(args, int);
187 }
188 if (precision < 0)
189 precision = 0;
190 }
191
192 /* get the conversion qualifier */
193 qualifier = -1;
194 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
195 qualifier = *fmt;
196 ++fmt;
197 if (qualifier == 'l' && *fmt == 'l') {
198 qualifier = 'L';
199 ++fmt;
200 }
201 }
202
203 /* default base */
204 base = 10;
205
206 switch (*fmt) {
207 case 'b':
208 num = va_arg(args, int64_t);
209 switch(field_width) {
210 case 1:
211 if(str <= end)
212 *(int8_t *)str = (int8_t)num;
213 ++str;
214 break;
215 case 2:
216 if (_stp_endian != STP_NATIVE) {
217 if (_stp_endian == STP_BIG)
218 num = cpu_to_be16(num);
219 else
220 num = cpu_to_le16(num);
221 }
222 if((str + 1) <= end)
223 *(int16_t *)str = (int16_t)num;
224 str+=2;
225 break;
226 case 8:
227 if (_stp_endian != STP_NATIVE) {
228 if (_stp_endian == STP_BIG)
229 num = cpu_to_be64(num);
230 else
231 num = cpu_to_le64(num);
232 }
233
234 if((str + 7) <= end)
235 *(int64_t *)str = num;
236 str+=8;
237 break;
238 case 4:
239 default: // "%4b" by default
240 if (_stp_endian != STP_NATIVE) {
241 if (_stp_endian == STP_BIG)
242 num = cpu_to_be32(num);
243 else
244 num = cpu_to_le32(num);
245 }
246
247 if((str + 3) <= end)
248 *(int32_t *)str = num;
249 str+=4;
250 break;
251 }
252 continue;
253
254 case 's':
255 s = va_arg(args, char *);
256 if ((unsigned long)s < PAGE_SIZE)
257 s = "<NULL>";
258
259 len = strnlen(s, precision);
260
261 if (!(flags & STP_LEFT)) {
262 while (len < field_width--) {
263 if (str <= end)
264 *str = ' ';
265 ++str;
266 }
267 }
268 for (i = 0; i < len; ++i) {
269 if (str <= end)
270 *str = *s;
271 ++str; ++s;
272 }
273 while (len < field_width--) {
274 if (str <= end)
275 *str = ' ';
276 ++str;
277 }
278 if(flags & STP_ZEROPAD) {
279 if (str <= end)
280 *str = '\0';
281 ++str;
282 }
283 continue;
284
285 case 'X':
286 flags |= STP_LARGE;
287 case 'x':
288 base = 16;
289 break;
290
291 case 'd':
292 case 'i':
293 flags |= STP_SIGN;
294 case 'u':
295 break;
296
297 case 'p':
298 len = 2*sizeof(void *) + 2;
299 flags |= STP_ZEROPAD;
300
301 if (field_width == -1)
302 field_width = len;
303
304 if (!(flags & STP_LEFT)) {
305 while (len < field_width) {
306 field_width--;
307 if (str <= end)
308 *str = ' ';
309 ++str;
310 }
311 }
312 if (str <= end) {
313 *str++ = '0';
314 field_width--;
315 }
316 if (str <= end) {
317 *str++ = 'x';
318 field_width--;
319 }
320
321 str = number(str, end,
322 (unsigned long) va_arg(args, int64_t),
323 16, field_width, field_width, flags);
324 continue;
325
326 case 'n':
327 write_len_ptr = str;
328 write_len_width = 2;
329 if (field_width == 1)
330 write_len_width = 1;
331 else if (field_width == 4)
332 write_len_width = 4;
333 str += write_len_width;
334 continue;
335
336 case '%':
337 if (str <= end)
338 *str = '%';
339 ++str;
340 continue;
341
342 /* integer number formats - set up the flags and "break" */
343 case 'o':
344 base = 8;
345 break;
346
347 case 'c':
348 if (!(flags & STP_LEFT)) {
349 while (--field_width > 0) {
350 if (str <= end)
351 *str = ' ';
352 ++str;
353 }
354 }
355 c = (unsigned char) va_arg(args, int);
356 if (str <= end)
357 *str = c;
358 ++str;
359 while (--field_width > 0) {
360 if (str <= end)
361 *str = ' ';
362 ++str;
363 }
364 continue;
365
366 default:
367 if (str <= end)
368 *str = '%';
369 ++str;
370 if (*fmt) {
371 if (str <= end)
372 *str = *fmt;
373 ++str;
374 } else {
375 --fmt;
376 }
377 continue;
378 }
379
380 if (qualifier == 'L')
381 num = va_arg(args, int64_t);
382 else if (qualifier == 'l') {
383 num = va_arg(args, unsigned long);
384 if (flags & STP_SIGN)
385 num = (signed long) num;
386 } else if (qualifier == 'h') {
387 num = (unsigned short) va_arg(args, int);
388 if (flags & STP_SIGN)
389 num = (signed short) num;
390 } else {
391 num = va_arg(args, unsigned int);
392 if (flags & STP_SIGN)
393 num = (signed int) num;
394 }
395 str = number(str, end, num, base,
396 field_width, precision, flags);
397 }
398
399 if (write_len_ptr) {
400 int written;
401 if (likely(str <= end))
402 written = str - write_len_ptr - write_len_width;
403 else
404 written = end - write_len_ptr - write_len_width;
405
406 if (likely(write_len_ptr + write_len_width < end)) {
407 switch (write_len_width) {
408 case 1:
409 *(uint8_t *)write_len_ptr = (uint8_t)written;
410 break;
411 case 2:
412 *(uint16_t *)write_len_ptr = (uint16_t)written;
413 break;
414
415 case 4:
416 *(uint32_t *)write_len_ptr = (uint32_t)written;
417 break;
418 }
419 }
420 }
421
422 if (likely(str <= end))
423 *str = '\0';
424 else if (size > 0)
425 /* don't write out a null byte if the buf size is zero */
426 *end = '\0';
427 return str-buf;
428 }
429
430 #endif /* _VSPRINTF_C_ */
This page took 0.071225 seconds and 5 git commands to generate.