490 lines
13 KiB
C
490 lines
13 KiB
C
/*
|
|
* 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;
|
|
}
|