summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Malesinski <tomal@rockbox.org>2006-01-25 01:35:04 +0000
committerTomasz Malesinski <tomal@rockbox.org>2006-01-25 01:35:04 +0000
commite8e0b241bdaa20a6745c845c5a027b608524d8fb (patch)
tree716370e448036356da6ede670bcf0926609aa38a
parent0868ac8cb22b557b742c1728f51312601826c270 (diff)
downloadrockbox-e8e0b241bdaa20a6745c845c5a027b608524d8fb.tar.gz
rockbox-e8e0b241bdaa20a6745c845c5a027b608524d8fb.tar.bz2
rockbox-e8e0b241bdaa20a6745c845c5a027b608524d8fb.zip
Simple sscanf implementation
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8444 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/common/sscanf.c216
-rw-r--r--firmware/include/sscanf.h30
2 files changed, 246 insertions, 0 deletions
diff --git a/firmware/common/sscanf.c b/firmware/common/sscanf.c
new file mode 100644
index 0000000000..a63a384456
--- /dev/null
+++ b/firmware/common/sscanf.c
@@ -0,0 +1,216 @@
+#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 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++;
+
+ while (isspace((*peek)(userp))) {
+ n_chars++;
+ (*pop)(userp);
+ }
+
+ switch (ch)
+ {
+ case 'x':
+ 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':
+ 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':
+ 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)
+ {
+ while (isspace((*peek)(userp))) {
+ (*pop)(userp);
+ n_chars++;
+ }
+ 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;
+}
diff --git a/firmware/include/sscanf.h b/firmware/include/sscanf.h
new file mode 100644
index 0000000000..e2fd3a9aec
--- /dev/null
+++ b/firmware/include/sscanf.h
@@ -0,0 +1,30 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 by Tomasz Malesinski
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __SSCANF_H__
+#define __SSCANF_H__
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <_ansi.h>
+
+int sscanf(const char *s, const char *fmt, ...)
+ ATTRIBUTE_SCANF(2, 3);
+
+#endif /* __SSCANF_H__ */