summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordzwdz2023-08-25 14:06:00 +0200
committerdzwdz2023-08-25 14:06:00 +0200
commita767724386c9fe175c2fe4311511c0a402339c37 (patch)
tree7667b5b9a0cd40da58a778c83666467e0a40b9ce
parent3e09037780ca95633749be3acd52e817eed7f98c (diff)
libc: get as+ld to work
-rw-r--r--src/libc/include/stdio.h2
-rw-r--r--src/libc/stdio/file.c4
-rw-r--r--src/libc/stdio/misc.c6
-rw-r--r--src/libc/vendor/sortix/sscanf.c31
-rw-r--r--src/libc/vendor/sortix/vcbscanf.c475
-rw-r--r--src/libc/vendor/sortix/vsscanf.c59
m---------toolchain/binutils0
7 files changed, 567 insertions, 10 deletions
diff --git a/src/libc/include/stdio.h b/src/libc/include/stdio.h
index 169646f..c579df5 100644
--- a/src/libc/include/stdio.h
+++ b/src/libc/include/stdio.h
@@ -85,3 +85,5 @@ int rename(const char *old, const char *new);
char *tmpnam(char *s);
int sscanf(const char *restrict s, const char *restrict format, ...);
+int vsscanf(const char* str, const char* format, va_list ap);
+int vcbscanf(void* fp, int (*fgetc)(void*), int (*ungetc)(int, void*), const char* restrict format, va_list ap);
diff --git a/src/libc/stdio/file.c b/src/libc/stdio/file.c
index 984c419..de88a9f 100644
--- a/src/libc/stdio/file.c
+++ b/src/libc/stdio/file.c
@@ -305,10 +305,6 @@ int fseeko(FILE *f, off_t offset, int whence) {
break;
case SEEK_CUR:
base = f->pos;
- // TODO untested
- if (f->readbuf) {
- base -= f->rblen;
- }
break;
case SEEK_END:
base = _sys_getsize(f->fd);
diff --git a/src/libc/stdio/misc.c b/src/libc/stdio/misc.c
index 45144f3..7e8e746 100644
--- a/src/libc/stdio/misc.c
+++ b/src/libc/stdio/misc.c
@@ -44,9 +44,3 @@ char *tmpnam(char *s) {
strcpy(s, "/tmp/tmpnam");
return s;
}
-
-// TODO sscanf
-int sscanf(const char *restrict s, const char *restrict format, ...) {
- (void)s; (void)format;
- return 0;
-}
diff --git a/src/libc/vendor/sortix/sscanf.c b/src/libc/vendor/sortix/sscanf.c
new file mode 100644
index 0000000..dd7aca4
--- /dev/null
+++ b/src/libc/vendor/sortix/sscanf.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012 Jonas 'Sortie' Termansen.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * stdio/sscanf.c
+ * Input format conversion.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+int sscanf(const char* str, const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ int ret = vsscanf(str, format, ap);
+ va_end(ap);
+ return ret;
+}
+
diff --git a/src/libc/vendor/sortix/vcbscanf.c b/src/libc/vendor/sortix/vcbscanf.c
new file mode 100644
index 0000000..3aef233
--- /dev/null
+++ b/src/libc/vendor/sortix/vcbscanf.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2012, 2014, 2016, 2020 Jonas 'Sortie' Termansen.
+ * Modified by dzwdz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * stdio/vcbscanf.c
+ * Input format conversion.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+enum scan_type
+{
+ TYPE_SHORT,
+ TYPE_SHORTSHORT,
+ TYPE_INT,
+ TYPE_LONG,
+ TYPE_LONGLONG,
+ TYPE_SIZE,
+ TYPE_PTRDIFF,
+ TYPE_MAX,
+ TYPE_PTR,
+};
+
+typedef int64_t ssize_t;
+
+static int debase(unsigned char c, int base)
+{
+ if ( c == '0' )
+ return 0;
+ int ret = -1;
+ if ( '0' <= c && c <= '9' ) { ret = c - '0' + 0; }
+ if ( 'a' <= c && c <= 'f' ) { ret = c - 'a' + 10; }
+ if ( 'A' <= c && c <= 'F' ) { ret = c - 'A' + 10; }
+ if ( base <= ret )
+ return -1;
+ return ret;
+}
+
+static size_t parse_scanset(bool scanset[256], const char* spec)
+{
+ bool negate = spec[0] == '^';
+ size_t offset = negate ? 1 : 0;
+ for ( size_t i = 0; i < 256; i++ )
+ scanset[i] = negate;
+ if ( spec[offset] == ']' )
+ {
+ offset++;
+ scanset[(unsigned char) ']'] = !negate;
+ }
+ for ( ; spec[offset] && spec[offset] != ']'; offset++ )
+ {
+ unsigned char c = (unsigned char) spec[offset];
+ // Only allow ASCII in the scanset besides negation.
+ if ( 128 < c )
+ return offset;
+ if ( spec[offset + 1] == '-' &&
+ spec[offset + 2] &&
+ spec[offset + 2] != ']' )
+ {
+ unsigned char to = (unsigned char) spec[offset + 2];
+ for ( int i = c; i <= to; i++ )
+ scanset[i] = !negate;
+ offset += 2;
+ }
+ else
+ scanset[(unsigned char) spec[offset]] = !negate;
+ }
+ return offset;
+}
+
+int vcbscanf(void* fp,
+ int (*fgetc)(void*),
+ int (*ungetc)(int, void*),
+ const char* restrict format,
+ va_list ap)
+{
+ int matched_items = 0;
+ uintmax_t bytes_parsed = 0;
+ int ic = 0;
+ while ( *format )
+ {
+ if ( isspace((unsigned char) *format) )
+ {
+ do format++;
+ while ( isspace((unsigned char) *format) );
+ while ( true )
+ {
+ ic = fgetc(fp);
+ if ( ic == EOF )
+ break;
+ bytes_parsed++;
+ if ( !isspace(ic) )
+ {
+ ungetc(ic, fp);
+ bytes_parsed--;
+ break;
+ }
+ }
+ continue;
+ }
+ else if ( format[0] != '%' || format[1] == '%' )
+ {
+ if ( *format == '%' )
+ format++;
+ unsigned char c = *format++;
+ ic = fgetc(fp);
+ if ( ic == EOF )
+ return matched_items ? matched_items : EOF;
+ bytes_parsed++;
+ if ( ic != c )
+ {
+ ungetc(ic, fp);
+ bytes_parsed--;
+ break;
+ }
+ continue;
+ }
+ format++;
+ bool discard = format[0] == '*';
+ if ( discard )
+ format++;
+ size_t field_width = 0;
+ while ( '0'<= *format && *format <= '9' )
+ field_width = field_width * 10 + *format++ - '0';
+ bool allocate = false;
+ if ( *format == 'm' )
+ {
+ allocate = true;
+ format++;
+ }
+ enum scan_type scan_type = TYPE_INT;
+ switch ( *format++ )
+ {
+ case 'h':
+ scan_type = *format == 'h' ?
+ (format++, TYPE_SHORTSHORT) :
+ TYPE_SHORT;
+ break;
+ case 'j': scan_type = TYPE_MAX; break;
+ case 'l':
+ scan_type = *format == 'l' ?
+ (format++, TYPE_LONGLONG) :
+ TYPE_LONG;
+ break;
+ case 'L': scan_type = TYPE_LONGLONG; break;
+ case 't': scan_type = TYPE_PTRDIFF; break;
+ case 'z': scan_type = TYPE_SIZE; break;
+ default: format--;
+ }
+ if ( *format == 'd' || *format == 'i' || *format == 'o' ||
+ *format == 'u' || *format == 'x' || *format == 'X' ||
+ *format == 'p' )
+ {
+ int base;
+ bool is_unsigned;
+ switch ( *format++ )
+ {
+ case 'd': base = 10; is_unsigned = false; break;
+ case 'i': base = 0; is_unsigned = false; break;
+ case 'o': base = 8; is_unsigned = true; break;
+ case 'u': base = 10; is_unsigned = true; break;
+ case 'p':
+ if ( scan_type != TYPE_INT )
+ return errno = EINVAL, matched_items ? matched_items : EOF;
+ scan_type = TYPE_PTR;
+ /* fallthrough */
+ case 'X':
+ case 'x': base = 16; is_unsigned = true; break;
+ default: __builtin_unreachable();
+ }
+ if ( allocate )
+ return errno = EINVAL, matched_items ? matched_items : EOF;
+ bool parsed_int = false;
+ uintmax_t int_value = 0;
+ bool negative = false;
+ bool has_prefix = false;
+ bool maybe_base16 = false;
+ if ( !field_width )
+ field_width = SIZE_MAX;
+ size_t i = 0;
+ for ( ; i < field_width; i++ )
+ {
+ ic = fgetc(fp);
+ if ( ic == EOF )
+ break;
+ bytes_parsed++;
+ if ( i == 0 && isspace(ic) )
+ {
+ i--;
+ continue;
+ }
+ if ( ic == '-' && i == 0 && !has_prefix )
+ {
+ negative = true;
+ has_prefix = true;
+ }
+ else if ( ic == '+' && i == 0 && !has_prefix )
+ {
+ negative = false;
+ has_prefix = true;
+ }
+ else if ( (ic == 'x' || ic == 'X') &&
+ (base == 0 || base == 16) &&
+ (has_prefix ? i == 2 : i == 1) &&
+ int_value == 0 )
+ {
+ maybe_base16 = true;
+ parsed_int = false;
+ }
+ else if ( ic == '0' && (has_prefix ? i == 1 : i == 0) )
+ {
+ int_value = 0;
+ parsed_int = true;
+ }
+ else
+ {
+ if ( base == 0 )
+ {
+ if ( maybe_base16 )
+ {
+ if ( debase(ic, 16) < 0 )
+ {
+ ungetc(ic, fp);
+ bytes_parsed--;
+ break;
+ }
+ base = 16;
+ }
+ else if ( parsed_int )
+ base = 8;
+ else
+ base = 10;
+ }
+ int cval = debase(ic, base);
+ if ( cval < 0 )
+ {
+ ungetc(ic, fp);
+ bytes_parsed--;
+ break;
+ }
+ if ( __builtin_mul_overflow(int_value, base, &int_value) )
+ int_value = UINTMAX_MAX;
+ if ( __builtin_add_overflow(int_value, cval, &int_value) )
+ int_value = UINTMAX_MAX;
+ parsed_int = true;
+ }
+ }
+ if ( !parsed_int )
+ return matched_items || i || ic != EOF ? matched_items : EOF;
+ if ( discard )
+ continue;
+ uintmax_t uintmaxval = int_value;
+ if ( negative )
+ uintmaxval = -uintmaxval;
+ intmax_t intmaxval = uintmaxval;
+ bool un = is_unsigned;
+ switch ( scan_type )
+ {
+ case TYPE_SHORTSHORT:
+ if ( un ) *va_arg(ap, unsigned char*) = uintmaxval;
+ else *va_arg(ap, signed char*) = intmaxval;
+ break;
+ case TYPE_SHORT:
+ if ( un ) *va_arg(ap, unsigned short*) = uintmaxval;
+ else *va_arg(ap, signed short*) = intmaxval;
+ break;
+ case TYPE_INT:
+ if ( un ) *va_arg(ap, unsigned int*) = uintmaxval;
+ else *va_arg(ap, signed int*) = intmaxval;
+ break;
+ case TYPE_LONG:
+ if ( un ) *va_arg(ap, unsigned long*) = uintmaxval;
+ else *va_arg(ap, signed long*) = intmaxval;
+ break;
+ case TYPE_LONGLONG:
+ if ( un ) *va_arg(ap, unsigned long long*) = uintmaxval;
+ else *va_arg(ap, signed long long*) = intmaxval;
+ break;
+ case TYPE_PTRDIFF:
+ *va_arg(ap, ptrdiff_t*) = intmaxval;
+ break;
+ case TYPE_SIZE:
+ if ( un ) *va_arg(ap, size_t*) = uintmaxval;
+ else *va_arg(ap, ssize_t*) = intmaxval;
+ break;
+ case TYPE_MAX:
+ if ( un ) *va_arg(ap, uintmax_t*) = uintmaxval;
+ else *va_arg(ap, intmax_t*) = intmaxval;
+ break;
+ case TYPE_PTR:
+ *va_arg(ap, void**) = (void*) (uintptr_t) uintmaxval;
+ break;
+ }
+ matched_items++;
+ }
+ else if ( *format == 's' || *format == '[' || *format == 'c' ||
+ *format == 'C' || *format == 'S' )
+ {
+ bool scanset[256];
+ bool string;
+ bool use_scanset;
+ switch ( *format++ )
+ {
+ case 'S':
+ if ( scan_type != TYPE_INT )
+ return errno = EINVAL, matched_items ? matched_items : EOF;
+ scan_type = TYPE_LONG;
+ /* fallthrough */
+ case 's': string = true; use_scanset = false; break;
+ case '[': string = true; use_scanset = true; break;
+ case 'C':
+ if ( scan_type != TYPE_INT )
+ return errno = EINVAL, matched_items ? matched_items : EOF;
+ scan_type = TYPE_LONG;
+ /* fallthrough */
+ case 'c': string = false; use_scanset = false; break;
+ default: __builtin_unreachable();
+ }
+ if ( use_scanset )
+ {
+ size_t offset = parse_scanset(scanset, format);
+ if ( format[offset] != ']' )
+ return errno = EINVAL, matched_items ? matched_items : EOF;
+ format += offset + 1;
+ }
+ if ( scan_type != TYPE_INT )
+ {
+#ifndef __is_sortix_libk
+ fprintf(stderr,
+ "%s:%u: error: scanf does not support wide strings\n",
+ __FILE__, __LINE__);
+#endif
+ return errno = EINVAL, matched_items ? matched_items : EOF;
+ }
+ if ( !field_width )
+ field_width = string ? SIZE_MAX : 1;
+ char** strptr = NULL;
+ char* str = NULL;
+ size_t strsize = 0;
+ if ( !discard )
+ {
+ if ( allocate )
+ {
+ strptr = va_arg(ap, char**);
+ strsize = 16;
+ str = (char*) malloc(strsize);
+ if ( !str )
+ return matched_items ? matched_items : EOF;
+ }
+ else
+ str = va_arg(ap, char*);
+ }
+ size_t i = 0;
+ while ( i < field_width )
+ {
+ ic = fgetc(fp);
+ if ( ic == EOF )
+ break;
+ bytes_parsed++;
+ if ( string && (use_scanset ? !scanset[ic] : isspace(ic)) )
+ {
+ if ( !use_scanset && !i )
+ continue;
+ ungetc(ic, fp);
+ bytes_parsed--;
+ break;
+ }
+ if ( !discard )
+ {
+ if ( allocate && i + string == strsize )
+ {
+ char* newstr = (char*) realloc(str, strsize * 2);
+ if ( !newstr )
+ {
+ free(str);
+ return matched_items ? matched_items : EOF;
+ }
+ str = newstr;
+ strsize *= 2;
+ }
+ str[i++] = (char) ic;
+ }
+ else
+ i++;
+ }
+ if ( string ? !i : i < field_width )
+ {
+ if ( !discard && allocate )
+ free(str);
+ return matched_items || i || ic != EOF ? matched_items : EOF;
+ }
+ if ( !discard )
+ {
+ if ( string )
+ str[i] = '\0';
+ if ( allocate )
+ {
+ char* newstr = realloc(str, i + string);
+ str = newstr ? newstr : str;
+ *strptr = str;
+ }
+ if ( string || i == field_width )
+ matched_items++;
+ }
+ }
+ else if ( *format == 'n' )
+ {
+ format++;
+ if ( allocate )
+ return errno = EINVAL, matched_items ? matched_items : EOF;
+ switch ( scan_type )
+ {
+ case TYPE_SHORTSHORT:
+ *va_arg(ap, signed char*) = bytes_parsed;
+ break;
+ case TYPE_SHORT:
+ *va_arg(ap, signed short*) = bytes_parsed;
+ break;
+ case TYPE_INT:
+ *va_arg(ap, signed int*) = bytes_parsed;
+ break;
+ case TYPE_LONG:
+ *va_arg(ap, signed long*) = bytes_parsed;
+ break;
+ case TYPE_LONGLONG:
+ *va_arg(ap, signed long long*) = bytes_parsed;
+ break;
+ case TYPE_PTRDIFF:
+ *va_arg(ap, ptrdiff_t*) = bytes_parsed;
+ break;
+ case TYPE_SIZE:
+ *va_arg(ap, ssize_t*) = bytes_parsed;
+ break;
+ case TYPE_MAX:
+ *va_arg(ap, intmax_t*) = bytes_parsed;
+ break;
+ case TYPE_PTR: __builtin_unreachable();
+ }
+ }
+ else if ( *format == 'a' || *format == 'A' ||
+ *format == 'e' || *format == 'E' ||
+ *format == 'f' || *format == 'F' ||
+ *format == 'g' || *format == 'G' )
+ {
+#ifndef __is_sortix_libk
+ fprintf(stderr, "%s:%u: error: scanf does not support \"%%%c\")\n",
+ __FILE__, __LINE__, *format);
+#endif
+ return errno = EINVAL, matched_items ? matched_items : EOF;
+ }
+ else
+ return errno = EINVAL, matched_items ? matched_items : EOF;
+ }
+ return matched_items;
+}
diff --git a/src/libc/vendor/sortix/vsscanf.c b/src/libc/vendor/sortix/vsscanf.c
new file mode 100644
index 0000000..d2388be
--- /dev/null
+++ b/src/libc/vendor/sortix/vsscanf.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, 2014 Jonas 'Sortie' Termansen.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * stdio/vsscanf.c
+ * Input format conversion.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+struct vsscanf_input
+{
+ union
+ {
+ const char* str;
+ const unsigned char* ustr;
+ };
+ size_t offset;
+};
+
+static int vsscanf_fgetc(void* fp)
+{
+ struct vsscanf_input* input = (struct vsscanf_input*) fp;
+ if ( !input->ustr[input->offset] )
+ return EOF;
+ return (int) input->ustr[input->offset++];
+}
+
+static int vsscanf_ungetc(int c, void* fp)
+{
+ struct vsscanf_input* input = (struct vsscanf_input*) fp;
+ if ( c == EOF && !input->ustr[input->offset] )
+ return c;
+ assert(input->offset);
+ input->offset--;
+ assert(input->ustr[input->offset] == (unsigned char) c);
+ return c;
+}
+
+int vsscanf(const char* str, const char* format, va_list ap)
+{
+ struct vsscanf_input input;
+ input.str = str;
+ input.offset = 0;
+ return vcbscanf(&input, vsscanf_fgetc, vsscanf_ungetc, format, ap);
+}
+
diff --git a/toolchain/binutils b/toolchain/binutils
-Subproject 2b7ad6882f6df005e946e69f9ca762d094640ab
+Subproject adfe55c88223d56525fb55aad09f8cd0c1719a6