1 /* 2 * libc printf and friends 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU Library General Public License version 2. 6 */ 7 8 #include "libcflat.h" 9 #include "ctype.h" 10 11 #define BUFSZ 2000 12 13 typedef struct pstream { 14 char *buffer; 15 int remain; 16 int added; 17 } pstream_t; 18 19 typedef struct strprops { 20 char pad; 21 int npad; 22 bool alternate; 23 int precision; 24 } strprops_t; 25 26 static void addchar(pstream_t *p, char c) 27 { 28 if (p->remain) { 29 *p->buffer++ = c; 30 --p->remain; 31 } 32 ++p->added; 33 } 34 35 static void print_str(pstream_t *p, const char *s, strprops_t props) 36 { 37 const char *s_orig = s; 38 int npad = props.npad; 39 40 if (npad > 0) { 41 npad -= strlen(s_orig); 42 while (npad > 0) { 43 addchar(p, props.pad); 44 --npad; 45 } 46 } 47 48 while (*s && props.precision--) 49 addchar(p, *s++); 50 51 if (npad < 0) { 52 props.pad = ' '; /* ignore '0' flag with '-' flag */ 53 npad += strlen(s_orig); 54 while (npad < 0) { 55 addchar(p, props.pad); 56 ++npad; 57 } 58 } 59 } 60 61 static char digits[16] = "0123456789abcdef"; 62 63 static void print_int(pstream_t *ps, long long n, int base, strprops_t props) 64 { 65 char buf[sizeof(long) * 3 + 2], *p = buf; 66 int s = 0, i; 67 68 if (n < 0) { 69 n = -n; 70 s = 1; 71 } 72 73 while (n) { 74 *p++ = digits[n % base]; 75 n /= base; 76 } 77 78 while (p == buf || (p - buf < props.precision)) 79 *p++ = '0'; 80 props.precision = -1; 81 82 if (s) 83 *p++ = '-'; 84 85 for (i = 0; i < (p - buf) / 2; ++i) { 86 char tmp; 87 88 tmp = buf[i]; 89 buf[i] = p[-1 - i]; 90 p[-1 - i] = tmp; 91 } 92 93 *p = 0; 94 95 print_str(ps, buf, props); 96 } 97 98 static void print_unsigned(pstream_t *ps, unsigned long long n, int base, 99 strprops_t props) 100 { 101 char buf[sizeof(long) * 3 + 3], *p = buf; 102 int i; 103 104 while (n) { 105 *p++ = digits[n % base]; 106 n /= base; 107 } 108 109 if (p == buf) 110 props.alternate = false; 111 112 while (p == buf || (p - buf < props.precision)) 113 *p++ = '0'; 114 props.precision = -1; 115 116 if (props.alternate && base == 16) { 117 if (props.pad == '0') { 118 addchar(ps, '0'); 119 addchar(ps, 'x'); 120 121 if (props.npad > 0) 122 props.npad = MAX(props.npad - 2, 0); 123 } else { 124 *p++ = 'x'; 125 *p++ = '0'; 126 } 127 } 128 129 for (i = 0; i < (p - buf) / 2; ++i) { 130 char tmp; 131 132 tmp = buf[i]; 133 buf[i] = p[-1 - i]; 134 p[-1 - i] = tmp; 135 } 136 137 *p = 0; 138 139 print_str(ps, buf, props); 140 } 141 142 static int fmtnum(const char **fmt) 143 { 144 const char *f = *fmt; 145 int len = 0, num; 146 147 if (*f == '-') 148 ++f, ++len; 149 150 while (*f >= '0' && *f <= '9') 151 ++f, ++len; 152 153 num = atol(*fmt); 154 *fmt += len; 155 return num; 156 } 157 158 /* 159 * Adapted from drivers/firmware/efi/libstub/vsprintf.c 160 */ 161 static int skip_atoi(const char **s) 162 { 163 int i = 0; 164 165 do { 166 i = i*10 + *((*s)++) - '0'; 167 } while (isdigit(**s)); 168 169 return i; 170 } 171 172 /* 173 * Adapted from drivers/firmware/efi/libstub/vsprintf.c 174 */ 175 static int get_int(const char **fmt, va_list *ap) 176 { 177 if (isdigit(**fmt)) 178 return skip_atoi(fmt); 179 180 if (**fmt == '*') { 181 ++(*fmt); 182 /* it's the next argument */ 183 return va_arg(*ap, int); 184 } 185 return 0; 186 } 187 188 int vsnprintf(char *buf, int size, const char *fmt, va_list va) 189 { 190 pstream_t s; 191 va_list args; 192 193 /* 194 * We want to pass our input va_list to helper functions by reference, 195 * but there's an annoying edge case. If va_list was originally passed 196 * to us by value, we could just pass &ap down to the helpers. This is 197 * the case on, for example, X86_32. 198 * However, on X86_64 (and possibly others), va_list is actually a 199 * size-1 array containing a structure. Our function parameter ap has 200 * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1], 201 * which is what will be expected by a function taking a va_list * 202 * parameter. 203 * One standard way to solve this mess is by creating a copy in a local 204 * variable of type va_list and then passing a pointer to that local 205 * copy instead, which is what we do here. 206 */ 207 va_copy(args, va); 208 209 s.buffer = buf; 210 s.remain = size - 1; 211 s.added = 0; 212 while (*fmt) { 213 char f = *fmt++; 214 int nlong = 0; 215 strprops_t props; 216 memset(&props, 0, sizeof(props)); 217 props.pad = ' '; 218 props.precision = -1; 219 220 if (f != '%') { 221 addchar(&s, f); 222 continue; 223 } 224 morefmt: 225 f = *fmt++; 226 switch (f) { 227 case '%': 228 addchar(&s, '%'); 229 break; 230 case 'c': 231 addchar(&s, va_arg(args, int)); 232 break; 233 case '\0': 234 --fmt; 235 break; 236 case '.': 237 props.pad = ' '; 238 props.precision = get_int(&fmt, &args); 239 goto morefmt; 240 case '#': 241 props.alternate = true; 242 goto morefmt; 243 case '0': 244 props.pad = '0'; 245 ++fmt; 246 /* fall through */ 247 case '1' ... '9': 248 case '-': 249 --fmt; 250 props.npad = fmtnum(&fmt); 251 goto morefmt; 252 case 'l': 253 ++nlong; 254 goto morefmt; 255 case 't': 256 case 'z': 257 /* Here we only care that sizeof(size_t) == sizeof(long). 258 * On a 32-bit platform it doesn't matter that size_t is 259 * typedef'ed to int or long; va_arg will work either way. 260 * Same for ptrdiff_t (%td). 261 */ 262 nlong = 1; 263 goto morefmt; 264 case 'd': 265 switch (nlong) { 266 case 0: 267 print_int(&s, va_arg(args, int), 10, props); 268 break; 269 case 1: 270 print_int(&s, va_arg(args, long), 10, props); 271 break; 272 default: 273 print_int(&s, va_arg(args, long long), 10, props); 274 break; 275 } 276 break; 277 case 'u': 278 switch (nlong) { 279 case 0: 280 print_unsigned(&s, va_arg(args, unsigned int), 10, props); 281 break; 282 case 1: 283 print_unsigned(&s, va_arg(args, unsigned long), 10, props); 284 break; 285 default: 286 print_unsigned(&s, va_arg(args, unsigned long long), 10, props); 287 break; 288 } 289 break; 290 case 'x': 291 switch (nlong) { 292 case 0: 293 print_unsigned(&s, va_arg(args, unsigned int), 16, props); 294 break; 295 case 1: 296 print_unsigned(&s, va_arg(args, unsigned long), 16, props); 297 break; 298 default: 299 print_unsigned(&s, va_arg(args, unsigned long long), 16, props); 300 break; 301 } 302 break; 303 case 'p': 304 props.alternate = true; 305 print_unsigned(&s, (unsigned long)va_arg(args, void *), 16, props); 306 break; 307 case 's': 308 print_str(&s, va_arg(args, const char *), props); 309 break; 310 default: 311 addchar(&s, f); 312 break; 313 } 314 } 315 va_end(args); 316 *s.buffer = 0; 317 return s.added; 318 } 319 320 int snprintf(char *buf, int size, const char *fmt, ...) 321 { 322 va_list va; 323 int r; 324 325 va_start(va, fmt); 326 r = vsnprintf(buf, size, fmt, va); 327 va_end(va); 328 return r; 329 } 330 331 int vprintf(const char *fmt, va_list va) 332 { 333 char buf[BUFSZ]; 334 int r; 335 336 r = vsnprintf(buf, sizeof(buf), fmt, va); 337 puts(buf); 338 return r; 339 } 340 341 int printf(const char *fmt, ...) 342 { 343 va_list va; 344 char buf[BUFSZ]; 345 int r; 346 347 va_start(va, fmt); 348 r = vsnprintf(buf, sizeof buf, fmt, va); 349 va_end(va); 350 puts(buf); 351 return r; 352 } 353 354 void binstr(unsigned long x, char out[BINSTR_SZ]) 355 { 356 int i; 357 char *c; 358 int n; 359 360 n = sizeof(unsigned long) * 8; 361 i = 0; 362 c = &out[0]; 363 for (;;) { 364 *c++ = (x & (1ul << (n - i - 1))) ? '1' : '0'; 365 i++; 366 367 if (i == n) { 368 *c = '\0'; 369 break; 370 } 371 if (i % 4 == 0) 372 *c++ = '\''; 373 } 374 assert(c + 1 - &out[0] == BINSTR_SZ); 375 } 376 377 void print_binstr(unsigned long x) 378 { 379 char out[BINSTR_SZ]; 380 binstr(x, out); 381 printf("%s", out); 382 } 383