xref: /haiku/src/system/kernel/lib/kernel_vsprintf.cpp (revision 25a7b01d15612846f332751841da3579db313082)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2003-2008, Axel Dörfler. All rights reserved.
4  * Distributed under the terms of the MIT license.
5  *
6  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
7  * Distributed under the terms of the NewOS License.
8  */
9 
10 
11 #include <SupportDefs.h>
12 
13 #include <ctype.h>
14 #include <stdarg.h>
15 #include <stdio.h>
16 #include <string.h>
17 
18 #include <algorithm>
19 
20 
21 #define ZEROPAD	1		/* pad with zero */
22 #define SIGN	2		/* unsigned/signed long */
23 #define PLUS	4		/* show plus */
24 #define SPACE	8		/* space if plus */
25 #define LEFT	16		/* left justified */
26 #define SPECIAL	32		/* 0x */
27 #define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
28 
29 #define FLOATING_SUPPORT
30 
31 
32 struct Buffer {
BufferBuffer33 	Buffer(char* buffer, size_t size)
34 		:
35 		fCurrent(buffer),
36 		fSize(size),
37 		fBytesWritten(0)
38 	{
39 	}
40 
BytesWrittenBuffer41 	size_t BytesWritten() const
42 	{
43 		return fBytesWritten;
44 	}
45 
PutCharacterBuffer46 	void PutCharacter(char c)
47 	{
48 		if (fBytesWritten < fSize) {
49 			*fCurrent = c;
50 			fCurrent++;
51 		}
52 
53 		fBytesWritten++;
54 	}
55 
PutPaddingBuffer56 	void PutPadding(int32 count)
57 	{
58 		if (count <= 0)
59 			return;
60 
61 		if (fBytesWritten < fSize) {
62 			int32 toWrite = std::min(fSize - fBytesWritten, (size_t)count);
63 			while (--toWrite >= 0)
64 				*fCurrent++ = ' ';
65 		}
66 
67 		fBytesWritten += count;
68 	}
69 
PutStringBuffer70 	void PutString(const char *source, int32 length)
71 	{
72 		if (length <= 0)
73 			return;
74 
75 		if (fBytesWritten < fSize) {
76 			int32 toWrite = std::min(fSize - fBytesWritten, (size_t)length);
77 			memcpy(fCurrent, source, toWrite);
78 			fCurrent += toWrite;
79 		}
80 
81 		fBytesWritten += length;
82 	}
83 
NullTerminateBuffer84 	void NullTerminate()
85 	{
86 		if (fBytesWritten < fSize)
87 			*fCurrent = '\0';
88 		else if (fSize > 0)
89 			fCurrent[-1] = '\0';
90 	}
91 
92 private:
93 	char*	fCurrent;
94 	size_t	fSize;
95 	size_t	fBytesWritten;
96 };
97 
98 
99 static int
skip_atoi(const char ** s)100 skip_atoi(const char **s)
101 {
102 	int i = 0;
103 
104 	while (isdigit(**s))
105 		i = i*10 + *((*s)++) - '0';
106 
107 	return i;
108 }
109 
110 
111 static uint64
do_div(uint64 * _number,uint32 base)112 do_div(uint64 *_number, uint32 base)
113 {
114 	uint64 result = *_number % (uint64)base;
115 	*_number = *_number / (uint64)base;
116 
117 	return result;
118 }
119 
120 
121 static char
sign_symbol(int flags,bool negative)122 sign_symbol(int flags, bool negative)
123 {
124 	if ((flags & SIGN) == 0)
125 		return '\0';
126 
127 	if (negative)
128 		return '-';
129 	else if ((flags & PLUS) != 0)
130 		return '+';
131 	else if ((flags & SPACE) != 0)
132 		return ' ';
133 
134 	return '\0';
135 }
136 
137 
138 static void
number(Buffer & outBuffer,uint64 num,uint32 base,int size,int precision,int flags)139 number(Buffer& outBuffer, uint64 num, uint32 base, int size,
140 	int precision, int flags)
141 {
142 	const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
143 	char c, sign, tmp[66];
144 	int i;
145 
146 	if (flags & LARGE)
147 		digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
148 	if (flags & LEFT)
149 		flags &= ~ZEROPAD;
150 	if (base < 2 || base > 36)
151 		return;
152 
153 	c = (flags & ZEROPAD) ? '0' : ' ';
154 
155 	if (flags & SIGN) {
156 		sign = sign_symbol(flags, (int64)num < 0);
157 		if ((int64)num < 0)
158 			num = -(int64)num;
159 		if (sign)
160 			size--;
161 	} else
162 		sign = 0;
163 
164 	if ((flags & SPECIAL) != 0) {
165 		if (base == 16)
166 			size -= 2;
167 		else if (base == 8)
168 			size--;
169 	}
170 
171 	i = 0;
172 	if (num == 0)
173 		tmp[i++] = '0';
174 	else while (num != 0)
175 		tmp[i++] = digits[do_div(&num, base)];
176 
177 	if (i > precision)
178 		precision = i;
179 	size -= precision;
180 
181 	if (!(flags & (ZEROPAD + LEFT))) {
182 		outBuffer.PutPadding(size);
183 		size = 0;
184 	}
185 	if (sign)
186 		outBuffer.PutCharacter(sign);
187 
188 	if ((flags & SPECIAL) != 0) {
189 		if (base == 8)
190 			outBuffer.PutCharacter('0');
191 		else if (base == 16) {
192 			outBuffer.PutCharacter('0');
193 			outBuffer.PutCharacter(digits[33]);
194 		}
195 	}
196 
197 	if (!(flags & LEFT)) {
198 		while (size-- > 0)
199 			outBuffer.PutCharacter(c);
200 	}
201 	while (i < precision--)
202 		outBuffer.PutCharacter('0');
203 	while (i-- > 0)
204 		outBuffer.PutCharacter(tmp[i]);
205 
206 	outBuffer.PutPadding(size);
207 }
208 
209 
210 #ifdef FLOATING_SUPPORT
211 /*!
212 	This is a very basic floating point to string conversion routine.
213 	It prints up to 3 fraction digits, and doesn't support any precision arguments.
214 	It's just here for your convenience so that you can use it for debug output.
215 */
216 static void
floating(Buffer & outBuffer,double value,int fieldWidth,int flags)217 floating(Buffer& outBuffer, double value, int fieldWidth, int flags)
218 {
219 	char buffer[66];
220 	uint64 fraction;
221 	uint64 integer;
222 	int32 length = 0;
223 	char sign;
224 
225 	sign = sign_symbol(flags, value < 0.0);
226 	if (value < 0.0)
227 		value = -value;
228 
229 	fraction = (uint64)(value * 1000) % 1000;
230 	integer = (uint64)value;
231 
232 	// put fraction part, if any
233 
234 	if (fraction != 0) {
235 		bool first = true;
236 		while (fraction != 0) {
237 			int digit = do_div(&fraction, 10);
238 			if (!first || digit > 0) {
239 				buffer[length++] = '0' + digit;
240 				first = false;
241 			}
242 		}
243 
244 		buffer[length++] = '.';
245 	}
246 
247 	// put integer part
248 
249 	if (integer == 0) {
250 		buffer[length++] = '0';
251 	} else {
252 		while (integer != 0)
253 			buffer[length++] = '0' + do_div(&integer, 10);
254 	}
255 
256 	// write back to string
257 
258 	if (!(flags & LEFT))
259 		outBuffer.PutPadding(fieldWidth);
260 
261 	if (sign)
262 		outBuffer.PutCharacter(sign);
263 
264 	while (length-- > 0)
265 		outBuffer.PutCharacter(buffer[length]);
266 
267 	if ((flags & LEFT) != 0)
268 		outBuffer.PutPadding(fieldWidth);
269 }
270 #endif	// FLOATING_SUPPORT
271 
272 
273 int
vsnprintf(char * buffer,size_t bufferSize,const char * format,va_list args)274 vsnprintf(char *buffer, size_t bufferSize, const char *format, va_list args)
275 {
276 	uint64 num;
277 	int base;
278 	int flags;			/* flags to number() */
279 	int fieldWidth;	/* width of output field */
280 	int precision;
281 		/* min. # of digits for integers; max number of chars for from string */
282 	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
283 
284 	Buffer outBuffer(buffer, bufferSize);
285 
286 	for (; format[0]; format++) {
287 		if (format[0] != '%') {
288 			outBuffer.PutCharacter(format[0]);
289 			continue;
290 		}
291 
292 		/* process flags */
293 
294 		flags = 0;
295 
296 	repeat:
297 		format++;
298 			/* this also skips first '%' */
299 		switch (format[0]) {
300 			case '-': flags |= LEFT; goto repeat;
301 			case '+': flags |= PLUS; goto repeat;
302 			case ' ': flags |= SPACE; goto repeat;
303 			case '#': flags |= SPECIAL; goto repeat;
304 			case '0': flags |= ZEROPAD; goto repeat;
305 
306 			case '%':
307 				outBuffer.PutCharacter(format[0]);
308 				continue;
309 		}
310 
311 		/* get field width */
312 
313 		fieldWidth = -1;
314 		if (isdigit(*format))
315 			fieldWidth = skip_atoi(&format);
316 		else if (format[0] == '*') {
317 			format++;
318 			/* it's the next argument */
319 			fieldWidth = va_arg(args, int);
320 			if (fieldWidth < 0) {
321 				fieldWidth = -fieldWidth;
322 				flags |= LEFT;
323 			}
324 		}
325 
326 		/* get the precision */
327 
328 		precision = -1;
329 		if (format[0] == '.') {
330 			format++;
331 			if (isdigit(*format))
332 				precision = skip_atoi(&format);
333 			else if (format[0] == '*') {
334 				format++;
335 				/* it's the next argument */
336 				precision = va_arg(args, int);
337 			}
338 			if (precision < 0)
339 				precision = 0;
340 		}
341 
342 		/* get the conversion qualifier */
343 
344 		qualifier = -1;
345 		if (format[0] == 'h' || format[0] == 'L' || format[0] == 'z') {
346 			qualifier = *format++;
347 		} else if (format[0] == 'l')  {
348 			format++;
349 			if (format[0] == 'l') {
350 				qualifier = 'L';
351 				format++;
352 			} else
353 				qualifier = 'l';
354 		}
355 
356 		/* default base */
357 		base = 10;
358 
359 		switch (format[0]) {
360 			case 'c':
361 				if (!(flags & LEFT))
362 					outBuffer.PutPadding(fieldWidth - 1);
363 
364 				outBuffer.PutCharacter((char)va_arg(args, int));
365 
366 				if ((flags & LEFT) != 0)
367 					outBuffer.PutPadding(fieldWidth - 1);
368 				continue;
369 
370 			case 's':
371 			{
372 				const char *argument = va_arg(args, char *);
373 				int32 length;
374 
375 				if (argument == NULL)
376 					argument = "<NULL>";
377 
378 				length = strnlen(argument, precision);
379 				fieldWidth -= length;
380 
381 				if (!(flags & LEFT))
382 					outBuffer.PutPadding(fieldWidth);
383 
384 				outBuffer.PutString(argument, length);
385 
386 				if ((flags & LEFT) != 0)
387 					outBuffer.PutPadding(fieldWidth);
388 				continue;
389 			}
390 
391 #ifdef FLOATING_SUPPORT
392 			case 'f':
393 			case 'F':
394 			case 'g':
395 			case 'G':
396 			{
397 				double value = va_arg(args, double);
398 				floating(outBuffer, value, fieldWidth, flags | SIGN);
399 				continue;
400 			}
401 #endif	// FLOATING_SUPPORT
402 
403 			case 'p':
404 				if (fieldWidth == -1) {
405 					fieldWidth = 2*sizeof(void *);
406 					flags |= ZEROPAD;
407 				}
408 
409 				outBuffer.PutString("0x", 2);
410 				number(outBuffer, (addr_t)va_arg(args, void *), 16, fieldWidth,
411 					precision, flags);
412 				continue;
413 
414 			case 'n':
415 				if (qualifier == 'l') {
416 					long *ip = va_arg(args, long *);
417 					*ip = outBuffer.BytesWritten();
418 				} else {
419 					int *ip = va_arg(args, int *);
420 					*ip = outBuffer.BytesWritten();
421 				}
422 				continue;
423 
424 			/* integer number formats - set up the flags and "break" */
425 			case 'o':
426 				base = 8;
427 				break;
428 
429 			case 'X':
430 				flags |= LARGE;
431 			case 'x':
432 				base = 16;
433 				break;
434 
435 			case 'd':
436 			case 'i':
437 				flags |= SIGN;
438 			case 'u':
439 				break;
440 
441 			default:
442 				if (format[0] != '%')
443 					outBuffer.PutCharacter('%');
444 
445 				if (!format[0])
446 					goto out;
447 
448 				outBuffer.PutCharacter(format[0]);
449 				continue;
450 		}
451 
452 		if (qualifier == 'L')
453 			num = va_arg(args, unsigned long long);
454 		else if (qualifier == 'l') {
455 			num = va_arg(args, unsigned long);
456 			if ((flags & SIGN) != 0)
457 				num = (long)num;
458 		} else if (qualifier == 'z') {
459 			num = va_arg(args, size_t);
460 			if ((flags & SIGN) != 0)
461 				num = (long)num;
462 		} else if (qualifier == 'h') {
463 			num = (unsigned short)va_arg(args, int);
464 			if ((flags & SIGN) != 0)
465 				num = (short)num;
466 		} else if ((flags & SIGN) != 0)
467 			num = va_arg(args, int);
468 		else
469 			num = va_arg(args, unsigned int);
470 
471 		number(outBuffer, num, base, fieldWidth, precision, flags);
472 	}
473 
474 out:
475 	outBuffer.NullTerminate();
476 	return outBuffer.BytesWritten();
477 }
478 
479 
480 int
vsprintf(char * buffer,const char * format,va_list args)481 vsprintf(char *buffer, const char *format, va_list args)
482 {
483 	return vsnprintf(buffer, ~0UL, format, args);
484 }
485 
486 
487 int
snprintf(char * buffer,size_t bufferSize,const char * format,...)488 snprintf(char *buffer, size_t bufferSize, const char *format, ...)
489 {
490 	va_list args;
491 	int i;
492 
493 	va_start(args, format);
494 	i = vsnprintf(buffer, bufferSize, format, args);
495 	va_end(args);
496 
497 	return i;
498 }
499 
500 
501 int
sprintf(char * buffer,const char * format,...)502 sprintf(char *buffer, const char *format, ...)
503 {
504 	va_list args;
505 	int i;
506 
507 	va_start(args, format);
508 	i = vsnprintf(buffer, ~0UL, format, args);
509 	va_end(args);
510 
511 	return i;
512 }
513 
514