summaryrefslogtreecommitdiff
path: root/user/lib
diff options
context:
space:
mode:
Diffstat (limited to 'user/lib')
-rw-r--r--user/lib/alloc.c215
-rw-r--r--user/lib/atox.c30
-rw-r--r--user/lib/bound.c12
-rw-r--r--user/lib/btoa.c43
-rw-r--r--user/lib/ctoi.c14
-rw-r--r--user/lib/delay.c8
-rw-r--r--user/lib/entry.S25
-rw-r--r--user/lib/isdigit.c6
-rw-r--r--user/lib/isspace.c16
-rw-r--r--user/lib/itoc.c10
-rw-r--r--user/lib/memcmp.c9
-rw-r--r--user/lib/memcpy.c10
-rw-r--r--user/lib/memmove.c20
-rw-r--r--user/lib/memset.c10
-rw-r--r--user/lib/printf.c602
-rw-r--r--user/lib/spawn.c32
-rw-r--r--user/lib/stpcpy.c9
-rw-r--r--user/lib/stpncpy.c10
-rw-r--r--user/lib/strcat.c7
-rw-r--r--user/lib/strcpy.c9
-rw-r--r--user/lib/strlen.c9
-rw-r--r--user/lib/strncmp.c11
-rw-r--r--user/lib/strncpy.c10
-rw-r--r--user/lib/strtoux.c44
-rw-r--r--user/lib/strtox.c53
-rw-r--r--user/lib/syscall.S93
-rw-r--r--user/lib/timetostr.c143
-rw-r--r--user/lib/uxtoa.c27
-rw-r--r--user/lib/xtoa.c31
29 files changed, 1518 insertions, 0 deletions
diff --git a/user/lib/alloc.c b/user/lib/alloc.c
new file mode 100644
index 0000000..49c762b
--- /dev/null
+++ b/user/lib/alloc.c
@@ -0,0 +1,215 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAGIC 0xBEEFCAFE
+#define PAGE_SIZE 4096
+
+struct page_header {
+ struct page_header *next;
+ struct page_header *prev;
+ size_t
+ node_number; // all headers on the same page alloc have the same node number (so they can be merged)
+ size_t
+ free; // free space after the node (if its the last node in the alloc block)
+ size_t used; // how much space this allocation is using
+ uint64_t magic;
+};
+
+static const size_t header_len = sizeof(struct page_header);
+static struct page_header *start_header = NULL;
+static struct page_header *end_header = NULL;
+
+static struct page_header *get_header(void *ptr)
+{
+ struct page_header *header =
+ (struct page_header *)((uintptr_t)ptr - header_len);
+
+ // PERF: do we want to make sure this pointer is paged
+ // before reading it???
+ if (header->magic != MAGIC) {
+ return NULL; // invalid pointer
+ }
+
+ return header;
+}
+
+static void *alloc_new(size_t size)
+{
+ size_t pages = ((size + header_len) / PAGE_SIZE) + 1;
+
+ // FIXME: use brk/sbrk
+ void *addr = alloc_pages(pages);
+ void *mem = (char *)addr + header_len;
+
+ size_t total = pages * PAGE_SIZE;
+ size_t free = total - (size + header_len);
+
+ if (addr == NULL) {
+ return NULL;
+ }
+
+ size_t node;
+ if (end_header != NULL) {
+ node = end_header->node_number + 1;
+ } else {
+ node = 0;
+ }
+
+ struct page_header *header = addr;
+ header->magic = 0xBEEFCAFE;
+ header->used = size;
+ header->free = free;
+ header->prev = end_header;
+ header->next = NULL;
+ header->node_number = node;
+
+ if (start_header == NULL) {
+ start_header = header;
+ }
+
+ if (end_header != NULL) {
+ end_header->next = header;
+ } else {
+ end_header = header;
+ }
+
+ return mem;
+}
+
+static void *alloc_block(size_t size, struct page_header *block)
+{
+ struct page_header *header =
+ (struct page_header *)((char *)block + block->used + header_len);
+
+ size_t free = block->free - (size + header_len);
+ block->free = 0;
+
+ header->magic = MAGIC;
+ header->used = size;
+ header->free = free;
+ header->prev = block;
+ header->next = block->next;
+ block->next = header;
+ header->node_number = block->node_number;
+
+ void *mem = (char *)header + header_len;
+
+ return mem;
+}
+
+void *malloc(size_t size)
+{
+ struct page_header *header = start_header;
+
+ for (; header != NULL; header = header->next) {
+ size_t free = header->free;
+ if (free < header_len)
+ continue;
+ if (size <=
+ (free - header_len)) { // we must be able to fit data + header
+ break;
+ }
+ }
+
+ if (header != NULL) {
+ return alloc_block(size, header);
+ } else {
+ return alloc_new(size);
+ }
+}
+
+void *realloc(void *src, size_t dst_len)
+{
+ struct page_header *header;
+ size_t src_len;
+ void *dst;
+
+ // realloc of 0 means free pointer
+ if (dst_len == 0) {
+ free(src);
+ return NULL;
+ }
+
+ // NULL src means allocate ptr
+ if (src == NULL) {
+ dst = malloc(dst_len);
+ return dst;
+ }
+
+ header = get_header(src);
+
+ if (header == NULL)
+ return NULL;
+
+ src_len = header->used;
+
+ if (src_len == 0)
+ return NULL;
+
+ dst = malloc(dst_len);
+
+ if (dst == NULL)
+ return NULL; // allocation failed
+
+ if (dst_len < src_len)
+ src_len = dst_len;
+
+ memcpy(dst, src, src_len);
+ free(src);
+
+ return dst;
+}
+
+void free(void *ptr)
+{
+ struct page_header *header;
+
+ if (ptr == NULL)
+ return;
+
+ header = get_header(ptr);
+
+ if (header == NULL)
+ return;
+
+ header->free += header->used;
+ header->used = 0;
+
+ struct page_header *neighbor;
+
+ // merge left
+ for (neighbor = header->prev; neighbor != NULL; neighbor = neighbor->prev) {
+ if (neighbor->node_number != header->node_number)
+ break;
+ if (neighbor->used && header->used)
+ break;
+ neighbor->free += header->free + header_len;
+ neighbor->next = header->next;
+ header = neighbor;
+ }
+
+ // merge right
+ for (neighbor = header->next; neighbor != NULL; neighbor = neighbor->next) {
+ if (neighbor->node_number != header->node_number)
+ break;
+ if (neighbor->used)
+ break;
+ header->free += neighbor->free + header_len;
+ header->next = neighbor->next;
+ }
+
+ if ((header->next == NULL ||
+ header->next->node_number != header->node_number) &&
+ (header->prev == NULL ||
+ header->prev->node_number != header->node_number) &&
+ header->used == 0) {
+ if (header->next)
+ header->next->prev = header->prev;
+ if (header->prev)
+ header->prev->next = header->next;
+ // FIXME: use brk/sbrk
+ free_pages(header);
+ }
+}
diff --git a/user/lib/atox.c b/user/lib/atox.c
new file mode 100644
index 0000000..c4bef59
--- /dev/null
+++ b/user/lib/atox.c
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+#include <ctype.h>
+
+#define ATOX(name, type) \
+ type name(const char *s) \
+ { \
+ for (; isspace(*s); s++) \
+ ; \
+ int neg = 0; \
+ switch (*s) { \
+ case '-': \
+ neg = 1; \
+ /* fallthrough */ \
+ case '+': \
+ s++; \
+ break; \
+ } \
+ type num = 0; \
+ for (; *s == '0'; s++) \
+ ; \
+ for (; isdigit(*s); s++) { \
+ num *= 10; \
+ num += *s - '0'; \
+ } \
+ return num * (neg ? -1 : 1); \
+ }
+
+ATOX(atoi, int)
+ATOX(atol, long int)
+ATOX(atoll, long long int)
diff --git a/user/lib/bound.c b/user/lib/bound.c
new file mode 100644
index 0000000..072a41a
--- /dev/null
+++ b/user/lib/bound.c
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+
+unsigned int bound(unsigned int min, unsigned int value, unsigned int max)
+{
+ if (value < min) {
+ value = min;
+ }
+ if (value > max) {
+ value = max;
+ }
+ return value;
+}
diff --git a/user/lib/btoa.c b/user/lib/btoa.c
new file mode 100644
index 0000000..fe5e275
--- /dev/null
+++ b/user/lib/btoa.c
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+
+static char suffixes[] = { 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q' };
+
+char *btoa(size_t bytes, char *buf)
+{
+ // no suffix if under 1K, print up to four digits
+ if (bytes < 1024) {
+ ultoa(bytes, buf, 10);
+ return buf;
+ }
+
+ // store one digit of remainder for decimal
+ unsigned int remainder;
+ // power of 1024
+ int power = 0;
+
+ // iterate until remaining bytes fits in three digits
+ while (bytes >= 1000) {
+ remainder = (bytes % 1024) * 10 / 1024;
+ bytes /= 1024;
+ power += 1;
+ }
+
+ // end of number
+ char *end;
+
+ if (bytes >= 10) {
+ // no decimal
+ end = ultoa(bytes, buf, 10);
+ } else {
+ // decimal
+ end = ultoa(bytes, buf, 10);
+ end[0] = '.';
+ end = ultoa(remainder, end + 1, 10);
+ }
+
+ // add suffix
+ end[0] = suffixes[power - 1];
+ end[1] = '\0';
+
+ return buf;
+}
diff --git a/user/lib/ctoi.c b/user/lib/ctoi.c
new file mode 100644
index 0000000..efe4fec
--- /dev/null
+++ b/user/lib/ctoi.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+
+int ctoi(char c)
+{
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else if (c >= 'A' && c <= 'Z') {
+ return c - 'A' + 10;
+ } else if (c >= 'a' && c <= 'z') {
+ return c - 'a' + 10;
+ } else {
+ return -1;
+ }
+}
diff --git a/user/lib/delay.c b/user/lib/delay.c
new file mode 100644
index 0000000..ff5ccc4
--- /dev/null
+++ b/user/lib/delay.c
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+
+void delay(int count)
+{
+ while (count-- > 0)
+ for (int i = 0; i < 100000; i++)
+ ;
+}
diff --git a/user/lib/entry.S b/user/lib/entry.S
new file mode 100644
index 0000000..87ad9c7
--- /dev/null
+++ b/user/lib/entry.S
@@ -0,0 +1,25 @@
+//
+// user-level startup routine
+//
+ .text
+ .globl _start
+ .globl main
+ .globl exit
+
+// entry point - this is where the kernel starts us running
+_start:
+ // we immediately call main()
+ call main
+
+ // if we come back from that, it means the user
+ // program didn't call exit(), in which case the
+ // value returned from main() is the exit status
+
+ // push that value onto the stack and call exit()
+ subl $12, %esp
+ pushl %eax
+ call exit
+
+ // if we come back from that, something bad has
+ // happened, so we just lock up
+1: jmp 1b
diff --git a/user/lib/isdigit.c b/user/lib/isdigit.c
new file mode 100644
index 0000000..aa93ced
--- /dev/null
+++ b/user/lib/isdigit.c
@@ -0,0 +1,6 @@
+#include <ctype.h>
+
+int isdigit(int c)
+{
+ return c >= '0' && c <= '9';
+}
diff --git a/user/lib/isspace.c b/user/lib/isspace.c
new file mode 100644
index 0000000..9e89d76
--- /dev/null
+++ b/user/lib/isspace.c
@@ -0,0 +1,16 @@
+#include <ctype.h>
+
+int isspace(int c)
+{
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\v':
+ case '\f':
+ case '\r':
+ case '\n':
+ return 1;
+ default:
+ return 0;
+ }
+}
diff --git a/user/lib/itoc.c b/user/lib/itoc.c
new file mode 100644
index 0000000..c19d814
--- /dev/null
+++ b/user/lib/itoc.c
@@ -0,0 +1,10 @@
+#include <stdlib.h>
+
+char itoc(int i)
+{
+ if (i < 10) {
+ return '0' + i;
+ } else {
+ return 'a' + (i - 10);
+ }
+}
diff --git a/user/lib/memcmp.c b/user/lib/memcmp.c
new file mode 100644
index 0000000..7f3dc01
--- /dev/null
+++ b/user/lib/memcmp.c
@@ -0,0 +1,9 @@
+#include <string.h>
+
+int memcmp(const void *restrict vl, const void *restrict vr, size_t n)
+{
+ const unsigned char *l = vl, *r = vr;
+ for (; n && *l == *r; n--, l++, r++)
+ ;
+ return n ? *l - *r : 0;
+}
diff --git a/user/lib/memcpy.c b/user/lib/memcpy.c
new file mode 100644
index 0000000..ec56537
--- /dev/null
+++ b/user/lib/memcpy.c
@@ -0,0 +1,10 @@
+#include <string.h>
+
+void *memcpy(void *restrict dest, const void *restrict src, size_t n)
+{
+ char *d = dest;
+ const char *s = src;
+ for (; n; n--)
+ *d++ = *s++;
+ return dest;
+}
diff --git a/user/lib/memmove.c b/user/lib/memmove.c
new file mode 100644
index 0000000..81f00fe
--- /dev/null
+++ b/user/lib/memmove.c
@@ -0,0 +1,20 @@
+#include <string.h>
+
+void *memmove(void *dest, const void *src, size_t n)
+{
+ char *d = dest;
+ const char *s = src;
+
+ if (d == s)
+ return d;
+
+ if (d < s) {
+ for (; n; n--)
+ *d++ = *s++;
+ } else {
+ while (n)
+ n--, d[n] = s[n];
+ }
+
+ return dest;
+}
diff --git a/user/lib/memset.c b/user/lib/memset.c
new file mode 100644
index 0000000..ddf42f8
--- /dev/null
+++ b/user/lib/memset.c
@@ -0,0 +1,10 @@
+#include <string.h>
+
+void *memset(void *dest, int c, size_t n)
+{
+ unsigned char *d = dest;
+ for (; n; n--) {
+ *d++ = c;
+ };
+ return dest;
+}
diff --git a/user/lib/printf.c b/user/lib/printf.c
new file mode 100644
index 0000000..4a85956
--- /dev/null
+++ b/user/lib/printf.c
@@ -0,0 +1,602 @@
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+
+#define PRINTF_NUMERIC_BUF_LEN 50
+
+typedef union {
+ unsigned long long int u;
+ signed long long int i;
+ char *str;
+ char c;
+} data_t;
+
+/// options that can be set inside a specifier
+/// flags, width, precision, length, and data type
+typedef struct {
+ /* flags */
+ /// left justify content
+ uint8_t left : 1;
+ /// force sign (+/-) on numeric output
+ uint8_t sign : 1;
+ /// leave space if no printed sign on numeric output
+ uint8_t space : 1;
+ /// preceed hex/octal output with '0x'
+ uint8_t hash : 1;
+ /// left pads numeric output with zeros
+ uint8_t zero : 1;
+ uint8_t : 3;
+
+ /* width & precision */
+ /// minimum number of characters to be printed (padding if origonal is less)
+ int width;
+ /// digit precision used when printing numerical answers
+ int precision;
+ /// if a fixed minimum width has been provided
+ uint8_t width_set : 1;
+ /// if the provided minimum width is in the next variable argument
+ uint8_t width_varies : 1;
+ /// if a fixed digit precision has been provided
+ uint8_t precision_set : 1;
+ /// if the provided digit precision is in the next variable argument
+ uint8_t precision_varies : 1;
+ uint8_t : 4;
+
+ /* length */
+ /// what size to read argument as
+ enum printf_len {
+ PRINTF_LEN_CHAR,
+ PRINTF_LEN_SHORT_INT,
+ PRINTF_LEN_INT,
+ PRINTF_LEN_LONG_INT,
+ PRINTF_LEN_LONG_LONG_INT,
+ PRINTF_LEN_SIZE_T,
+ } len;
+
+ /* other */
+ /// radix to print the numerical answers as
+ uint8_t radix;
+ /// case to print hexadecimal values as
+ bool is_uppercase;
+} options_t;
+
+typedef struct {
+ /* input */
+ /// the origonal format string
+ const char *format;
+ /// maximum allowed output length
+ size_t max_len;
+ /// if a maximum output length is set
+ bool has_max_len;
+
+ /* output */
+ size_t written_len;
+ bool to_file;
+ union {
+ FILE *file;
+ char *buf;
+ } out;
+
+ /* pass 2 */
+ char *output;
+} context_t;
+
+static void printf_putc(context_t *ctx, char c)
+{
+ // bounds check
+ if (ctx->has_max_len)
+ if (ctx->written_len >= ctx->max_len)
+ return;
+
+ // write to correct
+ if (ctx->to_file)
+ fputc(ctx->out.file, c);
+ else
+ *(ctx->out.buf++) = c;
+
+ ctx->written_len++;
+}
+
+static int parse_flag(const char **res, options_t *opts)
+{
+ const char *fmt = *res;
+ switch (*(fmt++)) {
+ case '-':
+ opts->left = 1;
+ break;
+ case '+':
+ opts->sign = 1;
+ break;
+ case ' ':
+ opts->space = 1;
+ break;
+ case '#':
+ opts->hash = 1;
+ break;
+ case '0':
+ opts->zero = 1;
+ break;
+ default:
+ return 0;
+ }
+
+ *res = fmt;
+ return 1;
+}
+
+static void parse_width(const char **res, options_t *opts)
+{
+ const char *fmt = *res;
+ char *end = NULL;
+
+ // check varies
+ if (*fmt == '*') {
+ opts->width_varies = true;
+ *res = fmt++;
+ return;
+ }
+
+ // parse num
+ long width = strtol(fmt, &end, 10);
+ if (end != NULL) {
+ opts->width_set = 1;
+ opts->width = width;
+ *res = end;
+ return;
+ }
+}
+
+static void parse_precision(const char **res, options_t *opts)
+{
+ const char *fmt = *res;
+ char *end = NULL;
+
+ // check for dot
+ if (*(fmt++) != '.')
+ return;
+
+ // check varies
+ if (*fmt == '*') {
+ opts->precision_varies = true;
+ *res = fmt++;
+ return;
+ }
+
+ // parse num
+ long precision = strtol(fmt, &end, 10);
+ if (end != NULL) {
+ opts->precision_set = 1;
+ opts->precision = precision;
+ *res = end;
+ return;
+ }
+}
+
+static void parse_length(const char **res, options_t *opts)
+{
+ const char *fmt = *res;
+
+ switch (*(fmt++)) {
+ // half
+ case 'h':
+ if (*fmt == 'h') {
+ opts->len = PRINTF_LEN_CHAR;
+ fmt++;
+ } else {
+ opts->len = PRINTF_LEN_SHORT_INT;
+ }
+ break;
+ // long
+ case 'l':
+ if (*fmt == 'l') {
+ opts->len = PRINTF_LEN_LONG_LONG_INT;
+ fmt++;
+ } else {
+ opts->len = PRINTF_LEN_LONG_INT;
+ }
+ break;
+ // size_t
+ case 'z':
+ opts->len = PRINTF_LEN_SIZE_T;
+ break;
+ default:
+ opts->len = PRINTF_LEN_INT;
+ return;
+ }
+
+ *res = fmt;
+}
+
+static void get_radix(char spec, options_t *opts)
+{
+ switch (spec) {
+ case 'x':
+ case 'X':
+ opts->radix = 16;
+ break;
+ case 'o':
+ opts->radix = 8;
+ break;
+ default:
+ opts->radix = 10;
+ break;
+ }
+}
+
+static void get_case(char spec, options_t *opts)
+{
+ if (spec == 'X')
+ opts->is_uppercase = 1;
+}
+
+static char printf_itoc(int uppercase, int i)
+{
+ // decimal
+ if (i < 10) {
+ return i + '0';
+ }
+ // hex
+ if (uppercase) {
+ return (i - 10) + 'A';
+ } else {
+ return (i - 10) + 'a';
+ }
+}
+
+static int printf_lltoa(char *buf, options_t *opts, bool is_neg,
+ unsigned long long int num)
+{
+ int precision = 0;
+ char *start = buf;
+
+ // get width of number
+ int len = 0;
+ unsigned long long int temp = num;
+ if (temp == 0)
+ len = 1;
+ while (temp) {
+ if (opts->precision_set && precision++ >= opts->precision)
+ break;
+ temp /= opts->radix;
+ len++;
+ }
+ precision = 0;
+
+ // sign
+ if (is_neg) {
+ *(buf++) = '-';
+ } else if (opts->sign) {
+ *(buf++) = '+';
+ } else if (opts->space) {
+ *(buf++) = ' ';
+ }
+
+ // radix specifier
+ if (opts->hash) {
+ if (opts->radix == 8) {
+ *(buf++) = '0';
+ *(buf++) = 'o';
+ }
+ if (opts->radix == 16) {
+ *(buf++) = '0';
+ *(buf++) = 'x';
+ }
+ }
+
+ // print zeros if needed
+ if (opts->width_set && len < opts->width && opts->zero) {
+ while (len++ < opts->width)
+ *(buf++) = '0';
+ }
+
+ // write number
+ if (num == 0) {
+ *(buf++) = '0';
+ }
+ while (num) {
+ if (opts->precision_set && precision++ >= opts->precision)
+ break;
+ *(buf++) = printf_itoc(opts->is_uppercase, num % opts->radix);
+ num /= opts->radix;
+ }
+ *(buf++) = '\0';
+
+ return buf - start;
+}
+
+static void handle_int_specifier(context_t *ctx, options_t *const opts,
+ bool has_sign_bit, data_t num)
+{
+ bool is_neg = false;
+
+ // get sign if possible neg
+ if (has_sign_bit) {
+ if (num.i < 0) {
+ num.i = -num.i;
+ is_neg = true;
+ }
+ }
+
+ // get length of number and number
+ char buf[PRINTF_NUMERIC_BUF_LEN];
+ int buf_len = printf_lltoa(buf, opts, is_neg, num.u);
+
+ // get needed padding
+ int padding = 0;
+ if (opts->width_set && (buf_len < opts->width))
+ padding = opts->width - buf_len;
+
+ /* print */
+ // left padding
+ if (opts->left == 0) {
+ for (int i = 0; i < padding; i++)
+ printf_putc(ctx, opts->zero ? '0' : ' ');
+ }
+ // number
+ for (int i = 0; i < buf_len; i++)
+ printf_putc(ctx, buf[i]);
+ // right padding
+ if (opts->left == 1) {
+ for (int i = 0; i < padding; i++)
+ printf_putc(ctx, opts->zero ? '0' : ' ');
+ }
+}
+
+static void handle_char_specifier(context_t *ctx, data_t c)
+{
+ printf_putc(ctx, c.c);
+}
+
+static void handle_string_specifier(context_t *ctx, options_t *opts,
+ data_t data)
+{
+ char *str = data.str;
+ int str_len = 0;
+
+ // get length of string
+ if (opts->precision_set)
+ str_len = opts->precision;
+ else
+ str_len = strlen(str);
+
+ // get needed padding
+ int padding = 0;
+ if (opts->width_set && (str_len < opts->width))
+ padding = opts->width - str_len;
+
+ /* print */
+ // left padding
+ if (opts->left == 0) {
+ for (int i = 0; i < padding; i++)
+ printf_putc(ctx, ' ');
+ }
+ // string
+ for (int i = 0; i < str_len; i++)
+ printf_putc(ctx, str[i]);
+ // right padding
+ if (opts->left == 1) {
+ for (int i = 0; i < padding; i++)
+ printf_putc(ctx, ' ');
+ }
+}
+
+static void do_printf(context_t *ctx, va_list args)
+{
+ const char *fmt = ctx->format;
+
+ char c;
+ while (c = *fmt++, c != '\0') {
+ // save start of fmt for current iteration
+ const char *start = fmt - 1;
+
+ // ignore if not %
+ if (c != '%') {
+ printf_putc(ctx, c);
+ continue;
+ }
+
+ // read opts
+ options_t opts = { 0 };
+ while (parse_flag(&fmt, &opts))
+ ;
+ parse_width(&fmt, &opts);
+ parse_precision(&fmt, &opts);
+ parse_length(&fmt, &opts);
+
+ // read specifier
+ char spec = *fmt++;
+ get_radix(spec, &opts);
+ get_case(spec, &opts);
+
+ // read varied width / precision
+ if (opts.width_varies) {
+ opts.width_set = 1;
+ opts.width = va_arg(args, int);
+ }
+ if (opts.precision_varies) {
+ opts.precision_set = 1;
+ opts.precision = va_arg(args, int);
+ }
+ // read data from args
+ data_t data;
+ switch (spec) {
+ case 'p':
+ opts.len = PRINTF_LEN_SIZE_T;
+ opts.width_set = true;
+ opts.radix = 16;
+ opts.hash = true;
+ opts.zero = true;
+ case 'd':
+ case 'i':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ // read number from arg
+ switch (opts.len) {
+ case PRINTF_LEN_CHAR:
+ data.u = va_arg(args, unsigned int); // char
+ break;
+ case PRINTF_LEN_SHORT_INT:
+ data.u = va_arg(args, unsigned int); // short int
+ break;
+ case PRINTF_LEN_INT:
+ data.u = va_arg(args, unsigned int);
+ break;
+ case PRINTF_LEN_LONG_INT:
+ data.u = va_arg(args, unsigned long int);
+ break;
+ case PRINTF_LEN_LONG_LONG_INT:
+ data.u = va_arg(args, unsigned long long int);
+ break;
+ case PRINTF_LEN_SIZE_T:
+ data.u = va_arg(args, size_t);
+ break;
+ }
+ break;
+ // end int
+ case 's':
+ // read string
+ data.str = va_arg(args, void *);
+ break;
+ // end string
+ case 'c':
+ // read char
+ data.c = va_arg(args, int);
+ break;
+ // end char
+ }
+
+ switch (spec) {
+ // signed int
+ case 'd':
+ case 'i':
+ handle_int_specifier(ctx, &opts, true, data);
+ break;
+ // unsigned int
+ case 'p':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ handle_int_specifier(ctx, &opts, false, data);
+ break;
+ // character
+ case 'c':
+ handle_char_specifier(ctx, data);
+ break;
+ // string
+ case 's':
+ handle_string_specifier(ctx, &opts, data);
+ break;
+ // unknown
+ default:
+ // print from % to current
+ for (; start < fmt; start++)
+ printf_putc(ctx, *start);
+ break;
+ }
+ }
+}
+
+void printf(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+}
+
+size_t sprintf(char *restrict s, const char *format, ...)
+{
+ va_list args;
+ size_t amt;
+ va_start(args, format);
+ amt = vsprintf(s, format, args);
+ va_end(args);
+ return amt;
+}
+
+size_t snprintf(char *restrict s, size_t maxlen, const char *format, ...)
+{
+ va_list args;
+ size_t amt;
+ va_start(args, format);
+ amt = vsnprintf(s, maxlen, format, args);
+ va_end(args);
+ return amt;
+}
+
+void vprintf(const char *format, va_list args)
+{
+ vfprintf(stdout, format, args);
+}
+
+size_t vsprintf(char *restrict s, const char *format, va_list args)
+{
+ // create context
+ context_t ctx = { 0 };
+ ctx.format = format;
+ // sprintf buffer
+ ctx.out.buf = s;
+ ctx.to_file = 0;
+ // print
+ do_printf(&ctx, args);
+ return ctx.written_len;
+}
+
+size_t vsnprintf(char *restrict s, size_t maxlen, const char *format,
+ va_list args)
+{
+ // create context
+ context_t ctx = { 0 };
+ ctx.format = format;
+ // sprintf buffer
+ ctx.out.buf = s;
+ ctx.to_file = 0;
+ // sprintf max_len
+ ctx.has_max_len = 1;
+ ctx.max_len = maxlen;
+ // print
+ do_printf(&ctx, args);
+ return ctx.written_len;
+}
+
+void fprintf(FILE *stream, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stream, format, args);
+ va_end(args);
+}
+
+void vfprintf(FILE *stream, const char *format, va_list args)
+{
+ // create context
+ context_t ctx = { 0 };
+ ctx.format = format;
+ // fprintf stream
+ ctx.out.file = stream;
+ ctx.to_file = 1;
+ // print
+ do_printf(&ctx, args);
+}
+
+void putc(char c)
+{
+ fputc(stdout, c);
+}
+
+void puts(const char *str)
+{
+ fputs(stdout, str);
+}
+
+void fputs(FILE *stream, const char *s)
+{
+ while (*s)
+ fputc(stream, *s++);
+}
diff --git a/user/lib/spawn.c b/user/lib/spawn.c
new file mode 100644
index 0000000..78b1a53
--- /dev/null
+++ b/user/lib/spawn.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <error.h>
+#include <unistd.h>
+
+int wait(int32_t *status)
+{
+ return (waitpid(0, status));
+}
+
+int spawn(uint_t prog, char **args)
+{
+ int32_t pid;
+
+ pid = fork();
+ if (pid != 0) {
+ // failure, or we are the parent
+ return (pid);
+ }
+
+ // we are the child
+ pid = getpid();
+
+ // child inherits parent's priority level
+
+ exec(prog, args);
+
+ // uh-oh....
+
+ fprintf(stderr, "Child %d exec() #%u failed\n", pid, prog);
+
+ exit(EXIT_FAILURE);
+}
diff --git a/user/lib/stpcpy.c b/user/lib/stpcpy.c
new file mode 100644
index 0000000..1586a37
--- /dev/null
+++ b/user/lib/stpcpy.c
@@ -0,0 +1,9 @@
+#include <string.h>
+
+char *stpcpy(char *restrict dest, const char *restrict src)
+{
+ char *d = dest;
+ for (; (*d = *src); d++, src++)
+ ;
+ return d;
+}
diff --git a/user/lib/stpncpy.c b/user/lib/stpncpy.c
new file mode 100644
index 0000000..4e0def6
--- /dev/null
+++ b/user/lib/stpncpy.c
@@ -0,0 +1,10 @@
+#include <string.h>
+
+char *stpncpy(char *restrict dest, const char *restrict src, size_t n)
+{
+ char *d = dest;
+ for (; (*d = *src) && n; d++, src++, n--)
+ ;
+ memset(d, 0, n);
+ return d;
+}
diff --git a/user/lib/strcat.c b/user/lib/strcat.c
new file mode 100644
index 0000000..33f749b
--- /dev/null
+++ b/user/lib/strcat.c
@@ -0,0 +1,7 @@
+#include <string.h>
+
+char *strcat(char *restrict dest, const char *restrict src)
+{
+ strcpy(dest + strlen(dest), src);
+ return dest;
+}
diff --git a/user/lib/strcpy.c b/user/lib/strcpy.c
new file mode 100644
index 0000000..70cd1ca
--- /dev/null
+++ b/user/lib/strcpy.c
@@ -0,0 +1,9 @@
+#include <string.h>
+
+char *strcpy(char *restrict dest, const char *restrict src)
+{
+ char *d = dest;
+ for (; (*d = *src); d++, src++)
+ ;
+ return dest;
+}
diff --git a/user/lib/strlen.c b/user/lib/strlen.c
new file mode 100644
index 0000000..6c4cc86
--- /dev/null
+++ b/user/lib/strlen.c
@@ -0,0 +1,9 @@
+#include <string.h>
+
+size_t strlen(const char *str)
+{
+ const char *p;
+ for (p = str; *p != 0; p++) {
+ }
+ return p - str;
+}
diff --git a/user/lib/strncmp.c b/user/lib/strncmp.c
new file mode 100644
index 0000000..e890517
--- /dev/null
+++ b/user/lib/strncmp.c
@@ -0,0 +1,11 @@
+#include <string.h>
+
+int strncmp(const char *restrict lhs, const char *restrict rhs, size_t n)
+{
+ const unsigned char *l = (void *)lhs, *r = (void *)rhs;
+ if (!n--)
+ return 0;
+ for (; *l && *r && n && *l == *r; l++, r++, n--)
+ ;
+ return *l - *r;
+}
diff --git a/user/lib/strncpy.c b/user/lib/strncpy.c
new file mode 100644
index 0000000..264fd9d
--- /dev/null
+++ b/user/lib/strncpy.c
@@ -0,0 +1,10 @@
+#include <string.h>
+
+char *strncpy(char *restrict dest, const char *restrict src, size_t n)
+{
+ char *d = dest;
+ for (; (*d = *src) && n; d++, src++, n--)
+ ;
+ memset(d, 0, n);
+ return dest;
+}
diff --git a/user/lib/strtoux.c b/user/lib/strtoux.c
new file mode 100644
index 0000000..7c2d7ee
--- /dev/null
+++ b/user/lib/strtoux.c
@@ -0,0 +1,44 @@
+#include <stdlib.h>
+#include <ctype.h>
+
+#define STRTOUX(name, type) \
+ type name(const char *restrict s, char **restrict endptr, int radix) \
+ { \
+ const char *s_start = s; \
+ for (; isspace(*s); s++) \
+ ; \
+ \
+ if ((radix == 0 || radix == 16) && \
+ (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))) { \
+ radix = 16; \
+ s += 2; \
+ } else if (radix == 0) { \
+ if (*s == '0') { \
+ radix = 8; \
+ s++; \
+ } else { \
+ radix = 10; \
+ } \
+ } \
+ \
+ type num = 0; \
+ int has_digit = 0; \
+ \
+ while (1) { \
+ int n = ctoi(*s++); \
+ if (n < 0 || n >= radix) \
+ break; \
+ has_digit = 1; \
+ num = num * radix + n; \
+ } \
+ \
+ if (endptr != NULL) { \
+ *endptr = has_digit ? (char *)(s - 1) : (char *)s_start; \
+ } \
+ \
+ return num; \
+ }
+
+STRTOUX(strtoui, unsigned int)
+STRTOUX(strtoul, unsigned long int)
+STRTOUX(strtoull, unsigned long long int)
diff --git a/user/lib/strtox.c b/user/lib/strtox.c
new file mode 100644
index 0000000..5f786f1
--- /dev/null
+++ b/user/lib/strtox.c
@@ -0,0 +1,53 @@
+#include <stdlib.h>
+#include <ctype.h>
+
+#define STRTOX(name, type) \
+ type name(const char *restrict s, char **restrict endptr, int radix) \
+ { \
+ const char *s_start = s; \
+ for (; isspace(*s); s++) \
+ ; \
+ \
+ int sign = 0; \
+ switch (*s) { \
+ case '-': \
+ sign = 1; /* fallthrough */ \
+ case '+': \
+ s++; \
+ break; \
+ } \
+ \
+ if ((radix == 0 || radix == 16) && \
+ (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))) { \
+ radix = 16; \
+ s += 2; \
+ } else if (radix == 0) { \
+ if (*s == '0') { \
+ radix = 8; \
+ s++; \
+ } else { \
+ radix = 10; \
+ } \
+ } \
+ \
+ type num = 0; \
+ int has_digit = 0; \
+ \
+ while (1) { \
+ int n = ctoi(*s++); \
+ if (n < 0 || n >= radix) \
+ break; \
+ has_digit = 1; \
+ num = num * radix + n; \
+ } \
+ \
+ if (endptr != NULL) { \
+ *endptr = has_digit ? (char *)(s - 1) : (char *)s_start; \
+ } \
+ \
+ return sign ? -num : num; \
+ }
+
+STRTOX(strtoi, int)
+STRTOX(strtol, long int)
+STRTOX(strtoll, long long int)
diff --git a/user/lib/syscall.S b/user/lib/syscall.S
new file mode 100644
index 0000000..46fcb89
--- /dev/null
+++ b/user/lib/syscall.S
@@ -0,0 +1,93 @@
+/**
+** @file ulibs.S
+**
+** @author CSCI-452 class of 20245
+**
+** @brief assembly-language user-level library functions
+*/
+
+#define ASM_SRC
+
+// get the system call codes
+
+#include <syscalls.h>
+
+/**
+** System call stubs
+**
+** All have the same structure:
+**
+** move a code into EAX
+** generate the interrupt
+** return to the caller
+**
+** As these are simple "leaf" routines, we don't use
+** the standard enter/leave method to set up a stack
+** frame - that takes time, and we don't really need it.
+**
+** Could be modified to use the UNIX/Linux convention of
+** having the syscall code set the 'C' flag to indicate that
+** the value being returned in %EAX is an error code:
+**
+** ...
+** int $VEC_SYSCALL
+** jc set_errno
+** ret
+** ...
+**
+** .globl errno
+** set_errno:
+** movl %eax, errno
+** movl $-1, %eax
+** ret
+*/
+
+#define SYSCALL(name) \
+ .globl name ; \
+name: ; \
+ movl $SYS_##name, %eax ; \
+ int $VEC_SYSCALL ; \
+ ret
+
+/*
+** "real" system calls
+*/
+
+SYSCALL(exit)
+SYSCALL(waitpid)
+SYSCALL(fork)
+SYSCALL(exec)
+SYSCALL(read)
+SYSCALL(write)
+SYSCALL(getpid)
+SYSCALL(getppid)
+SYSCALL(gettime)
+SYSCALL(getprio)
+SYSCALL(setprio)
+SYSCALL(kill)
+SYSCALL(sleep)
+
+/*
+** This is a bogus system call; it's here so that we can test
+** our handling of out-of-range syscall codes in the syscall ISR.
+*/
+SYSCALL(bogus)
+
+/*
+** Other library functions
+*/
+
+/**
+** fake_exit()
+**
+** Dummy "startup" function
+**
+** calls exit(%eax) - serves as the "return to" code for
+** main() functions, in case they don't call exit() themselves
+*/
+
+ .globl fake_exit
+fake_exit:
+ // alternate: could push a "fake exit" status
+ pushl %eax // termination status returned by main()
+ call exit // terminate this process
diff --git a/user/lib/timetostr.c b/user/lib/timetostr.c
new file mode 100644
index 0000000..fa77362
--- /dev/null
+++ b/user/lib/timetostr.c
@@ -0,0 +1,143 @@
+#include <lib.h>
+#include <time.h>
+
+static char *ABB_WEEKDAY[7] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static char *FULL_WEEKDAY[7] = { "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturady" };
+
+static char *ABB_MONTH[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+static char *FULL_MONTH[12] = {
+ "January", "Feburary", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+};
+
+static char *write_num(unsigned int num, unsigned int pad, char *buf, size_t n)
+{
+ size_t digits = 1;
+ unsigned int x = num;
+
+ while (x /= 10, x > 0)
+ digits++;
+ if (pad == 0)
+ pad = digits;
+
+ for (size_t i = 0; i < pad; i++) {
+ size_t digit;
+ if (i >= digits) {
+ digit = 0;
+ } else {
+ digit = num % 10;
+ num /= 10;
+ }
+
+ if (pad - i - 1 >= n)
+ continue;
+ buf[pad - i - 1] = '0' + digit;
+ }
+
+ if (pad > n)
+ pad = n;
+
+ return buf + pad;
+}
+
+void timetostr(time_t *time, char *format, char *buf, size_t n)
+{
+ char *index = buf;
+ char c;
+ int space;
+
+ while (c = *format++, space = (buf + n) - index, c != '\0' && space > 0) {
+ if (c != '%') {
+ *index++ = c;
+ continue;
+ } else {
+ c = *format++;
+ }
+
+ switch (c) {
+ case '%':
+ *index++ = '%';
+ break;
+ case 'a':
+ index = strncpy(index, ABB_WEEKDAY[time->wday], space);
+ break;
+ case 'A':
+ index = strncpy(index, FULL_WEEKDAY[time->wday], space);
+ break;
+ case 'b':
+ case 'h':
+ index = strncpy(index, ABB_MONTH[time->mon], space);
+ break;
+ case 'B':
+ index = strncpy(index, FULL_MONTH[time->mon], space);
+ break;
+ case 'C':
+ index = write_num(time->cen, 0, index, space);
+ break;
+ case 'd':
+ index = write_num(time->mday, 2, index, space);
+ break;
+ case 'H':
+ index = write_num(time->hour, 2, index, space);
+ break;
+ case 'I':
+ index = write_num((time->hour + 12) % 12 + 1, 2, index, space);
+ break;
+ case 'j':
+ index = write_num(time->yday, 3, index, space);
+ break;
+ case 'm':
+ index = write_num(time->mon + 1, 2, index, space);
+ break;
+ case 'M':
+ index = write_num(time->min, 2, index, space);
+ break;
+ case 'n':
+ *index++ = '\n';
+ break;
+ case 'p':
+ index = strncpy(index, time->hour > 11 ? "PM" : "AM", space);
+ break;
+ case 'P':
+ index = strncpy(index, time->hour > 11 ? "pm" : "am", space);
+ break;
+ case 'q':
+ index = write_num((time->mon + 3) / 3, 0, index, space);
+ break;
+ case 'S':
+ index = write_num(time->sec, 2, index, space);
+ break;
+ case 't':
+ *index++ = '\t';
+ break;
+ case 'u':
+ index = write_num(((time->wday + 1) % 7) + 1, 0, index, space);
+ break;
+ case 'w':
+ index = write_num(time->wday, 0, index, space);
+ break;
+ case 'y':
+ index = write_num(time->yn, 2, index, space);
+ break;
+ case 'Y':
+ index = write_num(time->year + 1900, 0, index, space);
+ break;
+ default: {
+ char b[3] = { '%', c, '\0' };
+ index = strncpy(index, b, space);
+ break;
+ }
+ }
+ }
+
+ if (space < 1)
+ buf[n - 1] = '\0';
+ else
+ *index = '\0';
+}
diff --git a/user/lib/uxtoa.c b/user/lib/uxtoa.c
new file mode 100644
index 0000000..8d4e0e1
--- /dev/null
+++ b/user/lib/uxtoa.c
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+
+#define UXTOA(type, name) \
+ char *name(unsigned type n, char *buffer, int radix) \
+ { \
+ if (n == 0) { \
+ buffer[0] = '0'; \
+ buffer[1] = '\0'; \
+ return buffer + 1; \
+ } \
+ char *start = buffer; \
+ for (; n; n /= radix) { \
+ *buffer++ = itoc(n % radix); \
+ } \
+ char *buf_end = buffer; \
+ *buffer-- = '\0'; \
+ while (buffer > start) { \
+ char tmp = *start; \
+ *start++ = *buffer; \
+ *buffer-- = tmp; \
+ } \
+ return buf_end; \
+ }
+
+UXTOA(int, utoa)
+UXTOA(long int, ultoa)
+UXTOA(long long int, ulltoa)
diff --git a/user/lib/xtoa.c b/user/lib/xtoa.c
new file mode 100644
index 0000000..bf02236
--- /dev/null
+++ b/user/lib/xtoa.c
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+
+#define XTOA(type, name) \
+ char *name(type n, char *buffer, int radix) \
+ { \
+ if (n == 0) { \
+ buffer[0] = '0'; \
+ buffer[1] = '\0'; \
+ return buffer + 1; \
+ } \
+ if (n < 0) { \
+ *buffer++ = '-'; \
+ n = -n; \
+ } \
+ char *start = buffer; \
+ for (; n; n /= radix) { \
+ *buffer++ = itoc(n % radix); \
+ } \
+ char *buf_end = buffer; \
+ *buffer-- = '\0'; \
+ while (buffer > start) { \
+ char tmp = *start; \
+ *start++ = *buffer; \
+ *buffer-- = tmp; \
+ } \
+ return buf_end; \
+ }
+
+XTOA(int, itoa)
+XTOA(long int, ltoa)
+XTOA(long long int, lltoa)