1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <stdio.h> 18 #include <printf.h> 19 #include <cpu/cpuMath.h> 20 21 #define FLAG_ALT (1 << 0) 22 #define FLAG_ZERO_EXTEND (1 << 1) 23 #define FLAG_IS_SIGNED (1 << 2) 24 #define FLAG_NEG_PAD (1 << 3) 25 #define FLAG_CAPS (1 << 4) 26 27 struct PrintfData 28 { 29 uint64_t number; 30 void *userData; 31 uint32_t fieldWidth; 32 uint32_t precision; 33 uint32_t flags; 34 uint8_t posChar; 35 uint8_t base; 36 }; 37 38 static uint32_t StrPrvPrintfEx_number(printf_write_c putc_, struct PrintfData *data, bool *bail) 39 { 40 char buf[64]; 41 uint32_t idx = sizeof(buf) - 1; 42 uint32_t chr, i; 43 uint32_t numPrinted = 0; 44 45 *bail = false; 46 47 #ifdef USE_PRINTF_FLAG_CHARS 48 if (data->fieldWidth > sizeof(buf) - 1) 49 data->fieldWidth = sizeof(buf) - 1; 50 51 if (data->precision > sizeof(buf) - 1) 52 data->precision = sizeof(buf) - 1; 53 #endif 54 55 buf[idx--] = 0; //terminate 56 57 if (data->flags & FLAG_IS_SIGNED) { 58 59 if (((int64_t)data->number) < 0) { 60 61 data->posChar = '-'; 62 data->number = -data->number; 63 } 64 } 65 66 do { 67 if (data->base == 8) { 68 69 chr = (data->number & 0x07) + '0'; 70 data->number >>= 3; 71 } 72 else if (data->base == 10) { 73 74 uint64_t t = U64_DIV_BY_CONST_U16(data->number, 10); 75 chr = (data->number - t * 10) + '0'; 76 data->number = t; 77 } 78 else { 79 80 chr = data->number & 0x0F; 81 data->number >>= 4; 82 chr = (chr >= 10) ? (chr + (data->flags & FLAG_CAPS ? 'A' : 'a') - 10) : (chr + '0'); 83 } 84 85 buf[idx--] = chr; 86 87 numPrinted++; 88 89 } while (data->number); 90 91 #ifdef USE_PRINTF_FLAG_CHARS 92 while (data->precision > numPrinted) { 93 94 buf[idx--] = '0'; 95 numPrinted++; 96 } 97 98 if (data->flags & FLAG_ALT) { 99 100 if (data->base == 8) { 101 102 if (buf[idx+1] != '0') { 103 buf[idx--] = '0'; 104 numPrinted++; 105 } 106 } 107 else if (data->base == 16) { 108 109 buf[idx--] = data->flags & FLAG_CAPS ? 'X' : 'x'; 110 numPrinted++; 111 buf[idx--] = '0'; 112 numPrinted++; 113 } 114 } 115 116 117 if (!(data->flags & FLAG_NEG_PAD)) { 118 119 if (data->fieldWidth > 0 && data->posChar != '\0') 120 data->fieldWidth--; 121 122 while (data->fieldWidth > numPrinted) { 123 124 buf[idx--] = data->flags & FLAG_ZERO_EXTEND ? '0' : ' '; 125 numPrinted++; 126 } 127 } 128 #endif 129 130 if (data->posChar != '\0') { 131 132 buf[idx--] = data->posChar; 133 numPrinted++; 134 } 135 136 idx++; 137 138 for(i = 0; i < numPrinted; i++) { 139 140 if (!putc_(data->userData,(buf + idx)[i])) { 141 142 *bail = true; 143 break; 144 } 145 } 146 147 #ifdef USE_PRINTF_FLAG_CHARS 148 if (!*bail && data->flags & FLAG_NEG_PAD) { 149 150 for(i = numPrinted; i < data->fieldWidth; i++) { 151 152 if (!putc_(data->userData, ' ')) { 153 154 *bail = true; 155 break; 156 } 157 } 158 } 159 #endif 160 161 return i; 162 } 163 164 static uint32_t StrVPrintf_StrLen_withMax(const char* s, uint32_t max) 165 { 166 uint32_t len = 0; 167 168 while ((*s++) && (len < max)) len++; 169 170 return len; 171 } 172 173 static uint32_t StrVPrintf_StrLen(const char* s) 174 { 175 uint32_t len = 0; 176 177 while (*s++) len++; 178 179 return len; 180 } 181 182 static inline char prvGetChar(const char** fmtP) 183 { 184 185 return *(*fmtP)++; 186 } 187 188 uint32_t cvprintf(printf_write_c putc_f, void* userData, const char* fmtStr, va_list vl) 189 { 190 191 char c, t; 192 uint32_t numPrinted = 0; 193 double dbl; 194 long double ldbl; 195 struct PrintfData data; 196 197 data.userData = userData; 198 199 #define putc_(_ud,_c) \ 200 do { \ 201 if (!putc_f(_ud,_c)) \ 202 goto out; \ 203 } while(0) 204 205 while ((c = prvGetChar(&fmtStr)) != 0) { 206 207 if (c == '\n') { 208 209 putc_(userData,c); 210 numPrinted++; 211 } 212 else if (c == '%') { 213 uint32_t len, i; 214 const char* str; 215 bool useChar = false, useShort = false, useLong = false, useLongLong = false, useLongDouble =false, useSizeT = false, usePtrdiffT = false; 216 bool havePrecision = false, bail = false; 217 218 data.fieldWidth = 0; 219 data.precision = 0; 220 data.flags = 0; 221 data.posChar = 0; 222 223 more_fmt: 224 225 c = prvGetChar(&fmtStr); 226 227 switch(c) { 228 229 case '%': 230 231 putc_(userData,c); 232 numPrinted++; 233 break; 234 235 case 'c': 236 237 t = va_arg(vl,unsigned int); 238 putc_(userData,t); 239 numPrinted++; 240 break; 241 242 case 's': 243 244 str = va_arg(vl,char*); 245 if (!str) str = "(null)"; 246 247 if (data.precision) 248 len = StrVPrintf_StrLen_withMax(str,data.precision); 249 else 250 len = StrVPrintf_StrLen(str); 251 252 #ifdef USE_PRINTF_FLAG_CHARS 253 if (!(data.flags & FLAG_NEG_PAD)) { 254 for(i = len; i < data.fieldWidth; i++) { 255 putc_(userData, ' '); 256 numPrinted++; 257 } 258 } 259 #endif 260 261 for(i = 0; i < len; i++) { 262 putc_(userData,*str++); 263 numPrinted++; 264 } 265 266 #ifdef USE_PRINTF_FLAG_CHARS 267 if (data.flags & FLAG_NEG_PAD) { 268 for(i = len; i < data.fieldWidth; i++) { 269 putc_(userData, ' '); 270 numPrinted++; 271 } 272 } 273 #endif 274 275 break; 276 277 case '.': 278 279 havePrecision = true; 280 goto more_fmt; 281 282 case '0': 283 284 if (!(data.flags & FLAG_ZERO_EXTEND) && !data.fieldWidth && !havePrecision) { 285 286 data.flags |= FLAG_ZERO_EXTEND; 287 goto more_fmt; 288 } 289 290 case '1': 291 case '2': 292 case '3': 293 case '4': 294 case '5': 295 case '6': 296 case '7': 297 case '8': 298 case '9': 299 300 if (havePrecision) 301 data.precision = (data.precision * 10) + c - '0'; 302 else 303 data.fieldWidth = (data.fieldWidth * 10) + c - '0'; 304 goto more_fmt; 305 306 case '#': 307 308 data.flags |= FLAG_ALT; 309 goto more_fmt; 310 311 case '-': 312 313 data.flags |= FLAG_NEG_PAD; 314 goto more_fmt; 315 316 case '+': 317 318 data.posChar = '+'; 319 goto more_fmt; 320 321 case ' ': 322 323 if (data.posChar != '+') 324 data.posChar = ' '; 325 goto more_fmt; 326 327 #define GET_UVAL64() \ 328 useSizeT ? va_arg(vl, size_t) : \ 329 usePtrdiffT ? va_arg(vl, ptrdiff_t) : \ 330 useLongLong ? va_arg(vl, unsigned long long) : \ 331 useLong ? va_arg(vl, unsigned long) : \ 332 useChar ? (unsigned char)va_arg(vl, unsigned int) : \ 333 useShort ? (unsigned short)va_arg(vl, unsigned int) : \ 334 va_arg(vl, unsigned int) 335 336 #define GET_SVAL64() \ 337 useSizeT ? va_arg(vl, size_t) : \ 338 usePtrdiffT ? va_arg(vl, ptrdiff_t) : \ 339 useLongLong ? va_arg(vl, signed long long) : \ 340 useLong ? va_arg(vl, signed long) : \ 341 useChar ? (signed char)va_arg(vl, signed int) : \ 342 useShort ? (signed short)va_arg(vl, signed int) : \ 343 va_arg(vl, signed int) 344 345 case 'u': 346 347 data.number = GET_UVAL64(); 348 data.base = 10; 349 data.flags &= ~(FLAG_ALT | FLAG_CAPS); 350 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); 351 if (bail) 352 goto out; 353 break; 354 355 case 'd': 356 case 'i': 357 358 data.number = GET_SVAL64(); 359 data.base = 10; 360 data.flags &= ~(FLAG_ALT | FLAG_CAPS); 361 data.flags |= FLAG_IS_SIGNED; 362 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); 363 if (bail) 364 goto out; 365 break; 366 367 case 'o': 368 369 data.number = GET_UVAL64(); 370 data.base = 8; 371 data.flags &= ~FLAG_CAPS; 372 data.posChar = '\0'; 373 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); 374 if (bail) 375 goto out; 376 break; 377 378 case 'X': 379 380 data.flags |= FLAG_CAPS; 381 382 case 'x': 383 384 data.number = GET_UVAL64(); 385 data.base = 16; 386 data.posChar = '\0'; 387 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); 388 if (bail) 389 goto out; 390 break; 391 392 case 'p': 393 394 data.number = (uintptr_t)va_arg(vl, const void*); 395 data.base = 16; 396 data.flags &= ~FLAG_CAPS; 397 data.flags |= FLAG_ALT; 398 data.posChar = '\0'; 399 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); 400 if (bail) 401 goto out; 402 break; 403 404 #undef GET_UVAL64 405 #undef GET_SVAL64 406 407 case 'f': 408 case 'g': 409 410 if (useLongDouble) { 411 ldbl = va_arg(vl, long double); 412 data.number = *(uint64_t *)(&ldbl); 413 } else { 414 dbl = va_arg(vl, double); 415 data.number = *(uint32_t *)(&dbl); 416 } 417 data.base = 16; 418 data.flags &= ~FLAG_CAPS; 419 data.flags |= FLAG_ALT; 420 data.posChar = '\0'; 421 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); 422 if (bail) 423 goto out; 424 break; 425 426 case 'h': 427 428 if (useShort) 429 useChar = true; 430 useShort = true; 431 goto more_fmt; 432 433 case 'L': 434 435 useLongDouble = true; 436 goto more_fmt; 437 438 case 'l': 439 440 if (useLong) 441 useLongLong = true; 442 useLong = true; 443 goto more_fmt; 444 445 case 'z': 446 447 useSizeT = true; 448 goto more_fmt; 449 450 case 't': 451 452 usePtrdiffT = true; 453 goto more_fmt; 454 455 default: 456 457 putc_(userData,c); 458 numPrinted++; 459 break; 460 461 } 462 } 463 else { 464 465 putc_(userData,c); 466 numPrinted++; 467 } 468 } 469 470 out: 471 472 return numPrinted; 473 } 474