Hi Dave,
Here's a cut-down scanf I wrote for the AVR (gcc). You'll have to see
if it builds in SDCC because I haven't tried it. There's a bit of
jiggery pokery going on with read_ram and read_rom because the format
string comes from AVR ROM space and the scanned string is in RAM.
Licence: Use at your own risk. Help is not guaranteed.
// === Scanf ===
bool in_set(prog_char *set, char c, short size) {
/* Searches max size chars at string set to find character c
* set is NOT required to be NUL-terminated.
*/
while (size--) if (pgm_read_byte(set++) == c) return true;
return false;
}
char read_ram(char *addr) {
return *addr;
}
char read_rom(char *addr) {
return pgm_read_byte(addr);
}
char hex2num(char hex) {
if (hex>='A') {
if (hex>='a') hex -= 'a'-'A'; // tolower
hex -= 'A'-'9'-1; // todigit
}
return hex-'0'; // tohex
}
// atoi that terminates at maxlen and adjust input string to point to
character that conversion stopped at)
// Note: a maxlen of -1 equals a length of 65535 which is essentially
infinite.
short bounded_atoi(char fetch(char *), prog_char **s_p, short maxlen) {
short number=0;
bool sign=false;
char c;
prog_char *s = *s_p;
if (fetch(s) == '-') {
sign = true;
s++;
}
while (maxlen-- and isdigit(c=fetch(s))) {
number = (number<<3) + number + number; // *= 10
number += c-'0';
s++;
}
if (sign) number=-number;
*s_p = s;
return number;
}
// cut-down scanf
// handles %% %s %[] %c %d %x (=%X) %n:
// return no. conversions stored. Returns early if mismatch
// Notes:
// - length modifier may be supplied after '%' and it defaults to
65535 (or for %c, 1)
// - %x requires length
// - '*' modifier not allowed
// - 'l' modifier not allowed
// - %n does increment count returned (scanf docs are undecided on
whether it should)
// - no special whitespace handling, so a space matches just one space
unsigned char vxsscanf(char *s, prog_char *fmt, va_list args) {
char *sstore, *set, *s0=s;
char c, count;
short length;
bool contains;
unsigned short *number;
count = 0; // how many conversions
stored
while (c=pgm_read_byte(fmt++), c) {
if (c!='%') {
if (*s++ != c) return count; // match s against c
} else {
if (isdigit(pgm_read_byte(fmt))) {
length = bounded_atoi(read_rom, &fmt, -1);
} else length = -1;
c = pgm_read_byte(fmt++);
switch (c) {
case 'n':
*va_arg(args, short *) = (short)(s-s0);
count++;
break;
case '[':
sstore = va_arg(args, char*);
if (pgm_read_byte(fmt) == '^') {
contains = false;
fmt++;
} else contains = true;
set = fmt;
while (pgm_read_byte(fmt) and pgm_read_byte(fmt++)
!= ']');
while (length-- and *s) {
if (in_set(set, *s, fmt-set) == contains)
*sstore++=*s++;
else break;
}
*sstore = 0;
count++;
break;
case 'c': // character
if (length==-1) length=1;
case 's':
sstore = va_arg(args, char*);
while (length-- and *s) *sstore++=*s++;
if (c=='s') *sstore = 0;
count++;
break;
case 'u': // unsigned short
decimal number
case 'd': // signed decimal number
sstore = s;
*va_arg(args, unsigned short *) =
bounded_atoi(read_ram, &s, length);
if (s==sstore) return count; // no number found
count++;
break;
case 'x':
case 'X':
number = va_arg(args, unsigned short *);
*number = 0;
if (not isxdigit(*s)) return count; // no hex
number found
while (length-- and isxdigit(c=*s)) {
s++;
*number <<= 4;
*number += hex2num(c);
}
count++;
break;
case '%':
if (*s++ != '%') return count; // match s
against '%'
break;
default:
return count; // can't decode
format string
}
}
}
return count;
}
-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08
_______________________________________________
Sdcc-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sdcc-user