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
32struct Buffer {
33	Buffer(char* buffer, size_t size)
34		:
35		fCurrent(buffer),
36		fSize(size),
37		fBytesWritten(0)
38	{
39	}
40
41	size_t BytesWritten() const
42	{
43		return fBytesWritten;
44	}
45
46	void PutCharacter(char c)
47	{
48		if (fBytesWritten < fSize) {
49			*fCurrent = c;
50			fCurrent++;
51		}
52
53		fBytesWritten++;
54	}
55
56	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
70	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
84	void NullTerminate()
85	{
86		if (fBytesWritten < fSize)
87			*fCurrent = '\0';
88		else if (fSize > 0)
89			fCurrent[-1] = '\0';
90	}
91
92private:
93	char*	fCurrent;
94	size_t	fSize;
95	size_t	fBytesWritten;
96};
97
98
99static int
100skip_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
111static uint64
112do_div(uint64 *_number, uint32 base)
113{
114	uint64 result = *_number % (uint64)base;
115	*_number = *_number / (uint64)base;
116
117	return result;
118}
119
120
121static char
122sign_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
138static void
139number(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*/
216static void
217floating(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
273int
274vsnprintf(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
474out:
475	outBuffer.NullTerminate();
476	return outBuffer.BytesWritten();
477}
478
479
480int
481vsprintf(char *buffer, const char *format, va_list args)
482{
483	return vsnprintf(buffer, ~0UL, format, args);
484}
485
486
487int
488snprintf(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
501int
502sprintf(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