]> git.buserror.net Git - polintos/scott/priv.git/commitdiff
Move some freestanding libc functions from the kernel into
authorScott Wood <scott@thor.buserror.net>
Sun, 5 Aug 2007 16:36:16 +0000 (11:36 -0500)
committerScott Wood <scott@thor.buserror.net>
Sun, 5 Aug 2007 16:36:16 +0000 (11:36 -0500)
libfreestanding.

12 files changed:
include/c/std/string.h
kernel/Makefile
kernel/arch/x86/Makefile.final
kernel/include/kern/libc.h
kernel/lib/libc.cc
lib/Makefile
lib/c/Makefile
lib/c/Makefile.final [new file with mode: 0644]
lib/c/freestanding/Makefile [new file with mode: 0644]
lib/c/freestanding/Makefile.final [new file with mode: 0644]
lib/c/freestanding/sprintf.c [new file with mode: 0644]
lib/c/freestanding/string.c [new file with mode: 0644]

index 31323c6419fc782c4fe6dddd0b309de7229e0507..e457ed59081462f325115a095cc452e778549eee 100644 (file)
@@ -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);
index 1086acf01badbdd6aca462115a6cea2ceac481e3..895841503a5a8405c74ba629f5735ec522ec1b2b 100644 (file)
@@ -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:
index 2a4988d3a617f78c47c4d2177802fe85dc69dbb1..34e87b8da2b4b05e40e2da025c0119630df299bb 100644 (file)
@@ -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
index 05f398b8d9ee837fda7c4232412a3eb2dfb55eaa..1867a6da9d9602f79e909aec21f4fb837b4b6bd1 100644 (file)
@@ -4,16 +4,15 @@
 #include <kern/types.h>
 #include <stdarg.h>
 
-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);
index 9c458572db35724d5b833e48a4e1b45b9d4dc33f..6518988b7b2538fe3acb510fa711f608dfabc2ed 100644 (file)
 #include <stdarg.h>
 #include <limits.h>
 
-// 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<const char *>(src);
-       char *cd = static_cast<char *>(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<const char *>(src);
-       char *cd = static_cast<char *>(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<const char *>(b1);
-       const char *c2 = static_cast<const char *>(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<char *>(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<char *>(b);
-       
-       while (len--)
-               *c++ = ch;
-
-       return b;
-}
-
 #include <kern/pagealloc.h>
 
 // Temporary hack until slab allocator is added
index 2e2b32f46fe0189e914304b327070ea314de4412..2f684ed6f7e6793c242ba16e43c23348366b23d1 100644 (file)
@@ -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)
index f61d9aaf19f7ae9c351ab22bdbeb60e3fd6fd53b..8b8725cd4408456c3f78df28d0ca4e26faebf4ae 100644 (file)
@@ -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 (file)
index 0000000..acf7e4d
--- /dev/null
@@ -0,0 +1 @@
+include c/freestanding/Makefile.final
diff --git a/lib/c/freestanding/Makefile b/lib/c/freestanding/Makefile
new file mode 100644 (file)
index 0000000..0257e46
--- /dev/null
@@ -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 (file)
index 0000000..d8106f9
--- /dev/null
@@ -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 (file)
index 0000000..a7aa115
--- /dev/null
@@ -0,0 +1,432 @@
+// sprintf() and related functions.
+//
+// This software is copyright (c) 2007 Scott Wood <scott@buserror.net>.
+// 
+// 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 <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+
+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 (file)
index 0000000..a3bb62e
--- /dev/null
@@ -0,0 +1,140 @@
+// string and memory functions.
+//
+// This software is copyright (c) 2007 Scott Wood <scott@buserror.net>.
+// 
+// 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 <stdint.h>
+#include <string.h>
+
+void *memcpy(void *dest, const void *src, size_t len)
+{
+       const char *cs = static_cast<const char *>(src);
+       char *cd = static_cast<char *>(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<const char *>(src);
+       char *cd = static_cast<char *>(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<const char *>(b1);
+       const char *c2 = static_cast<const char *>(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<char *>(b);
+       
+       while (len--)
+               *c++ = ch;
+
+       return b;
+}