File: /usr/src/linux/lib/vsprintf.c

1     /*
2      *  linux/lib/vsprintf.c
3      *
4      *  Copyright (C) 1991, 1992  Linus Torvalds
5      */
6     
7     /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
8     /*
9      * Wirzenius wrote this portably, Torvalds fucked it up :-)
10      */
11     
12     /* 
13      * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
14      * - changed to provide snprintf and vsnprintf functions
15      */
16     
17     #include <stdarg.h>
18     #include <linux/types.h>
19     #include <linux/string.h>
20     #include <linux/ctype.h>
21     
22     #include <asm/div64.h>
23     
24     /**
25      * simple_strtoul - convert a string to an unsigned long
26      * @cp: The start of the string
27      * @endp: A pointer to the end of the parsed string will be placed here
28      * @base: The number base to use
29      */
30     unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
31     {
32     	unsigned long result = 0,value;
33     
34     	if (!base) {
35     		base = 10;
36     		if (*cp == '0') {
37     			base = 8;
38     			cp++;
39     			if ((*cp == 'x') && isxdigit(cp[1])) {
40     				cp++;
41     				base = 16;
42     			}
43     		}
44     	}
45     	while (isxdigit(*cp) &&
46     	       (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
47     		result = result*base + value;
48     		cp++;
49     	}
50     	if (endp)
51     		*endp = (char *)cp;
52     	return result;
53     }
54     
55     /**
56      * simple_strtol - convert a string to a signed long
57      * @cp: The start of the string
58      * @endp: A pointer to the end of the parsed string will be placed here
59      * @base: The number base to use
60      */
61     long simple_strtol(const char *cp,char **endp,unsigned int base)
62     {
63     	if(*cp=='-')
64     		return -simple_strtoul(cp+1,endp,base);
65     	return simple_strtoul(cp,endp,base);
66     }
67     
68     /**
69      * simple_strtoull - convert a string to an unsigned long long
70      * @cp: The start of the string
71      * @endp: A pointer to the end of the parsed string will be placed here
72      * @base: The number base to use
73      */
74     unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
75     {
76     	unsigned long long result = 0,value;
77     
78     	if (!base) {
79     		base = 10;
80     		if (*cp == '0') {
81     			base = 8;
82     			cp++;
83     			if ((*cp == 'x') && isxdigit(cp[1])) {
84     				cp++;
85     				base = 16;
86     			}
87     		}
88     	}
89     	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
90     	    ? toupper(*cp) : *cp)-'A'+10) < base) {
91     		result = result*base + value;
92     		cp++;
93     	}
94     	if (endp)
95     		*endp = (char *)cp;
96     	return result;
97     }
98     
99     /**
100      * simple_strtoll - convert a string to a signed long long
101      * @cp: The start of the string
102      * @endp: A pointer to the end of the parsed string will be placed here
103      * @base: The number base to use
104      */
105     long long simple_strtoll(const char *cp,char **endp,unsigned int base)
106     {
107     	if(*cp=='-')
108     		return -simple_strtoull(cp+1,endp,base);
109     	return simple_strtoull(cp,endp,base);
110     }
111     
112     static int skip_atoi(const char **s)
113     {
114     	int i=0;
115     
116     	while (isdigit(**s))
117     		i = i*10 + *((*s)++) - '0';
118     	return i;
119     }
120     
121     #define ZEROPAD	1		/* pad with zero */
122     #define SIGN	2		/* unsigned/signed long */
123     #define PLUS	4		/* show plus */
124     #define SPACE	8		/* space if plus */
125     #define LEFT	16		/* left justified */
126     #define SPECIAL	32		/* 0x */
127     #define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
128     
129     static char * number(char * buf, char * end, long long num, int base, int size, int precision, int type)
130     {
131     	char c,sign,tmp[66];
132     	const char *digits;
133     	const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
134     	const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
135     	int i;
136     
137     	digits = (type & LARGE) ? large_digits : small_digits;
138     	if (type & LEFT)
139     		type &= ~ZEROPAD;
140     	if (base < 2 || base > 36)
141     		return 0;
142     	c = (type & ZEROPAD) ? '0' : ' ';
143     	sign = 0;
144     	if (type & SIGN) {
145     		if (num < 0) {
146     			sign = '-';
147     			num = -num;
148     			size--;
149     		} else if (type & PLUS) {
150     			sign = '+';
151     			size--;
152     		} else if (type & SPACE) {
153     			sign = ' ';
154     			size--;
155     		}
156     	}
157     	if (type & SPECIAL) {
158     		if (base == 16)
159     			size -= 2;
160     		else if (base == 8)
161     			size--;
162     	}
163     	i = 0;
164     	if (num == 0)
165     		tmp[i++]='0';
166     	else while (num != 0)
167     		tmp[i++] = digits[do_div(num,base)];
168     	if (i > precision)
169     		precision = i;
170     	size -= precision;
171     	if (!(type&(ZEROPAD+LEFT))) {
172     		while(size-->0) {
173     			if (buf <= end)
174     				*buf = ' ';
175     			++buf;
176     		}
177     	}
178     	if (sign) {
179     		if (buf <= end)
180     			*buf = sign;
181     		++buf;
182     	}
183     	if (type & SPECIAL) {
184     		if (base==8) {
185     			if (buf <= end)
186     				*buf = '0';
187     			++buf;
188     		} else if (base==16) {
189     			if (buf <= end)
190     				*buf = '0';
191     			++buf;
192     			if (buf <= end)
193     				*buf = digits[33];
194     			++buf;
195     		}
196     	}
197     	if (!(type & LEFT)) {
198     		while (size-- > 0) {
199     			if (buf <= end)
200     				*buf = c;
201     			++buf;
202     		}
203     	}
204     	while (i < precision--) {
205     		if (buf <= end)
206     			*buf = '0';
207     		++buf;
208     	}
209     	while (i-- > 0) {
210     		if (buf <= end)
211     			*buf = tmp[i];
212     		++buf;
213     	}
214     	while (size-- > 0) {
215     		if (buf <= end)
216     			*buf = ' ';
217     		++buf;
218     	}
219     	return buf;
220     }
221     
222     /**
223     * vsnprintf - Format a string and place it in a buffer
224     * @buf: The buffer to place the result into
225     * @size: The size of the buffer, including the trailing null space
226     * @fmt: The format string to use
227     * @args: Arguments for the format string
228     *
229     * Call this function if you are already dealing with a va_list.
230     * You probably want snprintf instead.
231      */
232     int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
233     {
234     	int len;
235     	unsigned long long num;
236     	int i, base;
237     	char *str, *end, c;
238     	const char *s;
239     
240     	int flags;		/* flags to number() */
241     
242     	int field_width;	/* width of output field */
243     	int precision;		/* min. # of digits for integers; max
244     				   number of chars for from string */
245     	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
246     				/* 'z' support added 23/7/1999 S.H.    */
247     				/* 'z' changed to 'Z' --davidm 1/25/99 */
248     
249     	str = buf;
250     	end = buf + size - 1;
251     
252     	if (end < buf - 1) {
253     		end = ((void *) -1);
254     		size = end - buf + 1;
255     	}
256     
257     	for (; *fmt ; ++fmt) {
258     		if (*fmt != '%') {
259     			if (str <= end)
260     				*str = *fmt;
261     			++str;
262     			continue;
263     		}
264     
265     		/* process flags */
266     		flags = 0;
267     		repeat:
268     			++fmt;		/* this also skips first '%' */
269     			switch (*fmt) {
270     				case '-': flags |= LEFT; goto repeat;
271     				case '+': flags |= PLUS; goto repeat;
272     				case ' ': flags |= SPACE; goto repeat;
273     				case '#': flags |= SPECIAL; goto repeat;
274     				case '0': flags |= ZEROPAD; goto repeat;
275     			}
276     
277     		/* get field width */
278     		field_width = -1;
279     		if (isdigit(*fmt))
280     			field_width = skip_atoi(&fmt);
281     		else if (*fmt == '*') {
282     			++fmt;
283     			/* it's the next argument */
284     			field_width = va_arg(args, int);
285     			if (field_width < 0) {
286     				field_width = -field_width;
287     				flags |= LEFT;
288     			}
289     		}
290     
291     		/* get the precision */
292     		precision = -1;
293     		if (*fmt == '.') {
294     			++fmt;	
295     			if (isdigit(*fmt))
296     				precision = skip_atoi(&fmt);
297     			else if (*fmt == '*') {
298     				++fmt;
299     				/* it's the next argument */
300     				precision = va_arg(args, int);
301     			}
302     			if (precision < 0)
303     				precision = 0;
304     		}
305     
306     		/* get the conversion qualifier */
307     		qualifier = -1;
308     		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') {
309     			qualifier = *fmt;
310     			++fmt;
311     			if (qualifier == 'l' && *fmt == 'l') {
312     				qualifier = 'L';
313     				++fmt;
314     			}
315     		}
316     
317     		/* default base */
318     		base = 10;
319     
320     		switch (*fmt) {
321     			case 'c':
322     				if (!(flags & LEFT)) {
323     					while (--field_width > 0) {
324     						if (str <= end)
325     							*str = ' ';
326     						++str;
327     					}
328     				}
329     				c = (unsigned char) va_arg(args, int);
330     				if (str <= end)
331     					*str = c;
332     				++str;
333     				while (--field_width > 0) {
334     					if (str <= end)
335     						*str = ' ';
336     					++str;
337     				}
338     				continue;
339     
340     			case 's':
341     				s = va_arg(args, char *);
342     				if (!s)
343     					s = "<NULL>";
344     
345     				len = strnlen(s, precision);
346     
347     				if (!(flags & LEFT)) {
348     					while (len < field_width--) {
349     						if (str <= end)
350     							*str = ' ';
351     						++str;
352     					}
353     				}
354     				for (i = 0; i < len; ++i) {
355     					if (str <= end)
356     						*str = *s;
357     					++str; ++s;
358     				}
359     				while (len < field_width--) {
360     					if (str <= end)
361     						*str = ' ';
362     					++str;
363     				}
364     				continue;
365     
366     			case 'p':
367     				if (field_width == -1) {
368     					field_width = 2*sizeof(void *);
369     					flags |= ZEROPAD;
370     				}
371     				str = number(str, end,
372     						(unsigned long) va_arg(args, void *),
373     						16, field_width, precision, flags);
374     				continue;
375     
376     
377     			case 'n':
378     				/* FIXME:
379     				* What does C99 say about the overflow case here? */
380     				if (qualifier == 'l') {
381     					long * ip = va_arg(args, long *);
382     					*ip = (str - buf);
383     				} else if (qualifier == 'Z') {
384     					size_t * ip = va_arg(args, size_t *);
385     					*ip = (str - buf);
386     				} else {
387     					int * ip = va_arg(args, int *);
388     					*ip = (str - buf);
389     				}
390     				continue;
391     
392     			case '%':
393     				if (str <= end)
394     					*str = '%';
395     				++str;
396     				continue;
397     
398     				/* integer number formats - set up the flags and "break" */
399     			case 'o':
400     				base = 8;
401     				break;
402     
403     			case 'X':
404     				flags |= LARGE;
405     			case 'x':
406     				base = 16;
407     				break;
408     
409     			case 'd':
410     			case 'i':
411     				flags |= SIGN;
412     			case 'u':
413     				break;
414     
415     			default:
416     				if (str <= end)
417     					*str = '%';
418     				++str;
419     				if (*fmt) {
420     					if (str <= end)
421     						*str = *fmt;
422     					++str;
423     				} else {
424     					--fmt;
425     				}
426     				continue;
427     		}
428     		if (qualifier == 'L')
429     			num = va_arg(args, long long);
430     		else if (qualifier == 'l') {
431     			num = va_arg(args, unsigned long);
432     			if (flags & SIGN)
433     				num = (signed long) num;
434     		} else if (qualifier == 'Z') {
435     			num = va_arg(args, size_t);
436     		} else if (qualifier == 'h') {
437     			num = (unsigned short) va_arg(args, int);
438     			if (flags & SIGN)
439     				num = (signed short) num;
440     		} else {
441     			num = va_arg(args, unsigned int);
442     			if (flags & SIGN)
443     				num = (signed int) num;
444     		}
445     		str = number(str, end, num, base,
446     				field_width, precision, flags);
447     	}
448     	if (str <= end)
449     		*str = '\0';
450     	else if (size > 0)
451     		/* don't write out a null byte if the buf size is zero */
452     		*end = '\0';
453     	/* the trailing null byte doesn't count towards the total
454     	* ++str;
455     	*/
456     	return str-buf;
457     }
458     
459     /**
460      * snprintf - Format a string and place it in a buffer
461      * @buf: The buffer to place the result into
462      * @size: The size of the buffer, including the trailing null space
463      * @fmt: The format string to use
464      * @...: Arguments for the format string
465      */
466     int snprintf(char * buf, size_t size, const char *fmt, ...)
467     {
468     	va_list args;
469     	int i;
470     
471     	va_start(args, fmt);
472     	i=vsnprintf(buf,size,fmt,args);
473     	va_end(args);
474     	return i;
475     }
476     
477     /**
478      * vsprintf - Format a string and place it in a buffer
479      * @buf: The buffer to place the result into
480      * @fmt: The format string to use
481      * @args: Arguments for the format string
482      *
483      * Call this function if you are already dealing with a va_list.
484      * You probably want sprintf instead.
485      */
486     int vsprintf(char *buf, const char *fmt, va_list args)
487     {
488     	return vsnprintf(buf, 0xFFFFFFFFUL, fmt, args);
489     }
490     
491     
492     /**
493      * sprintf - Format a string and place it in a buffer
494      * @buf: The buffer to place the result into
495      * @fmt: The format string to use
496      * @...: Arguments for the format string
497      */
498     int sprintf(char * buf, const char *fmt, ...)
499     {
500     	va_list args;
501     	int i;
502     
503     	va_start(args, fmt);
504     	i=vsprintf(buf,fmt,args);
505     	va_end(args);
506     	return i;
507     }
508     
509     /**
510      * vsscanf - Unformat a buffer into a list of arguments
511      * @buf:	input buffer
512      * @fmt:	format of buffer
513      * @args:	arguments
514      */
515     int vsscanf(const char * buf, const char * fmt, va_list args)
516     {
517     	const char *str = buf;
518     	char *next;
519     	int num = 0;
520     	int qualifier;
521     	int base;
522     	unsigned int field_width;
523     	int is_sign = 0;
524     
525     	for (; *fmt; fmt++) {
526     		/* skip any white space in format */
527     		if (isspace(*fmt)) {
528     			continue;
529     		}
530     
531     		/* anything that is not a conversion must match exactly */
532     		if (*fmt != '%') {
533     			if (*fmt++ != *str++)
534     				return num;
535     			continue;
536     		}
537     		++fmt;
538     		
539     		/* skip this conversion.
540     		 * advance both strings to next white space
541     		 */
542     		if (*fmt == '*') {
543     			while (!isspace(*fmt))
544     				fmt++;
545     			while(!isspace(*str))
546     				str++;
547     			continue;
548     		}
549     
550     		/* get field width */
551     		field_width = 0xffffffffUL;
552     		if (isdigit(*fmt))
553     			field_width = skip_atoi(&fmt);
554     
555     		/* get conversion qualifier */
556     		qualifier = -1;
557     		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z') {
558     			qualifier = *fmt;
559     			fmt++;
560     		}
561     		base = 10;
562     		is_sign = 0;
563     
564     		switch(*fmt) {
565     		case 'c':
566     		{
567     			char *s = (char *) va_arg(args,char*);
568     			do {
569     				*s++ = *str++;
570     			} while(field_width-- > 0);
571     			num++;
572     		}
573     		continue;
574     		case 's':
575     		{
576     			char *s = (char *) va_arg(args, char *);
577     			/* first, skip leading white space in buffer */
578     			while (isspace(*str))
579     				str++;
580     
581     			/* now copy until next white space */
582     			while (!isspace(*str) && field_width--) {
583     				*s++ = *str++;
584     			}
585     			*s = '\0';
586     			num++;
587     		}
588     		continue;
589     		case 'n':
590     			/* return number of characters read so far */
591     		{
592     			int *i = (int *)va_arg(args,int*);
593     			*i = str - buf;
594     		}
595     		continue;
596     		case 'o':
597     			base = 8;
598     			break;
599     		case 'x':
600     		case 'X':
601     			base = 16;
602     			break;
603     		case 'd':
604     		case 'i':
605     			is_sign = 1;
606     		case 'u':
607     			break;
608     		case '%':
609     			/* looking for '%' in str */
610     			if (*str++ != '%') 
611     				return num;
612     			continue;
613     		default:
614     			/* invalid format; stop here */
615     			return num;
616     		}
617     
618     		/* have some sort of integer conversion.
619     		 * first, skip white space in buffer.
620     		 */
621     		while (isspace(*str))
622     			str++;
623     
624     		switch(qualifier) {
625     		case 'h':
626     			if (is_sign) {
627     				short *s = (short *) va_arg(args,short *);
628     				*s = (short) simple_strtol(str,&next,base);
629     			} else {
630     				unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
631     				*s = (unsigned short) simple_strtoul(str, &next, base);
632     			}
633     			break;
634     		case 'l':
635     			if (is_sign) {
636     				long *l = (long *) va_arg(args,long *);
637     				*l = simple_strtol(str,&next,base);
638     			} else {
639     				unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
640     				*l = simple_strtoul(str,&next,base);
641     			}
642     			break;
643     		case 'L':
644     			if (is_sign) {
645     				long long *l = (long long*) va_arg(args,long long *);
646     				*l = simple_strtoll(str,&next,base);
647     			} else {
648     				unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
649     				*l = simple_strtoull(str,&next,base);
650     			}
651     			break;
652     		case 'Z':
653     		{
654     			size_t *s = (size_t*) va_arg(args,size_t*);
655     			*s = (size_t) simple_strtoul(str,&next,base);
656     		}
657     		break;
658     		default:
659     			if (is_sign) {
660     				int *i = (int *) va_arg(args, int*);
661     				*i = (int) simple_strtol(str,&next,base);
662     			} else {
663     				unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
664     				*i = (unsigned int) simple_strtoul(str,&next,base);
665     			}
666     			break;
667     		}
668     		num++;
669     
670     		if (!next)
671     			break;
672     		str = next;
673     	}
674     	return num;
675     }
676     
677     /**
678      * sscanf - Unformat a buffer into a list of arguments
679      * @buf:	input buffer
680      * @fmt:	formatting of buffer
681      * @...:	resulting arguments
682      */
683     int sscanf(const char * buf, const char * fmt, ...)
684     {
685     	va_list args;
686     	int i;
687     
688     	va_start(args,fmt);
689     	i = vsscanf(buf,fmt,args);
690     	va_end(args);
691     	return i;
692     }
693