| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdio.h> |
| #include <printf.h> |
| #include <cpu/cpuMath.h> |
| |
| #define FLAG_ALT (1 << 0) |
| #define FLAG_ZERO_EXTEND (1 << 1) |
| #define FLAG_IS_SIGNED (1 << 2) |
| #define FLAG_NEG_PAD (1 << 3) |
| #define FLAG_CAPS (1 << 4) |
| |
| struct PrintfData |
| { |
| uint64_t number; |
| void *userData; |
| uint32_t fieldWidth; |
| uint32_t precision; |
| uint32_t flags; |
| uint8_t posChar; |
| uint8_t base; |
| }; |
| |
| static uint32_t StrPrvPrintfEx_number(printf_write_c putc_, struct PrintfData *data, bool *bail) |
| { |
| char buf[64]; |
| uint32_t idx = sizeof(buf) - 1; |
| uint32_t chr, i; |
| uint32_t numPrinted = 0; |
| |
| *bail = false; |
| |
| #ifdef USE_PRINTF_FLAG_CHARS |
| if (data->fieldWidth > sizeof(buf) - 1) |
| data->fieldWidth = sizeof(buf) - 1; |
| |
| if (data->precision > sizeof(buf) - 1) |
| data->precision = sizeof(buf) - 1; |
| #endif |
| |
| buf[idx--] = 0; //terminate |
| |
| if (data->flags & FLAG_IS_SIGNED) { |
| |
| if (((int64_t)data->number) < 0) { |
| |
| data->posChar = '-'; |
| data->number = -data->number; |
| } |
| } |
| |
| do { |
| if (data->base == 8) { |
| |
| chr = (data->number & 0x07) + '0'; |
| data->number >>= 3; |
| } |
| else if (data->base == 10) { |
| |
| uint64_t t = U64_DIV_BY_CONST_U16(data->number, 10); |
| chr = (data->number - t * 10) + '0'; |
| data->number = t; |
| } |
| else { |
| |
| chr = data->number & 0x0F; |
| data->number >>= 4; |
| chr = (chr >= 10) ? (chr + (data->flags & FLAG_CAPS ? 'A' : 'a') - 10) : (chr + '0'); |
| } |
| |
| buf[idx--] = chr; |
| |
| numPrinted++; |
| |
| } while (data->number); |
| |
| #ifdef USE_PRINTF_FLAG_CHARS |
| while (data->precision > numPrinted) { |
| |
| buf[idx--] = '0'; |
| numPrinted++; |
| } |
| |
| if (data->flags & FLAG_ALT) { |
| |
| if (data->base == 8) { |
| |
| if (buf[idx+1] != '0') { |
| buf[idx--] = '0'; |
| numPrinted++; |
| } |
| } |
| else if (data->base == 16) { |
| |
| buf[idx--] = data->flags & FLAG_CAPS ? 'X' : 'x'; |
| numPrinted++; |
| buf[idx--] = '0'; |
| numPrinted++; |
| } |
| } |
| |
| |
| if (!(data->flags & FLAG_NEG_PAD)) { |
| |
| if (data->fieldWidth > 0 && data->posChar != '\0') |
| data->fieldWidth--; |
| |
| while (data->fieldWidth > numPrinted) { |
| |
| buf[idx--] = data->flags & FLAG_ZERO_EXTEND ? '0' : ' '; |
| numPrinted++; |
| } |
| } |
| #endif |
| |
| if (data->posChar != '\0') { |
| |
| buf[idx--] = data->posChar; |
| numPrinted++; |
| } |
| |
| idx++; |
| |
| for(i = 0; i < numPrinted; i++) { |
| |
| if (!putc_(data->userData,(buf + idx)[i])) { |
| |
| *bail = true; |
| break; |
| } |
| } |
| |
| #ifdef USE_PRINTF_FLAG_CHARS |
| if (!*bail && data->flags & FLAG_NEG_PAD) { |
| |
| for(i = numPrinted; i < data->fieldWidth; i++) { |
| |
| if (!putc_(data->userData, ' ')) { |
| |
| *bail = true; |
| break; |
| } |
| } |
| } |
| #endif |
| |
| return i; |
| } |
| |
| static uint32_t StrVPrintf_StrLen_withMax(const char* s, uint32_t max) |
| { |
| uint32_t len = 0; |
| |
| while ((*s++) && (len < max)) len++; |
| |
| return len; |
| } |
| |
| static uint32_t StrVPrintf_StrLen(const char* s) |
| { |
| uint32_t len = 0; |
| |
| while (*s++) len++; |
| |
| return len; |
| } |
| |
| static inline char prvGetChar(const char** fmtP) |
| { |
| |
| return *(*fmtP)++; |
| } |
| |
| uint32_t cvprintf(printf_write_c putc_f, uint32_t flags, void* userData, const char* fmtStr, va_list vl) |
| { |
| |
| char c, t; |
| uint32_t numPrinted = 0; |
| double dbl; |
| long double ldbl; |
| struct PrintfData data; |
| |
| data.userData = userData; |
| |
| #define putc_(_ud,_c) \ |
| do { \ |
| if (!putc_f(_ud,_c)) \ |
| goto out; \ |
| } while(0) |
| |
| while ((c = prvGetChar(&fmtStr)) != 0) { |
| |
| if (c == '\n') { |
| |
| putc_(userData,c); |
| numPrinted++; |
| } |
| else if (c == '%') { |
| uint32_t len, i; |
| const char* str; |
| bool useChar = false, useShort = false, useLong = false, useLongLong = false, useLongDouble =false, useSizeT = false, usePtrdiffT = false; |
| bool havePrecision = false, bail = false; |
| |
| data.fieldWidth = 0; |
| data.precision = 0; |
| data.flags = 0; |
| data.posChar = 0; |
| |
| more_fmt: |
| |
| c = prvGetChar(&fmtStr); |
| |
| switch(c) { |
| |
| case '%': |
| |
| putc_(userData,c); |
| numPrinted++; |
| break; |
| |
| case 'c': |
| |
| t = va_arg(vl,unsigned int); |
| putc_(userData,t); |
| numPrinted++; |
| break; |
| |
| case 's': |
| |
| str = va_arg(vl,char*); |
| if (!str) str = "(null)"; |
| |
| if (data.precision) |
| len = StrVPrintf_StrLen_withMax(str,data.precision); |
| else |
| len = StrVPrintf_StrLen(str); |
| |
| #ifdef USE_PRINTF_FLAG_CHARS |
| if (!(data.flags & FLAG_NEG_PAD)) { |
| for(i = len; i < data.fieldWidth; i++) { |
| putc_(userData, ' '); |
| numPrinted++; |
| } |
| } |
| #endif |
| |
| for(i = 0; i < len; i++) { |
| putc_(userData,*str++); |
| numPrinted++; |
| } |
| |
| #ifdef USE_PRINTF_FLAG_CHARS |
| if (data.flags & FLAG_NEG_PAD) { |
| for(i = len; i < data.fieldWidth; i++) { |
| putc_(userData, ' '); |
| numPrinted++; |
| } |
| } |
| #endif |
| |
| break; |
| |
| case '.': |
| |
| havePrecision = true; |
| goto more_fmt; |
| |
| case '0': |
| |
| if (!(data.flags & FLAG_ZERO_EXTEND) && !data.fieldWidth && !havePrecision) { |
| |
| data.flags |= FLAG_ZERO_EXTEND; |
| goto more_fmt; |
| } |
| |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| |
| if (havePrecision) |
| data.precision = (data.precision * 10) + c - '0'; |
| else |
| data.fieldWidth = (data.fieldWidth * 10) + c - '0'; |
| goto more_fmt; |
| |
| case '#': |
| |
| data.flags |= FLAG_ALT; |
| goto more_fmt; |
| |
| case '-': |
| |
| data.flags |= FLAG_NEG_PAD; |
| goto more_fmt; |
| |
| case '+': |
| |
| data.posChar = '+'; |
| goto more_fmt; |
| |
| case ' ': |
| |
| if (data.posChar != '+') |
| data.posChar = ' '; |
| goto more_fmt; |
| |
| #define GET_UVAL64() \ |
| useSizeT ? va_arg(vl, size_t) : \ |
| usePtrdiffT ? va_arg(vl, ptrdiff_t) : \ |
| useLongLong ? va_arg(vl, unsigned long long) : \ |
| useLong ? va_arg(vl, unsigned long) : \ |
| useChar ? (unsigned char)va_arg(vl, unsigned int) : \ |
| useShort ? (unsigned short)va_arg(vl, unsigned int) : \ |
| va_arg(vl, unsigned int) |
| |
| #define GET_SVAL64() \ |
| useSizeT ? va_arg(vl, size_t) : \ |
| usePtrdiffT ? va_arg(vl, ptrdiff_t) : \ |
| useLongLong ? va_arg(vl, signed long long) : \ |
| useLong ? va_arg(vl, signed long) : \ |
| useChar ? (signed char)va_arg(vl, signed int) : \ |
| useShort ? (signed short)va_arg(vl, signed int) : \ |
| va_arg(vl, signed int) |
| |
| case 'u': |
| |
| data.number = GET_UVAL64(); |
| data.base = 10; |
| data.flags &= ~(FLAG_ALT | FLAG_CAPS); |
| numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); |
| if (bail) |
| goto out; |
| break; |
| |
| case 'd': |
| case 'i': |
| |
| data.number = GET_SVAL64(); |
| data.base = 10; |
| data.flags &= ~(FLAG_ALT | FLAG_CAPS); |
| data.flags |= FLAG_IS_SIGNED; |
| numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); |
| if (bail) |
| goto out; |
| break; |
| |
| case 'o': |
| |
| data.number = GET_UVAL64(); |
| data.base = 8; |
| data.flags &= ~FLAG_CAPS; |
| data.posChar = '\0'; |
| numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); |
| if (bail) |
| goto out; |
| break; |
| |
| case 'X': |
| |
| data.flags |= FLAG_CAPS; |
| |
| case 'x': |
| |
| data.number = GET_UVAL64(); |
| data.base = 16; |
| data.posChar = '\0'; |
| numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); |
| if (bail) |
| goto out; |
| break; |
| |
| case 'p': |
| |
| data.number = (uintptr_t)va_arg(vl, const void*); |
| data.base = 16; |
| data.flags &= ~FLAG_CAPS; |
| data.flags |= FLAG_ALT; |
| data.posChar = '\0'; |
| numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); |
| if (bail) |
| goto out; |
| break; |
| |
| #undef GET_UVAL64 |
| #undef GET_SVAL64 |
| |
| case 'F': |
| |
| data.flags |= FLAG_CAPS; |
| |
| case 'f': |
| |
| if (flags & PRINTF_FLAG_CHRE) { |
| if (flags & PRINTF_FLAG_SHORT_DOUBLE) { |
| if (useLongDouble) { |
| dbl = va_arg(vl, double); |
| data.number = *(uint64_t *)(&dbl); |
| } else { |
| // just grab the 32-bits |
| data.number = va_arg(vl, uint32_t); |
| } |
| } else { |
| if (useLongDouble) { |
| ldbl = va_arg(vl, long double); |
| data.number = *(uint64_t *)(&ldbl); |
| } else { |
| dbl = va_arg(vl, double); |
| data.number = *(uint64_t *)(&dbl); |
| } |
| } |
| data.base = 16; |
| data.flags |= FLAG_ALT; |
| data.posChar = '\0'; |
| numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail); |
| } else { |
| bail = true; |
| } |
| if (bail) |
| goto out; |
| break; |
| |
| case 'h': |
| |
| if (useShort) |
| useChar = true; |
| useShort = true; |
| goto more_fmt; |
| |
| case 'L': |
| |
| useLongDouble = true; |
| goto more_fmt; |
| |
| case 'l': |
| |
| if (useLong) |
| useLongLong = true; |
| useLong = true; |
| goto more_fmt; |
| |
| case 'z': |
| |
| useSizeT = true; |
| goto more_fmt; |
| |
| case 't': |
| |
| usePtrdiffT = true; |
| goto more_fmt; |
| |
| default: |
| |
| putc_(userData,c); |
| numPrinted++; |
| break; |
| |
| } |
| } |
| else { |
| |
| putc_(userData,c); |
| numPrinted++; |
| } |
| } |
| |
| out: |
| |
| return numPrinted; |
| } |