1// Copyright 2016 The Fuchsia Authors 2// Copyright (c) 2008-2014 Travis Geiselbrecht 3// 4// Use of this source code is governed by a MIT-style 5// license that can be found in the LICENSE file or at 6// https://opensource.org/licenses/MIT 7 8#include <debug.h> 9#include <assert.h> 10#include <limits.h> 11#include <printf.h> 12#include <stdarg.h> 13#include <sys/types.h> 14#include <stdio.h> 15#include <stdlib.h> 16#include <string.h> 17#include <platform/debug.h> 18 19struct _output_args { 20 char *outstr; 21 size_t len; 22 size_t pos; 23}; 24 25static int _vsnprintf_output(const char *str, size_t len, void *state) 26{ 27 struct _output_args *args = (struct _output_args*)state; 28 29 size_t count = 0; 30 while (count < len) { 31 if (args->pos < args->len) { 32 args->outstr[args->pos++] = *str; 33 } 34 35 str++; 36 count++; 37 } 38 39 return (int)count; 40} 41 42PRINTF_DECL(vsnprintf)(char *str, size_t len, const char *fmt, va_list ap) 43{ 44 struct _output_args args; 45 int wlen; 46 47 args.outstr = str; 48 args.len = len; 49 args.pos = 0; 50 51 wlen = PRINTF_CALL(_printf_engine)(&_vsnprintf_output, (void *)&args, 52 fmt, ap); 53 if (args.pos >= len) 54 str[len-1] = '\0'; 55 else 56 str[wlen] = '\0'; 57 return wlen; 58} 59 60PRINTF_DECL(snprintf)(char *str, size_t len, const char *fmt, ...) 61{ 62 int err; 63 64 va_list ap; 65 va_start(ap, fmt); 66 err = PRINTF_CALL(vsnprintf)(str, len, fmt, ap); 67 va_end(ap); 68 69 return err; 70} 71 72#define LONGFLAG 0x00000001 73#define LONGLONGFLAG 0x00000002 74#define HALFFLAG 0x00000004 75#define HALFHALFFLAG 0x00000008 76#define SIZETFLAG 0x00000010 77#define INTMAXFLAG 0x00000020 78#define PTRDIFFFLAG 0x00000040 79#define ALTFLAG 0x00000080 80#define CAPSFLAG 0x00000100 81#define SHOWSIGNFLAG 0x00000200 82#define SIGNEDFLAG 0x00000400 83#define LEFTFORMATFLAG 0x00000800 84#define LEADZEROFLAG 0x00001000 85#define BLANKPOSFLAG 0x00002000 86#define FIELDWIDTHFLAG 0x00004000 87 88__NO_INLINE static char *longlong_to_string(char *buf, unsigned long long n, size_t len, uint flag, char *signchar) 89{ 90 size_t pos = len; 91 int negative = 0; 92 93 if ((flag & SIGNEDFLAG) && (long long)n < 0) { 94 negative = 1; 95 n = -n; 96 } 97 98 buf[--pos] = 0; 99 100 /* only do the math if the number is >= 10 */ 101 while (n >= 10) { 102 int digit = (int)(n % 10); 103 104 n /= 10; 105 106 buf[--pos] = (char)(digit + '0'); 107 } 108 buf[--pos] = (char)(n + '0'); 109 110 if (negative) 111 *signchar = '-'; 112 else if ((flag & SHOWSIGNFLAG)) 113 *signchar = '+'; 114 else if ((flag & BLANKPOSFLAG)) 115 *signchar = ' '; 116 else 117 *signchar = '\0'; 118 119 return &buf[pos]; 120} 121 122static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 123static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 124 125__NO_INLINE static const char *longlong_to_hexstring(char *buf, unsigned long long u, size_t len, uint flag) 126{ 127 size_t pos = len; 128 const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable; 129 130 // Special case because ALTFLAG does not prepend 0x to 0. 131 if (u == 0) 132 return "0"; 133 134 buf[--pos] = 0; 135 136 do { 137 unsigned int digit = u % 16; 138 u /= 16; 139 140 buf[--pos] = table[digit]; 141 } while (u != 0); 142 143 if (flag & ALTFLAG) { 144 buf[--pos] = (flag & CAPSFLAG) ? 'X' : 'x'; 145 buf[--pos] = '0'; 146 } 147 148 return &buf[pos]; 149} 150 151PRINTF_DECL(_printf_engine)(_printf_engine_output_func out, void *state, const char *fmt, va_list ap) 152{ 153 int err = 0; 154 char c; 155 unsigned char uc; 156 const char *s; 157 size_t string_len; 158 unsigned long long n; 159 void *ptr; 160 int flags; 161 unsigned int format_num; 162 char signchar; 163 size_t chars_written = 0; 164 char num_buffer[32]; 165 166#define OUTPUT_STRING(str, len) do { err = out(str, len, state); if (err < 0) { goto exit; } else { chars_written += err; } } while(0) 167#define OUTPUT_CHAR(c) do { char __temp[1] = { c }; OUTPUT_STRING(__temp, 1); } while (0) 168 169 for (;;) { 170 /* reset the format state */ 171 flags = 0; 172 format_num = 0; 173 signchar = '\0'; 174 175 /* handle regular chars that aren't format related */ 176 s = fmt; 177 string_len = 0; 178 while ((c = *fmt++) != 0) { 179 if (c == '%') 180 break; /* we saw a '%', break and start parsing format */ 181 string_len++; 182 } 183 184 /* output the string we've accumulated */ 185 OUTPUT_STRING(s, string_len); 186 187 /* make sure we haven't just hit the end of the string */ 188 if (c == 0) 189 break; 190 191next_format: 192 /* grab the next format character */ 193 c = *fmt++; 194 if (c == 0) 195 break; 196 197 switch (c) { 198 case '0'...'9': 199 if (c == '0' && format_num == 0) 200 flags |= LEADZEROFLAG; 201 format_num *= 10; 202 format_num += c - '0'; 203 goto next_format; 204 case '*': { 205 int width = va_arg(ap, int); 206 if (width < 0) { 207 flags |= LEFTFORMATFLAG; 208 width = -width; 209 } 210 format_num = width; 211 goto next_format; 212 } 213 case '.': 214 // Check the next character. It either should be * (if valid) 215 // or something else (if invalid) that we consume as invalid. 216 c = *fmt; 217 if (c == '*') { 218 fmt++; 219 flags |= FIELDWIDTHFLAG; 220 format_num = va_arg(ap, intmax_t); 221 } else if (c == 's') { 222 // %.s is invalid, and testing glibc printf it 223 // results in no output so force skipping the 's' 224 fmt++; 225 } 226 goto next_format; 227 case '%': 228 OUTPUT_CHAR('%'); 229 break; 230 case 'c': 231 uc = (unsigned char)va_arg(ap, unsigned int); 232 OUTPUT_CHAR(uc); 233 break; 234 case 's': 235 s = va_arg(ap, const char *); 236 if (s == 0) 237 s = "<null>"; 238 flags &= ~LEADZEROFLAG; /* doesn't make sense for strings */ 239 goto _output_string; 240 case '-': 241 flags |= LEFTFORMATFLAG; 242 goto next_format; 243 case '+': 244 flags |= SHOWSIGNFLAG; 245 goto next_format; 246 case ' ': 247 flags |= BLANKPOSFLAG; 248 goto next_format; 249 case '#': 250 flags |= ALTFLAG; 251 goto next_format; 252 case 'l': 253 if (flags & LONGFLAG) 254 flags |= LONGLONGFLAG; 255 flags |= LONGFLAG; 256 goto next_format; 257 case 'h': 258 if (flags & HALFFLAG) 259 flags |= HALFHALFFLAG; 260 flags |= HALFFLAG; 261 goto next_format; 262 case 'z': 263 flags |= SIZETFLAG; 264 goto next_format; 265 case 'j': 266 flags |= INTMAXFLAG; 267 goto next_format; 268 case 't': 269 flags |= PTRDIFFFLAG; 270 goto next_format; 271 case 'i': 272 case 'd': 273 n = (flags & LONGLONGFLAG) ? va_arg(ap, long long) : 274 (flags & LONGFLAG) ? va_arg(ap, long) : 275 (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) : 276 (flags & HALFFLAG) ? (short)va_arg(ap, int) : 277 (flags & SIZETFLAG) ? va_arg(ap, ssize_t) : 278 (flags & INTMAXFLAG) ? va_arg(ap, intmax_t) : 279 (flags & PTRDIFFFLAG) ? va_arg(ap, ptrdiff_t) : 280 va_arg(ap, int); 281 flags |= SIGNEDFLAG; 282 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar); 283 goto _output_string; 284 case 'u': 285 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) : 286 (flags & LONGFLAG) ? va_arg(ap, unsigned long) : 287 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) : 288 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) : 289 (flags & SIZETFLAG) ? va_arg(ap, size_t) : 290 (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) : 291 (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) : 292 va_arg(ap, unsigned int); 293 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar); 294 goto _output_string; 295 case 'p': 296 flags |= LONGFLAG | ALTFLAG; 297 goto hex; 298 case 'X': 299 flags |= CAPSFLAG; 300 /* fallthrough */ 301hex: 302 case 'x': 303 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) : 304 (flags & LONGFLAG) ? va_arg(ap, unsigned long) : 305 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) : 306 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) : 307 (flags & SIZETFLAG) ? va_arg(ap, size_t) : 308 (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) : 309 (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) : 310 va_arg(ap, unsigned int); 311 s = longlong_to_hexstring(num_buffer, n, sizeof(num_buffer), flags); 312 goto _output_string; 313 case 'n': 314 ptr = va_arg(ap, void *); 315 if (flags & LONGLONGFLAG) 316 *(long long *)ptr = chars_written; 317 else if (flags & LONGFLAG) 318 *(long *)ptr = chars_written; 319 else if (flags & HALFHALFFLAG) 320 *(signed char *)ptr = (signed char)chars_written; 321 else if (flags & HALFFLAG) 322 *(short *)ptr = (short)chars_written; 323 else if (flags & SIZETFLAG) 324 *(size_t *)ptr = chars_written; 325 else 326 *(int *)ptr = (int)chars_written; 327 break; 328 default: 329 OUTPUT_CHAR('%'); 330 OUTPUT_CHAR(c); 331 break; 332 } 333 334 /* move on to the next field */ 335 continue; 336 337 /* shared output code */ 338_output_string: 339 string_len = strlen(s); 340 341 // In the event of a field width smaller than the length, we need to 342 // truncate the width to fit. This only applies to %s. 343 if (flags & FIELDWIDTHFLAG) { 344 string_len = MIN(string_len, format_num); 345 } 346 347 if (flags & LEFTFORMATFLAG) { 348 /* left justify the text */ 349 OUTPUT_STRING(s, string_len); 350 uint written = err; 351 352 /* pad to the right (if necessary) */ 353 for (; format_num > written; format_num--) 354 OUTPUT_CHAR(' '); 355 } else { 356 /* right justify the text (digits) */ 357 358 /* if we're going to print a sign digit, 359 it'll chew up one byte of the format size */ 360 if (signchar != '\0' && format_num > 0) 361 format_num--; 362 363 /* output the sign char before the leading zeros */ 364 if (flags & LEADZEROFLAG && signchar != '\0') 365 OUTPUT_CHAR(signchar); 366 367 /* pad according to the format string */ 368 for (; format_num > string_len; format_num--) 369 OUTPUT_CHAR(flags & LEADZEROFLAG ? '0' : ' '); 370 371 /* if not leading zeros, output the sign char just before the number */ 372 if (!(flags & LEADZEROFLAG) && signchar != '\0') 373 OUTPUT_CHAR(signchar); 374 375 /* output the string */ 376 OUTPUT_STRING(s, string_len); 377 } 378 continue; 379 } 380 381#undef OUTPUT_STRING 382#undef OUTPUT_CHAR 383 384exit: 385 return (err < 0) ? err : (int)chars_written; 386} 387