#if !defined(__AVR_TINY__)
#include <avr/pgmspace.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sectionname.h"
#include "stdio_private.h"
#if !defined (SCANF_LEVEL)
# ifndef SCANF_WWIDTH
# define SCANF_WWIDTH 0
# endif
# ifndef SCANF_BRACKET
# define SCANF_BRACKET 0
# endif
# ifndef SCANF_FLOAT
# define SCANF_FLOAT 0
# endif
#elif SCANF_LEVEL == SCANF_MIN
# define SCANF_WWIDTH 0
# define SCANF_BRACKET 0
# define SCANF_FLOAT 0
#elif SCANF_LEVEL == SCANF_STD
# define SCANF_WWIDTH 0
# define SCANF_BRACKET 1
# define SCANF_FLOAT 0
#elif SCANF_LEVEL == SCANF_FLT
# define SCANF_WWIDTH 1
# define SCANF_BRACKET 1
# define SCANF_FLOAT 1
#else
# error "Not a known scanf level."
#endif
#if SCANF_WWIDTH
typedef unsigned int width_t;
#else
typedef unsigned char width_t;
#endif
#ifndef DISABLE_ASM
# if defined(__AVR__) && __AVR__
# define DISABLE_ASM 0
# else
# define DISABLE_ASM 1
# endif
#endif
#if SHRT_MAX != INT_MAX
# error "SHRT_MAX != INT_MAX for target: not supported"
#endif
#define FL_STAR 0x01
#define FL_WIDTH 0x02
#define FL_LONG 0x04
#define FL_CHAR 0x08
#define FL_OCT 0x10
#define FL_DEC 0x20
#define FL_HEX 0x40
#define FL_MINUS 0x80
#ifndef __AVR_HAVE_LPMX__
# if defined(__AVR_ENHANCED__) && __AVR_ENHANCED__
# define __AVR_HAVE_LPMX__ 1
# endif
#endif
#ifndef __AVR_HAVE_MOVW__
# if defined(__AVR_ENHANCED__) && __AVR_ENHANCED__
# define __AVR_HAVE_MOVW__ 1
# endif
#endif
#if DISABLE_ASM
# define GETBYTE(flag, mask, pnt) ({ \
unsigned char __c; \
__c = ((flag) & (mask)) \
? pgm_read_byte(pnt) : *pnt; \
pnt++; \
__c; \
})
#elif defined(__AVR_HAVE_LPMX__) && __AVR_HAVE_LPMX__
# define GETBYTE(flag, mask, pnt) ({ \
unsigned char __c; \
asm ( \
"sbrc %2,%3 \n\t" \
"lpm %0,Z+ \n\t" \
"sbrs %2,%3 \n\t" \
"ld %0,Z+ " \
: "=r" (__c), \
"+z" (pnt) \
: "r" (flag), \
"I" (_FFS(mask) - 1) \
); \
__c; \
})
#else
# define GETBYTE(flag, mask, pnt) ({ \
unsigned char __c; \
asm ( \
"sbrc %2,%3 \n\t" \
"lpm \n\t" \
"sbrs %2,%3 \n\t" \
"ld r0,Z \n\t" \
"adiw r30,1 \n\t" \
"mov %0,r0 " \
: "=r" (__c), \
"+z" (pnt) \
: "r" (flag), \
"I" (_FFS(mask) - 1) \
: "r0" \
); \
__c; \
})
#endif
__attribute__((noinline))
ATTRIBUTE_CLIB_SECTION
static void putval (void *addr, long val, unsigned char flags)
{
if (!(flags & FL_STAR)) {
#if DISABLE_ASM
if (flags & FL_CHAR)
*(char *)addr = val;
else if (flags & FL_LONG)
*(long *)addr = val;
else
*(int *)addr = val;
#else
asm volatile (
"sbrc %[flags], %[bit_char] \n\t"
"rjmp 1f \n\t"
"sbrs %[flags], %[bit_long] \n\t"
"rjmp 2f \n\t"
"std Z+3, %D1 \n\t"
"std Z+2, %C1 \n"
"2: std Z+1, %B1 \n"
"1: std Z+0, %A1"
:: "z"(addr), "r"(val), [flags]"r"(flags),
[bit_char] "M"(_FFS(FL_CHAR) - 1),
[bit_long] "M"(_FFS(FL_LONG) - 1)
);
#endif
}
}
__attribute__((noinline))
ATTRIBUTE_CLIB_SECTION
static unsigned long
mulacc (unsigned long val, unsigned char flags, unsigned char c)
{
unsigned char cnt;
if (flags & FL_OCT) {
cnt = 3;
} else if (flags & FL_HEX) {
cnt = 4;
} else {
#if DISABLE_ASM
val += (val << 2);
#else
asm (
# if defined(__AVR_HAVE_MOVW__) && __AVR_HAVE_MOVW__
"movw r26, %A0 \n\t"
"movw r30, %C0 \n"
# else
"mov r26, %A0 \n\t"
"mov r27, %B0 \n\t"
"mov r30, %C0 \n\t"
"mov r31, %D0 \n"
# endif
"1: lsl r26 \n\t"
"rol r27 \n\t"
"rol r30 \n\t"
"rol r31 \n\t"
"com __zero_reg__ \n\t"
"brne 1b \n\t"
"add %A0, r26 \n\t"
"adc %B0, r27 \n\t"
"adc %C0, r30 \n\t"
"adc %D0, r31"
: "=r"(val)
: "0"(val)
: "r26","r27","r30","r31"
);
#endif
cnt = 1;
}
do { val <<= 1; } while (--cnt);
return val + c;
}
__attribute__((noinline))
ATTRIBUTE_CLIB_SECTION
static unsigned char
conv_int (FILE *stream, width_t width, void *addr, unsigned char flags)
{
unsigned long val;
int i;
i = getc (stream);
switch ((unsigned char)i) {
case '-':
flags |= FL_MINUS;
case '+':
if (!--width || (i = getc(stream)) < 0)
goto err;
}
val = 0;
flags &= ~FL_WIDTH;
if (!(flags & (FL_DEC | FL_OCT)) && (unsigned char)i == '0') {
if (!--width || (i = getc (stream)) < 0)
goto putval;
flags |= FL_WIDTH;
if ((unsigned char)(i) == 'x' || (unsigned char)(i) == 'X') {
flags |= FL_HEX;
if (!--width || (i = getc(stream)) < 0)
goto putval;
} else {
if (!(flags & FL_HEX))
flags |= FL_OCT;
}
}
#if ('A' - '0') != (('a' - '0') & ~('A' ^ 'a'))
# error
#endif
do {
unsigned char c = i;
c -= '0';
if (c > 7) {
if (flags & FL_OCT) goto unget;
if (c > 9) {
if (!(flags & FL_HEX)) goto unget;
c &= ~('A' ^ 'a');
c += '0' - 'A';
if (c > 5) {
unget:
ungetc (i, stream);
break;
}
c += 10;
}
}
val = mulacc (val, flags, c);
flags |= FL_WIDTH;
if (!--width) goto putval;
} while ((i = getc(stream)) >= 0);
if (!(flags & FL_WIDTH))
goto err;
putval:
if (flags & FL_MINUS) val = -val;
putval (addr, val, flags);
return 1;
err:
return 0;
}
#if SCANF_BRACKET
__attribute__((noinline))
ATTRIBUTE_CLIB_SECTION
static const char *
conv_brk (FILE *stream, width_t width, char *addr, const char *fmt)
{
unsigned char msk[32];
unsigned char fnegate;
unsigned char frange;
unsigned char cabove;
int i;
memset (msk, 0, sizeof(msk));
fnegate = 0;
frange = 0;
cabove = 0;
for (i = 0; ; i++) {
unsigned char c = GETBYTE(stream->flags, __SPGM, fmt);
if (c == 0) {
return 0;
} else if (c == '^' && !i) {
fnegate = 1;
continue;
} else if (i > fnegate) {
if (c == ']') break;
if (c == '-' && !frange) {
frange = 1;
continue;
}
}
if (!frange) cabove = c;
for (;;) {
msk[c >> 3] |= 1 << (c & 7);
if (c == cabove) break;
if (c < cabove)
c++;
else
c--;
}
frange = 0;
}
if (frange)
msk['-'/8] |= 1 << ('-' & 7);
if (fnegate) {
unsigned char *p = msk;
do {
unsigned char c = *p;
*p++ = ~c;
} while (p != msk + sizeof(msk));
}
fnegate = 1;
do {
i = getc (stream);
if (i < 0) break;
if (!((msk[(unsigned char)i >> 3] >> (i & 7)) & 1)) {
ungetc (i, stream);
break;
}
if (addr) *addr++ = i;
fnegate = 0;
} while (--width);
if (fnegate) {
return 0;
} else {
if (addr) *addr = 0;
return fmt;
}
}
#endif
#if SCANF_FLOAT
extern double __floatunsisf (unsigned long);
PROGMEM static const float pwr_p10 [6] = {
1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32
};
PROGMEM static const float pwr_m10 [6] = {
1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32
};
PROGMEM static const char pstr_nfinity[] = "nfinity";
PROGMEM static const char pstr_an[] = "an";
__attribute__((noinline))
ATTRIBUTE_CLIB_SECTION
static unsigned char conv_flt (FILE *stream, width_t width, float *addr)
{
union {
unsigned long u32;
float flt;
} x;
int i;
const char *p;
int exp;
unsigned char flag;
#define FL_MINUS 0x80
#define FL_ANY 0x02
#define FL_OVFL 0x04
#define FL_DOT 0x08
#define FL_MEXP 0x10
i = getc (stream);
flag = 0;
switch ((unsigned char)i) {
case '-':
flag = FL_MINUS;
case '+':
if (!--width || (i = getc (stream)) < 0)
goto err;
}
switch (tolower (i)) {
case 'n':
p = pstr_an;
goto operate_pstr;
case 'i':
p = pstr_nfinity;
operate_pstr:
{
unsigned char c;
while ((c = pgm_read_byte (p++)) != 0) {
if (!--width
|| (i = getc (stream)) < 0
|| ((unsigned char)tolower(i) != c
&& (ungetc (i, stream), 1)))
{
if (p == pstr_nfinity + 3)
break;
goto err;
}
}
}
x.flt = (p == pstr_an + 3) ? NAN : INFINITY;
break;
default:
exp = 0;
x.u32 = 0;
do {
unsigned char c = i - '0';
if (c <= 9) {
flag |= FL_ANY;
if (flag & FL_OVFL) {
if (!(flag & FL_DOT))
exp += 1;
} else {
if (flag & FL_DOT)
exp -= 1;
x.u32 = mulacc (x.u32, FL_DEC, c);
if (x.u32 >= (ULONG_MAX - 9) / 10)
flag |= FL_OVFL;
}
} else if (c == (('.'-'0') & 0xff) && !(flag & FL_DOT)) {
flag |= FL_DOT;
} else {
break;
}
} while (--width && (i = getc (stream)) >= 0);
if (!(flag & FL_ANY))
goto err;
if ((unsigned char)i == 'e' || (unsigned char)i == 'E')
{
int expacc;
if (!--width || (i = getc (stream)) < 0) goto err;
switch ((unsigned char)i) {
case '-':
flag |= FL_MEXP;
case '+':
if (!--width) goto err;
i = getc (stream);
}
if (!isdigit (i)) goto err;
expacc = 0;
do {
expacc = mulacc (expacc, FL_DEC, i - '0');
} while (--width && isdigit (i = getc(stream)));
if (flag & FL_MEXP)
expacc = -expacc;
exp += expacc;
}
if (width && i >= 0) ungetc (i, stream);
x.flt = __floatunsisf (x.u32);
if (exp < 0) {
p = (void *)(pwr_m10 + 5);
exp = -exp;
} else {
p = (void *)(pwr_p10 + 5);
}
for (width = 32; width; width >>= 1) {
for (; (unsigned)exp >= width; exp -= width) {
union {
long lo;
float fl;
} y;
y.lo = pgm_read_dword (p);
x.flt *= y.fl;
}
p = (void *)p - sizeof(float);
}
}
if (flag & FL_MINUS)
x.flt = -x.flt;
if (addr) *addr = x.flt;
return 1;
err:
return 0;
}
#endif
__attribute__((noinline))
ATTRIBUTE_CLIB_SECTION
static int skip_spaces (FILE *stream)
{
int i;
do {
if ((i = getc (stream)) < 0)
return i;
} while (isspace (i));
ungetc (i, stream);
return i;
}
ATTRIBUTE_CLIB_SECTION
int vfscanf (FILE * stream, const char *fmt, va_list ap)
{
unsigned char nconvs;
unsigned char stream_flags;
unsigned char c;
width_t width;
void *addr;
unsigned char flags;
int i;
nconvs = 0;
stream->len = 0;
while ((c = GETBYTE (stream_flags = stream->flags, __SPGM, fmt)) != 0) {
if (isspace (c)) {
skip_spaces (stream);
} else if (c != '%'
|| (c = GETBYTE (stream_flags, __SPGM, fmt)) == '%')
{
if ((i = getc (stream)) < 0)
goto eof;
if ((unsigned char)i != c) {
ungetc (i, stream);
break;
}
} else {
flags = 0;
if (c == '*') {
flags = FL_STAR;
c = GETBYTE (stream_flags, __SPGM, fmt);
}
width = 0;
while ((c -= '0') < 10) {
flags |= FL_WIDTH;
width = mulacc (width, FL_DEC, c);
c = GETBYTE (stream_flags, __SPGM, fmt);
}
c += '0';
if (flags & FL_WIDTH) {
if (!width) break;
} else {
width = ~0;
}
switch (c) {
case 'h':
if ((c = GETBYTE (stream_flags, __SPGM, fmt)) != 'h')
break;
flags |= FL_CHAR;
case 'l':
flags |= FL_LONG;
c = GETBYTE (stream_flags, __SPGM, fmt);
}
#define CNV_BASE "cdinopsuxX"
#if SCANF_BRACKET
# define CNV_BRACKET "["
#else
# define CNV_BRACKET ""
#endif
#if SCANF_FLOAT
# define CNV_FLOAT "efgEFG"
#else
# define CNV_FLOAT ""
#endif
#define CNV_LIST CNV_BASE CNV_BRACKET CNV_FLOAT
if (!c || !strchr_P (PSTR (CNV_LIST), c))
break;
addr = (flags & FL_STAR) ? 0 : va_arg (ap, void *);
if (c == 'n') {
putval (addr, (unsigned)(stream->len), flags);
continue;
}
if (c == 'c') {
if (!(flags & FL_WIDTH)) width = 1;
do {
if ((i = getc (stream)) < 0)
goto eof;
if (addr) *(char *)addr++ = i;
} while (--width);
c = 1;
#if SCANF_BRACKET
} else if (c == '[') {
fmt = conv_brk (stream, width, addr, fmt);
c = (fmt != 0);
#endif
} else {
if (skip_spaces (stream) < 0)
goto eof;
switch (c) {
case 's':
do {
if ((i = getc (stream)) < 0)
break;
if (isspace (i)) {
ungetc (i, stream);
break;
}
if (addr) *(char *)addr++ = i;
} while (--width);
if (addr) *(char *)addr = 0;
c = 1;
break;
#if SCANF_FLOAT
case 'p':
case 'x':
case 'X':
flags |= FL_HEX;
goto conv_int;
case 'd':
case 'u':
flags |= FL_DEC;
goto conv_int;
case 'o':
flags |= FL_OCT;
case 'i':
conv_int:
c = conv_int (stream, width, addr, flags);
break;
default:
c = conv_flt (stream, width, addr);
#else
case 'd':
case 'u':
flags |= FL_DEC;
goto conv_int;
case 'o':
flags |= FL_OCT;
case 'i':
goto conv_int;
default:
flags |= FL_HEX;
conv_int:
c = conv_int (stream, width, addr, flags);
#endif
}
}
if (!c) {
if (stream->flags & (__SERR | __SEOF))
goto eof;
break;
}
if (!(flags & FL_STAR)) nconvs += 1;
}
}
return nconvs;
eof:
return nconvs ? nconvs : EOF;
}
#endif