Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix m_vsnprintf return value #1077

Merged
merged 12 commits into from
Apr 20, 2017
221 changes: 96 additions & 125 deletions Sming/system/m_printf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,15 @@ Descr: embedded very simple version of printf with float support

#define MPRINTF_BUF_SIZE 256

#define OVERFLOW_GUARD 24

static void defaultPrintChar(uart_t *uart, char c) {
return uart_tx_one_char(c);
}

void (*cbc_printchar)(uart_t *, char) = defaultPrintChar;
uart_t *cbc_printchar_uart = NULL;

#define SIGN (1<<1) /* Unsigned/signed long */

#define is_digit(c) ((c) >= '0' && (c) <= '9')

#define MIN(a, b) ( (a) < (b) ? (a) : (b) )

static int skip_atoi(const char **s)
{
int i = 0;
Expand Down Expand Up @@ -115,145 +109,122 @@ int m_printf(const char* fmt, ...)
return n;
}

int m_vsnprintf(char *buf, size_t maxLen, const char *fmt, va_list args)
{
int i, base, flags;
char *str;
const char *s;
int8_t precision, width;
char pad;
int m_vsnprintf(char *buf, size_t maxLen, const char *fmt, va_list args) {
size_t size = 0;
auto add = [&](char c) {
if (++size < maxLen) *buf++ = c;
};

char tempNum[40];
while (*fmt) {
// copy verbatim text
if (*fmt != '%') {
add(*fmt++);
continue;
}
fmt++;

const char* s; // source for string copy
char tempNum[40]; // buffer for number conversion

// reset attributes to defaults
bool minus = 0;
uint8_t base = 10;
int8_t precision = -1;
int8_t width = 0;
char pad = ' ';

// process flags
DO_FLAGS:
switch (*fmt) {
case '-':
minus = 1;

case '+':
case ' ':
case '#':
fmt++;
goto DO_FLAGS;
}

for (str = buf; *fmt; fmt++)
{
if(maxLen - (uint32_t)(str - buf) < OVERFLOW_GUARD)
{
*str++ = '(';
*str++ = '.';
*str++ = '.';
*str++ = '.';
*str++ = ')';

//mark end of string
*str = '\0';

//return maximum buffer len, so caller can detect not_enough_space
return maxLen;
// process padding
if (*fmt == '0') {
pad = '0';
fmt++;
}

if (*fmt != '%')
{
*str++ = *fmt;
continue;
// process width ('*' is not supported yet)
if ( is_digit(*fmt) ) {
width = skip_atoi(&fmt);
}

flags = 0;
fmt++; // This skips first '%'

//reset attributes to defaults
precision = -1;
width = 0;
pad = ' ';
base = 10;
bool minus = 0;

do
{
if ('-' == *fmt) minus = 1, fmt++;

//skip width and flags data - not supported
while ('+' == *fmt || '#' == *fmt || '*' == *fmt || 'l' == *fmt)
fmt++;
// process precision
if( *fmt == '.' ) {
fmt++;
if ( is_digit(*fmt) ) precision = skip_atoi(&fmt);
}

if (is_digit(*fmt)) {
if (*fmt == '0') {
pad = '0';
fmt++;
}
width = skip_atoi(&fmt);
}
// ignore length
while (*fmt == 'l' || *fmt == 'h' || *fmt == 'L') fmt++;

if('.' == *fmt)
{
fmt++;
if (is_digit(*fmt))
precision = skip_atoi(&fmt);
}
else
break;
}while(1);
// process type
switch (char f = *fmt++) {
case '%':
add('%');
continue;

switch (*fmt)
{
case 'c':
*str++ = (unsigned char) va_arg(args, int);
continue;
case 'c':
add( (unsigned char) va_arg(args, int) );
continue;

case 's': {
s = va_arg(args, char *);
case 's': {
s = va_arg(args, char *);

if (!s) s = "(null)";
size_t len = strlen(s);
len = MIN( len, precision );
len = MIN( len, maxLen - size_t(str - buf) - OVERFLOW_GUARD);
width = MIN( width, maxLen - size_t(str - buf) - OVERFLOW_GUARD);
if (!s) s = "(null)";
size_t len = strlen(s);
if (len > precision) len = precision;

int padding = width - len;
while (!minus && padding-- > 0) *str++ = ' ';
while (len--) *str++ = *s++;
while (minus && padding-- > 0) *str++ = ' ';
int padding = width - len;
while (!minus && padding-- > 0) add(' ');
while (len--) add(*s++);
while (minus && padding-- > 0) add(' ');
continue;
}

continue;
}
case 'p':
s = ultoa((unsigned long) va_arg(args, void *), tempNum, 16);
goto COPY;

case 'p':
s = ultoa((unsigned long) va_arg(args, void *), tempNum, 16);
while (*s && (maxLen - (uint32_t)(str - buf) > OVERFLOW_GUARD))
*str++ = *s++;
continue;
case 'd':
case 'i':
s = ltoa_wp(va_arg(args, int), tempNum, base, width, pad);
goto COPY;

case 'o':
base = 8;
break;
case 'f':
s = dtostrf_p(va_arg(args, double), width, precision, tempNum, pad);
goto COPY;

case 'x':
case 'X':
base = 16;
break;
case 'o':
base = 8;
goto UNSIGNED;

case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;
case 'x':
case 'X':
base = 16;
goto UNSIGNED;

case 'f':
case 'u':
UNSIGNED:
s = ultoa_wp(va_arg(args, unsigned int), tempNum, base, width, pad);

s = dtostrf_p(va_arg(args, double), width, precision, tempNum, pad);
while (*s && (maxLen - (uint32_t)(str - buf) > OVERFLOW_GUARD))
*str++ = *s++;
continue;
COPY:
while (*s) add(*s++);
break;

default:
if (*fmt != '%')
*str++ = '%';
if (*fmt)
*str++ = *fmt;
else
--fmt;
continue;
default:
add('%');
add(f);
}

if (flags & SIGN)
s = ltoa_wp(va_arg(args, int), tempNum, base, width, pad);
else
s = ultoa_wp(va_arg(args, unsigned int), tempNum, base, width, pad);

while (*s && (maxLen - (uint32_t)(str - buf) > OVERFLOW_GUARD))
*str++ = *s++;
}

*str = '\0';
return str - buf;
*buf = 0;
return size;
}