From 4b46e0122596ab988ca1c2045464ad7c2af0358b Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Sun, 5 Aug 2007 11:36:16 -0500 Subject: [PATCH] Move some freestanding libc functions from the kernel into libfreestanding. --- include/c/std/string.h | 1 + kernel/Makefile | 2 + kernel/arch/x86/Makefile.final | 4 +- kernel/include/kern/libc.h | 17 +- kernel/lib/libc.cc | 537 ------------------------------ lib/Makefile | 5 +- lib/c/Makefile | 6 +- lib/c/Makefile.final | 1 + lib/c/freestanding/Makefile | 10 + lib/c/freestanding/Makefile.final | 8 + lib/c/freestanding/sprintf.c | 432 ++++++++++++++++++++++++ lib/c/freestanding/string.c | 140 ++++++++ 12 files changed, 608 insertions(+), 555 deletions(-) create mode 100644 lib/c/Makefile.final create mode 100644 lib/c/freestanding/Makefile create mode 100644 lib/c/freestanding/Makefile.final create mode 100644 lib/c/freestanding/sprintf.c create mode 100644 lib/c/freestanding/string.c diff --git a/include/c/std/string.h b/include/c/std/string.h index 31323c6..e457ed5 100644 --- a/include/c/std/string.h +++ b/include/c/std/string.h @@ -7,6 +7,7 @@ extern "C" { #endif +void *memcpy(void *dest, const void *src, size_t len); void *memset(void *block, int count, size_t len); int memcmp(const void *b1, const void *b2, size_t len); size_t strlen(const char *s); diff --git a/kernel/Makefile b/kernel/Makefile index 1086acf..8958415 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -19,6 +19,8 @@ include lib/Makefile include tests/Makefile TARGETS := $(BUILDDIR)/kernel $(BUILDDIR)/kernel.stripped +RAW_LIBS := c/libfreestanding.a +LIBS += $(RAW_LIBS:%=$(ARCHBUILDDIR)/user/lib/%) .PHONY: symlinks symlinks: diff --git a/kernel/arch/x86/Makefile.final b/kernel/arch/x86/Makefile.final index 2a4988d..34e87b8 100644 --- a/kernel/arch/x86/Makefile.final +++ b/kernel/arch/x86/Makefile.final @@ -1,10 +1,10 @@ DIR := arch/x86/ CXXFLAGS += -fno-omit-frame-pointer -march=i686 -$(BUILDDIR)/kernel: $(OBJS) $(DIR)linker-script +$(BUILDDIR)/kernel: $(OBJS) $(LIBS) $(DIR)linker-script @echo $(COMP): Linking kernel: $@ @$(MKDIR) $(dir $@) - @$(CXX) $(OBJS) -lgcc -lsupc++ -nostdlib -o "$@" -Wl,-T$(DIR)linker-script + @$(CXX) $(OBJS) $(LIBS) -lgcc -lsupc++ -nostdlib -o "$@" -Wl,-T$(DIR)linker-script # GRUB refuses to use the addresses in the multiboot header if it # finds a valid ELF header, so the dd hacks a zero into the high byte diff --git a/kernel/include/kern/libc.h b/kernel/include/kern/libc.h index 05f398b..1867a6d 100644 --- a/kernel/include/kern/libc.h +++ b/kernel/include/kern/libc.h @@ -4,16 +4,15 @@ #include #include -size_t vsnprintf(char *buf, size_t size, const char *str, va_list args); -size_t snprintf(char *buf, size_t size, const char *str, ...) -__attribute__((format(printf, 3, 4))); -size_t sprintf(char *buf, const char *str, ...) -__attribute__((format(printf, 2, 3))); -size_t printf(const char *str, ...) -__attribute__((format(printf, 1, 2))); - -// These are C-ABI so libgcc and libsupc++ can use them. extern "C" { + size_t vsnprintf(char *buf, size_t size, const char *str, va_list args); + size_t snprintf(char *buf, size_t size, const char *str, ...) + __attribute__((format(printf, 3, 4))); + size_t sprintf(char *buf, const char *str, ...) + __attribute__((format(printf, 2, 3))); + size_t printf(const char *str, ...) + __attribute__((format(printf, 1, 2))); + // FIXME: template/alignof versions void *memcpy(void *dest, const void *src, size_t len); void *memmove(void *dest, const void *src, size_t len); diff --git a/kernel/lib/libc.cc b/kernel/lib/libc.cc index 9c45857..6518988 100644 --- a/kernel/lib/libc.cc +++ b/kernel/lib/libc.cc @@ -20,533 +20,6 @@ #include #include -// FIXME: Move printf to lib/kernel - -static const int alt_form = 0x0001; -static const int zero_pad = 0x0002; -static const int neg_field = 0x0004; -static const int leave_blank = 0x0008; -static const int always_sign = 0x0010; -static const int group_thousands = 0x0020; // FIXME -- unimplemented -static const int long_arg = 0x0040; -static const int long_long_arg = 0x0080; -static const int short_arg = 0x0100; -static const int short_short_arg = 0x0200; -static const int intmax_arg = 0x0400; -static const int ptrdiff_arg = 0x0800; -static const int size_t_arg = 0x1000; -static const int capital_hex = 0x2000; -static const int num_signed = 0x4000; -static const int has_precision = 0x8000; - -static void printf_string(char *buf, size_t &opos, size_t limit, - const char *src, size_t len) -{ - if (opos < limit) { - size_t olen = opos + len <= limit ? len : limit - opos; - memcpy(buf + opos, src, olen); - } - - opos += len; -} - -static void printf_fill(char *buf, size_t &opos, size_t limit, - char ch, int len) -{ - if (opos < limit) { - size_t olen = opos + len <= limit ? len : limit - opos; - memset(buf + opos, ch, olen); - } - - opos += len; -} - -static void printf_num(char *obuf, size_t &opos, size_t limit, - s64 value, long radix, int fieldwidth, - int precision, int flags) -{ - char buf[65]; - int pos = 64; - int letter = (flags & capital_hex) ? 'A' - 10 : 'a' - 10; - u64 uval; - - if (flags & num_signed) - uval = value < 0 ? -value : value; - else - uval = value; - - // An explicit precision of 0 suppresses all output if the value - // is zero. Otherwise, the output size is not limited by precision - // or field width. - - if (uval != 0 || !(flags & has_precision) || precision != 0) do { - int ch = uval % radix; - - if (ch < 10) - buf[pos] = ch + '0'; - else - buf[pos] = ch + letter; - - uval /= radix; - pos--; - } while (uval); - - int len = 64 - pos; - - // length which counts against fieldwidth but not precision - int extralen = 0; - - if (flags & num_signed) { - if (value < 0) { - printf_fill(obuf, opos, limit, '-', 1); - extralen += 1; - } else if (flags & always_sign) { - printf_fill(obuf, opos, limit, '+', 1); - extralen += 1; - } else if (flags & leave_blank) { - printf_fill(obuf, opos, limit, ' ', 1); - extralen += 1; - } - } - - if ((flags & alt_form) && value != 0) { - if (radix == 8 && (!(flags & has_precision) || precision <= len)) { - flags |= has_precision; - precision = len + 1; - } - - if (radix == 16) { - printf_string(obuf, opos, limit, "0x", 2); - extralen += 2; - } - } - - if ((flags & has_precision) && len < precision) { - precision -= len; - len += precision; - } else { - precision = 0; - } - - len += extralen; - - if (!(flags & neg_field) && len < fieldwidth) { - char padchar = (flags & zero_pad) ? '0' : ' '; - printf_fill(obuf, opos, limit, padchar, fieldwidth - len); - len = fieldwidth; - } - - if (precision != 0) { - printf_fill(obuf, opos, limit, '0', precision); - len += precision; - } - - printf_string(obuf, opos, limit, buf + pos + 1, 64 - pos); - - if ((flags & neg_field) && len < fieldwidth) - printf_fill(obuf, opos, limit, ' ', fieldwidth - len); -} - -size_t vsnprintf(char *buf, size_t size, const char *str, va_list args) -{ - size_t opos = 0; // position in the output string - unsigned int flags = 0; - int radix = 10; - int state = 0; - int fieldwidth = 0; - int precision = 0; - - for (size_t pos = 0; str[pos]; pos++) switch (state) { - case 0: - if (str[pos] == '%') { - flags = 0; - radix = 10; - state = 1; - fieldwidth = 0; - precision = 0; - break; - } - - if (opos < size) - buf[opos] = str[pos]; - - opos++; - break; - - case 1: // A percent has been seen; read in format characters - switch (str[pos]) { - case '#': - flags |= alt_form; - break; - - case '0': - if (!(flags & has_precision)) { - flags |= zero_pad; - break; - } - - // else fall through - - case '1' ... '9': - if (flags & has_precision) - goto default_case; - - do { - fieldwidth *= 10; - fieldwidth += str[pos++] - '0'; - } while (str[pos] >= '0' && str[pos] <= '9'); - - pos--; - break; - - case '*': - if (fieldwidth || (flags & has_precision)) - goto default_case; - - fieldwidth = va_arg(args, int); - break; - - case '.': - flags |= has_precision; - - if (str[pos + 1] == '*') { - pos++; - precision = va_arg(args, int); - } else while (str[pos + 1] >= '0' && str[pos + 1] <= '9') { - precision *= 10; - precision += str[++pos] - '0'; - } - - break; - - case '-': - flags |= neg_field; - break; - - case ' ': - flags |= leave_blank; - break; - - case '+': - flags |= always_sign; - break; - - case '\'': - flags |= group_thousands; - break; - - case 'l': - if (flags & long_arg) - flags |= long_long_arg; - else - flags |= long_arg; - - break; - - case 'h': - if (flags & long_arg) - flags |= short_short_arg; - else - flags |= short_arg; - - break; - - case 'j': - flags |= intmax_arg; - break; - - case 't': - flags |= ptrdiff_arg; - break; - - // Note that %z and other such "new" format characters are - // basically useless because some GCC coder actually went out - // of their way to make the compiler reject C99 format - // strings in C++ code, with no way of overriding it that I - // can find (the source code comments suggest the checking is - // only when using -pedantic, but I wasn't using -pedantic). - // - // Thus, we have the choice of either avoiding %z and friends - // (and possibly needing to insert hackish casts to silence - // the compiler's warnings if different architectures define - // types like size_t in different ways), or not using the - // format warnings at all. - // - // To mitigate this, 32-bit architectures should define - // pointer-sized special types as "long" rather than "int", - // so that %lx/%ld can always be used with them. Fixed-size - // 32-bit types should be declared as "int" rather than - // "long" for the same reason. - - case 'z': - flags |= size_t_arg; - break; - - case 'd': - case 'i': { - s64 arg; - - if ((flags & intmax_arg) || (flags & long_long_arg)) - arg = va_arg(args, long long); - else if (flags & size_t_arg) - arg = va_arg(args, ssize_t); - else if (flags & ptrdiff_arg) - arg = va_arg(args, ptrdiff_t); - else if (flags & long_arg) - arg = va_arg(args, long); - else if (flags & short_short_arg) - arg = (signed char)va_arg(args, int); - else if (flags & short_arg) - arg = (short)va_arg(args, int); - else - arg = va_arg(args, int); - - flags |= num_signed; - printf_num(buf, opos, size, arg, 10, - fieldwidth, precision, flags); - state = 0; - break; - } - - case 'X': - flags |= capital_hex; - // fall-through - - case 'x': - radix = 18; - // fall-through - - case 'o': - radix -= 2; - // fall-through - - case 'u': { - u64 arg; - - if ((flags & intmax_arg) || (flags & long_long_arg)) - arg = va_arg(args, unsigned long long); - else if (flags & size_t_arg) - arg = va_arg(args, size_t); - else if (flags & ptrdiff_arg) - arg = va_arg(args, intptr_t); - else if (flags & long_arg) - arg = va_arg(args, unsigned long); - else if (flags & short_short_arg) - arg = (unsigned char)va_arg(args, unsigned int); - else if (flags & short_arg) - arg = (unsigned short)va_arg(args, unsigned int); - else if (flags & short_short_arg) - arg = (signed char)va_arg(args, int); - else if (flags & short_arg) - arg = (short)va_arg(args, int); - else - arg = va_arg(args, unsigned int); - - printf_num(buf, opos, size, arg, radix, - fieldwidth, precision, flags); - state = 0; - break; - } - - case 'c': - if (opos < size) - buf[opos] = va_arg(args, int); - - opos++; - state = 0; - break; - - case 's': { - const char *arg = va_arg(args, const char *); - - if (!arg) - arg = "(null)"; - - size_t len = strlen(arg); - printf_string(buf, opos, size, arg, len); - state = 0; - break; - } - - case 'p': { - const void *arg = va_arg(args, const void *); - - printf_num(buf, opos, size, (ulong)arg, 16, - fieldwidth, precision, flags); - - state = 0; - break; - } - - case 'n': { - if ((flags & intmax_arg) || (flags & long_long_arg)) - *va_arg(args, unsigned long long *) = opos; - else if (flags & size_t_arg) - *va_arg(args, ssize_t *) = opos; - else if (flags & ptrdiff_arg) - *va_arg(args, ptrdiff_t *) = opos; - else if (flags & long_arg) - *va_arg(args, long *) = opos; - else if (flags & short_short_arg) - *va_arg(args, signed char *) = opos; - else if (flags & short_arg) - *va_arg(args, short *) = opos; - else - *va_arg(args, int *) = opos; - - state = 0; - break; - } - - default_case: // label for goto - default: - if (opos < size) - buf[opos] = str[pos]; - - opos++; - state = 0; - break; - } - } - - if (size > 0 && opos >= size) - buf[size - 1] = 0; - - return opos; -} - -size_t snprintf(char *buf, size_t size, const char *str, ...) -{ - va_list args; - va_start(args, str); - int ret = vsnprintf(buf, size, str, args); - va_end(args); - return ret; -} - -size_t sprintf(char *buf, const char *str, ...) -{ - va_list args; - va_start(args, str); - int ret = vsnprintf(buf, ULONG_MAX, str, args); - va_end(args); - return ret; -} - -void *memcpy(void *dest, const void *src, size_t len) -{ - const char *cs = static_cast(src); - char *cd = static_cast(dest); - - for (size_t i = 0; i < len; i++) - cd[i] = cs[i]; - - return dest; -} - -void *memmove(void *dest, const void *src, size_t len) -{ - if (dest < src) - return memcpy(dest, src, len); - - const char *cs = static_cast(src); - char *cd = static_cast(dest); - - for (size_t i = len - 1; i >= 0; i--) - cd[i] = cs[i]; - - return dest; -} - -int memcmp(const void *b1, const void *b2, size_t len) -{ - size_t pos; - const char *c1 = static_cast(b1); - const char *c2 = static_cast(b2); - - for (pos = 0; pos < len; pos++) { - if (c1[pos] != c2[pos]) - return c1[pos] - c2[pos]; - - pos++; - } - - return 0; -} - -size_t strnlen(const char *s, size_t n) -{ - size_t pos = 0; - while (pos < n && *s++) - pos++; - return pos; -} - -size_t strlen(const char *s) -{ - size_t pos = 0; - while (*s++) - pos++; - return pos; -} - -char *strcpy(char *dest, const char *src) -{ - char *orig = dest; - - do { - *dest = *src++; - } while (*dest++); - - return orig; -} - -char *strncpy(char *dest, const char *src, size_t len) -{ - char *orig = dest; - - while (len--) { - *dest = *src++; - - if (!*dest++) - break; - } - - bzero(dest, len); - return orig; -} - -char *strcat(char *dest, const char *src) -{ - char *orig = dest; - dest += strlen(dest); - - do { - *dest = *src++; - } while (*dest++); - - return orig; -} - -char *strncat(char *dest, const char *src, size_t len) -{ - char *orig = dest; - int orig_len = strlen(dest); - - len -= orig_len; - dest += orig_len; - - while (len--) { - *dest = *src++; - - if (!*dest++) - break; - } - - bzero(dest, len); - return orig; -} - void bzero(void *b, size_t len) { char *c = static_cast(b); @@ -555,16 +28,6 @@ void bzero(void *b, size_t len) *c++ = 0; } -void *memset(void *b, int ch, size_t len) -{ - char *c = static_cast(b); - - while (len--) - *c++ = ch; - - return b; -} - #include // Temporary hack until slab allocator is added diff --git a/lib/Makefile b/lib/Makefile index 2e2b32f..2f684ed 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -2,11 +2,12 @@ TOP := $(shell dirname `pwd -P`) COMP := lib include ../Makefile.head +TARGETS := objs + include c++/Makefile include c/Makefile -TARGETS := objs - include ../Makefile.tail +include c/Makefile.final objs: $(OBJS) diff --git a/lib/c/Makefile b/lib/c/Makefile index f61d9aa..8b8725c 100644 --- a/lib/c/Makefile +++ b/lib/c/Makefile @@ -1,5 +1 @@ -DIR := c/ -DIRS += $(DIR) - -RAW_CXXFILES := -CXXFILES += $(RAW_CXXFILES:%=$(DIR)%) +include c/freestanding/Makefile diff --git a/lib/c/Makefile.final b/lib/c/Makefile.final new file mode 100644 index 0000000..acf7e4d --- /dev/null +++ b/lib/c/Makefile.final @@ -0,0 +1 @@ +include c/freestanding/Makefile.final diff --git a/lib/c/freestanding/Makefile b/lib/c/freestanding/Makefile new file mode 100644 index 0000000..0257e46 --- /dev/null +++ b/lib/c/freestanding/Makefile @@ -0,0 +1,10 @@ +DIR := c/freestanding/ +DIRS += $(DIR) + +RAW_CFILES := sprintf string +CFILES += $(RAW_CFILES:%=$(DIR)%) + +RAW_CXXFILES := +CXXFILES += $(RAW_CXXFILES:%=$(DIR)%) + +TARGETS += $(BUILDDIR)/c/libfreestanding.a diff --git a/lib/c/freestanding/Makefile.final b/lib/c/freestanding/Makefile.final new file mode 100644 index 0000000..d8106f9 --- /dev/null +++ b/lib/c/freestanding/Makefile.final @@ -0,0 +1,8 @@ +MYOBJS := $(filter $(BUILDDIR)/c/freestanding/%,$(OBJS)) + +$(BUILDDIR)/c/libfreestanding.a: $(MYOBJS) + @echo $(COMP): Linking c/libfreestanding.a + @$(MKDIR) $(dir $@) + @$(RM) $@ + @$(AR) rc $@ $(MYOBJS) + @$(RANLIB) $@ diff --git a/lib/c/freestanding/sprintf.c b/lib/c/freestanding/sprintf.c new file mode 100644 index 0000000..a7aa115 --- /dev/null +++ b/lib/c/freestanding/sprintf.c @@ -0,0 +1,432 @@ +// sprintf() and related functions. +// +// This software is copyright (c) 2007 Scott Wood . +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors or contributors be held liable for any damages +// arising from the use of this software. +// +// Permission is hereby granted to everyone, free of charge, to use, copy, +// modify, prepare derivative works of, publish, distribute, perform, +// sublicense, and/or sell copies of the Software, provided that the above +// copyright notice and disclaimer of warranty be included in all copies or +// substantial portions of this software. + +#include +#include +#include +#include + +enum { + alt_form = 0x0001, + zero_pad = 0x0002, + neg_field = 0x0004, + leave_blank = 0x0008, + always_sign = 0x0010, + group_thousands = 0x0020, // FIXME -- unimplemented + long_arg = 0x0040, + long_long_arg = 0x0080, + short_arg = 0x0100, + short_short_arg = 0x0200, + intmax_arg = 0x0400, + ptrdiff_arg = 0x0800, + size_t_arg = 0x1000, + capital_hex = 0x2000, + num_signed = 0x4000, + has_precision = 0x8000, +}; + +static void printf_string(char *buf, size_t &opos, size_t limit, + const char *src, size_t len) +{ + if (opos < limit) { + size_t olen = opos + len <= limit ? len : limit - opos; + memcpy(buf + opos, src, olen); + } + + opos += len; +} + +static void printf_fill(char *buf, size_t &opos, size_t limit, + char ch, int len) +{ + if (opos < limit) { + size_t olen = opos + len <= limit ? len : limit - opos; + memset(buf + opos, ch, olen); + } + + opos += len; +} + +static void printf_num(char *obuf, size_t &opos, size_t limit, + int64_t value, long radix, int fieldwidth, + int precision, int flags) +{ + char buf[65]; + int pos = 64; + int letter = (flags & capital_hex) ? 'A' - 10 : 'a' - 10; + uint64_t uval; + + if (flags & num_signed) + uval = value < 0 ? -value : value; + else + uval = value; + + // An explicit precision of 0 suppresses all output if the value + // is zero. Otherwise, the output size is not limited by precision + // or field width. + + if (uval != 0 || !(flags & has_precision) || precision != 0) do { + int ch = uval % radix; + + if (ch < 10) + buf[pos] = ch + '0'; + else + buf[pos] = ch + letter; + + uval /= radix; + pos--; + } while (uval); + + int len = 64 - pos; + + // length which counts against fieldwidth but not precision + int extralen = 0; + + if (flags & num_signed) { + if (value < 0) { + printf_fill(obuf, opos, limit, '-', 1); + extralen += 1; + } else if (flags & always_sign) { + printf_fill(obuf, opos, limit, '+', 1); + extralen += 1; + } else if (flags & leave_blank) { + printf_fill(obuf, opos, limit, ' ', 1); + extralen += 1; + } + } + + if ((flags & alt_form) && value != 0) { + if (radix == 8 && (!(flags & has_precision) || precision <= len)) { + flags |= has_precision; + precision = len + 1; + } + + if (radix == 16) { + printf_string(obuf, opos, limit, "0x", 2); + extralen += 2; + } + } + + if ((flags & has_precision) && len < precision) { + precision -= len; + len += precision; + } else { + precision = 0; + } + + len += extralen; + + if (!(flags & neg_field) && len < fieldwidth) { + char padchar = (flags & zero_pad) ? '0' : ' '; + printf_fill(obuf, opos, limit, padchar, fieldwidth - len); + len = fieldwidth; + } + + if (precision != 0) { + printf_fill(obuf, opos, limit, '0', precision); + len += precision; + } + + printf_string(obuf, opos, limit, buf + pos + 1, 64 - pos); + + if ((flags & neg_field) && len < fieldwidth) + printf_fill(obuf, opos, limit, ' ', fieldwidth - len); +} + +extern "C" size_t vsnprintf(char *buf, size_t size, + const char *str, va_list args) +{ + size_t opos = 0; // position in the output string + unsigned int flags = 0; + int radix = 10; + int state = 0; + int fieldwidth = 0; + int precision = 0; + + for (size_t pos = 0; str[pos]; pos++) switch (state) { + case 0: + if (str[pos] == '%') { + flags = 0; + radix = 10; + state = 1; + fieldwidth = 0; + precision = 0; + break; + } + + if (opos < size) + buf[opos] = str[pos]; + + opos++; + break; + + case 1: // A percent has been seen; read in format characters + switch (str[pos]) { + case '#': + flags |= alt_form; + break; + + case '0': + if (!(flags & has_precision)) { + flags |= zero_pad; + break; + } + + // else fall through + + case '1' ... '9': + if (flags & has_precision) + goto default_case; + + do { + fieldwidth *= 10; + fieldwidth += str[pos++] - '0'; + } while (str[pos] >= '0' && str[pos] <= '9'); + + pos--; + break; + + case '*': + if (fieldwidth || (flags & has_precision)) + goto default_case; + + fieldwidth = va_arg(args, int); + break; + + case '.': + flags |= has_precision; + + if (str[pos + 1] == '*') { + pos++; + precision = va_arg(args, int); + } else while (str[pos + 1] >= '0' && str[pos + 1] <= '9') { + precision *= 10; + precision += str[++pos] - '0'; + } + + break; + + case '-': + flags |= neg_field; + break; + + case ' ': + flags |= leave_blank; + break; + + case '+': + flags |= always_sign; + break; + + case '\'': + flags |= group_thousands; + break; + + case 'l': + if (flags & long_arg) + flags |= long_long_arg; + else + flags |= long_arg; + + break; + + case 'h': + if (flags & long_arg) + flags |= short_short_arg; + else + flags |= short_arg; + + break; + + case 'j': + flags |= intmax_arg; + break; + + case 't': + flags |= ptrdiff_arg; + break; + + // Note that %z and other such "new" format characters are + // basically useless because some GCC coder actually went out + // of their way to make the compiler reject C99 format + // strings in C++ code, with no way of overriding it that I + // can find (the source code comments suggest the checking is + // only when using -pedantic, but I wasn't using -pedantic). + // + // Thus, we have the choice of either avoiding %z and friends + // (and possibly needing to insert hackish casts to silence + // the compiler's warnings if different architectures define + // types like size_t in different ways), or not using the + // format warnings at all. + // + // To mitigate this, 32-bit architectures should define + // pointer-sized special types as "long" rather than "int", + // so that %lx/%ld can always be used with them. Fixed-size + // 32-bit types should be declared as "int" rather than + // "long" for the same reason. + + case 'z': + flags |= size_t_arg; + break; + + case 'd': + case 'i': { + int64_t arg; + + if ((flags & intmax_arg) || (flags & long_long_arg)) + arg = va_arg(args, long long); + else if (flags & size_t_arg) + arg = va_arg(args, ssize_t); + else if (flags & ptrdiff_arg) + arg = va_arg(args, ptrdiff_t); + else if (flags & long_arg) + arg = va_arg(args, long); + else if (flags & short_short_arg) + arg = (signed char)va_arg(args, int); + else if (flags & short_arg) + arg = (short)va_arg(args, int); + else + arg = va_arg(args, int); + + flags |= num_signed; + printf_num(buf, opos, size, arg, 10, + fieldwidth, precision, flags); + state = 0; + break; + } + + case 'X': + flags |= capital_hex; + // fall-through + + case 'x': + radix = 18; + // fall-through + + case 'o': + radix -= 2; + // fall-through + + case 'u': { + uint64_t arg; + + if ((flags & intmax_arg) || (flags & long_long_arg)) + arg = va_arg(args, unsigned long long); + else if (flags & size_t_arg) + arg = va_arg(args, size_t); + else if (flags & ptrdiff_arg) + arg = va_arg(args, intptr_t); + else if (flags & long_arg) + arg = va_arg(args, unsigned long); + else if (flags & short_short_arg) + arg = (unsigned char)va_arg(args, unsigned int); + else if (flags & short_arg) + arg = (unsigned short)va_arg(args, unsigned int); + else if (flags & short_short_arg) + arg = (signed char)va_arg(args, int); + else if (flags & short_arg) + arg = (short)va_arg(args, int); + else + arg = va_arg(args, unsigned int); + + printf_num(buf, opos, size, arg, radix, + fieldwidth, precision, flags); + state = 0; + break; + } + + case 'c': + if (opos < size) + buf[opos] = va_arg(args, int); + + opos++; + state = 0; + break; + + case 's': { + const char *arg = va_arg(args, const char *); + + if (!arg) + arg = "(null)"; + + size_t len = strlen(arg); + printf_string(buf, opos, size, arg, len); + state = 0; + break; + } + + case 'p': { + const void *arg = va_arg(args, const void *); + + printf_num(buf, opos, size, (unsigned long)arg, 16, + fieldwidth, precision, flags); + + state = 0; + break; + } + + case 'n': { + if ((flags & intmax_arg) || (flags & long_long_arg)) + *va_arg(args, unsigned long long *) = opos; + else if (flags & size_t_arg) + *va_arg(args, ssize_t *) = opos; + else if (flags & ptrdiff_arg) + *va_arg(args, ptrdiff_t *) = opos; + else if (flags & long_arg) + *va_arg(args, long *) = opos; + else if (flags & short_short_arg) + *va_arg(args, signed char *) = opos; + else if (flags & short_arg) + *va_arg(args, short *) = opos; + else + *va_arg(args, int *) = opos; + + state = 0; + break; + } + + default_case: // label for goto + default: + if (opos < size) + buf[opos] = str[pos]; + + opos++; + state = 0; + break; + } + } + + if (size > 0 && opos >= size) + buf[size - 1] = 0; + + return opos; +} + +extern "C" size_t snprintf(char *buf, size_t size, const char *str, ...) +{ + va_list args; + va_start(args, str); + int ret = vsnprintf(buf, size, str, args); + va_end(args); + return ret; +} + +extern "C" size_t sprintf(char *buf, const char *str, ...) +{ + va_list args; + va_start(args, str); + int ret = vsnprintf(buf, ULONG_MAX, str, args); + va_end(args); + return ret; +} diff --git a/lib/c/freestanding/string.c b/lib/c/freestanding/string.c new file mode 100644 index 0000000..a3bb62e --- /dev/null +++ b/lib/c/freestanding/string.c @@ -0,0 +1,140 @@ +// string and memory functions. +// +// This software is copyright (c) 2007 Scott Wood . +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors or contributors be held liable for any damages +// arising from the use of this software. +// +// Permission is hereby granted to everyone, free of charge, to use, copy, +// modify, prepare derivative works of, publish, distribute, perform, +// sublicense, and/or sell copies of the Software, provided that the above +// copyright notice and disclaimer of warranty be included in all copies or +// substantial portions of this software. + +#include +#include + +void *memcpy(void *dest, const void *src, size_t len) +{ + const char *cs = static_cast(src); + char *cd = static_cast(dest); + + for (size_t i = 0; i < len; i++) + cd[i] = cs[i]; + + return dest; +} + +void *memmove(void *dest, const void *src, size_t len) +{ + if (dest < src) + return memcpy(dest, src, len); + + const char *cs = static_cast(src); + char *cd = static_cast(dest); + + for (size_t i = len - 1; i >= 0; i--) + cd[i] = cs[i]; + + return dest; +} + +int memcmp(const void *b1, const void *b2, size_t len) +{ + size_t pos; + const char *c1 = static_cast(b1); + const char *c2 = static_cast(b2); + + for (pos = 0; pos < len; pos++) { + if (c1[pos] != c2[pos]) + return c1[pos] - c2[pos]; + + pos++; + } + + return 0; +} + +size_t strnlen(const char *s, size_t n) +{ + size_t pos = 0; + while (pos < n && *s++) + pos++; + return pos; +} + +size_t strlen(const char *s) +{ + size_t pos = 0; + while (*s++) + pos++; + return pos; +} + +char *strcpy(char *dest, const char *src) +{ + char *orig = dest; + + do { + *dest = *src++; + } while (*dest++); + + return orig; +} + +char *strncpy(char *dest, const char *src, size_t len) +{ + char *orig = dest; + + while (len--) { + *dest = *src++; + + if (!*dest++) + break; + } + + memset(dest, 0, len); + return orig; +} + +char *strcat(char *dest, const char *src) +{ + char *orig = dest; + dest += strlen(dest); + + do { + *dest = *src++; + } while (*dest++); + + return orig; +} + +char *strncat(char *dest, const char *src, size_t len) +{ + char *orig = dest; + int orig_len = strlen(dest); + + len -= orig_len; + dest += orig_len; + + while (len--) { + *dest = *src++; + + if (!*dest++) + break; + } + + memset(dest, 0, len); + return orig; +} + +void *memset(void *b, int ch, size_t len) +{ + char *c = static_cast(b); + + while (len--) + *c++ = ch; + + return b; +} -- 2.39.2