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(s: &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(s: &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(p: s, maxlen: 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 | num: (unsigned long) va_arg(args, void *), base: 16, |
222 | size: field_width, precision, type: 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, size: field_width, precision, type: 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 | |