xref: /linux/arch/alpha/boot/stdio.c (revision ff19a8dee196d757dbc32a946843260f0b784ca3)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) Paul Mackerras 1997.
4  */
5 #include <linux/string.h>
6 #include <linux/stdarg.h>
7 
8 size_t strnlen(const char * s, size_t count)
9 {
10 	const char *sc;
11 
12 	for (sc = s; count-- && *sc != '\0'; ++sc)
13 		/* nothing */;
14 	return sc - s;
15 }
16 
17 # define do_div(n, base) ({						\
18 	unsigned int __base = (base);					\
19 	unsigned int __rem;						\
20 	__rem = ((unsigned long long)(n)) % __base;			\
21 	(n) = ((unsigned long long)(n)) / __base;			\
22 	__rem;								\
23 })
24 
25 
26 static int skip_atoi(const char **s)
27 {
28 	int i, c;
29 
30 	for (i = 0; '0' <= (c = **s) && c <= '9'; ++*s)
31 		i = i*10 + c - '0';
32 	return i;
33 }
34 
35 #define ZEROPAD	1		/* pad with zero */
36 #define SIGN	2		/* unsigned/signed long */
37 #define PLUS	4		/* show plus */
38 #define SPACE	8		/* space if plus */
39 #define LEFT	16		/* left justified */
40 #define SPECIAL	32		/* 0x */
41 #define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
42 
43 static char * number(char * str, unsigned long long num, int base, int size, int precision, int type)
44 {
45 	char c, sign, tmp[66];
46 	const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
47 	int i;
48 
49 	if (type & LARGE)
50 		digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
51 	if (type & LEFT)
52 		type &= ~ZEROPAD;
53 	if (base < 2 || base > 36)
54 		return 0;
55 	c = (type & ZEROPAD) ? '0' : ' ';
56 	sign = 0;
57 	if (type & SIGN) {
58 		if ((signed long long)num < 0) {
59 			sign = '-';
60 			num = - (signed long long)num;
61 			size--;
62 		} else if (type & PLUS) {
63 			sign = '+';
64 			size--;
65 		} else if (type & SPACE) {
66 			sign = ' ';
67 			size--;
68 		}
69 	}
70 	if (type & SPECIAL) {
71 		if (base == 16)
72 			size -= 2;
73 		else if (base == 8)
74 			size--;
75 	}
76 	i = 0;
77 	if (num == 0)
78 		tmp[i++]='0';
79 	else while (num != 0) {
80 		tmp[i++] = digits[do_div(num, base)];
81 	}
82 	if (i > precision)
83 		precision = i;
84 	size -= precision;
85 	if (!(type&(ZEROPAD+LEFT)))
86 		while (size-- > 0)
87 			*str++ = ' ';
88 	if (sign)
89 		*str++ = sign;
90 	if (type & SPECIAL) {
91 		if (base==8)
92 			*str++ = '0';
93 		else if (base == 16) {
94 			*str++ = '0';
95 			*str++ = digits[33];
96 		}
97 	}
98 	if (!(type & LEFT))
99 		while (size-- > 0)
100 			*str++ = c;
101 	while (i < precision--)
102 		*str++ = '0';
103 	while (i-- > 0)
104 		*str++ = tmp[i];
105 	while (size-- > 0)
106 		*str++ = ' ';
107 	return str;
108 }
109 
110 int vsprintf(char *buf, const char *fmt, va_list args)
111 {
112 	int len;
113 	unsigned long long num;
114 	int i, base;
115 	char * str;
116 	const char *s;
117 
118 	int flags;		/* flags to number() */
119 
120 	int field_width;	/* width of output field */
121 	int precision;		/* min. # of digits for integers; max
122 				   number of chars for from string */
123 	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
124 	                        /* 'z' support added 23/7/1999 S.H.    */
125 				/* 'z' changed to 'Z' --davidm 1/25/99 */
126 
127 
128 	for (str = buf ; *fmt ; ++fmt) {
129 		if (*fmt != '%') {
130 			*str++ = *fmt;
131 			continue;
132 		}
133 
134 		/* process flags */
135 		flags = 0;
136 		repeat:
137 			++fmt;		/* this also skips first '%' */
138 			switch (*fmt) {
139 				case '-': flags |= LEFT; goto repeat;
140 				case '+': flags |= PLUS; goto repeat;
141 				case ' ': flags |= SPACE; goto repeat;
142 				case '#': flags |= SPECIAL; goto repeat;
143 				case '0': flags |= ZEROPAD; goto repeat;
144 				}
145 
146 		/* get field width */
147 		field_width = -1;
148 		if ('0' <= *fmt && *fmt <= '9')
149 			field_width = skip_atoi(&fmt);
150 		else if (*fmt == '*') {
151 			++fmt;
152 			/* it's the next argument */
153 			field_width = va_arg(args, int);
154 			if (field_width < 0) {
155 				field_width = -field_width;
156 				flags |= LEFT;
157 			}
158 		}
159 
160 		/* get the precision */
161 		precision = -1;
162 		if (*fmt == '.') {
163 			++fmt;
164 			if ('0' <= *fmt && *fmt <= '9')
165 				precision = skip_atoi(&fmt);
166 			else if (*fmt == '*') {
167 				++fmt;
168 				/* it's the next argument */
169 				precision = va_arg(args, int);
170 			}
171 			if (precision < 0)
172 				precision = 0;
173 		}
174 
175 		/* get the conversion qualifier */
176 		qualifier = -1;
177 		if (*fmt == 'l' && *(fmt + 1) == 'l') {
178 			qualifier = 'q';
179 			fmt += 2;
180 		} else if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L'
181 			|| *fmt == 'Z') {
182 			qualifier = *fmt;
183 			++fmt;
184 		}
185 
186 		/* default base */
187 		base = 10;
188 
189 		switch (*fmt) {
190 		case 'c':
191 			if (!(flags & LEFT))
192 				while (--field_width > 0)
193 					*str++ = ' ';
194 			*str++ = (unsigned char) va_arg(args, int);
195 			while (--field_width > 0)
196 				*str++ = ' ';
197 			continue;
198 
199 		case 's':
200 			s = va_arg(args, char *);
201 			if (!s)
202 				s = "<NULL>";
203 
204 			len = strnlen(s, precision);
205 
206 			if (!(flags & LEFT))
207 				while (len < field_width--)
208 					*str++ = ' ';
209 			for (i = 0; i < len; ++i)
210 				*str++ = *s++;
211 			while (len < field_width--)
212 				*str++ = ' ';
213 			continue;
214 
215 		case 'p':
216 			if (field_width == -1) {
217 				field_width = 2*sizeof(void *);
218 				flags |= ZEROPAD;
219 			}
220 			str = number(str,
221 				(unsigned long) va_arg(args, void *), 16,
222 				field_width, precision, flags);
223 			continue;
224 
225 
226 		case 'n':
227 			if (qualifier == 'l') {
228 				long * ip = va_arg(args, long *);
229 				*ip = (str - buf);
230 			} else if (qualifier == 'Z') {
231 				size_t * ip = va_arg(args, size_t *);
232 				*ip = (str - buf);
233 			} else {
234 				int * ip = va_arg(args, int *);
235 				*ip = (str - buf);
236 			}
237 			continue;
238 
239 		case '%':
240 			*str++ = '%';
241 			continue;
242 
243 		/* integer number formats - set up the flags and "break" */
244 		case 'o':
245 			base = 8;
246 			break;
247 
248 		case 'X':
249 			flags |= LARGE;
250 		case 'x':
251 			base = 16;
252 			break;
253 
254 		case 'd':
255 		case 'i':
256 			flags |= SIGN;
257 		case 'u':
258 			break;
259 
260 		default:
261 			*str++ = '%';
262 			if (*fmt)
263 				*str++ = *fmt;
264 			else
265 				--fmt;
266 			continue;
267 		}
268 		if (qualifier == 'l') {
269 			num = va_arg(args, unsigned long);
270 			if (flags & SIGN)
271 				num = (signed long) num;
272 		} else if (qualifier == 'q') {
273 			num = va_arg(args, unsigned long long);
274 			if (flags & SIGN)
275 				num = (signed long long) num;
276 		} else if (qualifier == 'Z') {
277 			num = va_arg(args, size_t);
278 		} else if (qualifier == 'h') {
279 			num = (unsigned short) va_arg(args, int);
280 			if (flags & SIGN)
281 				num = (signed short) num;
282 		} else {
283 			num = va_arg(args, unsigned int);
284 			if (flags & SIGN)
285 				num = (signed int) num;
286 		}
287 		str = number(str, num, base, field_width, precision, flags);
288 	}
289 	*str = '\0';
290 	return str-buf;
291 }
292 
293 int sprintf(char * buf, const char *fmt, ...)
294 {
295 	va_list args;
296 	int i;
297 
298 	va_start(args, fmt);
299 	i = vsprintf(buf, fmt, args);
300 	va_end(args);
301 	return i;
302 }
303