diff options
Diffstat (limited to 'user/lib/libc')
-rw-r--r-- | user/lib/libc/entry.S | 17 | ||||
-rw-r--r-- | user/lib/libc/errno.c | 3 | ||||
-rw-r--r-- | user/lib/libc/malloc.c | 1326 | ||||
-rw-r--r-- | user/lib/libc/printf.c | 169 | ||||
-rw-r--r-- | user/lib/libc/quad.c | 414 | ||||
-rw-r--r-- | user/lib/libc/rand.c | 22 | ||||
-rw-r--r-- | user/lib/libc/scanf.c | 73 | ||||
-rw-r--r-- | user/lib/libc/stream.c | 11 | ||||
-rw-r--r-- | user/lib/libc/string.c | 509 | ||||
-rw-r--r-- | user/lib/libc/strtol.c | 162 | ||||
-rw-r--r-- | user/lib/libc/syscall.c | 391 | ||||
-rw-r--r-- | user/lib/libc/vsnprintf.c | 566 | ||||
-rw-r--r-- | user/lib/libc/vsscanf.c | 447 |
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; +} |