aboutsummaryrefslogtreecommitdiff
path: root/user/lib/libc
diff options
context:
space:
mode:
authornthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
committernthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
commitc63f340d90800895f007de64b7d2d14624263331 (patch)
tree2c0849fa597dd6da831c8707b6f2603403778d7b /user/lib/libc
Created student weenix repository
Diffstat (limited to 'user/lib/libc')
-rw-r--r--user/lib/libc/entry.S17
-rw-r--r--user/lib/libc/errno.c3
-rw-r--r--user/lib/libc/malloc.c1326
-rw-r--r--user/lib/libc/printf.c169
-rw-r--r--user/lib/libc/quad.c414
-rw-r--r--user/lib/libc/rand.c22
-rw-r--r--user/lib/libc/scanf.c73
-rw-r--r--user/lib/libc/stream.c11
-rw-r--r--user/lib/libc/string.c509
-rw-r--r--user/lib/libc/strtol.c162
-rw-r--r--user/lib/libc/syscall.c391
-rw-r--r--user/lib/libc/vsnprintf.c566
-rw-r--r--user/lib/libc/vsscanf.c447
13 files changed, 4110 insertions, 0 deletions
diff --git a/user/lib/libc/entry.S b/user/lib/libc/entry.S
new file mode 100644
index 0000000..e642fe4
--- /dev/null
+++ b/user/lib/libc/entry.S
@@ -0,0 +1,17 @@
+#ifndef __DYNAMIC__
+
+.globl __libc_static_entry
+
+__libc_static_entry:
+ add $8, %rsp /* Make sure when we overwrite dummy return address
+ with the correct one, so args will be in the right
+ place when we call main */
+ movq (%rsp), %rdi /* Copy arguments from stack into registers */
+ movq 8(%rsp), %rsi
+ movq 16(%rsp), %rdx
+ movq 24(%rsp), %rcx
+ call main
+ mov %rax, %rdi
+ call exit
+
+#endif
diff --git a/user/lib/libc/errno.c b/user/lib/libc/errno.c
new file mode 100644
index 0000000..c3d6594
--- /dev/null
+++ b/user/lib/libc/errno.c
@@ -0,0 +1,3 @@
+#include <errno.h>
+
+int _libc_errno;
diff --git a/user/lib/libc/malloc.c b/user/lib/libc/malloc.c
new file mode 100644
index 0000000..3498dc8
--- /dev/null
+++ b/user/lib/libc/malloc.c
@@ -0,0 +1,1326 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ */
+#include "fcntl.h"
+#include "stddef.h"
+#include "stdlib.h"
+#include "string.h"
+#include "sys/mman.h"
+#include "sys/types.h"
+#include "unistd.h"
+
+#define __inline__ inline
+
+/* Necessary BSD types and functions */
+typedef unsigned short u_short;
+typedef unsigned char u_char;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+#define _open open
+#define _write write
+
+/*
+ * Defining EXTRA_SANITY will enable extra checks which are related
+ * to internal conditions and consistency in malloc.c. This has a
+ * noticeable runtime performance hit, and generally will not do you
+ * any good unless you fiddle with the internals of malloc or want
+ * to catch random pointer corruption as early as possible.
+ */
+#ifndef MALLOC_EXTRA_SANITY
+#undef MALLOC_EXTRA_SANITY
+#endif
+
+/*
+ * What to use for Junk. This is the byte value we use to fill with
+ * when the 'J' option is enabled.
+ */
+#define SOME_JUNK 0xd0 /* as in "Duh" :-) */
+
+/*
+ * The basic parameters you can tweak.
+ *
+ * malloc_pageshift pagesize = 1 << malloc_pageshift
+ * It's probably best if this is the native
+ * page size, but it doesn't have to be.
+ *
+ * malloc_minsize minimum size of an allocation in bytes.
+ * If this is too small it's too much work
+ * to manage them. This is also the smallest
+ * unit of alignment used for the storage
+ * returned by malloc/realloc.
+ *
+ */
+#define malloc_pageshift 12U
+#define malloc_minsize 16U
+
+static int fdzero;
+#define MMAP_FD fdzero
+#define INIT_MMAP() \
+ { \
+ if ((fdzero = _open("/dev/zero", O_RDWR, 0000)) == -1) \
+ wrterror("open of /dev/zero"); \
+ }
+#define MADV_FREE MADV_DONTNEED
+
+/*
+ * No user serviceable parts behind this point.
+ */
+
+/*
+ * This structure describes a page worth of chunks.
+ */
+
+struct pginfo
+{
+ struct pginfo *next; /* next on the free list */
+ void *page; /* Pointer to the page */
+ u_short size; /* size of this page's chunks */
+ u_short shift; /* How far to shift for this size chunks */
+ u_short free; /* How many free chunks */
+ u_short total; /* How many chunk */
+ u_int bits[1]; /* Which chunks are free */
+};
+
+/*
+ * This structure describes a number of free pages.
+ */
+
+struct pgfree
+{
+ struct pgfree *next; /* next run of free pages */
+ struct pgfree *prev; /* prev run of free pages */
+ void *page; /* pointer to free pages */
+ void *end; /* pointer to end of free pages */
+ size_t size; /* number of bytes free */
+};
+
+/*
+ * How many bits per u_int in the bitmap.
+ * Change only if not 8 bits/byte
+ */
+#define MALLOC_BITS (8 * sizeof(u_int))
+
+/*
+ * Magic values to put in the page_directory
+ */
+#define MALLOC_NOT_MINE ((struct pginfo *)0)
+#define MALLOC_FREE ((struct pginfo *)1)
+#define MALLOC_FIRST ((struct pginfo *)2)
+#define MALLOC_FOLLOW ((struct pginfo *)3)
+#define MALLOC_MAGIC ((struct pginfo *)4)
+
+#ifndef malloc_pageshift
+#define malloc_pageshift 12U
+#endif
+
+#ifndef malloc_minsize
+#define malloc_minsize 16U
+#endif
+
+#if !defined(malloc_pagesize)
+#define malloc_pagesize (1UL << malloc_pageshift)
+#endif
+
+#if ((1 << malloc_pageshift) != malloc_pagesize)
+#error "(1<<malloc_pageshift) != malloc_pagesize"
+#endif
+
+#ifndef malloc_maxsize
+#define malloc_maxsize ((malloc_pagesize) >> 1)
+#endif
+
+/* A mask for the offset inside a page. */
+#define malloc_pagemask ((malloc_pagesize)-1)
+
+#define pageround(foo) (((foo) + (malloc_pagemask)) & (~(malloc_pagemask)))
+#define ptr2index(foo) (((u_long)(foo) >> malloc_pageshift) - malloc_origo)
+
+#ifndef THREAD_LOCK
+#define THREAD_LOCK()
+#endif
+
+#ifndef THREAD_UNLOCK
+#define THREAD_UNLOCK()
+#endif
+
+#ifndef MMAP_FD
+#define MMAP_FD (-1)
+#endif
+
+#ifndef INIT_MMAP
+#define INIT_MMAP()
+#endif
+
+/* Set when initialization has been done */
+static unsigned malloc_started;
+
+/* Recusion flag for public interface. */
+static int malloc_active;
+
+/* Number of free pages we cache */
+static unsigned malloc_cache = 16;
+
+/* The offset from pagenumber to index into the page directory */
+static u_long malloc_origo;
+
+/* The last index in the page directory we care about */
+static u_long last_index;
+
+/* Pointer to page directory. Allocated "as if with" malloc */
+static struct pginfo **page_dir;
+
+/* How many slots in the page directory */
+static unsigned malloc_ninfo;
+
+/* Free pages line up here */
+static struct pgfree free_list;
+
+/* Abort(), user doesn't handle problems. */
+static int malloc_abort;
+
+/* Are we trying to die ? */
+static int suicide;
+
+/* always realloc ? */
+static int malloc_realloc;
+
+/* pass the kernel a hint on free pages ? */
+static int malloc_hint = 0;
+
+/* xmalloc behaviour ? */
+static int malloc_xmalloc;
+
+/* sysv behaviour for malloc(0) ? */
+static int malloc_sysv;
+
+/* zero fill ? */
+static int malloc_zero;
+
+/* junk fill ? */
+static int malloc_junk;
+
+#ifdef HAS_UTRACE
+
+/* utrace ? */
+static int malloc_utrace;
+
+struct ut
+{
+ void *p;
+ size_t s;
+ void *r;
+};
+
+void utrace __P((struct ut *, int));
+
+#define UTRACE(a, b, c) \
+ if (malloc_utrace) \
+ { \
+ struct ut u; \
+ u.p = a; \
+ u.s = b; \
+ u.r = c; \
+ utrace(&u, sizeof u); \
+ }
+#else /* !HAS_UTRACE */
+#define UTRACE(a, b, c)
+#endif /* HAS_UTRACE */
+
+/* my last break. */
+static void *malloc_brk;
+
+/* one location cache for free-list holders */
+static struct pgfree *px;
+
+/* compile-time options */
+char *malloc_options;
+typedef char *caddr_t;
+/* Name of the current public function */
+static char *malloc_func;
+
+/* Macro for mmap */
+#define MMAP(size) \
+ mmap(0, (size), PROT_READ | PROT_WRITE, MAP_PRIVATE, MMAP_FD, 0);
+
+/*
+ * Necessary function declarations
+ */
+static int extend_pgdir(u_long index);
+
+static void *imalloc(size_t size);
+
+static void ifree(void *ptr);
+
+static void *irealloc(void *ptr, size_t size);
+
+#ifdef HAS_PROGNAME
+extern char *__progname;
+#else
+static char *__progname = "";
+#endif
+
+#ifndef HAS_ABORT
+#define abort() exit(1)
+#endif
+
+static void wrterror(char *p)
+{
+ char *q = " error: ";
+ _write(STDERR_FILENO, __progname, strlen(__progname));
+ _write(STDERR_FILENO, malloc_func, strlen(malloc_func));
+ _write(STDERR_FILENO, q, strlen(q));
+ _write(STDERR_FILENO, p, strlen(p));
+ suicide = 1;
+ abort();
+}
+
+static void wrtwarning(char *p)
+{
+ char *q = " warning: ";
+ if (malloc_abort)
+ {
+ wrterror(p);
+ }
+ _write(STDERR_FILENO, __progname, strlen(__progname));
+ _write(STDERR_FILENO, malloc_func, strlen(malloc_func));
+ _write(STDERR_FILENO, q, strlen(q));
+ _write(STDERR_FILENO, p, strlen(p));
+}
+
+/*
+ * Allocate a number of pages from the OS
+ */
+static void *map_pages(int pages)
+{
+ caddr_t result, tail;
+
+ result = (caddr_t)pageround((u_long)sbrk(0));
+ tail = result + (pages << malloc_pageshift);
+
+ if (brk(tail))
+ {
+#ifdef EXTRA_SANITY
+ wrterror("(ES): map_pages fails\n");
+#endif /* EXTRA_SANITY */
+ return 0;
+ }
+
+ last_index = ptr2index(tail) - 1;
+ malloc_brk = tail;
+
+ if ((last_index + 1) >= malloc_ninfo && !extend_pgdir(last_index))
+ {
+ return 0;
+ };
+
+ return result;
+}
+
+/*
+ * Extend page directory
+ */
+static int extend_pgdir(u_long index)
+{
+ struct pginfo **new, **old;
+ int i, oldlen;
+
+ /* Make it this many pages */
+ i = index * sizeof *page_dir;
+ i /= malloc_pagesize;
+ i += 2;
+
+ /* remember the old mapping size */
+ oldlen = malloc_ninfo * sizeof *page_dir;
+
+ /*
+ * NOTE: we allocate new pages and copy the directory rather than tempt
+ * fate by trying to "grow" the region.. There is nothing to prevent
+ * us from accidently re-mapping space that's been allocated by our caller
+ * via dlopen() or other mmap().
+ *
+ * The copy problem is not too bad, as there is 4K of page index per
+ * 4MB of malloc arena.
+ *
+ * We can totally avoid the copy if we open a file descriptor to associate
+ * the anon mappings with. Then, when we remap the pages at the new
+ * address, the old pages will be "magically" remapped.. But this means
+ * keeping open a "secret" file descriptor.....
+ */
+
+ /* Get new pages */
+ new = (struct pginfo **)MMAP(i * malloc_pagesize);
+ if (new == (struct pginfo **)-1)
+ {
+ return 0;
+ }
+
+ /* Copy the old stuff */
+ memcpy(new, page_dir, malloc_ninfo * sizeof *page_dir);
+
+ /* register the new size */
+ malloc_ninfo = i * malloc_pagesize / sizeof *page_dir;
+
+ /* swap the pointers */
+ old = page_dir;
+ page_dir = new;
+
+ /* Now free the old stuff */
+ munmap((char *)old, oldlen);
+ return 1;
+}
+
+/*
+ * Initialize the world
+ */
+static void malloc_init()
+{
+ char *p;
+ int i, j;
+
+ INIT_MMAP();
+
+#ifdef EXTRA_SANITY
+ malloc_junk = 1;
+#endif /* EXTRA_SANITY */
+
+ for (i = 0; i < 3; i++)
+ {
+ if (i == 0)
+ {
+#ifdef HAS_READLINK
+ errnosave = errno;
+ j = readlink("/etc/malloc.conf", b, sizeof b - 1);
+ errno = errnosave;
+ if (j <= 0)
+ continue;
+ b[j] = '\0';
+ p = b;
+#else
+ p = NULL;
+#endif
+ }
+ else if (i == 1)
+ {
+#ifdef HAS_GETENV
+ p = getenv("MALLOC_OPTIONS");
+#else
+ p = NULL;
+#endif
+ }
+ else
+ {
+ p = malloc_options;
+ }
+ for (; p && *p; p++)
+ {
+ switch (*p)
+ {
+ case '>':
+ malloc_cache <<= 1;
+ break;
+ case '<':
+ malloc_cache >>= 1;
+ break;
+ case 'a':
+ malloc_abort = 0;
+ break;
+ case 'A':
+ malloc_abort = 1;
+ break;
+ case 'h':
+ malloc_hint = 0;
+ break;
+ case 'H':
+ malloc_hint = 1;
+ break;
+ case 'r':
+ malloc_realloc = 0;
+ break;
+ case 'R':
+ malloc_realloc = 1;
+ break;
+ case 'j':
+ malloc_junk = 0;
+ break;
+ case 'J':
+ malloc_junk = 1;
+ break;
+#ifdef HAS_UTRACE
+ case 'u':
+ malloc_utrace = 0;
+ break;
+ case 'U':
+ malloc_utrace = 1;
+ break;
+#endif
+ case 'v':
+ malloc_sysv = 0;
+ break;
+ case 'V':
+ malloc_sysv = 1;
+ break;
+ case 'x':
+ malloc_xmalloc = 0;
+ break;
+ case 'X':
+ malloc_xmalloc = 1;
+ break;
+ case 'z':
+ malloc_zero = 0;
+ break;
+ case 'Z':
+ malloc_zero = 1;
+ break;
+ default:
+ j = malloc_abort;
+ malloc_abort = 0;
+ wrtwarning("unknown char in MALLOC_OPTIONS\n");
+ malloc_abort = j;
+ break;
+ }
+ }
+ }
+
+ UTRACE(0, 0, 0);
+
+ /*
+ * We want junk in the entire allocation, and zero only in the part
+ * the user asked for.
+ */
+ if (malloc_zero)
+ {
+ malloc_junk = 1;
+ }
+
+ /*
+ * If we run with junk (or implicitly from above: zero), we want to
+ * force realloc() to get new storage, so we can DTRT with it.
+ */
+ if (malloc_junk)
+ {
+ malloc_realloc = 1;
+ }
+
+ /* Allocate one page for the page directory */
+ page_dir = (struct pginfo **)MMAP(malloc_pagesize);
+
+ if (page_dir == (struct pginfo **)-1)
+ {
+ wrterror("mmap(2) failed, check limits\n");
+ }
+
+ /*
+ * We need a maximum of malloc_pageshift buckets, steal these from the
+ * front of the page_directory;
+ */
+ malloc_origo = ((u_long)pageround((u_long)sbrk(0))) >> malloc_pageshift;
+ malloc_origo -= malloc_pageshift;
+
+ malloc_ninfo = malloc_pagesize / sizeof *page_dir;
+
+ /* Recalculate the cache size in bytes, and make sure it's nonzero */
+
+ if (!malloc_cache)
+ {
+ malloc_cache++;
+ }
+
+ malloc_cache <<= malloc_pageshift;
+
+ /*
+ * This is a nice hack from Kaleb Keithly (kaleb@x.org).
+ * We can sbrk(2) further back when we keep this on a low address.
+ */
+ px = (struct pgfree *)imalloc(sizeof *px);
+
+ /* Been here, done that */
+ malloc_started++;
+}
+
+/*
+ * Allocate a number of complete pages
+ */
+static void *malloc_pages(size_t size)
+{
+ void *p, *delay_free = 0;
+ unsigned int i;
+ struct pgfree *pf;
+ u_long index;
+
+ size = pageround(size);
+
+ p = 0;
+
+ /* Look for free pages before asking for more */
+ for (pf = free_list.next; pf; pf = pf->next)
+ {
+#ifdef EXTRA_SANITY
+ if (pf->size & malloc_pagemask)
+ wrterror("(ES): junk length entry on free_list\n");
+ if (!pf->size)
+ wrterror("(ES): zero length entry on free_list\n");
+ if (pf->page == pf->end)
+ wrterror("(ES): zero entry on free_list\n");
+ if (pf->page > pf->end)
+ wrterror("(ES): sick entry on free_list\n");
+ if ((void *)pf->page >= (void *)sbrk(0))
+ wrterror("(ES): entry on free_list past brk\n");
+ if (page_dir[ptr2index(pf->page)] != MALLOC_FREE)
+ wrterror("(ES): non-free first page on free-list\n");
+ if (page_dir[ptr2index(pf->end) - 1] != MALLOC_FREE)
+ wrterror("(ES): non-free last page on free-list\n");
+#endif /* EXTRA_SANITY */
+
+ if (pf->size < size)
+ {
+ continue;
+ }
+
+ if (pf->size == size)
+ {
+ p = pf->page;
+ if (pf->next)
+ {
+ pf->next->prev = pf->prev;
+ }
+ pf->prev->next = pf->next;
+ delay_free = pf;
+ break;
+ }
+
+ p = pf->page;
+ pf->page = (char *)pf->page + size;
+ pf->size -= size;
+ break;
+ }
+
+#ifdef EXTRA_SANITY
+ if (p && page_dir[ptr2index(p)] != MALLOC_FREE)
+ wrterror("(ES): allocated non-free page on free-list\n");
+#endif /* EXTRA_SANITY */
+
+ size >>= malloc_pageshift;
+
+ /* Map new pages */
+ if (!p)
+ {
+ p = map_pages(size);
+ }
+
+ if (p)
+ {
+ index = ptr2index(p);
+ page_dir[index] = MALLOC_FIRST;
+ for (i = 1; i < size; i++)
+ {
+ page_dir[index + i] = MALLOC_FOLLOW;
+ }
+
+ if (malloc_junk)
+ {
+ memset(p, SOME_JUNK, size << malloc_pageshift);
+ }
+ }
+
+ if (delay_free)
+ {
+ if (!px)
+ {
+ px = delay_free;
+ }
+ else
+ {
+ ifree(delay_free);
+ }
+ }
+
+ return p;
+}
+
+/*
+ * Allocate a page of fragments
+ */
+
+static __inline__ int malloc_make_chunks(int bits)
+{
+ struct pginfo *bp;
+ void *pp;
+ int i, k, l;
+
+ /* Allocate a new bucket */
+ pp = malloc_pages(malloc_pagesize);
+ if (!pp)
+ {
+ return 0;
+ }
+
+ /* Find length of admin structure */
+ l = offsetof(struct pginfo, bits[0]);
+ l += sizeof bp->bits[0] *
+ (((malloc_pagesize >> bits) + MALLOC_BITS - 1) / MALLOC_BITS);
+
+ /* Don't waste more than two chunks on this */
+ if ((1 << bits) <= l + l)
+ {
+ bp = (struct pginfo *)pp;
+ }
+ else
+ {
+ bp = (struct pginfo *)imalloc(l);
+ if (!bp)
+ {
+ ifree(pp);
+ return 0;
+ }
+ }
+
+ bp->size = (1 << bits);
+ bp->shift = bits;
+ bp->total = bp->free = malloc_pagesize >> bits;
+ bp->page = pp;
+
+ /* set all valid bits in the bitmap */
+ k = bp->total;
+ i = 0;
+
+ /* Do a bunch at a time */
+ for (; k - i >= (int)MALLOC_BITS; i += MALLOC_BITS)
+ {
+ bp->bits[i / MALLOC_BITS] = ~0;
+ }
+
+ for (; i < k; i++)
+ {
+ bp->bits[i / MALLOC_BITS] |= 1 << (i % MALLOC_BITS);
+ }
+
+ if (bp == bp->page)
+ {
+ /* Mark the ones we stole for ourselves */
+ for (i = 0; l > 0; i++)
+ {
+ bp->bits[i / MALLOC_BITS] &= ~(1 << (i % MALLOC_BITS));
+ bp->free--;
+ bp->total--;
+ l -= (1 << bits);
+ }
+ }
+
+ /* MALLOC_LOCK */
+
+ page_dir[ptr2index(pp)] = bp;
+
+ bp->next = page_dir[bits];
+ page_dir[bits] = bp;
+
+ /* MALLOC_UNLOCK */
+
+ return 1;
+}
+
+/*
+ * Allocate a fragment
+ */
+static void *malloc_bytes(size_t size)
+{
+ int i, j;
+ u_int u;
+ struct pginfo *bp;
+ int k;
+ u_int *lp;
+
+ /* Don't bother with anything less than this */
+ if (size < malloc_minsize)
+ {
+ size = malloc_minsize;
+ }
+
+ /* Find the right bucket */
+ j = 1;
+ i = size - 1;
+ while (i >>= 1)
+ j++;
+
+ /* If it's empty, make a page more of that size chunks */
+ if (!page_dir[j] && !malloc_make_chunks(j))
+ {
+ return 0;
+ }
+
+ bp = page_dir[j];
+
+ /* Find first word of bitmap which isn't empty */
+ for (lp = bp->bits; !*lp; lp++)
+ ;
+
+ /* Find that bit, and tweak it */
+ u = 1;
+ k = 0;
+ while (!(*lp & u))
+ {
+ u += u;
+ k++;
+ }
+ *lp ^= u;
+
+ /* If there are no more free, remove from free-list */
+ if (!--bp->free)
+ {
+ page_dir[j] = bp->next;
+ bp->next = 0;
+ }
+
+ /* Adjust to the real offset of that chunk */
+ k += (lp - bp->bits) * MALLOC_BITS;
+ k <<= bp->shift;
+
+ if (malloc_junk)
+ {
+ memset((u_char *)bp->page + k, SOME_JUNK, bp->size);
+ }
+
+ return (u_char *)bp->page + k;
+}
+
+/*
+ * Allocate a piece of memory
+ */
+static void *imalloc(size_t size)
+{
+ void *result;
+
+ if (suicide)
+ abort();
+
+ if ((size + malloc_pagesize) < size)
+ { /* Check for overflow */
+ result = 0;
+ }
+ else if (size <= malloc_maxsize)
+ {
+ result = malloc_bytes(size);
+ }
+ else
+ {
+ result = malloc_pages(size);
+ }
+
+ if (malloc_abort && !result)
+ {
+ wrterror("allocation failed.\n");
+ }
+
+ if (malloc_zero && result)
+ {
+ memset(result, 0, size);
+ }
+
+ return result;
+}
+
+/*
+ * Change the size of an allocation.
+ */
+static void *irealloc(void *ptr, size_t size)
+{
+ void *p;
+ u_long osize, index;
+ struct pginfo **mp;
+ int i;
+
+ if (suicide)
+ abort();
+
+ index = ptr2index(ptr);
+
+ if (index < malloc_pageshift)
+ {
+ wrtwarning("junk pointer, too low to make sense.\n");
+ return 0;
+ }
+
+ if (index > last_index)
+ {
+ wrtwarning("junk pointer, too high to make sense.\n");
+ return 0;
+ }
+
+ mp = &page_dir[index];
+
+ if (*mp == MALLOC_FIRST)
+ { /* Page allocation */
+
+ /* Check the pointer */
+ if ((u_long)ptr & malloc_pagemask)
+ {
+ wrtwarning("modified (page-) pointer.\n");
+ return 0;
+ }
+
+ /* Find the size in bytes */
+ for (osize = malloc_pagesize; *++mp == MALLOC_FOLLOW;)
+ {
+ osize += malloc_pagesize;
+ }
+
+ if (!malloc_realloc && /* unless we have to, */
+ size <= osize && /* .. or are too small, */
+ size > (osize - malloc_pagesize))
+ { /* .. or can free a page, */
+ return ptr; /* don't do anything. */
+ }
+ }
+ else if (*mp >= MALLOC_MAGIC)
+ { /* Chunk allocation */
+
+ /* Check the pointer for sane values */
+ if (((u_long)ptr & ((*mp)->size - 1)))
+ {
+ wrtwarning("modified (chunk-) pointer.\n");
+ return 0;
+ }
+
+ /* Find the chunk index in the page */
+ i = ((u_long)ptr & malloc_pagemask) >> (*mp)->shift;
+
+ /* Verify that it isn't a free chunk already */
+ if ((*mp)->bits[i / MALLOC_BITS] & (1 << (i % MALLOC_BITS)))
+ {
+ wrtwarning("chunk is already free.\n");
+ return 0;
+ }
+
+ osize = (*mp)->size;
+
+ if (!malloc_realloc && /* Unless we have to, */
+ size < osize && /* ..or are too small, */
+ (size > osize / 2 || /* ..or could use a smaller size, */
+ osize == malloc_minsize))
+ { /* ..(if there is one) */
+ return ptr; /* ..Don't do anything */
+ }
+ }
+ else
+ {
+ wrtwarning("pointer to wrong page.\n");
+ return 0;
+ }
+
+ p = imalloc(size);
+
+ if (p)
+ {
+ /* copy the lesser of the two sizes, and free the old one */
+ if (!size || !osize)
+ {
+ }
+ else if (osize < size)
+ {
+ memcpy(p, ptr, osize);
+ }
+ else
+ {
+ memcpy(p, ptr, size);
+ }
+ ifree(ptr);
+ }
+ return p;
+}
+
+/*
+ * Free a sequence of pages
+ */
+
+static __inline__ void free_pages(void *ptr, int index, struct pginfo *info)
+{
+ unsigned int i;
+ struct pgfree *pf, *pt = 0;
+ u_long l;
+ void *tail;
+
+ if (info == MALLOC_FREE)
+ {
+ wrtwarning("page is already free.\n");
+ return;
+ }
+
+ if (info != MALLOC_FIRST)
+ {
+ wrtwarning("pointer to wrong page.\n");
+ return;
+ }
+
+ if ((u_long)ptr & malloc_pagemask)
+ {
+ wrtwarning("modified (page-) pointer.\n");
+ return;
+ }
+
+ /* Count how many pages and mark them free at the same time */
+ page_dir[index] = MALLOC_FREE;
+ for (i = 1; page_dir[index + i] == MALLOC_FOLLOW; i++)
+ {
+ page_dir[index + i] = MALLOC_FREE;
+ }
+
+ l = i << malloc_pageshift;
+
+ if (malloc_junk)
+ {
+ memset(ptr, SOME_JUNK, l);
+ }
+
+#ifdef HAS_MADVISE
+ if (malloc_hint)
+ madvise(ptr, l, MADV_FREE);
+#endif
+
+ tail = (char *)ptr + l;
+
+ /* add to free-list */
+ if (!px)
+ {
+ px = imalloc(sizeof *pt);
+ } /* This cannot fail... */
+ px->page = ptr;
+ px->end = tail;
+ px->size = l;
+ if (!free_list.next)
+ {
+ /* Nothing on free list, put this at head */
+ px->next = free_list.next;
+ px->prev = &free_list;
+ free_list.next = px;
+ pf = px;
+ px = 0;
+ }
+ else
+ {
+ /* Find the right spot, leave pf pointing to the modified entry. */
+ tail = (char *)ptr + l;
+
+ for (pf = free_list.next; pf->end < ptr && pf->next; pf = pf->next)
+ ; /* Race ahead here */
+
+ if (pf->page > tail)
+ {
+ /* Insert before entry */
+ px->next = pf;
+ px->prev = pf->prev;
+ pf->prev = px;
+ px->prev->next = px;
+ pf = px;
+ px = 0;
+ }
+ else if (pf->end == ptr)
+ {
+ /* Append to the previous entry */
+ pf->end = (char *)pf->end + l;
+ pf->size += l;
+ if (pf->next && pf->end == pf->next->page)
+ {
+ /* And collapse the next too. */
+ pt = pf->next;
+ pf->end = pt->end;
+ pf->size += pt->size;
+ pf->next = pt->next;
+ if (pf->next)
+ pf->next->prev = pf;
+ }
+ }
+ else if (pf->page == tail)
+ {
+ /* Prepend to entry */
+ pf->size += l;
+ pf->page = ptr;
+ }
+ else if (!pf->next)
+ {
+ /* Append at tail of chain */
+ px->next = 0;
+ px->prev = pf;
+ pf->next = px;
+ pf = px;
+ px = 0;
+ }
+ else
+ {
+ wrterror("freelist is destroyed.\n");
+ }
+ }
+
+ /* Return something to OS ? */
+ if (!pf->next && /* If we're the last one, */
+ pf->size > malloc_cache && /* ..and the cache is full, */
+ pf->end == malloc_brk && /* ..and none behind us, */
+ malloc_brk == sbrk(0))
+ { /* ..and it's OK to do... */
+
+ /*
+ * Keep the cache intact. Notice that the '>' above guarantees that
+ * the pf will always have at least one page afterwards.
+ */
+ pf->end = (char *)pf->page + malloc_cache;
+ pf->size = malloc_cache;
+
+ brk(pf->end);
+ malloc_brk = pf->end;
+
+ index = ptr2index(pf->end);
+ last_index = index - 1;
+
+ for (i = index; i <= last_index;)
+ page_dir[i++] = MALLOC_NOT_MINE;
+
+ /* XXX: We could realloc/shrink the pagedir here I guess. */
+ }
+ if (pt)
+ ifree(pt);
+}
+
+/*
+ * Free a chunk, and possibly the page it's on, if the page becomes empty.
+ */
+
+static __inline__ void free_bytes(void *ptr, int index, struct pginfo *info)
+{
+ int i;
+ struct pginfo **mp;
+ void *vp;
+
+ /* Find the chunk number on the page */
+ i = ((u_long)ptr & malloc_pagemask) >> info->shift;
+
+ if (((u_long)ptr & (info->size - 1)))
+ {
+ wrtwarning("modified (chunk-) pointer.\n");
+ return;
+ }
+
+ if (info->bits[i / MALLOC_BITS] & (1 << (i % MALLOC_BITS)))
+ {
+ wrtwarning("chunk is already free.\n");
+ return;
+ }
+
+ if (malloc_junk)
+ {
+ memset(ptr, SOME_JUNK, info->size);
+ }
+
+ info->bits[i / MALLOC_BITS] |= 1 << (i % MALLOC_BITS);
+ info->free++;
+
+ mp = page_dir + info->shift;
+
+ if (info->free == 1)
+ {
+ /* Page became non-full */
+
+ mp = page_dir + info->shift;
+ /* Insert in address order */
+ while (*mp && (*mp)->next && (*mp)->next->page < info->page)
+ mp = &(*mp)->next;
+ info->next = *mp;
+ *mp = info;
+ return;
+ }
+
+ if (info->free != info->total)
+ {
+ return;
+ }
+
+ /* Find & remove this page in the queue */
+ while (*mp != info)
+ {
+ mp = &((*mp)->next);
+#ifdef EXTRA_SANITY
+ if (!*mp)
+ wrterror("(ES): Not on queue\n");
+#endif /* EXTRA_SANITY */
+ }
+ *mp = info->next;
+
+ /* Free the page & the info structure if need be */
+ page_dir[ptr2index(info->page)] = MALLOC_FIRST;
+ vp = info->page; /* Order is important ! */
+ if (vp != (void *)info)
+ {
+ ifree(info);
+ }
+ ifree(vp);
+}
+
+static void ifree(void *ptr)
+{
+ struct pginfo *info;
+ unsigned int index;
+
+ /* This is legal */
+ if (!ptr)
+ {
+ return;
+ }
+
+ if (!malloc_started)
+ {
+ wrtwarning("malloc() has never been called.\n");
+ return;
+ }
+
+ /* If we're already sinking, don't make matters any worse. */
+ if (suicide)
+ {
+ return;
+ }
+
+ index = ptr2index(ptr);
+
+ if (index < malloc_pageshift)
+ {
+ wrtwarning("junk pointer, too low to make sense.\n");
+ return;
+ }
+
+ if (index > last_index)
+ {
+ wrtwarning("junk pointer, too high to make sense.\n");
+ return;
+ }
+
+ info = page_dir[index];
+
+ if (info < MALLOC_MAGIC)
+ {
+ free_pages(ptr, index, info);
+ }
+ else
+ {
+ free_bytes(ptr, index, info);
+ }
+ return;
+}
+
+/*
+ * These are the public exported interface routines.
+ */
+
+void *malloc(size_t size)
+{
+ register void *r;
+
+ THREAD_LOCK();
+ malloc_func = " in malloc():";
+ if (malloc_active++)
+ {
+ wrtwarning("recursive call.\n");
+ malloc_active--;
+ return (0);
+ }
+ if (!malloc_started)
+ {
+ malloc_init();
+ }
+ if (malloc_sysv && !size)
+ {
+ r = 0;
+ }
+ else
+ {
+ r = imalloc(size);
+ }
+ UTRACE(0, size, r);
+ malloc_active--;
+ THREAD_UNLOCK();
+ if (malloc_xmalloc && !r)
+ {
+ wrterror("out of memory.\n");
+ }
+ return (r);
+}
+
+void free(void *ptr)
+{
+ THREAD_LOCK();
+ malloc_func = " in free():";
+ if (malloc_active++)
+ {
+ wrtwarning("recursive call.\n");
+ malloc_active--;
+ return;
+ }
+ else
+ {
+ ifree(ptr);
+ UTRACE(ptr, 0, 0);
+ }
+ malloc_active--;
+ THREAD_UNLOCK();
+ return;
+}
+
+void *realloc(void *ptr, size_t size)
+{
+ register void *r;
+
+ THREAD_LOCK();
+ malloc_func = " in realloc():";
+ if (malloc_active++)
+ {
+ wrtwarning("recursive call.\n");
+ malloc_active--;
+ return (0);
+ }
+ if (ptr && !malloc_started)
+ {
+ wrtwarning("malloc() has never been called.\n");
+ ptr = 0;
+ }
+ if (!malloc_started)
+ {
+ malloc_init();
+ }
+ if (malloc_sysv && !size)
+ {
+ ifree(ptr);
+ r = 0;
+ }
+ else if (!ptr)
+ {
+ r = imalloc(size);
+ }
+ else
+ {
+ r = irealloc(ptr, size);
+ }
+ UTRACE(ptr, size, r);
+ malloc_active--;
+ THREAD_UNLOCK();
+ if (malloc_xmalloc && !r)
+ {
+ wrterror("out of memory.\n");
+ }
+ return (r);
+}
+
+/* Added */
+void *calloc(size_t nelem, size_t elsize)
+{
+ void *tmp;
+ if (NULL == (tmp = malloc(nelem * elsize)))
+ {
+ return NULL;
+ }
+ else
+ {
+ memset(tmp, 0, nelem * elsize);
+ return tmp;
+ }
+}
diff --git a/user/lib/libc/printf.c b/user/lib/libc/printf.c
new file mode 100644
index 0000000..609ff37
--- /dev/null
+++ b/user/lib/libc/printf.c
@@ -0,0 +1,169 @@
+/*
+ ****************************************************************************
+ * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: printf.c
+ * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
+ * Changes: Grzegorz Milos (gm281@cam.ac.uk)
+ *
+ * Date: Aug 2003, Aug 2005
+ *
+ * Environment: Xen Minimal OS
+ * Description: Library functions for printing
+ * (freebsd port, mainly sys/subr_prf.c)
+ *
+ ****************************************************************************
+ *
+ *-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $
+ */
+
+#include "stdio.h"
+#include "unistd.h"
+
+int printf(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vprintf(fmt, args);
+ va_end(args);
+ return i;
+}
+
+int fprintf(FILE *stream, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vfprintf(stream, fmt, args);
+ va_end(args);
+ return i;
+}
+
+int sprintf(char *buf, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsprintf(buf, fmt, args);
+ va_end(args);
+ return i;
+}
+
+int snprintf(char *buf, size_t size, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buf, size, fmt, args);
+ va_end(args);
+ return i;
+}
+
+int vprintf(const char *fmt, va_list args)
+{
+ return vfprintf(stdout, fmt, args);
+}
+
+#define __LIBC_PRINTF_BUFSIZE 1024
+
+int vfprintf(FILE *stream, const char *fmt, va_list args)
+{
+ /* I'm really lazy */
+ char buf[__LIBC_PRINTF_BUFSIZE];
+ int ret = vsnprintf(buf, __LIBC_PRINTF_BUFSIZE, fmt, args);
+ if (ret > 0)
+ {
+ // write(stream->fd, buf, ret);
+
+#if 1
+ /* slow but simple */
+ for (int i = 0; i < ret; i++)
+ {
+ stream->buffer[stream->offset++] = buf[i];
+ if (stream->offset == sizeof(stream->buffer) || buf[i] == '\n')
+ {
+ // flush
+ fflush(stream);
+ }
+ }
+#endif
+ }
+ return ret;
+}
+
+int vsprintf(char *buf, const char *fmt, va_list args)
+{
+ return vsnprintf(buf, 0xffffffffUL, fmt, args);
+}
+
+void __do_fflush(FILE *stream)
+{
+ if (!stream)
+ {
+ return;
+ }
+
+ if (stream->offset > 0)
+ {
+ write(stream->fd, stream->buffer, stream->offset);
+ stream->offset = 0;
+ }
+}
+
+int fflush(FILE *stream)
+{
+ if (!stream)
+ {
+ __do_fflush(stdin);
+ __do_fflush(stdout);
+ __do_fflush(stderr);
+ }
+ else
+ {
+ __do_fflush(stream);
+ }
+
+ return 0;
+}
diff --git a/user/lib/libc/quad.c b/user/lib/libc/quad.c
new file mode 100644
index 0000000..2cf944c
--- /dev/null
+++ b/user/lib/libc/quad.c
@@ -0,0 +1,414 @@
+/* Arithmetic with 64-bit integers. This is not supported natively */
+
+/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
+ ****************************************************************************
+ * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: math.c
+ * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
+ * Changes:
+ *
+ * Date: Aug 2003
+ *
+ * Environment: Xen Minimal OS
+ * Description: Library functions for 64bit arith and other
+ * from freebsd, files in sys/libkern/ (qdivrem.c, etc)
+ *
+ ****************************************************************************
+ * $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $
+ ****************************************************************************
+ *-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $
+ */
+
+/*
+ * Depending on the desired operation, we view a `long long' (aka quad_t) in
+ * one or more of the following formats.
+ */
+union uu {
+ long long q; /* as a (signed) quad */
+ long long uq; /* as an unsigned quad */
+ long sl[2]; /* as two signed longs */
+ unsigned long ul[2]; /* as two unsigned longs */
+};
+/* XXX RN: Yuck hardcoded endianess :) */
+#define _QUAD_HIGHWORD 1
+#define _QUAD_LOWWORD 0
+/*
+ * Define high and low longwords.
+ */
+#define H _QUAD_HIGHWORD
+#define L _QUAD_LOWWORD
+
+/*
+ * Total number of bits in a quad_t and in the pieces that make it up.
+ * These are used for shifting, and also below for halfword extraction
+ * and assembly.
+ */
+#define CHAR_BIT 8 /* number of bits in a char */
+#define QUAD_BITS (sizeof(long long) * CHAR_BIT)
+#define LONG_BITS (sizeof(long) * CHAR_BIT)
+#define HALF_BITS (sizeof(long) * CHAR_BIT / 2)
+
+/*
+ * Extract high and low shortwords from longword, and move low shortword of
+ * longword to upper half of long, i.e., produce the upper longword of
+ * ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.)
+ *
+ * These are used in the multiply code, to split a longword into upper
+ * and lower halves, and to reassemble a product as a quad_t, shifted left
+ * (sizeof(long)*CHAR_BIT/2).
+ */
+#define HHALF(x) ((x) >> HALF_BITS)
+#define LHALF(x) ((x) & ((1UL << HALF_BITS) - 1))
+#define LHUP(x) ((x) << HALF_BITS)
+
+/*
+ * Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed),
+ * section 4.3.1, pp. 257--259.
+ */
+#define B (1UL << HALF_BITS) /* digit base */
+
+/* Combine two `digits' to make a single two-digit number. */
+#define COMBINE(a, b) (((unsigned long)(a) << HALF_BITS) | (b))
+
+/* select a type for digits in base B: use unsigned short if they fit */
+/* #if ULONG_MAX == 0xffffffff && USHORT_MAX >= 0xffff
+typedef unsigned short digit;
+#else */
+typedef unsigned long digit;
+/* #endif */
+
+/*
+ * Shift p[0]..p[len] left `sh' bits, ignoring any bits that
+ * `fall out' the left (there never will be any such anyway).
+ * We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS.
+ */
+static void shl(register digit *p, register int len, register int sh)
+{
+ register int i;
+
+ for (i = 0; i < len; i++)
+ {
+ p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh));
+ }
+ p[i] = LHALF(p[i] << sh);
+}
+
+/*
+ * __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v.
+ *
+ * We do this in base 2-sup-HALF_BITS, so that all intermediate products
+ * fit within u_long. As a consequence, the maximum length dividend and
+ * divisor are 4 `digits' in this base (they are shorter if they have
+ * leading zeros).
+ */
+unsigned long long __qdivrem(unsigned long long uq, unsigned long long vq,
+ unsigned long long *arq)
+{
+ union uu tmp = {.q = 0};
+ digit *u, *v, *q;
+ register digit v1, v2;
+ unsigned long qhat, rhat, t;
+ int m, n, d, j, i;
+ digit uspace[5], vspace[5], qspace[5];
+
+ /*
+ * Take care of special cases: divide by zero, and u < v.
+ */
+ if (vq == 0)
+ {
+ /* divide by zero. */
+ static volatile const unsigned int zero = 0;
+
+ tmp.ul[H] = tmp.ul[L] = 1 / zero;
+ if (arq)
+ {
+ *arq = uq;
+ }
+ return (tmp.q);
+ }
+ if (uq < vq)
+ {
+ if (arq)
+ {
+ *arq = uq;
+ }
+ return (0);
+ }
+ u = &uspace[0];
+ v = &vspace[0];
+ q = &qspace[0];
+
+ /*
+ * Break dividend and divisor into digits in base B, then
+ * count leading zeros to determine m and n. When done, we
+ * will have:
+ * u = (u[1]u[2]...u[m+n]) sub B
+ * v = (v[1]v[2]...v[n]) sub B
+ * v[1] != 0
+ * 1 < n <= 4 (if n = 1, we use a different division algorithm)
+ * m >= 0 (otherwise u < v, which we already checked)
+ * m + n = 4
+ * and thus
+ * m = 4 - n <= 2
+ */
+ tmp.uq = uq;
+ u[0] = 0;
+ u[1] = HHALF(tmp.ul[H]);
+ u[2] = LHALF(tmp.ul[H]);
+ u[3] = HHALF(tmp.ul[L]);
+ u[4] = LHALF(tmp.ul[L]);
+ tmp.uq = vq;
+ v[1] = HHALF(tmp.ul[H]);
+ v[2] = LHALF(tmp.ul[H]);
+ v[3] = HHALF(tmp.ul[L]);
+ v[4] = LHALF(tmp.ul[L]);
+ for (n = 4; v[1] == 0; v++)
+ {
+ if (--n == 1)
+ {
+ unsigned long rbj; /* r*B+u[j] (not root boy jim) */
+ digit q1, q2, q3, q4;
+
+ /*
+ * Change of plan, per exercise 16.
+ * r = 0;
+ * for j = 1..4:
+ * q[j] = floor((r*B + u[j]) / v),
+ * r = (r*B + u[j]) % v;
+ * We unroll this completely here.
+ */
+ t = v[2]; /* nonzero, by definition */
+ q1 = u[1] / t;
+ rbj = COMBINE(u[1] % t, u[2]);
+ q2 = rbj / t;
+ rbj = COMBINE(rbj % t, u[3]);
+ q3 = rbj / t;
+ rbj = COMBINE(rbj % t, u[4]);
+ q4 = rbj / t;
+ if (arq)
+ {
+ *arq = rbj % t;
+ }
+ tmp.ul[H] = COMBINE(q1, q2);
+ tmp.ul[L] = COMBINE(q3, q4);
+ return (tmp.q);
+ }
+ }
+
+ /*
+ * By adjusting q once we determine m, we can guarantee that
+ * there is a complete four-digit quotient at &qspace[1] when
+ * we finally stop.
+ */
+ for (m = 4 - n; u[1] == 0; u++)
+ {
+ m--;
+ }
+ for (i = 4 - m; --i >= 0;)
+ {
+ q[i] = 0;
+ }
+ q += 4 - m;
+
+ /*
+ * Here we run Program D, translated from MIX to C and acquiring
+ * a few minor changes.
+ *
+ * D1: choose multiplier 1 << d to ensure v[1] >= B/2.
+ */
+ d = 0;
+ for (t = v[1]; t < B / 2; t <<= 1)
+ {
+ d++;
+ }
+ if (d > 0)
+ {
+ shl(&u[0], m + n, d); /* u <<= d */
+ shl(&v[1], n - 1, d); /* v <<= d */
+ }
+ /*
+ * D2: j = 0.
+ */
+ j = 0;
+ v1 = v[1]; /* for D3 -- note that v[1..n] are constant */
+ v2 = v[2]; /* for D3 */
+ do
+ {
+ register digit uj0, uj1, uj2;
+
+ /*
+ * D3: Calculate qhat (\^q, in TeX notation).
+ * Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and
+ * let rhat = (u[j]*B + u[j+1]) mod v[1].
+ * While rhat < B and v[2]*qhat > rhat*B+u[j+2],
+ * decrement qhat and increase rhat correspondingly.
+ * Note that if rhat >= B, v[2]*qhat < rhat*B.
+ */
+ uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */
+ uj1 = u[j + 1]; /* for D3 only */
+ uj2 = u[j + 2]; /* for D3 only */
+ if (uj0 == v1)
+ {
+ qhat = B;
+ rhat = uj1;
+ goto qhat_too_big;
+ }
+ else
+ {
+ unsigned long nn = COMBINE(uj0, uj1);
+ qhat = nn / v1;
+ rhat = nn % v1;
+ }
+ while (v2 * qhat > COMBINE(rhat, uj2))
+ {
+ qhat_too_big:
+ qhat--;
+ if ((rhat += v1) >= B)
+ {
+ break;
+ }
+ }
+ /*
+ * D4: Multiply and subtract.
+ * The variable `t' holds any borrows across the loop.
+ * We split this up so that we do not require v[0] = 0,
+ * and to eliminate a final special case.
+ */
+ for (t = 0, i = n; i > 0; i--)
+ {
+ t = u[i + j] - v[i] * qhat - t;
+ u[i + j] = LHALF(t);
+ t = (B - HHALF(t)) & (B - 1);
+ }
+ t = u[j] - t;
+ u[j] = LHALF(t);
+ /*
+ * D5: test remainder.
+ * There is a borrow if and only if HHALF(t) is nonzero;
+ * in that (rare) case, qhat was too large (by exactly 1).
+ * Fix it by adding v[1..n] to u[j..j+n].
+ */
+ if (HHALF(t))
+ {
+ qhat--;
+ for (t = 0, i = n; i > 0; i--)
+ { /* D6: add back. */
+ t += u[i + j] + v[i];
+ u[i + j] = LHALF(t);
+ t = HHALF(t);
+ }
+ u[j] = LHALF(u[j] + t);
+ }
+ q[j] = qhat;
+ } while (++j <= m); /* D7: loop on j. */
+
+ /*
+ * If caller wants the remainder, we have to calculate it as
+ * u[m..m+n] >> d (this is at most n digits and thus fits in
+ * u[m+1..m+n], but we may need more source digits).
+ */
+ if (arq)
+ {
+ if (d)
+ {
+ for (i = m + n; i > m; --i)
+ {
+ u[i] = (u[i] >> d) | LHALF(u[i - 1] << (HALF_BITS - d));
+ }
+ u[i] = 0;
+ }
+ tmp.ul[H] = COMBINE(uspace[1], uspace[2]);
+ tmp.ul[L] = COMBINE(uspace[3], uspace[4]);
+ *arq = tmp.q;
+ }
+
+ tmp.ul[H] = COMBINE(qspace[1], qspace[2]);
+ tmp.ul[L] = COMBINE(qspace[3], qspace[4]);
+ return (tmp.q);
+}
+
+/*
+ * Divide two signed quads.
+ * ??? if -1/2 should produce -1 on this machine, this code is wrong
+ */
+long long __divdi3(long long a, long long b)
+{
+ unsigned long long ua, ub, uq;
+ int neg;
+
+ if (a < 0)
+ {
+ ua = -(unsigned long long)a, neg = 1;
+ }
+ else
+ {
+ ua = a, neg = 0;
+ }
+ if (b < 0)
+ {
+ ub = -(unsigned long long)b, neg ^= 1;
+ }
+ else
+ {
+ ub = b;
+ }
+ uq = __qdivrem(ua, ub, (unsigned long long *)0);
+ return (neg ? -uq : uq);
+}
+
+/*
+ * Divide two unsigned quads.
+ */
+unsigned long long __udivdi3(unsigned long long a, unsigned long long b)
+{
+ return (__qdivrem(a, b, (unsigned long long *)0));
+}
+
+/*
+ * Return remainder after dividing two unsigned quads.
+ */
+unsigned long long __umoddi3(unsigned long long a, unsigned long long b)
+{
+ unsigned long long r;
+
+ (void)__qdivrem(a, b, &r);
+ return (r);
+}
diff --git a/user/lib/libc/rand.c b/user/lib/libc/rand.c
new file mode 100644
index 0000000..711ae8e
--- /dev/null
+++ b/user/lib/libc/rand.c
@@ -0,0 +1,22 @@
+#include <stdlib.h>
+
+/* Random int between lo and hi inclusive */
+
+/* TODO Fix the rand/srand implementation to use the implementation in the
+ * (unused) macro which actually has decent pseudo-randomness properties. (No,
+ * you can't just change the mod base and expect it to still work fine...) */
+
+#define RANDOM(lo, hi) \
+ ((lo) + \
+ (((hi) - (lo) + 1) * (randseed = (randseed * 4096 + 150889) % 714025)) / \
+ 714025)
+
+static unsigned long long randseed = 123456L;
+
+int rand(void)
+{
+ randseed = (randseed * 4096 + 150889) % RAND_MAX;
+ return randseed;
+}
+
+void srand(unsigned int seed) { randseed = seed; }
diff --git a/user/lib/libc/scanf.c b/user/lib/libc/scanf.c
new file mode 100644
index 0000000..c7faf27
--- /dev/null
+++ b/user/lib/libc/scanf.c
@@ -0,0 +1,73 @@
+/*
+ ****************************************************************************
+ * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: printf.c
+ * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
+ * Changes: Grzegorz Milos (gm281@cam.ac.uk)
+ *
+ * Date: Aug 2003, Aug 2005
+ *
+ * Environment: Xen Minimal OS
+ * Description: Library functions for printing
+ * (freebsd port, mainly sys/subr_prf.c)
+ *
+ ****************************************************************************
+ *
+ *-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $
+ */
+#include "stdio.h"
+
+/**
+ * sscanf - Unformat a buffer into a list of arguments
+ * @buf: input buffer
+ * @fmt: formatting of buffer
+ * @...: resulting arguments
+ */
+int sscanf(const char *buf, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsscanf(buf, fmt, args);
+ va_end(args);
+ return i;
+}
diff --git a/user/lib/libc/stream.c b/user/lib/libc/stream.c
new file mode 100644
index 0000000..049ee4e
--- /dev/null
+++ b/user/lib/libc/stream.c
@@ -0,0 +1,11 @@
+#include "stdio.h"
+
+static FILE stdstreams[3] = {
+ {.fd = 0, .offset = 0},
+ {.fd = 1, .offset = 0},
+ {.fd = 2, .offset = 0},
+};
+
+FILE *stdin = &stdstreams[0];
+FILE *stdout = &stdstreams[1];
+FILE *stderr = &stdstreams[2];
diff --git a/user/lib/libc/string.c b/user/lib/libc/string.c
new file mode 100644
index 0000000..ae3042d
--- /dev/null
+++ b/user/lib/libc/string.c
@@ -0,0 +1,509 @@
+#include "string.h"
+#include "errno.h"
+#include "sys/types.h"
+#include <stdlib.h>
+
+int memcmp(const void *cs, const void *ct, size_t count)
+{
+ const unsigned char *su1, *su2;
+ signed char res = 0;
+
+ for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
+ {
+ if ((res = *su1 - *su2) != 0)
+ {
+ break;
+ }
+ }
+ return res;
+}
+
+void *memcpy(void *dest, const void *src, size_t count)
+{
+ char *tmp = (char *)dest;
+ const char *s = src;
+
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+
+int strncmp(const char *cs, const char *ct, size_t count)
+{
+ register signed char __res = 0;
+
+ while (count)
+ {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ {
+ break;
+ }
+ count--;
+ }
+
+ return __res;
+}
+
+int strcmp(const char *cs, const char *ct)
+{
+ register signed char __res;
+
+ while (1)
+ {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ {
+ break;
+ }
+ }
+
+ return __res;
+}
+
+char *strcpy(char *dest, const char *src)
+{
+ char *tmp = dest;
+
+ while ((*dest++ = *src++) != '\0') /* nothing */
+ ;
+ return tmp;
+}
+
+char *strncpy(char *dest, const char *src, size_t count)
+{
+ char *tmp = dest;
+
+ while (count)
+ {
+ if ((*dest = *src) != 0)
+ src++;
+ dest++;
+ count--;
+ }
+
+ return tmp;
+}
+
+void *memset(void *s, int c, size_t count)
+{
+ char *xs = (char *)s;
+
+ while (count--)
+ *xs++ = c;
+
+ return s;
+}
+
+size_t strnlen(const char *s, size_t count)
+{
+ const char *sc;
+
+ for (sc = s; count-- && *sc != '\0'; ++sc)
+ {
+ /* nothing */}
+ return sc - s;
+}
+
+char *strcat(char *dest, const char *src)
+{
+ char *tmp = dest;
+
+ while (*dest)
+ dest++;
+
+ while ((*dest++ = *src++) != '\0')
+ ;
+
+ return tmp;
+}
+
+size_t strlen(const char *s)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0'; ++sc)
+ {
+ /* nothing */}
+ return sc - s;
+}
+
+char *strchr(const char *s, int c)
+{
+ for (; *s != (char)c; ++s)
+ {
+ if (*s == '\0')
+ {
+ return NULL;
+ }
+ }
+ return (char *)s;
+}
+
+char *strrchr(const char *s, int c)
+{
+ char *r = NULL;
+ for (; *s; ++s)
+ {
+ if (*s == (char)c)
+ {
+ r = (char *)s;
+ }
+ }
+ return r;
+}
+
+char *strstr(const char *s1, const char *s2)
+{
+ int l1, l2;
+
+ l2 = strlen(s2);
+ if (!l2)
+ {
+ return (char *)s1;
+ }
+ l1 = strlen(s1);
+ while (l1 >= l2)
+ {
+ l1--;
+ if (!memcmp(s1, s2, l2))
+ {
+ return (char *)s1;
+ }
+ s1++;
+ }
+ return NULL;
+}
+
+char *strdup(const char *s)
+{
+ size_t len = strlen(s) + 1;
+ char *str = (char *)malloc(len);
+ memcpy(str, s, len);
+ return str;
+}
+
+/*
+ * The following three functions were ripped out of OpenSolaris. Legally, they
+ * might have to be in a separate file. Leaving it here out of laziness.
+ * Got this from /onnv-gate/usr/src/common/uti/string.c.
+ */
+
+char *strpbrk(const char *string, const char *brkset)
+{
+ const char *p;
+
+ do
+ {
+ for (p = brkset; *p != '\0' && *p != *string; ++p)
+ ;
+ if (*p != '\0')
+ {
+ return ((char *)string);
+ }
+ } while (*string++);
+
+ return (NULL);
+}
+
+size_t strspn(const char *string, const char *charset)
+{
+ const char *p, *q;
+
+ for (q = string; *q != '\0'; ++q)
+ {
+ for (p = charset; *p != '\0' && *p != *q; ++p)
+ ;
+ if (*p == '\0')
+ {
+ break;
+ }
+ }
+
+ return (q - string);
+}
+
+char *strtok(char *string, const char *sepset)
+{
+ char *p, *q, *r;
+ static char *savept;
+
+ /*
+ * Set `p' to our current location in the string.
+ */
+ p = (string == NULL) ? savept : string;
+ if (p == NULL)
+ {
+ return (NULL);
+ }
+
+ /*
+ * Skip leading separators; bail if no tokens remain.
+ */
+ q = p + strspn(p, sepset);
+ if (*q == '\0')
+ {
+ return (NULL);
+ }
+
+ /*
+ * Mark the end of the token and set `savept' for the next iteration.
+ */
+ if ((r = strpbrk(q, sepset)) == NULL)
+ {
+ savept = NULL;
+ }
+ else
+ {
+ *r = '\0';
+ savept = ++r;
+ }
+
+ return (q);
+}
+
+/* created with the help of:
+ * perl -p -e 's/#define\s+(\w+)\s+\d+\s+\/\* ([^\t\*]+)\s*\*\/\s*$/case $1:
+ * return "$2";\n/' < /usr/include/sys/errno.h
+ */
+char *strerror(int errnum)
+{
+ switch (errnum)
+ {
+ case EPERM:
+ return "Not super-user";
+ case ENOENT:
+ return "No such file or directory";
+ case ESRCH:
+ return "No such process";
+ case EINTR:
+ return "interrupted system call";
+ case EIO:
+ return "I/O error";
+ case ENXIO:
+ return "No such device or address";
+ case E2BIG:
+ return "Arg list too long";
+ case ENOEXEC:
+ return "Exec format error";
+ case EBADF:
+ return "Bad file number";
+ case ECHILD:
+ return "No children";
+ case EAGAIN:
+ return "Resource temporarily unavailable";
+ case ENOMEM:
+ return "Not enough core";
+ case EACCES:
+ return "Permission denied";
+ case EFAULT:
+ return "Bad address";
+ case ENOTBLK:
+ return "Block device required";
+ case EBUSY:
+ return "Mount device busy";
+ case EEXIST:
+ return "File exists";
+ case EXDEV:
+ return "Cross-device link";
+ case ENODEV:
+ return "No such device";
+ case ENOTDIR:
+ return "Not a directory";
+ case EISDIR:
+ return "Is a directory";
+ case EINVAL:
+ return "Invalid argument";
+ case ENFILE:
+ return "File table overflow";
+ case EMFILE:
+ return "Too many open files";
+ case ENOTTY:
+ return "Inappropriate ioctl for device";
+ case ETXTBSY:
+ return "Text file busy";
+ case EFBIG:
+ return "File too large";
+ case ENOSPC:
+ return "No space left on device";
+ case ESPIPE:
+ return "Illegal seek";
+ case EROFS:
+ return "Read only file system";
+ case EMLINK:
+ return "Too many links";
+ case EPIPE:
+ return "Broken pipe";
+ case EDOM:
+ return "Math arg out of domain of func";
+ case ERANGE:
+ return "Math result not representable";
+ case ENOMSG:
+ return "No message of desired type";
+ case EIDRM:
+ return "Identifier removed";
+ case ECHRNG:
+ return "Channel number out of range";
+ case EL2NSYNC:
+ return "Level 2 not synchronized";
+ case EL3HLT:
+ return "Level 3 halted";
+ case EL3RST:
+ return "Level 3 reset";
+ case ELNRNG:
+ return "Link number out of range";
+ case EUNATCH:
+ return "Protocol driver not attached";
+ case ENOCSI:
+ return "No CSI structure available";
+ case EL2HLT:
+ return "Level 2 halted";
+ case EDEADLK:
+ return "Deadlock condition.";
+ case ENOLCK:
+ return "No record locks available.";
+ case ECANCELED:
+ return "Operation canceled";
+ case ENOTSUP:
+ return "Operation not supported";
+ case EDQUOT:
+ return "Disc quota exceeded";
+ case EBADE:
+ return "invalid exchange";
+ case EBADR:
+ return "invalid request descriptor";
+ case EXFULL:
+ return "exchange full";
+ case ENOANO:
+ return "no anode";
+ case EBADRQC:
+ return "invalid request code";
+ case EBADSLT:
+ return "invalid slot";
+ case EBFONT:
+ return "bad font file fmt";
+ case EOWNERDEAD:
+ return "process died with the lock";
+ case ENOTRECOVERABLE:
+ return "lock is not recoverable";
+ case ENOSTR:
+ return "Device not a stream";
+ case ENODATA:
+ return "no data (for no delay io)";
+ case ETIME:
+ return "timer expired";
+ case ENOSR:
+ return "out of streams resources";
+ case ENONET:
+ return "Machine is not on the network";
+ case ENOPKG:
+ return "Package not installed";
+ case EREMOTE:
+ return "The object is remote";
+ case ENOLINK:
+ return "the link has been severed";
+ case EADV:
+ return "advertise error";
+ case ESRMNT:
+ return "srmount error";
+ case ECOMM:
+ return "Communication error on send";
+ case EPROTO:
+ return "Protocol error";
+ case EMULTIHOP:
+ return "multihop attempted";
+ case EBADMSG:
+ return "trying to read unreadable message";
+ case ENAMETOOLONG:
+ return "path name is too long";
+ case EOVERFLOW:
+ return "value too large to be stored in data type";
+ case ENOTUNIQ:
+ return "given log. name not unique";
+ case EBADFD:
+ return "f.d. invalid for this operation";
+ case EREMCHG:
+ return "Remote address changed";
+ case ELIBACC:
+ return "Can't access a needed shared lib.";
+ case ELIBBAD:
+ return "Accessing a corrupted shared lib.";
+ case ELIBSCN:
+ return ".lib section in a.out corrupted.";
+ case ELIBMAX:
+ return "Attempting to link in too many libs.";
+ case ELIBEXEC:
+ return "Attempting to exec a shared library.";
+ case EILSEQ:
+ return "Illegal byte sequence.";
+ case ENOSYS:
+ return "Unsupported file system operation";
+ case ELOOP:
+ return "Symbolic link loop";
+ case ERESTART:
+ return "Restartable system call";
+ case ESTRPIPE:
+ return "if pipe/FIFO, don't sleep in stream head";
+ case ENOTEMPTY:
+ return "directory not empty";
+ case EUSERS:
+ return "Too many users (for UFS)";
+ case ENOTSOCK:
+ return "Socket operation on non-socket";
+ case EDESTADDRREQ:
+ return "Destination address required";
+ case EMSGSIZE:
+ return "Message too long";
+ case EPROTOTYPE:
+ return "Protocol wrong type for socket";
+ case ENOPROTOOPT:
+ return "Protocol not available";
+ case EPROTONOSUPPORT:
+ return "Protocol not supported";
+ case ESOCKTNOSUPPORT:
+ return "Socket type not supported";
+ case EPFNOSUPPORT:
+ return "Protocol family not supported";
+ case EAFNOSUPPORT:
+ return "Address family not supported by protocol family";
+ case EADDRINUSE:
+ return "Address already in use";
+ case EADDRNOTAVAIL:
+ return "Can't assign requested address";
+ case ENETDOWN:
+ return "Network is down";
+ case ENETUNREACH:
+ return "Network is unreachable";
+ case ENETRESET:
+ return "Network dropped connection because of reset";
+ case ECONNABORTED:
+ return "Software caused connection abort";
+ case ECONNRESET:
+ return "Connection reset by peer";
+ case ENOBUFS:
+ return "No buffer space available";
+ case EISCONN:
+ return "Socket is already connected";
+ case ENOTCONN:
+ return "Socket is not connected";
+ case ESHUTDOWN:
+ return "Can't send after socket shutdown";
+ case ETOOMANYREFS:
+ return "Too many references: can't splice";
+ case ETIMEDOUT:
+ return "Connection timed out";
+ case ECONNREFUSED:
+ return "Connection refused";
+ case EHOSTDOWN:
+ return "Host is down";
+ case EHOSTUNREACH:
+ return "No route to host";
+ case EALREADY:
+ return "operation already in progress";
+ case EINPROGRESS:
+ return "operation now in progress";
+ case ESTALE:
+ return "Stale NFS file handle";
+ default:
+ return 0;
+ }
+}
diff --git a/user/lib/libc/strtol.c b/user/lib/libc/strtol.c
new file mode 100644
index 0000000..f33a3ae
--- /dev/null
+++ b/user/lib/libc/strtol.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+/*
+ * Convert a string to a long integer.
+ *
+ * Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+long strtol(const char *__restrict nptr, char **__restrict endptr, int base)
+{
+ const char *s;
+ unsigned long acc;
+ char c;
+ unsigned long cutoff;
+ int neg, any, cutlim;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ s = nptr;
+ do
+ {
+ c = *s++;
+ } while (isspace((unsigned char)c));
+ if (c == '-')
+ {
+ neg = 1;
+ c = *s++;
+ }
+ else
+ {
+ neg = 0;
+ if (c == '+')
+ {
+ c = *s++;
+ }
+ }
+ if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X') &&
+ ((s[1] >= '0' && s[1] <= '9') || (s[1] >= 'A' && s[1] <= 'F') ||
+ (s[1] >= 'a' && s[1] <= 'f')))
+ {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ {
+ base = c == '0' ? 8 : 10;
+ }
+ acc = any = 0;
+ if (base < 2 || base > 36)
+ {
+ goto noconv;
+ }
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set 'any' if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? (unsigned long)-(LONG_MIN + LONG_MAX) + LONG_MAX : LONG_MAX;
+ cutlim = cutoff % base;
+ cutoff /= base;
+ for (;; c = *s++)
+ {
+ if (c >= '0' && c <= '9')
+ {
+ c -= '0';
+ }
+ else if (c >= 'A' && c <= 'Z')
+ {
+ c -= 'A' - 10;
+ }
+ else if (c >= 'a' && c <= 'z')
+ {
+ c -= 'a' - 10;
+ }
+ else
+ {
+ break;
+ }
+ if (c >= base)
+ {
+ break;
+ }
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ {
+ any = -1;
+ }
+ else
+ {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0)
+ {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ errno = ERANGE;
+ }
+ else if (!any)
+ {
+ noconv:
+ errno = EINVAL;
+ }
+ else if (neg)
+ {
+ acc = -acc;
+ }
+ if (endptr != NULL)
+ {
+ *endptr = (char *)(any ? s - 1 : nptr);
+ }
+ return (acc);
+}
diff --git a/user/lib/libc/syscall.c b/user/lib/libc/syscall.c
new file mode 100644
index 0000000..3d921d1
--- /dev/null
+++ b/user/lib/libc/syscall.c
@@ -0,0 +1,391 @@
+#include "sys/types.h"
+
+#include "stdlib.h"
+#include "string.h"
+#include "unistd.h"
+
+#include "stdio.h"
+#include "weenix/trap.h"
+
+#include "dirent.h"
+
+static void *__curbrk = NULL;
+#define MAX_EXIT_HANDLERS 32
+
+static void (*atexit_func[MAX_EXIT_HANDLERS])();
+
+static int atexit_handlers = 0;
+
+void *sbrk(intptr_t incr)
+{
+ uintptr_t oldbrk;
+
+ /* If we don't have a saved break, find it from the kernel */
+ if (!__curbrk)
+ {
+ if (0 > (long)(__curbrk = (void *)trap(SYS_brk, (uintptr_t)NULL)))
+ {
+ return (void *)-1;
+ }
+ }
+
+ oldbrk = (uintptr_t)__curbrk;
+
+ /* Increment or decrement the saved break */
+
+ if (incr < 0)
+ {
+ if ((uintptr_t)-incr > oldbrk)
+ {
+ return (void *)-1;
+ }
+ else if (brk((void *)(oldbrk - (uintptr_t)-incr)) < 0)
+ {
+ return (void *)-1;
+ }
+ }
+ else if (incr > 0)
+ {
+ if (brk((void *)(oldbrk + (uintptr_t)incr)) < 0)
+ {
+ return (void *)-1;
+ }
+ }
+ return (void *)oldbrk;
+}
+
+int brk(void *addr)
+{
+ if (NULL == addr)
+ {
+ return -1;
+ }
+ void *newbrk = (void *)trap(SYS_brk, (uintptr_t)addr);
+ if (newbrk == (void *)-1)
+ {
+ return -1;
+ }
+ __curbrk = newbrk;
+ return 0;
+}
+
+pid_t fork(void) { return (pid_t)trap(SYS_fork, 0); }
+
+int atexit(void (*func)(void))
+{
+ if (atexit_handlers < MAX_EXIT_HANDLERS)
+ {
+ atexit_func[atexit_handlers++] = func;
+ return 0;
+ }
+
+ return 1;
+}
+
+__attribute__((noreturn)) void exit(int status)
+{
+ while (atexit_handlers--)
+ {
+ atexit_func[atexit_handlers]();
+ }
+
+ fflush(NULL);
+ trap(SYS_exit, (ssize_t)status);
+ __builtin_unreachable();
+}
+
+void _Exit(int status)
+{
+ trap(SYS_exit, (ssize_t)status);
+ __builtin_unreachable();
+}
+
+int sched_yield(void) { return (int)trap(SYS_sched_yield, NULL); }
+
+pid_t wait(int *status)
+{
+ waitpid_args_t args;
+
+ args.wpa_pid = -1;
+ args.wpa_options = 0;
+ args.wpa_status = status;
+
+ return (int)trap(SYS_waitpid, (uintptr_t)&args);
+}
+
+pid_t waitpid(pid_t pid, int *status, int options)
+{
+ waitpid_args_t args;
+
+ args.wpa_pid = pid;
+ args.wpa_status = status;
+ args.wpa_options = options;
+
+ return (int)trap(SYS_waitpid, (uintptr_t)&args);
+}
+
+void thr_exit(int status) { trap(SYS_thr_exit, (ssize_t)status); }
+
+pid_t getpid(void) { return (int)trap(SYS_getpid, 0); }
+
+int halt(void) { return (int)trap(SYS_halt, 0); }
+
+void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off)
+{
+ mmap_args_t args;
+
+ args.mma_addr = addr;
+ args.mma_len = len;
+ args.mma_prot = prot;
+ args.mma_flags = flags;
+ args.mma_fd = fd;
+ args.mma_off = off;
+
+ return (void *)trap(SYS_mmap, (uintptr_t)&args);
+}
+
+int munmap(void *addr, size_t len)
+{
+ munmap_args_t args;
+
+ args.addr = addr;
+ args.len = len;
+
+ return (int)trap(SYS_munmap, (uintptr_t)&args);
+}
+
+int debug(const char *str)
+{
+ argstr_t argstr;
+ argstr.as_len = strlen(str);
+ argstr.as_str = str;
+ return (int)trap(SYS_debug, (uintptr_t)&argstr);
+}
+
+void sync(void) { trap(SYS_sync, NULL); }
+
+int open(const char *filename, int flags, int mode)
+{
+ open_args_t args;
+
+ args.filename.as_len = strlen(filename);
+ args.filename.as_str = filename;
+ args.flags = flags;
+ args.mode = mode;
+
+ return (int)trap(SYS_open, (uintptr_t)&args);
+}
+
+off_t lseek(int fd, off_t offset, int whence)
+{
+ lseek_args_t args;
+
+ args.fd = fd;
+ args.offset = offset;
+ args.whence = whence;
+
+ return (int)trap(SYS_lseek, (uintptr_t)&args);
+}
+
+ssize_t read(int fd, void *buf, size_t nbytes)
+{
+ read_args_t args;
+
+ args.fd = fd;
+ args.buf = buf;
+ args.nbytes = nbytes;
+
+ return trap(SYS_read, (uintptr_t)&args);
+}
+
+ssize_t write(int fd, const void *buf, size_t nbytes)
+{
+ write_args_t args;
+
+ args.fd = fd;
+ args.buf = (void *)buf;
+ args.nbytes = nbytes;
+
+ return trap(SYS_write, (uintptr_t)&args);
+}
+
+int close(int fd) { return (int)trap(SYS_close, (ssize_t)fd); }
+
+int dup(int fd) { return (int)trap(SYS_dup, (ssize_t)fd); }
+
+int dup2(int ofd, int nfd)
+{
+ dup2_args_t args;
+
+ args.ofd = ofd;
+ args.nfd = nfd;
+
+ return (int)trap(SYS_dup2, (uintptr_t)&args);
+}
+
+int mkdir(const char *path, int mode)
+{
+ mkdir_args_t args;
+
+ args.path.as_len = strlen(path);
+ args.path.as_str = path;
+ args.mode = mode;
+
+ return (int)trap(SYS_mkdir, (uintptr_t)&args);
+}
+
+int rmdir(const char *path)
+{
+ argstr_t args;
+ args.as_len = strlen(path);
+ args.as_str = path;
+ return (int)trap(SYS_rmdir, (uintptr_t)&args);
+}
+
+int unlink(const char *path)
+{
+ argstr_t args;
+ args.as_len = strlen(path);
+ args.as_str = path;
+ return (int)trap(SYS_unlink, (uintptr_t)&args);
+}
+
+int link(const char *from, const char *to)
+{
+ link_args_t args;
+
+ args.from.as_len = strlen(from);
+ args.from.as_str = from;
+ args.to.as_len = strlen(to);
+ args.to.as_str = to;
+
+ return (int)trap(SYS_link, (uintptr_t)&args);
+}
+
+int rename(const char *oldpath, const char *newpath)
+{
+ rename_args_t args;
+
+ args.oldpath.as_len = strlen(oldpath);
+ args.oldpath.as_str = oldpath;
+ args.newpath.as_len = strlen(newpath);
+ args.newpath.as_str = newpath;
+
+ return (int)trap(SYS_rename, (uintptr_t)&args);
+}
+
+int chdir(const char *path)
+{
+ argstr_t args;
+ args.as_len = strlen(path);
+ args.as_str = path;
+ return (int)trap(SYS_chdir, (uintptr_t)&args);
+}
+
+size_t get_free_mem(void) { return (size_t)trap(SYS_get_free_mem, 0); }
+
+int execve(const char *filename, char *const argv[], char *const envp[])
+{
+ execve_args_t args;
+
+ size_t i;
+
+ args.filename.as_len = strlen(filename);
+ args.filename.as_str = filename;
+
+ /* Build argv vector */
+ for (i = 0; argv[i] != NULL; i++)
+ ;
+ args.argv.av_len = i;
+ args.argv.av_vec = malloc((args.argv.av_len + 1) * sizeof(argstr_t));
+ for (i = 0; argv[i] != NULL; i++)
+ {
+ args.argv.av_vec[i].as_len = strlen(argv[i]);
+ args.argv.av_vec[i].as_str = argv[i];
+ }
+ args.argv.av_vec[i].as_len = 0;
+ args.argv.av_vec[i].as_str = NULL;
+
+ /* Build envp vector */
+ for (i = 0; envp[i] != NULL; i++)
+ ;
+ args.envp.av_len = i;
+ args.envp.av_vec = malloc((args.envp.av_len + 1) * sizeof(argstr_t));
+ for (i = 0; envp[i] != NULL; i++)
+ {
+ args.envp.av_vec[i].as_len = strlen(envp[i]);
+ args.envp.av_vec[i].as_str = envp[i];
+ }
+ args.envp.av_vec[i].as_len = 0;
+ args.envp.av_vec[i].as_str = NULL;
+
+ /* Note that we don't need to worry about freeing since we are going to exec
+ * (so all our memory will be cleaned up) */
+
+ return (int)trap(SYS_execve, (uintptr_t)&args);
+}
+
+void thr_set_errno(int n) { trap(SYS_set_errno, (ssize_t)n); }
+
+int thr_errno(void) { return (int)trap(SYS_errno, 0); }
+
+int getdents(int fd, dirent_t *dir, size_t size)
+{
+ getdents_args_t args;
+
+ args.fd = fd;
+ args.dirp = dir;
+ args.count = size;
+
+ return (int)trap(SYS_getdents, (uintptr_t)&args);
+}
+
+#ifdef __MOUNTING__
+int mount(const char *spec, const char *dir, const char *fstype)
+{
+ mount_args_t args;
+
+ args.spec.as_len = strlen(spec);
+ args.spec.as_str = spec;
+ args.dir.as_len = strlen(dir);
+ args.dir.as_str = dir;
+ args.fstype.as_len = strlen(fstype);
+ args.fstype.as_str = fstype;
+
+ return (int)trap(SYS_mount, (uintptr_t)&args);
+}
+
+int umount(const char *path)
+{
+ argstr_t argstr;
+
+ argstr.as_len = strlen(path);
+ argstr.as_str = path;
+
+ return (int)trap(SYS_umount, (uintptr_t)&argstr);
+}
+#endif /* MOUNTING */
+
+int stat(const char *path, stat_t *buf)
+{
+ stat_args_t args;
+
+ args.path.as_len = strlen(path);
+ args.path.as_str = path;
+ args.buf = buf;
+
+ return (int)trap(SYS_stat, (uintptr_t)&args);
+}
+
+int pipe(int pipefd[2]) { return (int)trap(SYS_pipe, (uintptr_t)pipefd); }
+
+int uname(struct utsname *buf) { return (int)trap(SYS_uname, (uintptr_t)buf); }
+
+time_t time(time_t *tloc) { return (time_t)trap(SYS_time, (uintptr_t)tloc); }
+
+long usleep(useconds_t usec)
+{
+ usleep_args_t args;
+ args.usec = usec;
+ return (long)trap(SYS_usleep, (uintptr_t)&args);
+} \ No newline at end of file
diff --git a/user/lib/libc/vsnprintf.c b/user/lib/libc/vsnprintf.c
new file mode 100644
index 0000000..a73c75e
--- /dev/null
+++ b/user/lib/libc/vsnprintf.c
@@ -0,0 +1,566 @@
+/*
+ ****************************************************************************
+ * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: printf.c
+ * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
+ * Changes: Grzegorz Milos (gm281@cam.ac.uk)
+ *
+ * Date: Aug 2003, Aug 2005
+ *
+ * Environment: Xen Minimal OS
+ * Description: Library functions for printing
+ * (freebsd port, mainly sys/subr_prf.c)
+ *
+ ****************************************************************************
+ *
+ *-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $
+ */
+
+#include "ctype.h"
+#include "stdarg.h"
+#include "stdio.h"
+#include "string.h"
+
+static int skip_atoi(const char **s)
+{
+ int i = 0;
+
+ while (isdigit(**s))
+ i = i * 10 + *((*s)++) - '0';
+ return i;
+}
+
+#define ZEROPAD 1 /* pad with zero */
+#define SIGN 2 /* unsigned/signed long */
+#define PLUS 4 /* show plus */
+#define SPACE 8 /* space if plus */
+#define LEFT 16 /* left justified */
+#define SPECIAL 32 /* 0x */
+#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
+
+static char *number(char *buf, char *end, long long num, int base, int size,
+ int precision, int type)
+{
+ char c, sign, tmp[66];
+ const char *digits;
+ const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ int i;
+
+ digits = (type & LARGE) ? large_digits : small_digits;
+ if (type & LEFT)
+ {
+ type &= ~ZEROPAD;
+ }
+ if (base < 2 || base > 36)
+ {
+ return buf;
+ }
+ c = (type & ZEROPAD) ? '0' : ' ';
+ sign = 0;
+ if (type & SIGN)
+ {
+ if (num < 0)
+ {
+ sign = '-';
+ num = -num;
+ size--;
+ }
+ else if (type & PLUS)
+ {
+ sign = '+';
+ size--;
+ }
+ else if (type & SPACE)
+ {
+ sign = ' ';
+ size--;
+ }
+ }
+ if (type & SPECIAL)
+ {
+ if (base == 16)
+ {
+ size -= 2;
+ }
+ else if (base == 8)
+ {
+ size--;
+ }
+ }
+ i = 0;
+ if (num == 0)
+ {
+ tmp[i++] = '0';
+ }
+ else
+ {
+ /* XXX KAF: force unsigned mod and div. */
+ /* XXX kernel does not support long long division */
+ unsigned long long num2 = (unsigned long long)num;
+ unsigned int base2 = (unsigned int)base;
+ while (num2 != 0)
+ {
+ tmp[i++] = digits[num2 % base2];
+ num2 /= base2;
+ }
+ }
+ if (i > precision)
+ {
+ precision = i;
+ }
+ size -= precision;
+ if (!(type & (ZEROPAD + LEFT)))
+ {
+ while (size-- > 0)
+ {
+ if (buf <= end)
+ {
+ *buf = ' ';
+ }
+ ++buf;
+ }
+ }
+ if (sign)
+ {
+ if (buf <= end)
+ {
+ *buf = sign;
+ }
+ ++buf;
+ }
+ if (type & SPECIAL)
+ {
+ if (base == 8)
+ {
+ if (buf <= end)
+ {
+ *buf = '0';
+ }
+ ++buf;
+ }
+ else if (base == 16)
+ {
+ if (buf <= end)
+ {
+ *buf = '0';
+ }
+ ++buf;
+ if (buf <= end)
+ {
+ *buf = digits[33];
+ }
+ ++buf;
+ }
+ }
+ if (!(type & LEFT))
+ {
+ while (size-- > 0)
+ {
+ if (buf <= end)
+ {
+ *buf = c;
+ }
+ ++buf;
+ }
+ }
+ while (i < precision--)
+ {
+ if (buf <= end)
+ {
+ *buf = '0';
+ }
+ ++buf;
+ }
+ while (i-- > 0)
+ {
+ if (buf <= end)
+ {
+ *buf = tmp[i];
+ }
+ ++buf;
+ }
+ while (size-- > 0)
+ {
+ if (buf <= end)
+ {
+ *buf = ' ';
+ }
+ ++buf;
+ }
+ return buf;
+}
+
+/* TODO This is a copy of the kernel vsnprintf. It has (almost)
+ * no error checking. It also is completely unable to handle
+ * floating point. - alvin */
+/**
+ * vsnprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want snprintf instead.
+ */
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ int len;
+ unsigned long long num;
+ int i, base;
+ char *str, *end, c;
+ const char *s;
+
+ int flags; /* flags to number() */
+
+ int field_width; /* width of output field */
+ int precision; /* min. # of digits for integers; max
+ number of chars for from string */
+ int qualifier; /* 'h', 'l', or 'L' for integer fields */
+ /* 'z' support added 23/7/1999 S.H. */
+ /* 'z' changed to 'Z' --davidm 1/25/99 */
+
+ str = buf;
+ end = buf + size - 1;
+
+ if (end < buf - 1)
+ {
+ end = ((void *)-1);
+ size = end - buf + 1;
+ }
+
+ for (; *fmt; ++fmt)
+ {
+ if (*fmt != '%')
+ {
+ if (str <= end)
+ {
+ *str = *fmt;
+ }
+ ++str;
+ continue;
+ }
+
+ /* process flags */
+ flags = 0;
+ repeat:
+ ++fmt; /* this also skips first '%' */
+ switch (*fmt)
+ {
+ case '-':
+ flags |= LEFT;
+ goto repeat;
+ case '+':
+ flags |= PLUS;
+ goto repeat;
+ case ' ':
+ flags |= SPACE;
+ goto repeat;
+ case '#':
+ flags |= SPECIAL;
+ goto repeat;
+ case '0':
+ flags |= ZEROPAD;
+ goto repeat;
+ }
+
+ /* get field width */
+ field_width = -1;
+ if (isdigit(*fmt))
+ {
+ field_width = skip_atoi(&fmt);
+ }
+ else if (*fmt == '*')
+ {
+ ++fmt;
+ /* it's the next argument */
+ field_width = va_arg(args, int);
+ if (field_width < 0)
+ {
+ field_width = -field_width;
+ flags |= LEFT;
+ }
+ }
+
+ /* get the precision */
+ precision = -1;
+ if (*fmt == '.')
+ {
+ ++fmt;
+ if (isdigit(*fmt))
+ {
+ precision = skip_atoi(&fmt);
+ }
+ else if (*fmt == '*')
+ {
+ ++fmt;
+ /* it's the next argument */
+ precision = va_arg(args, int);
+ }
+ if (precision < 0)
+ {
+ precision = 0;
+ }
+ }
+
+ /* get the conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z')
+ {
+ qualifier = *fmt;
+ ++fmt;
+ if (qualifier == 'l' && *fmt == 'l')
+ {
+ qualifier = 'L';
+ ++fmt;
+ }
+ }
+ if (*fmt == 'q')
+ {
+ qualifier = 'L';
+ ++fmt;
+ }
+
+ /* default base */
+ base = 10;
+
+ switch (*fmt)
+ {
+ case 'c':
+ if (!(flags & LEFT))
+ {
+ while (--field_width > 0)
+ {
+ if (str <= end)
+ {
+ *str = ' ';
+ }
+ ++str;
+ }
+ }
+ c = (unsigned char)va_arg(args, int);
+ if (str <= end)
+ {
+ *str = c;
+ }
+ ++str;
+ while (--field_width > 0)
+ {
+ if (str <= end)
+ {
+ *str = ' ';
+ }
+ ++str;
+ }
+ continue;
+
+ case 's':
+ s = va_arg(args, char *);
+ if (!s)
+ {
+ s = "<NULL>";
+ }
+
+ len = strnlen(s, precision);
+
+ if (!(flags & LEFT))
+ {
+ while (len < field_width--)
+ {
+ if (str <= end)
+ {
+ *str = ' ';
+ }
+ ++str;
+ }
+ }
+ for (i = 0; i < len; ++i)
+ {
+ if (str <= end)
+ {
+ *str = *s;
+ }
+ ++str;
+ ++s;
+ }
+ while (len < field_width--)
+ {
+ if (str <= end)
+ {
+ *str = ' ';
+ }
+ ++str;
+ }
+ continue;
+
+ case 'p':
+ if (field_width == -1)
+ {
+ field_width = 2 * sizeof(void *);
+ flags |= ZEROPAD;
+ }
+ str = number(str, end, (unsigned long)va_arg(args, void *), 16,
+ field_width, precision, flags);
+ continue;
+
+ case 'n':
+ /* FIXME:
+ * What does C99 say about the overflow case here? */
+ if (qualifier == 'l')
+ {
+ long *ip = va_arg(args, long *);
+ *ip = (str - buf);
+ }
+ else if (qualifier == 'Z')
+ {
+ size_t *ip = va_arg(args, size_t *);
+ *ip = (str - buf);
+ }
+ else
+ {
+ int *ip = va_arg(args, int *);
+ *ip = (str - buf);
+ }
+ continue;
+
+ case '%':
+ if (str <= end)
+ {
+ *str = '%';
+ }
+ ++str;
+ continue;
+
+ /* integer number formats - set up the flags and "break" */
+ case 'o':
+ base = 8;
+ break;
+
+ case 'X':
+ flags |= LARGE;
+ base = 16;
+ break;
+ case 'x':
+ base = 16;
+ break;
+
+ case 'd':
+ case 'i':
+ flags |= SIGN;
+ case 'u':
+ break;
+ /* Added - TODO - alvin */
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ return -1;
+
+ default:
+ if (str <= end)
+ {
+ *str = '%';
+ }
+ ++str;
+ if (*fmt)
+ {
+ if (str <= end)
+ {
+ *str = *fmt;
+ }
+ ++str;
+ }
+ else
+ {
+ --fmt;
+ }
+ continue;
+ }
+ if (qualifier == 'L')
+ {
+ num = va_arg(args, long long);
+ }
+ else if (qualifier == 'l')
+ {
+ num = va_arg(args, unsigned long);
+ if (flags & SIGN)
+ {
+ num = (signed long)num;
+ }
+ }
+ else if (qualifier == 'Z')
+ {
+ num = va_arg(args, size_t);
+ }
+ else if (qualifier == 'h')
+ {
+ num = (unsigned short)va_arg(args, int);
+ if (flags & SIGN)
+ {
+ num = (signed short)num;
+ }
+ }
+ else
+ {
+ num = va_arg(args, unsigned int);
+ if (flags & SIGN)
+ {
+ num = (signed int)num;
+ }
+ }
+
+ str = number(str, end, num, base, field_width, precision, flags);
+ }
+ if (str <= end)
+ {
+ *str = '\0';
+ }
+ else if (size > 0)
+ {
+ /* don't write out a null byte if the buf size is zero */
+ *end = '\0';
+ }
+ /* the trailing null byte doesn't count towards the total
+ * ++str;
+ */
+ return str - buf;
+}
diff --git a/user/lib/libc/vsscanf.c b/user/lib/libc/vsscanf.c
new file mode 100644
index 0000000..ab3beac
--- /dev/null
+++ b/user/lib/libc/vsscanf.c
@@ -0,0 +1,447 @@
+/*
+ ****************************************************************************
+ * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: printf.c
+ * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
+ * Changes: Grzegorz Milos (gm281@cam.ac.uk)
+ *
+ * Date: Aug 2003, Aug 2005
+ *
+ * Environment: Xen Minimal OS
+ * Description: Library functions for printing
+ * (freebsd port, mainly sys/subr_prf.c)
+ *
+ ****************************************************************************
+ *
+ *-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $
+ */
+#include "ctype.h"
+#include "limits.h"
+#include "stdarg.h"
+#include "stddef.h"
+
+static int skip_atoi(const char **s)
+{
+ int i = 0;
+
+ while (isdigit(**s))
+ i = i * 10 + *((*s)++) - '0';
+ return i;
+}
+
+/**
+ * simple_strtoul - convert a string to an unsigned long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long result = 0, value;
+
+ if (!base)
+ {
+ base = 10;
+ if (*cp == '0')
+ {
+ base = 8;
+ cp++;
+ if ((*cp == 'x') && isxdigit(cp[1]))
+ {
+ cp++;
+ base = 16;
+ }
+ }
+ }
+ while (isxdigit(*cp) &&
+ (value = isdigit(*cp) ? *cp - '0' : toupper(*cp) - 'A' + 10) <
+ base)
+ {
+ result = result * base + value;
+ cp++;
+ }
+ if (endp)
+ {
+ *endp = (char *)cp;
+ }
+ return result;
+}
+
+/**
+ * simple_strtol - convert a string to a signed long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long simple_strtol(const char *cp, char **endp, unsigned int base)
+{
+ if (*cp == '-')
+ {
+ return -simple_strtoul(cp + 1, endp, base);
+ }
+ return simple_strtoul(cp, endp, base);
+}
+
+/**
+ * simple_strtoull - convert a string to an unsigned long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long long simple_strtoull(const char *cp, char **endp,
+ unsigned int base)
+{
+ unsigned long long result = 0, value;
+
+ if (!base)
+ {
+ base = 10;
+ if (*cp == '0')
+ {
+ base = 8;
+ cp++;
+ if ((*cp == 'x') && isxdigit(cp[1]))
+ {
+ cp++;
+ base = 16;
+ }
+ }
+ }
+ while (isxdigit(*cp) &&
+ (value = isdigit(*cp) ? *cp - '0'
+ : (islower(*cp) ? toupper(*cp) : *cp) - 'A' +
+ 10) < base)
+ {
+ result = result * base + value;
+ cp++;
+ }
+ if (endp)
+ {
+ *endp = (char *)cp;
+ }
+ return result;
+}
+
+/**
+ * simple_strtoll - convert a string to a signed long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long long simple_strtoll(const char *cp, char **endp, unsigned int base)
+{
+ if (*cp == '-')
+ {
+ return -simple_strtoull(cp + 1, endp, base);
+ }
+ return simple_strtoull(cp, endp, base);
+}
+
+/**
+ * vsscanf - Unformat a buffer into a list of arguments
+ * @buf: input buffer
+ * @fmt: format of buffer
+ * @args: arguments
+ */
+int vsscanf(const char *buf, const char *fmt, va_list args)
+{
+ const char *str = buf;
+ char *next;
+ char digit;
+ int num = 0;
+ int qualifier;
+ int base;
+ int field_width;
+ int is_sign = 0;
+
+ while (*fmt && *str)
+ {
+ /* skip any white space in format */
+ /* white space in format matchs any amount of
+ * white space, including none, in the input.
+ */
+ if (isspace(*fmt))
+ {
+ while (isspace(*fmt))
+ ++fmt;
+ while (isspace(*str))
+ ++str;
+ }
+
+ /* anything that is not a conversion must match exactly */
+ if (*fmt != '%' && *fmt)
+ {
+ if (*fmt++ != *str++)
+ {
+ break;
+ }
+ continue;
+ }
+
+ if (!*fmt)
+ {
+ break;
+ }
+ ++fmt;
+
+ /* skip this conversion.
+ * advance both strings to next white space
+ */
+ if (*fmt == '*')
+ {
+ while (!isspace(*fmt) && *fmt)
+ fmt++;
+ while (!isspace(*str) && *str)
+ str++;
+ continue;
+ }
+
+ /* get field width */
+ field_width = -1;
+ if (isdigit(*fmt))
+ {
+ field_width = skip_atoi(&fmt);
+ }
+
+ /* get conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z' ||
+ *fmt == 'z')
+ {
+ qualifier = *fmt++;
+ if (qualifier == *fmt)
+ {
+ if (qualifier == 'h')
+ {
+ qualifier = 'H';
+ fmt++;
+ }
+ else if (qualifier == 'l')
+ {
+ qualifier = 'L';
+ fmt++;
+ }
+ }
+ }
+ base = 10;
+ is_sign = 0;
+
+ if (!*fmt || !*str)
+ {
+ break;
+ }
+
+ switch (*fmt++)
+ {
+ case 'c':
+ {
+ char *s = (char *)va_arg(args, char *);
+ if (field_width == -1)
+ {
+ field_width = 1;
+ }
+ do
+ {
+ *s++ = *str++;
+ } while (--field_width > 0 && *str);
+ num++;
+ }
+ continue;
+ case 's':
+ {
+ char *s = (char *)va_arg(args, char *);
+ if (field_width == -1)
+ {
+ field_width = INT_MAX;
+ }
+ /* first, skip leading white space in buffer */
+ while (isspace(*str))
+ str++;
+
+ /* now copy until next white space */
+ while (*str && !isspace(*str) && field_width--)
+ {
+ *s++ = *str++;
+ }
+ *s = '\0';
+ num++;
+ }
+ continue;
+ case 'n':
+ /* return number of characters read so far */
+ {
+ int *i = (int *)va_arg(args, int *);
+ *i = str - buf;
+ }
+ continue;
+ case 'o':
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ base = 16;
+ break;
+ case 'i':
+ base = 0;
+ is_sign = 1;
+ break;
+ case 'd':
+ is_sign = 1;
+ break;
+ case 'u':
+ break;
+ case '%':
+ /* looking for '%' in str */
+ if (*str++ != '%')
+ {
+ return num;
+ }
+ continue;
+ default:
+ /* invalid format; stop here */
+ return num;
+ }
+
+ /* have some sort of integer conversion.
+ * first, skip white space in buffer.
+ */
+ while (isspace(*str))
+ str++;
+
+ digit = *str;
+ if (is_sign && digit == '-')
+ {
+ digit = *(str + 1);
+ }
+
+ if (!digit || (base == 16 && !isxdigit(digit)) ||
+ (base == 10 && !isdigit(digit)) ||
+ (base == 8 && (!isdigit(digit) || digit > '7')) ||
+ (base == 0 && !isdigit(digit)))
+ {
+ break;
+ }
+
+ switch (qualifier)
+ {
+ case 'H': /* that's 'hh' in format */
+ if (is_sign)
+ {
+ signed char *s = (signed char *)va_arg(args, signed char *);
+ *s = (signed char)simple_strtol(str, &next, base);
+ }
+ else
+ {
+ unsigned char *s =
+ (unsigned char *)va_arg(args, unsigned char *);
+ *s = (unsigned char)simple_strtoul(str, &next, base);
+ }
+ break;
+ case 'h':
+ if (is_sign)
+ {
+ short *s = (short *)va_arg(args, short *);
+ *s = (short)simple_strtol(str, &next, base);
+ }
+ else
+ {
+ unsigned short *s =
+ (unsigned short *)va_arg(args, unsigned short *);
+ *s = (unsigned short)simple_strtoul(str, &next, base);
+ }
+ break;
+ case 'l':
+ if (is_sign)
+ {
+ long *l = (long *)va_arg(args, long *);
+ *l = simple_strtol(str, &next, base);
+ }
+ else
+ {
+ unsigned long *l =
+ (unsigned long *)va_arg(args, unsigned long *);
+ *l = simple_strtoul(str, &next, base);
+ }
+ break;
+ case 'L':
+ if (is_sign)
+ {
+ long long *l = (long long *)va_arg(args, long long *);
+ *l = simple_strtoll(str, &next, base);
+ }
+ else
+ {
+ unsigned long long *l = (unsigned long long *)va_arg(
+ args, unsigned long long *);
+ *l = simple_strtoull(str, &next, base);
+ }
+ break;
+ case 'Z':
+ case 'z':
+ {
+ size_t *s = (size_t *)va_arg(args, size_t *);
+ *s = (size_t)simple_strtoul(str, &next, base);
+ }
+ break;
+ default:
+ if (is_sign)
+ {
+ int *i = (int *)va_arg(args, int *);
+ *i = (int)simple_strtol(str, &next, base);
+ }
+ else
+ {
+ unsigned int *i =
+ (unsigned int *)va_arg(args, unsigned int *);
+ *i = (unsigned int)simple_strtoul(str, &next, base);
+ }
+ break;
+ }
+ num++;
+
+ if (!next)
+ {
+ break;
+ }
+ str = next;
+ }
+ return num;
+}