Topic : sscanf from C standard lib. Linker Error

Forum : ST7/STM8

Original Post
Post Information Post
November 29, 2012 - 11:44am
Guest

I am writing to enquire about sscanf function functionality in standard C library.

We want to change our development environment for STM8. During initial tests an error has occurred:

RLSTM8 LINKER/LOCATOR V2.46.12.262 - Lite (32 Kb code limited)
Copyright (c) Raisonance S.A.S. 1987-2012. All rights reserved.
***ERROR 100 : UNRESOLVED EXTERNAL
SYMBOL : ?sscanf(PARSER)
***ERROR 100 : UNRESOLVED EXTERNAL
SYMBOL : ?sscanf(MAIN)
LINK/LOCATE RUN COMPLETE, 2 ERRORS FOUND.
Error Code: 0x1

According to information from forum:

http://forum.raisonance.com/viewtopic.php?pid=9962#p9962 - in mid 2009 sscanf was not implemented in standard C library for STM8.

Is sscanf function still not implemented, or we need to add some linker options ?

We have tried to PRESERVE(sscanf) and delete RMUN parameter , we also add SCINAME(STM8) and dummy calls for putchar(0); getchar(); ungetchar(0); gets(0); .

Nothing from this worked , so we have suspicion that sscanf is still not implemented.

Replies
Post Information Post
+1
0
-1
November 29, 2012 - 5:42pm
Raisonance Support Team

Indeed you're right, sscanf isn't currently implemented in the RCSTM8 libraries, but you can add your own such as this one:

/* Source : minix_scanf.c */
/* Purpose: scanf source from Minix. */

#include
#include
#include
#include
#include
#include

#define io_testflag(p,x) ((p)->_flags & (x))

#define FL_LJUST 0x0001 /* left-justify field */
#define FL_SIGN 0x0002 /* sign in signed conversions */
#define FL_SPACE 0x0004 /* space in signed conversions */
#define FL_ALT 0x0008 /* alternate form */
#define FL_ZEROFILL 0x0010 /* fill with zero's */
#define FL_SHORT 0x0020 /* optional h */
#define FL_LONG 0x0040 /* optional l */
#define FL_LONGDOUBLE 0x0080 /* optional L */
#define FL_WIDTHSPEC 0x0100 /* field width is specified */
#define FL_PRECSPEC 0x0200 /* precision is specified */
#define FL_SIGNEDCONV 0x0400 /* may contain a sign */
#define FL_NOASSIGN 0x0800 /* do not assign (in scanf) */
#define FL_NOMORE 0x1000 /* all flags collected */

#if _EM_WSIZE == _EM_PSIZE
#define set_pointer(flags) /* nothing */
#elif _EM_LSIZE == _EM_PSIZE
#define set_pointer(flags) (flags |= FL_LONG)
#else
#error garbage pointer size
#define set_pointer(flags) /* compilation might continue */
#endif

#define NUMLEN 512
#define NR_CHARS 256

static char data Xtable[NR_CHARS];
static char data inp_buf[NUMLEN];

char flowchar; /* Current flow character */
char unchar; /* Unread character */
unsigned char chars; /* Number of read characters */

/**
* 'Unget'a character from the flow
*/
static void __unreadchar(char c)
{
assert(unchar == 0);
--chars;
unchar = c;
}

/**
* Return the next character from buffer or from stdin
* A '\0' signals the end of stream
*/
static char __readchar(char **buffer)
{
char c;
if(unchar)
{
c = unchar;
unchar = 0;
}
else if(*buffer != NULL)
{
c = *(*buffer)++;
}
else
{
/* *buffer is null: We must read from stdin */
c = getchar();

#if SUPPORT_EOF_ON_STDIN /* Usually getchar will never report EOF */
if(c == EOF)
c = 0;
#endif
}

++chars;
return c;
}

/* Collect a number of characters which constitite an ordinal number.
* When the type is 'i', the base can be 8, 10, or 16, depending on the
* first 1 or 2 characters. This means that the base must be adjusted
* according to the format of the number. At the end of the function, base
* is then set to 0, so strtol() will get the right argument.
*/
static char* o_collect(register int c, register char* buffer, char type,
unsigned int width, int *basep)
{
register char *bufp = inp_buf;
register int base;

switch (type) {
case 'i': /* i means octal, decimal or hexadecimal */
case 'p':
case 'x':
case 'X': base = 16; break;
case 'd':
case 'u': base = 10; break;
case 'o': base = 8; break;
case 'b': base = 2; break;
}

if (c == '-' || c == '+') {
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
}

if (width && c == '0' && base == 16) {
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
if (c != 'x' && c != 'X') {
if (type == 'i') base = 8;
}
else if (width) {
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
}
}
else if (type == 'i') base = 10;

while (width) {
if (((base == 10) && isdigit(c))
|| ((base == 16) && isxdigit(c))
|| ((base == 8) && isdigit(c) && (c < '8'))
|| ((base == 2) && isdigit(c) && (c < '2'))) {
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
}
else break;
}

if (width && c != EOF) __unreadchar(c);
if (type == 'i') base = 0;
*basep = base;
*bufp = '\0';
return bufp - 1;
}

#ifndef NOFLOAT
/* The function f_collect() reads a string that has the format of a
* floating-point number. The function returns as soon as a format-error
* is encountered, leaving the offending character in the input. This means
* that 1.el leaves the 'l' in the input queue. Since all detection of
* format errors is done here, _doscan() doesn't call strtod() when it's
* not necessary, although the use of the width field can cause incomplete
* numbers to be passed to strtod(). (e.g. 1.3e+)
*/
static char* f_collect(register int c, register char *buffer, register unsigned int width)
{
register char *bufp = inp_buf;
int digit_seen = 0;

if (c == '-' || c == '+') {
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
}

while (width && isdigit(c)) {
digit_seen++;
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
}
if (width && c == '.') {
*bufp++ = c;
if(--width)
c = __readchar(&buffer);
while (width && isdigit(c)) {
digit_seen++;
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
}
}

if (!digit_seen) {
if (width && c != EOF) __unreadchar(c);
return inp_buf - 1;
}
else digit_seen = 0;

if (width && (c == 'e' || c == 'E')) {
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
if (width && (c == '+' || c == '-')) {
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
}
while (width && isdigit(c)) {
digit_seen++;
*bufp++ = c;
if (--width)
c = __readchar(&buffer);
}
if (!digit_seen) {
if (width && c != EOF) __unreadchar(c);
return inp_buf - 1;
}
}

if (width && c != EOF) __unreadchar(c);
*bufp = '\0';
return bufp - 1;
}
#endif /* NOFLOAT */

/*
* the routine that does the scanning
*/

int
_doscan(register char* buffer, const char *format, va_list ap)
{
int done = 0; /* number of items done */
int nrchars = 0; /* number of characters read */
int conv = 0; /* # of conversions */
int base; /* conversion base */
unsigned long val; /* an integer value */
register char *str; /* temporary pointer */
char *tmp_string; /* ditto */
unsigned width = 0; /* width of field */
int flags; /* some flags */
int reverse; /* reverse the checking in [...] */
int kind;
register int ic = EOF; /* the input character */
#ifndef NOFLOAT
long double ld_val;
#endif

if (!*format) return 0;

while (1) {
if (isspace(*format)) {
while (isspace(*format))
format++; /* skip whitespace */
ic = __readchar(&buffer);
nrchars++;
while (isspace (ic)) {
ic = __readchar(&buffer);
nrchars++;
}
if (ic != EOF) __unreadchar(ic);
nrchars--;
}
if (!*format) break; /* end of format */

if (*format != '%') {
ic = __readchar(&buffer);
nrchars++;
if (ic != *format++) break; /* error */
continue;
}
format++;
if (*format == '%') {
ic = __readchar(&buffer);
nrchars++;
if (ic == '%') {
format++;
continue;
}
else break;
}
flags = 0;
if (*format == '*') {
format++;
flags |= FL_NOASSIGN;
}
if (isdigit (*format)) {
flags |= FL_WIDTHSPEC;
for (width = 0; isdigit (*format);)
width = width * 10 + *format++ - '0';
}

switch (*format) {
case 'h': flags |= FL_SHORT; format++; break;
case 'l': flags |= FL_LONG; format++; break;
case 'L': flags |= FL_LONGDOUBLE; format++; break;
}
kind = *format;
if ((kind != 'c') && (kind != '[') && (kind != 'n')) {
do {
ic = __readchar(&buffer);
nrchars++;
} while (isspace(ic));
if (ic == EOF) break; /* outer while */
} else if (kind != 'n') { /* %c or %[ */
ic = __readchar(&buffer);
if (ic == EOF) break; /* outer while */
nrchars++;
}
switch (kind) {
default:
/* not recognized, like %q */
return conv || (ic != EOF) ? done : EOF;
break;
case 'n':
if (!(flags & FL_NOASSIGN)) { /* silly, though */
if (flags & FL_SHORT)
*va_arg(ap, short *) = (short) nrchars;
else if (flags & FL_LONG)
*va_arg(ap, long *) = (long) nrchars;
else
*va_arg(ap, int *) = (int) nrchars;
}
break;
case 'p': /* pointer */
set_pointer(flags);
/* fallthrough */
case 'b': /* binary */
case 'd': /* decimal */
case 'i': /* general integer */
case 'o': /* octal */
case 'u': /* unsigned */
case 'x': /* hexadecimal */
case 'X': /* ditto */
if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
width = NUMLEN;
if (!width) return done;

str = o_collect(ic, buffer, kind, width, &base);
if (str < inp_buf
|| (str == inp_buf
&& (*str == '-'
|| *str == '+'))) return done;

/*
* Although the length of the number is str-inp_buf+1
* we don't add the 1 since we counted it already
*/
nrchars += str - inp_buf;

if (!(flags & FL_NOASSIGN)) {
if (kind == 'd' || kind == 'i')
val = strtol(inp_buf, &tmp_string, base);
else
val = strtoul(inp_buf, &tmp_string, base);
if (flags & FL_LONG)
*va_arg(ap, unsigned long *) = (unsigned long) val;
else if (flags & FL_SHORT)
*va_arg(ap, unsigned short *) = (unsigned short) val;
else
*va_arg(ap, unsigned *) = (unsigned) val;
}
break;
case 'c':
if (!(flags & FL_WIDTHSPEC))
width = 1;
if (!(flags & FL_NOASSIGN))
str = va_arg(ap, char *);
if (!width) return done;

while (width && ic != EOF) {
if (!(flags & FL_NOASSIGN))
*str++ = (char) ic;
if (--width) {
ic = __readchar(&buffer);
nrchars++;
}
}

if (width) {
if (ic != EOF) __unreadchar(ic);
nrchars--;
}
break;
case 's':
if (!(flags & FL_WIDTHSPEC))
width = 0xffff;
if (!(flags & FL_NOASSIGN))
str = va_arg(ap, char *);
if (!width) return done;

while (width && ic != EOF && !isspace(ic)) {
if (!(flags & FL_NOASSIGN))
*str++ = (char) ic;
if (--width) {
ic = __readchar(&buffer);
nrchars++;
}
}
/* terminate the string */
if (!(flags & FL_NOASSIGN))
*str = '\0';
if (width) {
if (ic != EOF) __unreadchar(ic);
nrchars--;
}
break;
case '[':
if (!(flags & FL_WIDTHSPEC))
width = 0xffff;
if (!width) return done;

if ( *++format == '^' ) {
reverse = 1;
format++;
} else
reverse = 0;

for (str = Xtable; str < &Xtable[NR_CHARS]
; str++)
*str = 0;

if (*format == ']') Xtable[*format++] = 1;

while (*format && *format != ']') {
Xtable[*format++] = 1;
if (*format == '-') {
format++;
if (*format
&& *format != ']'
&& *(format) >= *(format -2)) {
int c;

for( c = *(format -2) + 1
; c <= *format ; c++)
Xtable[c] = 1;
format++;
}
else Xtable['-'] = 1;
}
}
if (!*format) return done;

if (!(Xtable[ic] ^ reverse)) {
/* MAT 8/9/96 no match must return character */
__unreadchar(ic);
return done;
}

if (!(flags & FL_NOASSIGN))
str = va_arg(ap, char *);

do {
if (!(flags & FL_NOASSIGN))
*str++ = (char) ic;
if (--width) {
ic = __readchar(&buffer);
nrchars++;
}
} while (width && ic != EOF && (Xtable[ic] ^ reverse));

if (width) {
if (ic != EOF) __unreadchar(ic);
nrchars--;
}
if (!(flags & FL_NOASSIGN)) { /* terminate string */
*str = '\0';
}
break;
#ifndef NOFLOAT
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (!(flags & FL_WIDTHSPEC) || width > NUMLEN)
width = NUMLEN;

if (!width) return done;
str = f_collect(ic, buffer, width);

if (str < inp_buf
|| (str == inp_buf
&& (*str == '-'
|| *str == '+'))) return done;

/*
* Although the length of the number is str-inp_buf+1
* we don't add the 1 since we counted it already
*/
nrchars += str - inp_buf;

if (!(flags & FL_NOASSIGN)) {
ld_val = strtod(inp_buf, &tmp_string);
if (flags & FL_LONGDOUBLE)
*va_arg(ap, long double *) = (long double) ld_val;
else
if (flags & FL_LONG)
*va_arg(ap, double *) = (double) ld_val;
else
*va_arg(ap, float *) = (float) ld_val;
}
break;
#endif
} /* end switch */
conv++;
if (!(flags & FL_NOASSIGN) && kind != 'n') done++;
format++;
}
return conv || (ic != EOF) ? done : EOF;
}

int _sscanf(char* buffer,const char *format, ...)
{
va_list ap;
int retval;

va_start(ap, format);

retval = _doscan( buffer, format, ap);

va_end(ap);

return retval;
}

char data str1[8];
char data str2[8];
char data str3[8];

void main (void)
{
int i;
long l;
float f;
int scanned;

scanned = _sscanf( "Hello", "%s", str1);
assert(scanned == 1 && !strcmp(str1, "Hello"));

scanned = _sscanf( "Hello", "%4s%c", str1, str2);
assert(scanned == 2 && !strcmp(str1, "Hell") && str2[0] == 'o');

scanned = _sscanf( "12345", "%i", &i);
assert(scanned == 1 && i == 12345);

scanned = _sscanf( "1234567", "%li", &l);
assert(scanned == 1 && l == 1234567);

scanned = _sscanf( "123.45e2", "%f", &f);
assert(scanned == 1 && f == 12345.0);

printf( "OK" );
}