cynix

x86 UNIX-like OS
git clone git://git.2f30.org/cynix
Log | Files | Refs | README | LICENSE

vsprintf.c (5076B)


      1 /* This file is released under the GPL v2 public license */
      2 
      3 /*
      4  *  linux/kernel/vsprintf.c
      5  *
      6  *  Copyright (C) 1991, 1992  Linus Torvalds
      7  */
      8 
      9 /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
     10 /*
     11  * Wirzenius wrote this portably, Torvalds fucked it up :-)
     12  */
     13 
     14 #include <stdarg.h>
     15 #include <string.h>
     16 #include <ctype.h>
     17 
     18 static int skip_atoi(const char **s)
     19 {
     20 	int i = 0;
     21 
     22 	while (isdigit(**s))
     23 		i = i * 10 + *((*s)++) - '0';
     24 	return i;
     25 }
     26 
     27 #define ZEROPAD   1           /* pad with zero */
     28 #define SIGN      2           /* unsigned/signed long */
     29 #define PLUS      4           /* show plus */
     30 #define SPACE     8           /* space if plus */
     31 #define LEFT      16          /* left justified */
     32 #define SPECIAL   32          /* 0x */
     33 #define SMALL     64          /* use 'abcdef' instead of 'ABCDEF' */
     34 
     35 #define do_div(n,base) \
     36 	({ \
     37 		int __res; \
     38 		__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
     39 		__res; \
     40 	})
     41 
     42 static char * number(char * str, int num, int base, int size, int precision
     43 		     , int type)
     44 {
     45 	char c, sign, tmp[36];
     46 	const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     47 	int i;
     48 
     49 	if (type & SMALL) digits = "0123456789abcdefghijklmnopqrstuvwxyz";
     50 	if (type & LEFT) type &= ~ZEROPAD;
     51 	if (base < 2 || base > 36)
     52 		return 0;
     53 	c = (type & ZEROPAD) ? '0' : ' ' ;
     54 	if (type & SIGN && num < 0) {
     55 		sign = '-';
     56 		num = -num;
     57 	} else
     58 		sign = (type & PLUS) ? '+' : ((type & SPACE) ? ' ' : 0);
     59 	if (sign) size--;
     60 	if (type & SPECIAL) {
     61 		if (base == 16)
     62 			size -= 2;
     63 		else if (base == 8)
     64 			size--;
     65 	}
     66 	i = 0;
     67 	if (num == 0)
     68 		tmp[i++] = '0';
     69 	else while (num != 0)
     70 			tmp[i++] = digits[do_div(num, base)];
     71 	if (i > precision) precision = i;
     72 	size -= precision;
     73 	if (!(type&(ZEROPAD + LEFT)))
     74 		while (size-- > 0)
     75 			*str++ = ' ';
     76 	if (sign)
     77 		*str++ = sign;
     78 	if (type & SPECIAL) {
     79 		if (base == 8) {
     80 			*str++ = '0';
     81 		} else if (base == 16) {
     82 			*str++ = '0';
     83 			*str++ = digits[33];
     84 		}
     85 	}
     86 	if (!(type & LEFT))
     87 		while (size-- > 0)
     88 			*str++ = c;
     89 	while (i < precision--)
     90 		*str++ = '0';
     91 	while (i-- > 0)
     92 		*str++ = tmp[i];
     93 	while (size-- > 0)
     94 		*str++ = ' ';
     95 	return str;
     96 }
     97 
     98 int vsprintf(char *buf, const char *fmt, va_list args)
     99 {
    100 	int len;
    101 	int i;
    102 	char * str;
    103 	char *s;
    104 	int *ip;
    105 
    106 	int flags;        /* flags to number() */
    107 
    108 	int field_width;  /* width of output field */
    109 	int precision;          /* min. # of digits for integers; max
    110                            number of chars for from string */
    111 	int qualifier;          /* 'h', 'l', or 'L' for integer fields */
    112 
    113 	for (str = buf ; *fmt ; ++fmt) {
    114 		if (*fmt != '%') {
    115 			*str++ = *fmt;
    116 			continue;
    117 		}
    118 
    119 		/* process flags */
    120 		flags = 0;
    121 repeat:
    122 		++fmt;            /* this also skips first '%' */
    123 		switch (*fmt) {
    124 		case '-': flags |= LEFT; goto repeat;
    125 		case '+': flags |= PLUS; goto repeat;
    126 		case ' ': flags |= SPACE; goto repeat;
    127 		case '#': flags |= SPECIAL; goto repeat;
    128 		case '0': flags |= ZEROPAD; goto repeat;
    129 		}
    130 
    131 		/* get field width */
    132 		field_width = -1;
    133 		if (isdigit(*fmt))
    134 			field_width = skip_atoi(&fmt);
    135 		else if (*fmt == '*') {
    136 			/* it's the next argument */
    137 			field_width = va_arg(args, int);
    138 			if (field_width < 0) {
    139 				field_width = -field_width;
    140 				flags |= LEFT;
    141 			}
    142 		}
    143 
    144 		/* get the precision */
    145 		precision = -1;
    146 		if (*fmt == '.') {
    147 			++fmt;
    148 			if (isdigit(*fmt))
    149 				precision = skip_atoi(&fmt);
    150 			else if (*fmt == '*') {
    151 				/* it's the next argument */
    152 				precision = va_arg(args, int);
    153 			}
    154 			if (precision < 0)
    155 				precision = 0;
    156 		}
    157 
    158 		/* get the conversion qualifier */
    159 		qualifier = -1;
    160 		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
    161 			qualifier = *fmt;
    162 			++fmt;
    163 		}
    164 
    165 		switch (*fmt) {
    166 		case 'c':
    167 			if (!(flags & LEFT))
    168 				while (--field_width > 0)
    169 					*str++ = ' ';
    170 			*str++ = (unsigned char) va_arg(args, int);
    171 			while (--field_width > 0)
    172 				*str++ = ' ';
    173 			break;
    174 
    175 		case 's':
    176 			s = va_arg(args, char *);
    177 			if (!s)
    178 				s = "<NULL>";
    179 			len = strlen(s);
    180 			if (precision < 0)
    181 				precision = len;
    182 			else if (len > precision)
    183 				len = precision;
    184 
    185 			if (!(flags & LEFT))
    186 				while (len < field_width--)
    187 					*str++ = ' ';
    188 			for (i = 0; i < len; ++i)
    189 				*str++ = *s++;
    190 			while (len < field_width--)
    191 				*str++ = ' ';
    192 			break;
    193 
    194 		case 'o':
    195 			str = number(str, va_arg(args, unsigned long), 8,
    196 				     field_width, precision, flags);
    197 			break;
    198 
    199 		case 'p':
    200 			if (field_width == -1) {
    201 				field_width = 8;
    202 				flags |= ZEROPAD;
    203 			}
    204 			str = number(str,
    205 				     (unsigned long) va_arg(args, void *), 16,
    206 				     field_width, precision, flags);
    207 			break;
    208 
    209 		case 'x':
    210 			flags |= SMALL;
    211 		case 'X':
    212 			str = number(str, va_arg(args, unsigned long), 16,
    213 				     field_width, precision, flags);
    214 			break;
    215 
    216 		case 'd':
    217 		case 'i':
    218 			flags |= SIGN;
    219 		case 'u':
    220 			str = number(str, va_arg(args, unsigned long), 10,
    221 				     field_width, precision, flags);
    222 			break;
    223 
    224 		case 'n':
    225 			ip = va_arg(args, int *);
    226 			*ip = (str - buf);
    227 			break;
    228 
    229 		default:
    230 			if (*fmt != '%')
    231 				*str++ = '%';
    232 			if (*fmt)
    233 				*str++ = *fmt;
    234 			else
    235 				--fmt;
    236 			break;
    237 		}
    238 	}
    239 	*str = '\0';
    240 	return str - buf;
    241 }
    242