rockbox/firmware/common/sscanf.c
Tomasz Malesinski 91f08b5a24 Made %n not eat white space.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8557 a1c6a512-1295-4272-9138-f99709370657
2006-02-03 23:39:12 +00:00

223 lines
5.2 KiB
C

#include <stdarg.h>
#include <string.h>
#include <stdbool.h>
static inline bool isspace(char c)
{
return (c == ' ') || (c == '\t') || (c == '\n');
}
static inline bool isdigit(char c)
{
return (c >= '0') && (c <= '9');
}
static inline bool isxdigit(char c)
{
return ((c >= '0') && (c <= '9'))
|| ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F'));
}
static int parse_dec(int (*peek)(void *userp),
void (*pop)(void *userp),
void *userp,
long *vp)
{
long v = 0;
int n = 0;
int minus = 0;
char ch;
if ((*peek)(userp) == '-')
{
(*pop)(userp);
n++;
minus = 1;
}
ch = (*peek)(userp);
if (!isdigit(ch))
return -1;
do
{
v = v * 10 + ch - '0';
(*pop)(userp);
n++;
ch = (*peek)(userp);
} while (isdigit(ch));
*vp = minus ? -v : v;
return n;
}
static int parse_hex(int (*peek)(void *userp),
void (*pop)(void *userp),
void *userp,
unsigned long *vp)
{
unsigned long v = 0;
int n = 0;
char ch;
ch = (*peek)(userp);
if (!isxdigit(ch))
return -1;
do
{
if (ch >= 'a')
ch = ch - 'a' + 10;
else if (ch >= 'A')
ch = ch - 'A' + 10;
else
ch = ch - '0';
v = v * 16 + ch;
(*pop)(userp);
n++;
ch = (*peek)(userp);
} while (isxdigit(ch));
*vp = v;
return n;
}
static int skip_spaces(int (*peek)(void *userp),
void (*pop)(void *userp),
void *userp)
{
int n = 0;
while (isspace((*peek)(userp))) {
n++;
(*pop)(userp);
}
return n;
}
static int scan(int (*peek)(void *userp),
void (*pop)(void *userp),
void *userp,
const char *fmt,
va_list ap)
{
char ch;
int n = 0;
int n_chars = 0;
int r;
long lval;
unsigned long ulval;
while ((ch = *fmt++) != '\0')
{
bool literal = false;
if (ch == '%')
{
ch = *fmt++;
switch (ch)
{
case 'x':
n_chars += skip_spaces(peek, pop, userp);
if ((r = parse_hex(peek, pop, userp, &ulval)) >= 0)
{
*(va_arg(ap, unsigned int *)) = ulval;
n++;
n_chars += r;
}
else
return n;
break;
case 'd':
n_chars += skip_spaces(peek, pop, userp);
if ((r = parse_dec(peek, pop, userp, &lval)) >= 0)
{
*(va_arg(ap, int *)) = lval;
n++;
n_chars += r;
}
else
return n;
break;
case 'n':
*(va_arg(ap, int *)) = n_chars;
n++;
break;
case 'l':
n_chars += skip_spaces(peek, pop, userp);
ch = *fmt++;
switch (ch)
{
case 'x':
if ((r = parse_hex(peek, pop, userp, &ulval)) >= 0)
{
*(va_arg(ap, unsigned long *)) = ulval;
n++;
n_chars += r;
}
else
return n;
break;
case 'd':
if ((r = parse_dec(peek, pop, userp, &lval)) >= 0)
{
*(va_arg(ap, long *)) = lval;
n++;
n_chars += r;
}
else
return n;
break;
case '\0':
return n;
default:
literal = true;
break;
}
break;
case '\0':
return n;
default:
literal = true;
break;
}
} else
literal = true;
if (literal)
{
n_chars += skip_spaces(peek, pop, userp);
if ((*peek)(userp) != ch)
return n;
else
{
(*pop)(userp);
n_chars++;
}
}
}
return n;
}
static int sspeek(void *userp)
{
return **((char **)userp);
}
static void sspop(void *userp)
{
(*((char **)userp))++;
}
int sscanf(const char *s, const char *fmt, ...)
{
int r;
va_list ap;
const char *p;
p = s;
va_start(ap, fmt);
r = scan(sspeek, sspop, &p, fmt, ap);
va_end(ap);
return r;
}