summaryrefslogtreecommitdiff
path: root/userland/libc/string/sscanf.c
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-10-22 19:50:38 +0200
committerAnton Kling <anton@kling.gg>2023-10-22 19:50:38 +0200
commit4e09bca9e34c226b6d7e34b4fa11248405fd988e (patch)
tree80f156b7940d9d19971395f335530170c69516c7 /userland/libc/string/sscanf.c
Move everything into a new repo.
Diffstat (limited to 'userland/libc/string/sscanf.c')
-rw-r--r--userland/libc/string/sscanf.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/userland/libc/string/sscanf.c b/userland/libc/string/sscanf.c
new file mode 100644
index 0000000..28e1ce1
--- /dev/null
+++ b/userland/libc/string/sscanf.c
@@ -0,0 +1,193 @@
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+extern int errno;
+extern int get_value(char c, long base);
+
+long ftnum(FILE *stream, int base, int *error) {
+ char c;
+ long ret_value = 0;
+ *error = 0;
+ // Ignore inital white-space sequence
+ for (;;) {
+ if (EOF == (c = fgetc(stream))) {
+ *error = 1;
+ return 0;
+ }
+
+ if (!isspace(c)) {
+ ungetc(c, stream);
+ break;
+ }
+ }
+ if (c == '\0') {
+ *error = 1;
+ return 0;
+ }
+ if (!isdigit(c)) {
+ *error = 1;
+ return 0;
+ }
+ if (!(2 <= base && 36 >= base)) {
+ *error = 1;
+ return 0;
+ }
+ for (;;) {
+ if (EOF == (c = fgetc(stream)))
+ break;
+ if (c == '\0') {
+ ungetc(c, stream);
+ break;
+ }
+ int val = get_value(c, base);
+ if (-1 == val) {
+ ungetc(c, stream);
+ break;
+ }
+ if (ret_value * base > LONG_MAX - val) {
+ ungetc(c, stream);
+ errno = ERANGE;
+ *error = 1;
+ return 0;
+ }
+ ret_value *= base;
+ ret_value += val;
+ }
+ return ret_value;
+}
+
+int vfscanf(FILE *stream, const char *format, va_list ap) {
+ int rc = 0; // Upon successful completion, these functions shall return the
+ // number of successfully matched and assigned input items
+ int cont = 0;
+ int suppress = 0;
+ for (; *format; format++) {
+ if (*format != '%' && !cont) {
+ char c;
+ if (isspace(*format))
+ continue;
+ if (EOF == (c = fgetc(stream))) {
+ break;
+ }
+ if (*format == c) // TODO: Make sure this is the correct behaviour
+ continue;
+ // TODO: Make sure this is the correct behaviour
+ errno = EINVAL;
+ assert(0);
+ break;
+ }
+
+ if (*format == '%' && !cont) {
+ cont = 1;
+ continue;
+ }
+
+ int is_long = 0;
+ switch (*format) {
+ case 'l':
+ is_long++;
+ assert(is_long < 3);
+ cont = 1;
+ break;
+ case 'i': // Matches an optionally signed integer, whose format is the same
+ // as expected for the subject sequence of strtol() with 0 for the
+ // base argument.
+ case 'd': {
+ // Matches an optionally signed decimal integer, whose format is the
+ // same as expected for the subject sequence of strtol() with the value
+ // 10 for the base argument. In the absence of a size modifier, the
+ // application shall ensure that the corresponding argument is a pointer
+ // to int.
+ int err = 0;
+ int result = ftnum(stream, 10, &err);
+ if (err) {
+ cont = 0;
+ break;
+ }
+ if (!suppress) {
+ if (2 == is_long) {
+ *((long long *)va_arg(ap, long long *)) = result;
+ } else if (1 == is_long) {
+ *((long *)va_arg(ap, long *)) = result;
+ } else {
+ *((int *)va_arg(ap, int *)) = result;
+ }
+ rc++;
+ }
+ assert(0 == err);
+ cont = 0;
+ break;
+ }
+ case 'c': {
+ char result = fgetc(stream);
+ if (!suppress) {
+ *((char *)va_arg(ap, char *)) = result;
+ rc++;
+ }
+ cont = 0;
+ break;
+ }
+ case '*': // Assignment suppression
+ suppress = 1;
+ cont = 1;
+ break;
+ default:
+ printf("vfscanf: Got %c but not supported.\n", *format);
+ assert(0);
+ break;
+ }
+ if (!cont) {
+ suppress = 0;
+ }
+ }
+ return rc;
+}
+
+struct sscanf_cookie {
+ const char *s;
+};
+
+size_t sscanf_read(FILE *f, unsigned char *s, size_t l) {
+ struct sscanf_cookie *c = f->cookie;
+ if (!*(c->s)) {
+ return 0;
+ }
+ size_t r = 0;
+ for (; l && *(c->s); l--, c->s += 1) {
+ *s = *(c->s);
+ s++;
+ r++;
+ }
+ if (!(*(c->s)))
+ f->is_eof = 1;
+ /*
+ memcpy(s, c->s, l);
+ c->s += l;*/
+ return r;
+}
+
+int vsscanf(const char *s, const char *restrict format, va_list ap) {
+ struct sscanf_cookie c = {.s = s};
+ FILE f = {
+ .read = sscanf_read,
+ .cookie = &c,
+ .has_buffered_char = 0,
+ .is_eof = 0,
+ .has_error = 0,
+ .offset_in_file = 0,
+ };
+ return vfscanf(&f, format, ap);
+}
+
+int sscanf(const char *s, const char *restrict format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vsscanf(s, format, ap);
+ va_end(ap);
+ return rc;
+}