1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) Paul Mackerras 1997. 4 */ 5 #include <stdarg.h> 6 #include <stddef.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