Update MentOs code to the latest development version.

This commit is contained in:
Enrico Fraccaroli
2021-10-04 11:44:02 +02:00
parent 6fd063984a
commit b01eccca2e
484 changed files with 42358 additions and 20285 deletions
+21
View File
@@ -0,0 +1,21 @@
/// MentOS, The Mentoring Operating system project
/// @file assert.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "assert.h"
#include "stdio.h"
#include "panic.h"
void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line)
{
char message[1024];
sprintf(message,
"FILE: %s\n"
"FUNC: %s\n"
"LINE: %d\n\n"
"Assertion `%s` failed.\n",
file, (function ? function : "NO_FUN"), line, assertion);
kernel_panic(message);
}
+63
View File
@@ -0,0 +1,63 @@
/// MentOS, The Mentoring Operating system project
/// @file ctype.c
/// @brief Functions related to character handling.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "ctype.h"
/// Distance from a uppercase character to the correspondent lowercase in ASCII.
#define OFFSET 32
int isdigit(int c)
{
return (c >= 48 && c <= 57);
}
int isalpha(int c)
{
return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122));
}
int isalnum(int c)
{
return (isalpha(c) || isdigit(c));
}
int isxdigit(int c)
{
return (isdigit(c) || (c >= 65 && c <= 70));
}
int islower(int c)
{
return (c >= 97 && c <= 122);
}
int isupper(int c)
{
return (c >= 65 && c <= 90);
}
int tolower(int c)
{
if (isalpha(c) == 0 || islower(c)) {
return c;
}
return c + OFFSET;
}
int toupper(int c)
{
if (isalpha(c) == 0 || isupper(c)) {
return c;
}
return c - OFFSET;
}
int isspace(int c)
{
return (c == ' ');
}
+104
View File
@@ -0,0 +1,104 @@
/// MentOS, The Mentoring Operating system project
/// @file fcvt.c
/// @brief Define the functions required to turn double values into a string.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "fcvt.h"
#include "math.h"
static void cvt(double arg, int ndigits, int *decpt, int *sign, char *buf, unsigned buf_size, int eflag)
{
int r2;
double fi, fj;
char *p, *p1;
char *buf_end = (buf + buf_size);
if (ndigits < 0) {
ndigits = 0;
}
if (ndigits >= buf_size - 1) {
ndigits = buf_size - 2;
}
r2 = 0;
*sign = 0;
p = &buf[0];
if (arg < 0) {
*sign = 1;
arg = -arg;
}
arg = modf(arg, &fi);
p1 = buf_end;
if (fi != 0) {
p1 = buf_end;
while (fi != 0) {
fj = modf(fi / 10, &fi);
*--p1 = (int)((fj + .03) * 10) + '0';
r2++;
}
while (p1 < buf_end) {
*p++ = *p1++;
}
} else if (arg > 0) {
while ((fj = arg * 10) < 1) {
arg = fj;
r2--;
}
}
p1 = &buf[ndigits];
if (eflag == 0) {
p1 += r2;
}
*decpt = r2;
if (p1 < &buf[0]) {
buf[0] = '\0';
return;
}
while (p <= p1 && p < buf_end) {
arg *= 10;
arg = modf(arg, &fj);
*p++ = (int)fj + '0';
}
if (p1 >= buf_end) {
buf[buf_size - 1] = '\0';
return;
}
p = p1;
*p1 += 5;
while (*p1 > '9') {
*p1 = '0';
if (p1 > buf) {
++*--p1;
} else {
*p1 = '1';
(*decpt)++;
if (eflag == 0) {
if (p > buf)
*p = '0';
p++;
}
}
}
*p = '\0';
}
void ecvtbuf(double arg, int chars, int *decpt, int *sign, char *buf, unsigned buf_size)
{
cvt(arg, chars, decpt, sign, buf, buf_size, 1);
}
void fcvtbuf(double arg, int decimals, int *decpt, int *sign, char *buf, unsigned buf_size)
{
cvt(arg, decimals, decpt, sign, buf, buf_size, 0);
}
+254
View File
@@ -0,0 +1,254 @@
/// MentOS, The Mentoring Operating system project
/// @file hashmap.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "hashmap.h"
#include "assert.h"
#include "string.h"
#include "slab.h"
/// @brief Stores information of an entry of the hashmap.
struct hashmap_entry_t {
/// Key of the entry.
char *key;
/// Value of the entry.
void *value;
/// Pointer to the next entry.
struct hashmap_entry_t *next;
};
/// @brief Stores information of a hashmap.
struct hashmap_t {
/// Hashing function, used to generate hash keys.
hashmap_hash_t hash_func;
/// Comparison function, used to compare hash keys.
hashmap_comp_t hash_comp;
/// Key duplication function, used to duplicate hash keys.
hashmap_dupe_t hash_key_dup;
/// Key deallocation function, used to free the memory occupied by hash keys.
hashmap_free_t hash_key_free;
/// Size of the hashmap.
unsigned int size;
/// List of entries.
hashmap_entry_t **entries;
};
static inline hashmap_t *__alloc_hashmap()
{
hashmap_t *hashmap = kmalloc(sizeof(hashmap_t));
memset(hashmap, 0, sizeof(hashmap_t));
return hashmap;
}
static inline hashmap_entry_t *__alloc_entry()
{
hashmap_entry_t *entry = kmalloc(sizeof(hashmap_entry_t));
memset(entry, 0, sizeof(hashmap_entry_t));
return entry;
}
static inline void __dealloc_entry(hashmap_entry_t *entry)
{
assert(entry && "Invalid pointer to an entry.");
kfree(entry);
}
static inline hashmap_entry_t **__alloc_entries(unsigned int size)
{
hashmap_entry_t **entries = kmalloc(sizeof(hashmap_entry_t *) * size);
memset(entries, 0, sizeof(hashmap_entry_t *) * size);
return entries;
}
static inline void __dealloc_entries(hashmap_entry_t **entries)
{
assert(entries && "Invalid pointer to entries.");
kfree(entries);
}
unsigned int hashmap_int_hash(const void *key)
{
return (unsigned int)key;
}
int hashmap_int_comp(const void *a, const void *b)
{
return (int)a == (int)b;
}
unsigned int hashmap_str_hash(const void *_key)
{
unsigned int hash = 0;
const char *key = (const char *)_key;
char c;
// This is the so-called "sdbm" hash. It comes from a piece of public
// domain code from a clone of ndbm.
while ((c = *key++))
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
}
int hashmap_str_comp(const void *a, const void *b)
{
return !strcmp(a, b);
}
void *hashmap_do_not_duplicate(const void *value)
{
return (void *)value;
}
void hashmap_do_not_free(void *value)
{
(void)value;
}
hashmap_t *hashmap_create(
unsigned int size,
hashmap_hash_t hash_fun,
hashmap_comp_t comp_fun,
hashmap_dupe_t dupe_fun,
hashmap_free_t key_free_fun)
{
// Allocate the map.
hashmap_t *map = __alloc_hashmap();
// Initialize the entries.
map->size = size;
map->entries = __alloc_entries(size);
// Initialize its functions.
map->hash_func = hash_fun;
map->hash_comp = comp_fun;
map->hash_key_dup = dupe_fun;
map->hash_key_free = key_free_fun;
return map;
}
void hashmap_free(hashmap_t *map)
{
for (unsigned int i = 0; i < map->size; ++i) {
hashmap_entry_t *x = map->entries[i], *p;
while (x) {
p = x;
x = x->next;
map->hash_key_free(p->key);
__dealloc_entry(p);
}
}
__dealloc_entries(map->entries);
}
void *hashmap_set(hashmap_t *map, const void *key, void *value)
{
unsigned int hash = map->hash_func(key) % map->size;
hashmap_entry_t *x = map->entries[hash];
if (x == NULL) {
hashmap_entry_t *e = __alloc_entry();
e->key = map->hash_key_dup(key);
e->value = value;
e->next = NULL;
map->entries[hash] = e;
return NULL;
}
hashmap_entry_t *p = NULL;
do {
if (map->hash_comp(x->key, key)) {
void *out = x->value;
x->value = value;
return out;
}
p = x;
x = x->next;
} while (x);
hashmap_entry_t *e = __alloc_entry();
e->key = map->hash_key_dup(key);
e->value = value;
e->next = NULL;
p->next = e;
return NULL;
}
void *hashmap_get(hashmap_t *map, const void *key)
{
unsigned int hash = map->hash_func(key) % map->size;
for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next)
if (map->hash_comp(x->key, key))
return x->value;
return NULL;
}
void *hashmap_remove(hashmap_t *map, const void *key)
{
unsigned int hash = map->hash_func(key) % map->size;
hashmap_entry_t *x = map->entries[hash];
if (x == NULL) {
return NULL;
}
if (map->hash_comp(x->key, key)) {
void *out = x->value;
map->entries[hash] = x->next;
map->hash_key_free(x->key);
__dealloc_entry(x);
return out;
}
hashmap_entry_t *p = x;
x = x->next;
do {
if (map->hash_comp(x->key, key)) {
void *out = x->value;
p->next = x->next;
map->hash_key_free(x->key);
__dealloc_entry(x);
return out;
}
p = x;
x = x->next;
} while (x);
return NULL;
}
int hashmap_is_empty(hashmap_t *map)
{
for (unsigned int i = 0; i < map->size; ++i)
if (map->entries[i])
return 0;
return 1;
}
int hashmap_has(hashmap_t *map, const void *key)
{
unsigned int hash = map->hash_func(key) % map->size;
for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next)
if (map->hash_comp(x->key, key))
return 1;
return 0;
}
list_t *hashmap_keys(hashmap_t *map)
{
list_t *l = list_create();
for (unsigned int i = 0; i < map->size; ++i)
for (hashmap_entry_t *x = map->entries[i]; x; x = x->next)
list_insert_back(l, x->key);
return l;
}
list_t *hashmap_values(hashmap_t *map)
{
list_t *l = list_create();
for (unsigned int i = 0; i < map->size; ++i)
for (hashmap_entry_t *x = map->entries[i]; x; x = x->next)
list_insert_back(l, x->value);
return l;
}
+142
View File
@@ -0,0 +1,142 @@
/// MentOS, The Mentoring Operating system project
/// @file libgen.c
/// @brief String routines.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "syscall.h"
#include "libgen.h"
#include "string.h"
#include "initrd.h"
#include "limits.h"
#include "assert.h"
#include "paging.h"
int parse_path(char *out, char **cur, char sep, size_t max)
{
if (**cur == '\0') {
return 0;
}
*out++ = **cur;
++*cur;
--max;
while ((max > 0) && (**cur != '\0') && (**cur != sep)) {
*out++ = **cur;
++*cur;
--max;
}
*out = '\0';
return 1;
}
char *dirname(const char *path)
{
static char s[PATH_MAX];
static char dot[2] = ".";
// Check the input path.
if (path == NULL) {
return dot;
}
// Copy the path to the support string.
strcpy(s, path);
// Get the last occurrence of '/'.
char *last_slash = strrchr(s, '/');
if (last_slash == s) {
// If the slash is acutally the first character of the string, move the
// pointer to the last slash after it.
++last_slash;
} else if ((last_slash != NULL) && (last_slash[1] == '\0')) {
// If the slash is the last character, we need to search before it.
last_slash = memchr(s, '/', last_slash - s);
}
if (last_slash != NULL) {
// If we have found it, close the string.
last_slash[0] = '\0';
} else {
// Otherwise, return '.'.
return dot;
}
return s;
}
char *basename(const char *path)
{
char *p = strrchr(path, '/');
return p ? p + 1 : (char *)path;
}
char *realpath(const char *path, char *resolved)
{
assert(path && "Provided null path.");
if (resolved == NULL)
resolved = kmalloc(sizeof(char) * PATH_MAX);
char abspath[PATH_MAX];
// Initialize the absolute path.
memset(abspath, '\0', PATH_MAX);
int remaining;
if (path[0] != '/') {
// Get the current task.
sys_getcwd(abspath, PATH_MAX);
// Check the current task.
assert((strlen(abspath) > 0) && "There is no current task.");
// Check that the current working directory is an absolute path.
assert((abspath[0] == '/') && "Current working directory is not an absolute path.");
// Count the remaining space in the absolute path.
remaining = PATH_MAX - strlen(abspath) - 1;
// Add the separator to the end (se strncat for safety).
strncat(abspath, "/", remaining);
// Set the number of characters that should be copied,
// based on the current absolute path.
remaining = PATH_MAX - strlen(abspath) - 1;
// Append the path.
strncat(abspath, path, remaining);
} else {
// Copy the path into the absolute path.
strncpy(abspath, path, PATH_MAX - 1);
}
// Count the remaining space in the absolute path.
remaining = PATH_MAX - strlen(abspath) - 1;
// Add the separator to the end (se strncat for safety).
strncat(abspath, "/", remaining);
int absidx = 0, pathidx = 0;
while (abspath[absidx]) {
// Skip multiple consecutive / characters
if (!strncmp("//", abspath + absidx, 2)) {
absidx++;
}
// Go to previous directory if /../ is found
else if (!strncmp("/../", abspath + absidx, 4)) {
// Go to a valid path character (pathidx points to the next one)
if (pathidx)
pathidx--;
while (pathidx && resolved[pathidx] != '/') {
pathidx--;
}
absidx += 3;
} else if (!strncmp("/./", abspath + absidx, 3)) {
absidx += 2;
} else {
resolved[pathidx++] = abspath[absidx++];
}
}
// Remove the last /
if (pathidx > 1)
resolved[pathidx - 1] = '\0';
else
resolved[1] = '\0';
return resolved;
}
+331
View File
@@ -0,0 +1,331 @@
/// MentOS, The Mentoring Operating system project
/// @file list.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "list.h"
#include "assert.h"
#include "string.h"
#include "slab.h"
static inline listnode_t *__node_alloc()
{
listnode_t *node = kmalloc(sizeof(listnode_t));
memset(node, 0, sizeof(listnode_t));
return node;
}
static inline void __node_dealloc(listnode_t *node)
{
assert(node && "Invalid pointer to node.");
kfree(node);
}
static inline void __list_dealloc(list_t *list)
{
assert(list && "Invalid pointer to list.");
kfree(list);
}
list_t *list_create()
{
list_t *list = kmalloc(sizeof(list_t));
memset(list, 0, sizeof(list_t));
return list;
}
unsigned int list_size(list_t *list)
{
assert(list && "List is null.");
return list->size;
}
int list_empty(list_t *list)
{
assert(list && "List is null.");
if (list->size == 0) {
return 1;
}
return 0;
}
listnode_t *list_insert_front(list_t *list, void *value)
{
assert(list && "List is null.");
assert(value && "Value is null.");
// Create a new node.
listnode_t *node = __node_alloc();
list->head->prev = node;
node->next = list->head;
node->value = value;
// If it's the first element, then it's both head and tail
if (!list->head) {
list->tail = node;
}
list->head = node;
list->size++;
return node;
}
listnode_t *list_insert_back(list_t *list, void *value)
{
assert(list && "List is null.");
assert(value && "Value is null.");
// Create a new node.
listnode_t *node = __node_alloc();
node->prev = list->tail;
if (list->tail != NULL) {
list->tail->next = node;
}
node->value = value;
if (list->head == NULL) {
list->head = node;
}
list->tail = node;
list->size++;
return node;
}
void *list_remove_node(list_t *list, listnode_t *node)
{
assert(list && "List is null.");
assert(node && "Node is null.");
if (list->head == node) {
return list_remove_front(list);
} else if (list->tail == node) {
return list_remove_back(list);
}
void *value = node->value;
node->next->prev = node->prev;
node->prev->next = node->next;
list->size--;
__node_dealloc(node);
return value;
}
void *list_remove_front(list_t *list)
{
assert(list && "List is null.");
if (list->head == NULL) {
return NULL;
}
listnode_t *node = list->head;
void *value = node->value;
list->head = node->next;
if (list->head) {
list->head->prev = NULL;
}
__node_dealloc(node);
list->size--;
return value;
}
void *list_remove_back(list_t *list)
{
assert(list && "List is null.");
if (list->head == NULL) {
return NULL;
}
listnode_t *node = list->tail;
void *value = node->value;
list->tail = node->prev;
if (list->tail) {
list->tail->next = NULL;
}
__node_dealloc(node);
list->size--;
return value;
}
listnode_t *list_find(list_t *list, void *value)
{
listnode_foreach(listnode, list)
{
if (listnode->value == value) {
return listnode;
}
}
return NULL;
}
void list_push_back(list_t *list, void *value)
{
assert(list && "List is null.");
assert(value && "Value is null.");
list_insert_back(list, value);
}
listnode_t *list_pop_back(list_t *list)
{
assert(list && "List is null.");
if (!list->head) {
return NULL;
}
listnode_t *node = list->tail;
list->tail = node->prev;
if (list->tail) {
list->tail->next = NULL;
}
list->size--;
return node;
}
listnode_t *list_pop_front(list_t *list)
{
assert(list && "List is null.");
if (!list->head) {
return NULL;
}
listnode_t *node = list->head;
list->head = list->head->next;
list->size--;
return node;
}
void list_push_front(list_t *list, void *value)
{
assert(list && "List is null.");
assert(value && "Value is null.");
list_insert_front(list, value);
}
void *list_peek_front(list_t *list)
{
assert(list && "List is null.");
if (!list->head) {
return NULL;
}
return list->head->value;
}
void *list_peek_back(list_t *list)
{
assert(list && "List is null.");
if (!list->tail) {
return NULL;
}
return list->tail->value;
}
int list_get_index_of_value(list_t *list, void *value)
{
assert(list && "List is null.");
assert(value && "Value is null.");
int idx = 0;
listnode_foreach(listnode, list)
{
if (listnode->value == value) {
return idx;
}
++idx;
}
return -1;
}
listnode_t *list_get_node_by_index(list_t *list, unsigned int index)
{
assert(list && "List is null.");
if (index >= list_size(list)) {
return NULL;
}
unsigned int curr = 0;
listnode_foreach(listnode, list)
{
if (index == curr) {
return listnode;
}
curr++;
}
return NULL;
}
void *list_remove_by_index(list_t *list, unsigned int index)
{
assert(list && "List is null.");
listnode_t *node = list_get_node_by_index(list, index);
if (node != NULL) {
return list_remove_node(list, node);
}
return NULL;
}
void list_destroy(list_t *list)
{
assert(list && "List is null.");
// Deallocate each node.
listnode_t *node = list->head;
while (node != NULL) {
listnode_t *save = node;
node = node->next;
__node_dealloc(save);
}
// Free the list.
__list_dealloc(list);
}
void list_merge(list_t *target, list_t *source)
{
assert(target && "Target list is null.");
assert(source && "Source list is null.");
// Destructively merges source into target.
if (target->tail) {
target->tail->next = source->head;
} else {
target->head = source->head;
}
if (source->tail) {
target->tail = source->tail;
}
target->size += source->size;
__list_dealloc(source);
}
+180
View File
@@ -0,0 +1,180 @@
/// MentOS, The Mentoring Operating system project
/// @file math.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "math.h"
#include "stdint.h"
double round(double x)
{
double out;
__asm__ __volatile__("fldln2; fldl %1; frndint"
: "=t"(out)
: "m"(x));
return out;
}
double floor(double x)
{
if (x > -1.0 && x < 1.0) {
if (x >= 0)
return 0.0;
return -1.0;
}
int i = (int)x;
if (x < 0)
return (double)(i - 1);
return (double)i;
}
double ceil(double x)
{
if (x > -1.0 && x < 1.0) {
if (x <= 0)
return 0.0;
return 1.0;
}
int i = (int)x;
if (x > 0)
return (double)(i + 1);
return (double)i;
}
double pow(double base, double exponent)
{
double out;
__asm__ __volatile__("fyl2x;"
"fld %%st;"
"frndint;"
"fsub %%st,%%st(1);"
"fxch;"
"fchs;"
"f2xm1;"
"fld1;"
"faddp;"
"fxch;"
"fld1;"
"fscale;"
"fstp %%st(1);"
"fmulp;"
: "=t"(out)
: "0"(base), "u"(exponent)
: "st(1)");
return out;
}
double exp(double x)
{
return pow(M_E, x);
}
double fabs(double x)
{
double out;
__asm__ __volatile__("fldln2; fldl %1; fabs"
: "=t"(out)
: "m"(x));
return out;
}
float fabsf(float x)
{
float out;
__asm__ __volatile__("fldln2; fldl %1; fabs"
: "=t"(out)
: "m"(x));
return out;
}
double sqrt(double x)
{
double out;
__asm__ __volatile__("fldln2; fldl %1; fsqrt"
: "=t"(out)
: "m"(x));
return out;
}
float sqrtf(float x)
{
float out;
__asm__ __volatile__("fldln2; fldl %1; fsqrt"
: "=t"(out)
: "m"(x));
return out;
}
int isinf(double x)
{
union {
unsigned long long u;
double f;
} ieee754;
ieee754.f = x;
return ((unsigned)(ieee754.u >> 32U) & 0x7fffffffU) == 0x7ff00000U &&
((unsigned)ieee754.u == 0);
}
int isnan(double x)
{
union {
unsigned long long u;
double f;
} ieee754;
ieee754.f = x;
return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) +
((unsigned)ieee754.u != 0) >
0x7ff00000;
}
double log10(double x)
{
return ln(x) / ln(10);
}
double ln(double x)
{
double out;
__asm__ __volatile__("fldln2; fldl %1; fyl2x"
: "=t"(out)
: "m"(x));
return out;
}
double logx(double x, double y)
{
// Base may not equal 1 or be negative.
if (y == 1.f || y < 0.f || ln(y) == 0.f)
return 0.f;
return ln(x) / ln(y);
}
/// Max power for forward and reverse projections.
#define MAXPOWTWO 4.503599627370496000E+15
double modf(double x, double *intpart)
{
register double absvalue;
if ((absvalue = (x >= 0.0) ? x : -x) >= MAXPOWTWO) {
// It must be an integer.
(*intpart) = x;
} else {
// Shift fraction off right.
(*intpart) = absvalue + MAXPOWTWO;
// Shift back without fraction.
(*intpart) -= MAXPOWTWO;
// Above arithmetic might round.
while ((*intpart) > absvalue) {
// Test again just to be sure.
(*intpart) -= 1.0;
}
if (x < 0.0) {
(*intpart) = -(*intpart);
}
}
// Signed fractional part.
return (x - (*intpart));
}
+34
View File
@@ -0,0 +1,34 @@
/// MentOS, The Mentoring Operating system project
/// @file mutex.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "mutex.h"
#include "debug.h"
void mutex_lock(mutex_t *mutex, uint32_t owner)
{
pr_debug("[%d] Trying to lock mutex...\n", owner);
int failure = 1;
while (mutex->state == 0 || failure || mutex->owner != owner) {
failure = 1;
if (mutex->state == 0) {
asm("movl $0x01,%%eax\n\t" // move 1 to eax
"xchg %%eax,%0\n\t" // try to set the lock bit
"mov %%eax,%1\n\t" // export our result to a test var
: "=m"(mutex->state), "=r"(failure)
: "m"(mutex->state)
: "%eax");
}
if (failure == 0) {
mutex->owner = owner; //test to see if we got the lock bit
}
}
}
void mutex_unlock(mutex_t *mutex)
{
mutex->state = 0;
}
+378
View File
@@ -0,0 +1,378 @@
/// MentOS, The Mentoring Operating system project
/// @file ndtree.c
/// @brief Red/Black tree.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "debug.h"
#include "ndtree.h"
#include "assert.h"
#include "list_head.h"
#include "slab.h"
// ============================================================================
// Tree types.
/// @brief Stores data about an NDTree node.
struct ndtree_node_t {
/// User provided, used indirectly via ndtree_tree_cmp_f.
void *value;
/// Pointer to the parent.
ndtree_node_t *parent;
/// List of siblings.
list_head siblings;
/// List of children.
list_head children;
};
/// @brief Stores data about an NDTree.
struct ndtree_t {
/// Comparison function.
ndtree_tree_cmp_f cmp;
/// Size of the tree.
size_t size;
/// Pointer to the root node.
ndtree_node_t *root;
/// List of orphans.
list_head orphans;
};
/// @brief Stores data about an NDTree iterator.
struct ndtree_iter_t {
/// Pointer to the head of the list.
list_head *head;
/// Pointer to the current element of the list.
list_head *current;
};
// ============================================================================
// Default Comparison functions.
static inline int __ndtree_tree_node_cmp_ptr_cb(ndtree_t *self, void *a, void *b)
{
return (a > b) - (a < b);
}
// ============================================================================
// Node management functions.
ndtree_node_t *ndtree_node_alloc()
{
return kmalloc(sizeof(ndtree_node_t));
}
ndtree_node_t *ndtree_node_create(void *value)
{
ndtree_node_t *node = ndtree_node_alloc();
node = ndtree_node_init(node, value);
return node;
}
ndtree_node_t *ndtree_node_init(ndtree_node_t *node, void *value)
{
if (node) {
node->value = value;
node->parent = NULL;
list_head_init(&node->siblings);
list_head_init(&node->children);
}
return node;
}
void ndtree_node_set_value(ndtree_node_t *node, void *value)
{
if (node && value) {
node->value = value;
}
}
void *ndtree_node_get_value(ndtree_node_t *node)
{
if (node)
return node->value;
return NULL;
}
void ndtree_set_root(ndtree_t *tree, ndtree_node_t *node)
{
tree->root = node;
++tree->size;
}
ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value)
{
ndtree_node_t *node = ndtree_node_create(value);
ndtree_set_root(tree, node);
return node;
}
ndtree_node_t *ndtree_get_root(ndtree_t *tree)
{
return tree->root;
}
void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child)
{
child->parent = parent;
list_head_add(&child->siblings, &parent->children);
++tree->size;
}
ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value)
{
ndtree_node_t *child = ndtree_node_create(value);
ndtree_add_child_to_node(tree, parent, child);
return child;
}
unsigned int ndtree_node_count_children(ndtree_node_t *node)
{
unsigned int children = 0;
if (node) {
list_for_each_decl(it, &node->children)
{
++children;
}
}
return children;
}
void ndtree_node_dealloc(ndtree_node_t *node)
{
if (node)
kfree(node);
}
// ============================================================================
// Tree management functions.
ndtree_t *ndtree_tree_alloc()
{
return kmalloc(sizeof(ndtree_t));
}
ndtree_t *ndtree_tree_create(ndtree_tree_cmp_f cmp)
{
return ndtree_tree_init(ndtree_tree_alloc(), cmp);
}
ndtree_t *ndtree_tree_init(ndtree_t *tree, ndtree_tree_cmp_f node_cmp_cb)
{
if (tree) {
tree->size = 0;
tree->cmp = node_cmp_cb ? node_cmp_cb : __ndtree_tree_node_cmp_ptr_cb;
tree->root = NULL;
}
return tree;
}
static void __ndtree_tree_dealloc_rec(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb)
{
if (node && node_cb) {
if (!list_head_empty(&node->children)) {
list_head *it_save;
list_for_each_decl(it, &node->children)
{
ndtree_node_t *entry = list_entry(it, ndtree_node_t, siblings);
it_save = it->prev;
list_head_del(it);
it = it_save;
__ndtree_tree_dealloc_rec(tree, entry, node_cb);
}
}
node_cb(tree, node);
kfree(node);
}
}
void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb)
{
if (tree && tree->root && node_cb)
__ndtree_tree_dealloc_rec(tree, tree->root, node_cb);
kfree(tree);
}
static ndtree_node_t *__ndtree_tree_find_rec(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value, ndtree_node_t *node)
{
ndtree_node_t *result = NULL;
if (tree && cmp && node && value) {
if (cmp(tree, node->value, value) == 0) {
result = node;
} else if (!list_head_empty(&node->children)) {
list_for_each_decl(it, &node->children)
{
ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings);
if ((result = __ndtree_tree_find_rec(tree, cmp, value, child)) != NULL) {
break;
}
}
}
}
return result;
}
ndtree_node_t *ndtree_tree_find(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value)
{
if (tree && tree->root && value)
return __ndtree_tree_find_rec(tree, cmp, value, tree->root);
return NULL;
}
ndtree_node_t *ndtree_node_find(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_cmp_f cmp, void *value)
{
if (tree && node && value) {
// Check only if the node has children.
if (!list_head_empty(&node->children)) {
// Check which compare function we need to use.
ndtree_tree_cmp_f cmp_fun = cmp ? cmp : tree->cmp;
// If neither the tree nor the function argument are valid, rollback to the
// default comparison function.
if (cmp_fun == NULL)
cmp_fun = __ndtree_tree_node_cmp_ptr_cb;
// Iterate throught the children.
list_for_each_decl(it, &node->children)
{
ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings);
if (cmp_fun(tree, child->value, value) == 0)
return child;
}
}
}
return NULL;
}
unsigned int ndtree_tree_size(ndtree_t *tree)
{
if (tree)
return tree->size;
return 0;
}
int ndtree_tree_remove_node_with_cb(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb)
{
if (tree && node) {
// Remove the node from the parent list.
list_head_del(&node->siblings);
// If the node has children, we need to migrate them.
if (!list_head_empty(&node->children)) {
// The new parent, by default it is NULL.
ndtree_node_t *new_parent = NULL;
// The new list, by default it is the list of orphans of the tree.
list_head *new_list = &tree->orphans;
// If the found node has a parent, we need to set the variables
// so that we can migrate the children.
if (node->parent) {
new_parent = node->parent;
new_list = &node->parent->children;
}
// Migrate the children.
list_for_each_decl(it, &node->children)
{
ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings);
child->parent = new_parent;
}
// Merge the lists.
list_head_merge(new_list, &node->children);
}
if (node_cb)
node_cb(tree, node);
else
ndtree_node_dealloc(node);
--tree->size;
return 1;
}
return 0;
}
int ndtree_tree_remove_with_cb(ndtree_t *tree, void *value, ndtree_tree_node_f node_cb)
{
if (tree && value) {
ndtree_node_t *node = ndtree_tree_find(tree, tree->cmp, value);
return ndtree_tree_remove_node_with_cb(tree, node, node_cb);
}
return 0;
}
// ============================================================================
// Iterators.
ndtree_iter_t *ndtree_iter_alloc()
{
ndtree_iter_t *iter = kmalloc(sizeof(ndtree_iter_t));
iter->head = NULL;
iter->current = NULL;
return iter;
}
void ndtree_iter_dealloc(ndtree_iter_t *iter)
{
if (iter)
kfree(iter);
}
ndtree_node_t *ndtree_iter_first(ndtree_node_t *node, ndtree_iter_t *iter)
{
if (node && iter) {
if (!list_head_empty(&node->children)) {
iter->head = &node->children;
iter->current = iter->head->next;
return list_entry(iter->current, ndtree_node_t, siblings);
}
}
return NULL;
}
ndtree_node_t *ndtree_iter_last(ndtree_node_t *node, ndtree_iter_t *iter)
{
if (node && iter) {
if (!list_head_empty(&node->children)) {
iter->head = &node->children;
iter->current = iter->head->prev;
return list_entry(iter->current, ndtree_node_t, siblings);
}
}
return NULL;
}
ndtree_node_t *ndtree_iter_next(ndtree_iter_t *iter)
{
if (iter) {
if (iter->current->next != iter->head) {
iter->current = iter->current->next;
return list_entry(iter->current, ndtree_node_t, siblings);
}
}
return NULL;
}
ndtree_node_t *ndtree_iter_prev(ndtree_iter_t *iter)
{
if (iter) {
if (iter->current->next != iter->head) {
iter->current = iter->current->prev;
return list_entry(iter->current, ndtree_node_t, siblings);
}
}
return NULL;
}
// ============================================================================
// Tree debugging functions.
static void __ndtree_tree_visitor_iter(ndtree_t *tree,
ndtree_node_t *node,
ndtree_tree_node_f enter_fun,
ndtree_tree_node_f exit_fun)
{
assert(tree);
assert(node);
if (enter_fun)
enter_fun(tree, node);
if (!list_head_empty(&node->children))
list_for_each_decl(it, &node->children)
__ndtree_tree_visitor_iter(tree, list_entry(it, ndtree_node_t, siblings), enter_fun, exit_fun);
if (exit_fun)
exit_fun(tree, node);
}
void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun)
{
if (tree && tree->root)
__ndtree_tree_visitor_iter(tree, tree->root, enter_fun, exit_fun);
}
+564
View File
@@ -0,0 +1,564 @@
/// MentOS, The Mentoring Operating system project
/// @file rbtree.c
/// @brief Red/Black tree.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "rbtree.h"
#include "assert.h"
#include "debug.h"
#include "slab.h"
/// @brief Stores information of a node.
struct rbtree_node_t {
/// Color red (1), black (0)
int red;
/// Link left [0] and right [1]
rbtree_node_t *link[2];
/// User provided, used indirectly via rbtree_tree_node_cmp_f.
void *value;
};
/// @brief Stores information of a rbtree.
struct rbtree_t {
/// Root of the tree.
rbtree_node_t *root;
/// Comparison function for insertion.
rbtree_tree_node_cmp_f cmp;
/// Size of the tree.
unsigned int size;
};
/// @brief Stores information for iterating a rbtree.
struct rbtree_iter_t {
/// Pointer to the tree itself.
rbtree_t *tree;
/// Current node
rbtree_node_t *node;
/// Traversal path
rbtree_node_t *path[RBTREE_ITER_MAX_HEIGHT];
/// Top of stack
unsigned int top;
};
rbtree_node_t *rbtree_node_alloc()
{
return kmalloc(sizeof(rbtree_node_t));
}
rbtree_node_t *rbtree_node_init(rbtree_node_t *node, void *value)
{
if (node) {
node->red = 1;
node->link[0] = node->link[1] = NULL;
node->value = value;
}
return node;
}
rbtree_node_t *rbtree_node_create(void *value)
{
return rbtree_node_init(rbtree_node_alloc(), value);
}
void *rbtree_node_get_value(rbtree_node_t *node)
{
if (node)
return node->value;
return NULL;
}
void rbtree_node_dealloc(rbtree_node_t *node)
{
if (node)
kfree(node);
}
static int rbtree_node_is_red(const rbtree_node_t *node)
{
return node ? node->red : 0;
}
static rbtree_node_t *rbtree_node_rotate(rbtree_node_t *node, int dir)
{
rbtree_node_t *result = NULL;
if (node) {
result = node->link[!dir];
node->link[!dir] = result->link[dir];
result->link[dir] = node;
node->red = 1;
result->red = 0;
}
return result;
}
static rbtree_node_t *rbtree_node_rotate2(rbtree_node_t *node, int dir)
{
rbtree_node_t *result = NULL;
if (node) {
node->link[!dir] = rbtree_node_rotate(node->link[!dir], !dir);
result = rbtree_node_rotate(node, dir);
}
return result;
}
// rbtree_t - default callbacks
static int rbtree_tree_node_cmp_ptr_cb(
rbtree_t *tree,
rbtree_node_t *a,
rbtree_node_t *b)
{
(void)tree;
return (a->value > b->value) - (a->value < b->value);
}
static void rbtree_tree_node_dealloc_cb(rbtree_t *tree, rbtree_node_t *node)
{
if (tree)
if (node)
rbtree_node_dealloc(node);
}
// rbtree_t
rbtree_t *rbtree_tree_alloc()
{
return kmalloc(sizeof(rbtree_t));
}
rbtree_t *rbtree_tree_init(rbtree_t *tree, rbtree_tree_node_cmp_f node_cmp_cb)
{
if (tree) {
tree->root = NULL;
tree->size = 0;
tree->cmp = node_cmp_cb ? node_cmp_cb : rbtree_tree_node_cmp_ptr_cb;
}
return tree;
}
rbtree_t *rbtree_tree_create(rbtree_tree_node_cmp_f node_cb)
{
return rbtree_tree_init(rbtree_tree_alloc(), node_cb);
}
void rbtree_tree_dealloc(rbtree_t *tree, rbtree_tree_node_f node_cb)
{
assert(tree);
if (node_cb) {
rbtree_node_t *node = tree->root;
rbtree_node_t *save = NULL;
// Rotate away the left links so that
// we can treat this like the destruction
// of a linked list
while (node) {
if (node->link[0] == NULL) {
// No left links, just kill the node and move on
save = node->link[1];
node_cb(tree, node);
kfree(node);
node = NULL;
} else {
// Rotate away the left link and check again
save = node->link[0];
node->link[0] = save->link[1];
save->link[1] = node;
}
node = save;
}
}
kfree(tree);
}
void *rbtree_tree_find(rbtree_t *tree, void *value)
{
void *result = NULL;
if (tree) {
rbtree_node_t node = { .value = value };
rbtree_node_t *it = tree->root;
int cmp = 0;
while (it) {
if ((cmp = tree->cmp(tree, it, &node))) {
// If the tree supports duplicates, they should be
// chained to the right subtree for this to work
it = it->link[cmp < 0];
} else {
break;
}
}
result = it ? it->value : NULL;
}
return result;
}
void *rbtree_tree_find_by_value(rbtree_t *tree,
rbtree_tree_cmp_f cmp_fun,
void *value)
{
void *result = NULL;
if (tree) {
rbtree_node_t *it = tree->root;
int cmp = 0;
while (it) {
if ((cmp = cmp_fun(tree, it, value))) {
// If the tree supports duplicates, they should be
// chained to the right subtree for this to work
it = it->link[cmp < 0];
} else {
break;
}
}
result = it ? it->value : NULL;
}
return result;
}
// Creates (kmalloc'ates)
int rbtree_tree_insert(rbtree_t *tree, void *value)
{
return rbtree_tree_insert_node(tree, rbtree_node_create(value));
}
// Returns 1 on success, 0 otherwise.
int rbtree_tree_insert_node(rbtree_t *tree, rbtree_node_t *node)
{
if (tree && node) {
if (tree->root == NULL) {
tree->root = node;
} else {
rbtree_node_t head = { 0 }; // False tree root
rbtree_node_t *g, *t; // Grandparent & parent
rbtree_node_t *p, *q; // Iterator & parent
int dir = 0, last = 0;
// Set up our helpers
t = &head;
g = p = NULL;
q = t->link[1] = tree->root;
// Search down the tree for a place to insert
while (1) {
if (q == NULL) {
// Insert node at the first null link.
p->link[dir] = q = node;
} else if (rbtree_node_is_red(q->link[0]) &&
rbtree_node_is_red(q->link[1])) {
// Simple red violation: color flip
q->red = 1;
q->link[0]->red = 0;
q->link[1]->red = 0;
}
if (rbtree_node_is_red(q) && rbtree_node_is_red(p)) {
// Hard red violation: rotations necessary
int dir2 = t->link[1] == g;
if (q == p->link[last]) {
t->link[dir2] = rbtree_node_rotate(g, !last);
} else {
t->link[dir2] = rbtree_node_rotate2(g, !last);
}
}
// Stop working if we inserted a node. This
// check also disallows duplicates in the tree
if (tree->cmp(tree, q, node) == 0) {
break;
}
last = dir;
dir = tree->cmp(tree, q, node) < 0;
// Move the helpers down
if (g != NULL) {
t = g;
}
g = p, p = q;
q = q->link[dir];
}
// Update the root (it may be different)
tree->root = head.link[1];
}
// Make the root black for simplified logic
tree->root->red = 0;
++tree->size;
}
return 1;
}
// Returns 1 if the value was removed, 0 otherwise. Optional node callback
// can be provided to dealloc node and/or user data. Use rbtree_tree_node_dealloc
// default callback to deallocate node created by rbtree_tree_insert(...).
int rbtree_tree_remove_with_cb(rbtree_t *tree,
void *value,
rbtree_tree_node_f node_cb)
{
if (tree->root != NULL) {
rbtree_node_t head = { 0 }; // False tree root
rbtree_node_t node = { .value = value }; // Value wrapper node
rbtree_node_t *q, *p, *g; // Helpers
rbtree_node_t *f = NULL; // Found item
int dir = 1;
// Set up our helpers
q = &head;
g = p = NULL;
q->link[1] = tree->root;
// Search and push a red node down
// to fix red violations as we go
while (q->link[dir] != NULL) {
int last = dir;
// Move the helpers down
g = p, p = q;
q = q->link[dir];
dir = tree->cmp(tree, q, &node) < 0;
// Save the node with matching value and keep
// going; we'll do removal tasks at the end
if (tree->cmp(tree, q, &node) == 0) {
f = q;
}
// Push the red node down with rotations and color flips
if (!rbtree_node_is_red(q) && !rbtree_node_is_red(q->link[dir])) {
if (rbtree_node_is_red(q->link[!dir])) {
p = p->link[last] = rbtree_node_rotate(q, dir);
} else if (!rbtree_node_is_red(q->link[!dir])) {
rbtree_node_t *s = p->link[!last];
if (s) {
if (!rbtree_node_is_red(s->link[!last]) &&
!rbtree_node_is_red(s->link[last])) {
// Color flip
p->red = 0;
s->red = 1;
q->red = 1;
} else {
int dir2 = g->link[1] == p;
if (rbtree_node_is_red(s->link[last])) {
g->link[dir2] = rbtree_node_rotate2(p, last);
} else if (rbtree_node_is_red(s->link[!last])) {
g->link[dir2] = rbtree_node_rotate(p, last);
}
// Ensure correct coloring
q->red = g->link[dir2]->red = 1;
g->link[dir2]->link[0]->red = 0;
g->link[dir2]->link[1]->red = 0;
}
}
}
}
}
// Replace and remove the saved node
if (f) {
void *tmp = f->value;
f->value = q->value;
q->value = tmp;
p->link[p->link[1] == q] = q->link[q->link[0] == NULL];
if (node_cb) {
node_cb(tree, q);
}
q = NULL;
}
// Update the root (it may be different)
tree->root = head.link[1];
// Make the root black for simplified logic
if (tree->root != NULL) {
tree->root->red = 0;
}
--tree->size;
}
return 1;
}
int rbtree_tree_remove(rbtree_t *tree, void *value)
{
int result = 0;
if (tree) {
result = rbtree_tree_remove_with_cb(
tree, value, rbtree_tree_node_dealloc_cb);
}
return result;
}
unsigned int rbtree_tree_size(rbtree_t *tree)
{
unsigned int result = 0;
if (tree) {
result = tree->size;
}
return result;
}
// rbtree_iter_t
rbtree_iter_t *rbtree_iter_alloc()
{
return kmalloc(sizeof(rbtree_iter_t));
}
rbtree_iter_t *rbtree_iter_init(rbtree_iter_t *iter)
{
if (iter) {
iter->tree = NULL;
iter->node = NULL;
iter->top = 0;
}
return iter;
}
rbtree_iter_t *rbtree_iter_create()
{
return rbtree_iter_init(rbtree_iter_alloc());
}
void rbtree_iter_dealloc(rbtree_iter_t *iter)
{
if (iter) {
kfree(iter);
}
}
// Internal function, init traversal object, dir determines whether
// to begin traversal at the smallest or largest valued node.
static void *rbtree_iter_start(rbtree_iter_t *iter, rbtree_t *tree, int dir)
{
void *result = NULL;
if (iter) {
iter->tree = tree;
iter->node = tree->root;
iter->top = 0;
// Save the path for later selfersal
if (iter->node != NULL) {
while (iter->node->link[dir] != NULL) {
iter->path[iter->top++] = iter->node;
iter->node = iter->node->link[dir];
}
}
result = iter->node == NULL ? NULL : iter->node->value;
}
return result;
}
// Traverse a red black tree in the user-specified direction (0 asc, 1 desc)
static void *rbtree_iter_move(rbtree_iter_t *iter, int dir)
{
if (iter->node->link[dir] != NULL) {
// Continue down this branch
iter->path[iter->top++] = iter->node;
iter->node = iter->node->link[dir];
while (iter->node->link[!dir] != NULL) {
iter->path[iter->top++] = iter->node;
iter->node = iter->node->link[!dir];
}
} else {
// Move to the next branch
rbtree_node_t *last = NULL;
do {
if (iter->top == 0) {
iter->node = NULL;
break;
}
last = iter->node;
iter->node = iter->path[--iter->top];
} while (last == iter->node->link[dir]);
}
return iter->node == NULL ? NULL : iter->node->value;
}
void *rbtree_iter_first(rbtree_iter_t *iter, rbtree_t *tree)
{
return rbtree_iter_start(iter, tree, 0);
}
void *rbtree_iter_last(rbtree_iter_t *iter, rbtree_t *tree)
{
return rbtree_iter_start(iter, tree, 1);
}
void *rbtree_iter_next(rbtree_iter_t *iter)
{
return rbtree_iter_move(iter, 1);
}
void *rbtree_iter_prev(rbtree_iter_t *iter)
{
return rbtree_iter_move(iter, 0);
}
int rbtree_tree_test(rbtree_t *tree, rbtree_node_t *root)
{
int lh, rh;
if (root == NULL)
return 1;
else {
rbtree_node_t *ln = root->link[0];
rbtree_node_t *rn = root->link[1];
/* Consecutive red links */
if (rbtree_node_is_red(root)) {
if (rbtree_node_is_red(ln) || rbtree_node_is_red(rn)) {
pr_err("Red violation");
return 0;
}
}
lh = rbtree_tree_test(tree, ln);
rh = rbtree_tree_test(tree, rn);
/* Invalid binary search tree */
if ((ln != NULL && tree->cmp(tree, ln, root) >= 0) || (rn != NULL && tree->cmp(tree, rn, root) <= 0)) {
pr_err("Binary tree violation");
return 0;
}
/* Black height mismatch */
if (lh != 0 && rh != 0 && lh != rh) {
pr_err("Black violation");
return 0;
}
/* Only count black links */
if (lh != 0 && rh != 0)
return rbtree_node_is_red(root) ? lh : lh + 1;
else
return 0;
}
}
static void rbtree_tree_print_iter(rbtree_t *tree,
rbtree_node_t *node,
rbtree_tree_node_f fun)
{
assert(tree);
assert(node);
assert(fun);
fun(tree, node);
if (node->link[0])
rbtree_tree_print_iter(tree, node->link[0], fun);
if (node->link[1])
rbtree_tree_print_iter(tree, node->link[1], fun);
}
void rbtree_tree_print(rbtree_t *tree, rbtree_tree_node_f fun)
{
assert(tree);
assert(fun);
rbtree_tree_print_iter(tree, tree->root, fun);
}
+34
View File
@@ -0,0 +1,34 @@
/// MentOS, The Mentoring Operating system project
/// @file spinlock.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "spinlock.h"
void spinlock_init(spinlock_t *spinlock)
{
(*spinlock) = SPINLOCK_FREE;
}
void spinlock_lock(spinlock_t *spinlock)
{
while (1) {
if (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0) {
break;
}
while (*spinlock)
cpu_relax();
}
}
void spinlock_unlock(spinlock_t *spinlock)
{
barrier();
atomic_set(spinlock, SPINLOCK_FREE);
}
int spinlock_trylock(spinlock_t *spinlock)
{
return atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0;
}
+480
View File
@@ -0,0 +1,480 @@
/// MentOS, The Mentoring Operating system project
/// @file strerror.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "strerror.h"
#include "string.h"
char *strerror(int errnum)
{
static char error[1024];
switch (errnum) {
case 0:
strcpy(error, "Success");
break;
#ifdef ENOENT
case ENOENT:
strcpy(error, "No such file or directory");
break;
#endif
#ifdef ESRCH
case ESRCH:
strcpy(error, "No such process");
break;
#endif
#ifdef EINTR
case EINTR:
strcpy(error, "Interrupted system call");
break;
#endif
#ifdef EIO
case EIO:
strcpy(error, "I/O error");
break;
#endif
#if defined(ENXIO) && (!defined(ENODEV) || (ENXIO != ENODEV))
case ENXIO:
strcpy(error, "No such device or address");
break;
#endif
#ifdef E2BIG
case E2BIG:
strcpy(error, "Arg list too long");
break;
#endif
#ifdef ENOEXEC
case ENOEXEC:
strcpy(error, "Exec format error");
break;
#endif
#ifdef EALREADY
case EALREADY:
strcpy(error, "Socket already connected");
break;
#endif
#ifdef EBADF
case EBADF:
strcpy(error, "Bad file number");
break;
#endif
#ifdef ECHILD
case ECHILD:
strcpy(error, "No children");
break;
#endif
#ifdef EDESTADDRREQ
case EDESTADDRREQ:
strcpy(error, "Destination address required");
break;
#endif
#ifdef EAGAIN
case EAGAIN:
strcpy(error, "No more processes");
break;
#endif
#ifdef ENOMEM
case ENOMEM:
strcpy(error, "Not enough space");
break;
#endif
#ifdef EACCES
case EACCES:
strcpy(error, "Permission denied");
break;
#endif
#ifdef EFAULT
case EFAULT:
strcpy(error, "Bad address");
break;
#endif
#ifdef ENOTBLK
case ENOTBLK:
strcpy(error, "Block device required");
break;
#endif
#ifdef EBUSY
case EBUSY:
strcpy(error, "Device or resource busy");
break;
#endif
#ifdef EEXIST
case EEXIST:
strcpy(error, "File exists");
break;
#endif
#ifdef EXDEV
case EXDEV:
strcpy(error, "Cross-device link");
break;
#endif
#ifdef ENODEV
case ENODEV:
strcpy(error, "No such device");
break;
#endif
#ifdef ENOTDIR
case ENOTDIR:
strcpy(error, "Not a directory");
break;
#endif
#ifdef EHOSTDOWN
case EHOSTDOWN:
strcpy(error, "Host is down");
break;
#endif
#ifdef EINPROGRESS
case EINPROGRESS:
strcpy(error, "Connection already in progress");
break;
#endif
#ifdef EISDIR
case EISDIR:
strcpy(error, "Is a directory");
break;
#endif
#ifdef EINVAL
case EINVAL:
strcpy(error, "Invalid argument");
break;
#endif
#ifdef EISNAM
case EISNAM:
strcpy(error, "Is a named type file");
break;
#endif
#ifdef ENETDOWN
case ENETDOWN:
strcpy(error, "Network interface is not configured");
break;
#endif
#ifdef ENFILE
case ENFILE:
strcpy(error, "Too many open files in system");
break;
#endif
#ifdef EMFILE
case EMFILE:
strcpy(error, "Too many open files");
break;
#endif
#ifdef ENOTTY
case ENOTTY:
strcpy(error, "Not a character device");
break;
#endif
#ifdef ETXTBSY
case ETXTBSY:
strcpy(error, "Text file busy");
break;
#endif
#ifdef EFBIG
case EFBIG:
strcpy(error, "File too large");
break;
#endif
#ifdef EHOSTUNREACH
case EHOSTUNREACH:
strcpy(error, "Host is unreachable");
break;
#endif
#ifdef ENOSPC
case ENOSPC:
strcpy(error, "No space left on device");
break;
#endif
#ifdef ENOTSUP
case ENOTSUP:
strcpy(error, "Not supported");
break;
#endif
#ifdef ESPIPE
case ESPIPE:
strcpy(error, "Illegal seek");
break;
#endif
#ifdef EROFS
case EROFS:
strcpy(error, "Read-only file system");
break;
#endif
#ifdef EMLINK
case EMLINK:
strcpy(error, "Too many links");
break;
#endif
#ifdef EPIPE
case EPIPE:
strcpy(error, "Broken pipe");
break;
#endif
#ifdef EDOM
case EDOM:
strcpy(error, "Math argument");
break;
#endif
#ifdef ERANGE
case ERANGE:
strcpy(error, "Result too large");
break;
#endif
#ifdef ENOMSG
case ENOMSG:
strcpy(error, "No message of desired type");
break;
#endif
#ifdef EIDRM
case EIDRM:
strcpy(error, "Identifier removed");
break;
#endif
#ifdef EDEADLK
case EDEADLK:
strcpy(error, "Deadlock");
break;
#endif
#ifdef ENETUNREACH
case ENETUNREACH:
strcpy(error, "Network is unreachable");
break;
#endif
#ifdef ENOLCK
case ENOLCK:
strcpy(error, "No lock");
break;
#endif
#ifdef ENOSTR
case ENOSTR:
strcpy(error, "Not a stream");
break;
#endif
#ifdef ETIME
case ETIME:
strcpy(error, "Stream ioctl timeout");
break;
#endif
#ifdef ENOSR
case ENOSR:
strcpy(error, "No stream resources");
break;
#endif
#ifdef ENONET
case ENONET:
strcpy(error, "Machine is not on the network");
break;
#endif
#ifdef ENOPKG
case ENOPKG:
strcpy(error, "No package");
break;
#endif
#ifdef EREMOTE
case EREMOTE:
strcpy(error, "Resource is remote");
break;
#endif
#ifdef ENOLINK
case ENOLINK:
strcpy(error, "Virtual circuit is gone");
break;
#endif
#ifdef EADV
case EADV:
strcpy(error, "Advertise error");
break;
#endif
#ifdef ESRMNT
case ESRMNT:
strcpy(error, "Srmount error");
break;
#endif
#ifdef ECOMM
case ECOMM:
strcpy(error, "Communication error");
break;
#endif
#ifdef EPROTO
case EPROTO:
strcpy(error, "Protocol error");
break;
#endif
#ifdef EPROTONOSUPPORT
case EPROTONOSUPPORT:
strcpy(error, "Unknown protocol");
break;
#endif
#ifdef EMULTIHOP
case EMULTIHOP:
strcpy(error, "Multihop attempted");
break;
#endif
#ifdef EBADMSG
case EBADMSG:
strcpy(error, "Bad message");
break;
#endif
#ifdef ELIBACC
case ELIBACC:
strcpy(error, "Cannot access a needed shared library");
break;
#endif
#ifdef ELIBBAD
case ELIBBAD:
strcpy(error, "Accessing a corrupted shared library");
break;
#endif
#ifdef ELIBSCN
case ELIBSCN:
strcpy(error, ".lib section in a.out corrupted");
break;
#endif
#ifdef ELIBMAX
case ELIBMAX:
strcpy(error,
"Attempting to link in more shared libraries than system limit");
break;
#endif
#ifdef ELIBEXEC
case ELIBEXEC:
strcpy(error, "Cannot exec a shared library directly");
break;
#endif
#ifdef ENOSYS
case ENOSYS:
strcpy(error, "Function not implemented");
break;
#endif
#ifdef ENMFILE
case ENMFILE:
strcpy(error, "No more files");
break;
#endif
#ifdef ENOTEMPTY
case ENOTEMPTY:
strcpy(error, "Directory not empty");
break;
#endif
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
strcpy(error, "File or path name too long");
break;
#endif
#ifdef ELOOP
case ELOOP:
strcpy(error, "Too many symbolic links");
break;
#endif
#ifdef ENOBUFS
case ENOBUFS:
strcpy(error, "No buffer space available");
break;
#endif
#ifdef EAFNOSUPPORT
case EAFNOSUPPORT:
strcpy(error, "Address family not supported by protocol family");
break;
#endif
#ifdef EPROTOTYPE
case EPROTOTYPE:
strcpy(error, "Protocol wrong type for socket");
break;
#endif
#ifdef ENOTSOCK
case ENOTSOCK:
strcpy(error, "Socket operation on non-socket");
break;
#endif
#ifdef ENOPROTOOPT
case ENOPROTOOPT:
strcpy(error, "Protocol not available");
break;
#endif
#ifdef ESHUTDOWN
case ESHUTDOWN:
strcpy(error, "Can't send after socket shutdown");
break;
#endif
#ifdef ECONNREFUSED
case ECONNREFUSED:
strcpy(error, "Connection refused");
break;
#endif
#ifdef EADDRINUSE
case EADDRINUSE:
strcpy(error, "Address already in use");
break;
#endif
#ifdef ECONNABORTED
case ECONNABORTED:
strcpy(error, "Software caused connection abort");
break;
#endif
#if (defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)))
case EWOULDBLOCK:
strcpy(error, "Operation would block");
break;
#endif
#ifdef ENOTCONN
case ENOTCONN:
strcpy(error, "Socket is not connected");
break;
#endif
#ifdef ESOCKTNOSUPPORT
case ESOCKTNOSUPPORT:
strcpy(error, "Socket type not supported");
break;
#endif
#ifdef EISCONN
case EISCONN:
strcpy(error, "Socket is already connected");
break;
#endif
#ifdef ECANCELED
case ECANCELED:
strcpy(error, "Operation canceled");
break;
#endif
#ifdef ENOTRECOVERABLE
case ENOTRECOVERABLE:
strcpy(error, "State not recoverable");
break;
#endif
#ifdef EOWNERDEAD
case EOWNERDEAD:
strcpy(error, "Previous owner died");
break;
#endif
#ifdef ESTRPIPE
case ESTRPIPE:
strcpy(error, "Streams pipe error");
break;
#endif
#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP))
case EOPNOTSUPP:
strcpy(error, "Operation not supported on socket");
break;
#endif
#ifdef EMSGSIZE
case EMSGSIZE:
strcpy(error, "Message too long");
break;
#endif
#ifdef ETIMEDOUT
case ETIMEDOUT:
strcpy(error, "Connection timed out");
break;
#endif
#ifdef ENOTSCHEDULABLE
case ENOTSCHEDULABLE:
strcpy(error, "The process cannot be scheduled");
break;
#endif
default:
strcpy(error, "Unknown error");
break;
}
return error;
}
+701
View File
@@ -0,0 +1,701 @@
/// MentOS, The Mentoring Operating system project
/// @file string.c
/// @brief String routines.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "kheap.h"
#include "stdio.h"
#include "fcntl.h"
#include "string.h"
#include "ctype.h"
char *strncpy(char *destination, const char *source, size_t num)
{
char *start = destination;
while (num && (*destination++ = *source++)) {
num--;
}
if (num) {
while (--num) {
*destination++ = '\0';
}
}
return start;
}
int strncmp(const char *s1, const char *s2, size_t n)
{
if (!n)
return 0;
while ((--n > 0) && (*s1) && (*s2) && (*s1 == *s2)) {
s1++;
s2++;
}
return *(unsigned char *)s1 - *(unsigned char *)s2;
}
int stricmp(const char *s1, const char *s2)
{
while (*s2 != 0 && toupper(*s1) == toupper(*s2)) {
s1++, s2++;
}
return (toupper(*s1) - toupper(*s2));
}
int strnicmp(const char *s1, const char *s2, size_t n)
{
int f, l;
do {
if (((f = (unsigned char)(*(s1++))) >= 'A') && (f <= 'Z')) {
f -= 'A' - 'a';
}
if (((l = (unsigned char)(*(s2++))) >= 'A') && (l <= 'Z')) {
l -= 'A' - 'a';
}
} while (--n && f && (f == l));
return f - l;
}
char *strchr(const char *s, int ch)
{
while (*s && *s != (char)ch) {
s++;
}
if (*s == (char)ch)
return (char *)s;
{
return NULL;
}
}
char *strrchr(const char *s, int ch)
{
char *start = (char *)s;
while (*s++) {}
while (--s != start && *s != (char)ch) {}
if (*s == (char)ch) {
return (char *)s;
}
return NULL;
}
char *strstr(const char *str1, const char *str2)
{
char *cp = (char *)str1;
char *s1, *s2;
if (!*str2) {
return (char *)str1;
}
while (*cp) {
s1 = cp;
s2 = (char *)str2;
while (*s1 && *s2 && !(*s1 - *s2)) {
s1++, s2++;
}
if (!*s2) {
return cp;
}
cp++;
}
return NULL;
}
size_t strspn(const char *string, const char *control)
{
const char *str = string;
const char *ctrl = control;
char map[32];
size_t n;
// Clear out bit map.
for (n = 0; n < 32; n++) {
map[n] = 0;
}
// Set bits in control map.
while (*ctrl) {
map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7));
ctrl++;
}
// 1st char NOT in control map stops search.
if (*str) {
n = 0;
while (map[*str >> 3] & (1 << (*str & 7))) {
n++;
str++;
}
return n;
}
return 0;
}
size_t strcspn(const char *string, const char *control)
{
const char *str = string;
const char *ctrl = control;
char map[32];
size_t n;
// Clear out bit map.
for (n = 0; n < 32; n++)
map[n] = 0;
// Set bits in control map.
while (*ctrl) {
map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7));
ctrl++;
}
// 1st char in control map stops search.
n = 0;
map[0] |= 1;
while (!(map[*str >> 3] & (1 << (*str & 7)))) {
n++;
str++;
}
return n;
}
char *strpbrk(const char *string, const char *control)
{
const char *str = string;
const char *ctrl = control;
char map[32];
int n;
// Clear out bit map.
for (n = 0; n < 32; n++)
map[n] = 0;
// Set bits in control map.
while (*ctrl) {
map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7));
ctrl++;
}
// 1st char in control map stops search.
while (*str) {
if (map[*str >> 3] & (1 << (*str & 7))) {
return (char *)str;
}
str++;
}
return NULL;
}
void *memmove(void *dst, const void *src, size_t n)
{
void *ret = dst;
if (dst <= src || (char *)dst >= ((char *)src + n)) {
/* Non-overlapping buffers; copy from lower addresses to higher
* addresses.
*/
while (n--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
} else {
// Overlapping buffers; copy from higher addresses to lower addresses.
dst = (char *)dst + n - 1;
src = (char *)src + n - 1;
while (n--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return ret;
}
void *memchr(const void *ptr, int ch, size_t n)
{
while (n && (*(unsigned char *)ptr != (unsigned char)ch)) {
ptr = (unsigned char *)ptr + 1;
n--;
}
return (n ? (void *)ptr : NULL);
}
char *strlwr(char *s)
{
char *p = s;
while (*p) {
*p = (char)tolower(*p);
p++;
}
return s;
}
char *strupr(char *s)
{
char *p = s;
while (*p) {
*p = (char)toupper(*p);
p++;
}
return s;
}
char *strcat(char *dst, const char *src)
{
char *cp = dst;
while (*cp) {
cp++;
}
while ((*cp++ = *src++) != '\0') {}
return dst;
}
char *strncat(char *s1, const char *s2, size_t n)
{
char *start = s1;
while (*s1++) {}
s1--;
while (n--) {
if (!(*s1++ = *s2++))
return start;
}
*s1 = '\0';
return start;
}
char *strrev(char *s)
{
char *start = s;
char *left = s;
char ch;
while (*s++) {}
s -= 2;
while (left < s) {
ch = *left;
*left++ = *s;
*s-- = ch;
}
return start;
}
char *strtok_r(char *str, const char *delim, char **saveptr)
{
char *s;
const char *ctrl = delim;
char map[32];
int n;
// Clear delim map.
for (n = 0; n < 32; n++) {
map[n] = 0;
}
// Set bits in delimiter table.
do {
map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7));
} while (*ctrl++);
/* Initialize s. If str is NULL, set s to the saved
* pointer (i.e., continue breaking tokens out of the str
* from the last strtok call).
*/
if (str) {
s = str;
} else {
s = *saveptr;
}
/* Find beginning of token (skip over leading delimiters). Note that
* there is no token iff this loop sets s to point to the terminal
* null (*s == '\0').
*/
while ((map[*s >> 3] & (1 << (*s & 7))) && *s) {
s++;
}
str = s;
/* Find the end of the token. If it is not the end of the str,
* put a null there.
*/
for (; *s; s++) {
if (map[*s >> 3] & (1 << (*s & 7))) {
*s++ = '\0';
break;
}
}
// Update nexttoken.
*saveptr = s;
// Determine if a token has been found.
if (str == (char *)s) {
return NULL;
} else {
return str;
}
}
// Intrinsic functions.
/*
* #pragma function(memset)
* #pragma function(memcmp)
* #pragma function(memcpy)
* #pragma function(strcpy)
* #pragma function(strlen)
* #pragma function(strcat)
* #pragma function(strcmp)
* #pragma function(strset)
*/
void *memset(void *ptr, int value, size_t num)
{
// Truncate c to 8 bits.
value = (value & 0xFF);
char *dst = (char *)ptr;
// Initialize the rest of the size.
while (num--) {
*dst++ = (char)value;
}
return ptr;
}
int memcmp(const void *dst, const void *src, size_t n)
{
if (!n) {
return 0;
}
while (--n && *(char *)dst == *(char *)src) {
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return *((unsigned char *)dst) - *((unsigned char *)src);
}
void *memcpy(void *dst, const void *src, size_t num)
{
char *_dst = dst;
const char *_src = src;
while (num--) {
*_dst++ = *_src++;
}
return dst;
}
void *memccpy(void *dst, const void *src, int c, size_t n)
{
while (n && (*((char *)(dst = (char *)dst + 1) - 1) =
*((char *)(src = (char *)src + 1) - 1)) != (char)c) {
n--;
}
return n ? dst : NULL;
}
char *strcpy(char *dst, const char *src)
{
char *save = dst;
while ((*dst++ = *src++) != '\0') {}
return save;
}
size_t strlen(const char *s)
{
const char *eos;
for (eos = s; *eos != 0; ++eos) {}
long len = eos - s;
return (len < 0) ? 0 : (size_t)len;
}
size_t strnlen(const char *s, size_t count)
{
const char *sc;
for (sc = s; *sc != '\0' && count--; ++sc) {}
long len = sc - s;
return (len < 0) ? 0 : (size_t)len;
}
int strcmp(const char *s1, const char *s2)
{
int ret = 0;
const char *s1t = s1, *s2t = s2;
for (; !(ret = *s1t - *s2t) && *s2t; ++s1t, ++s2t) {}
return (ret < 0) ? -1 : (ret > 0) ? 1 :
0;
}
char *strset(char *s, int c)
{
char *start = s;
while (*s) {
*s++ = (char)c;
}
return start;
}
char *strnset(char *s, int c, size_t n)
{
while (n-- && *s) {
*s++ = (char)c;
}
return s;
}
char *strtok(char *str, const char *delim)
{
const char *spanp;
int c, sc;
char *tok;
static char *last;
if (str == NULL && (str = last) == NULL) {
return (NULL);
}
cont:
c = *str++;
for (spanp = delim; (sc = *spanp++) != 0;) {
if (c == sc) {
goto cont;
}
}
if (c == 0) {
last = NULL;
return (NULL);
}
tok = str - 1;
for (;;) {
c = *str++;
spanp = delim;
do {
if ((sc = *spanp++) == c) {
if (c == 0) {
str = NULL;
} else {
str[-1] = 0;
}
last = str;
return (tok);
}
} while (sc != 0);
}
}
char *trim(char *str)
{
size_t len = 0;
char *frontp = str;
char *endp = NULL;
if (str == NULL) {
return NULL;
}
if (str[0] == '\0') {
return str;
}
len = strlen(str);
endp = str + len;
/* Move the front and back pointers to address the first non-whitespace
* characters from each end.
*/
while (isspace((unsigned char)*frontp)) {
++frontp;
}
if (endp != frontp) {
while (isspace((unsigned char)*(--endp)) && endp != frontp) {}
}
if (str + len - 1 != endp) {
*(endp + 1) = '\0';
} else if (frontp != str && endp == frontp) {
*str = '\0';
}
/* Shift the string so that it starts at str so that if it's dynamically
* allocated, we can still free it on the returned pointer. Note the reuse
* of endp to mean the front of the string buffer now.
*/
endp = str;
if (frontp != str) {
while (*frontp) {
*endp++ = *frontp++;
}
*endp = '\0';
}
return str;
}
char *strdup(const char *s)
{
size_t len = strlen(s) + 1;
char *new = kmalloc(len);
if (new == NULL)
return NULL;
new[len] = '\0';
return (char *)memcpy(new, s, len);
}
char *strndup(const char *s, size_t n)
{
size_t len = strnlen(s, n);
char *new = kmalloc(len);
if (new == NULL)
return NULL;
new[len] = '\0';
return (char *)memcpy(new, s, len);
}
char *strsep(char **stringp, const char *delim)
{
char *s;
const char *spanp;
int c, sc;
char *tok;
if ((s = *stringp) == NULL) {
return (NULL);
}
for (tok = s;;) {
c = *s++;
spanp = delim;
do {
if ((sc = *spanp++) == c) {
if (c == 0) {
s = NULL;
} else {
s[-1] = 0;
}
*stringp = s;
return (tok);
}
} while (sc != 0);
}
}
char *itoa(char *buffer, unsigned int num, unsigned int base)
{
// int numval;
char *p, *pbase;
p = pbase = buffer;
if (base == 16) {
sprintf(buffer, "%0x", num);
} else {
if (num == 0) {
*p++ = '0';
}
while (num != 0) {
*p++ = (char)('0' + (num % base));
num = num / base;
}
*p-- = 0;
while (p > pbase) {
char tmp;
tmp = *p;
*p = *pbase;
*pbase = tmp;
p--;
pbase++;
}
}
return buffer;
}
char *replace_char(char *str, char find, char replace)
{
char *current_pos = strchr(str, find);
while (current_pos) {
*current_pos = replace;
current_pos = strchr(current_pos, find);
}
return str;
}
void strmode(mode_t mode, char *p)
{
// Usr.
*p++ = mode & S_IRUSR ? 'r' : '-';
*p++ = mode & S_IWUSR ? 'w' : '-';
*p++ = mode & S_IXUSR ? 'x' : '-';
// Group.
*p++ = mode & S_IRGRP ? 'r' : '-';
*p++ = mode & S_IWGRP ? 'w' : '-';
*p++ = mode & S_IXGRP ? 'x' : '-';
// Other.
*p++ = mode & S_IROTH ? 'r' : '-';
*p++ = mode & S_IWOTH ? 'w' : '-';
*p++ = mode & S_IXOTH ? 'x' : '-';
// Will be a '+' if ACL's implemented.
*p++ = ' ';
*p = '\0';
}
+115
View File
@@ -0,0 +1,115 @@
/// MentOS, The Mentoring Operating system project
/// @file time.c
/// @brief Clock functions.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "debug.h"
#include "time.h"
#include "stdio.h"
#include "stddef.h"
#include "port_io.h"
#include "timer.h"
#include "rtc.h"
static const char *str_weekdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday" };
static const char *str_months[] = { "January", "February", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December" };
time_t sys_time(time_t *time)
{
tm_t curr_time;
gettime(&curr_time);
// January and February are counted as months 13 and 14 of the previous year.
if (curr_time.tm_mon <= 2) {
curr_time.tm_mon += 12;
curr_time.tm_year -= 1;
}
time_t t;
// Convert years to days
t = (365 * curr_time.tm_year) + (curr_time.tm_year / 4) - (curr_time.tm_year / 100) +
(curr_time.tm_year / 400);
// Convert months to days
t += (30 * curr_time.tm_mon) + (3 * (curr_time.tm_mon + 1) / 5) + curr_time.tm_mday;
// Unix time starts on January 1st, 1970
t -= 719561;
// Convert days to seconds
t *= 86400;
// Add hours, minutes and seconds
t += (3600 * curr_time.tm_hour) + (60 * curr_time.tm_min) + curr_time.tm_sec;
if (time) {
(*time) = t;
}
return t;
}
time_t difftime(time_t time1, time_t time2)
{
return time1 - time2;
}
/// @brief Computes day of week
/// @param y Year
/// @param m Month of year (in range 1 to 12)
/// @param d Day of month (in range 1 to 31)
/// @return Day of week (in range 1 to 7)
static inline int day_of_week(unsigned int y, unsigned int m, unsigned int d)
{
int h, j, k;
// January and February are counted as months 13 and 14 of the previous year
if (m <= 2) {
m += 12;
y -= 1;
}
// J is the century
j = (int)(y / 100);
// K the year of the century
k = (int)(y % 100);
// Compute H using Zeller's congruence
h = (int)(d + (26 * (m + 1) / 10) + k + (k / 4) + (5 * j) + (j / 4));
// Return the day of the week
return ((h + 5) % 7) + 1;
}
tm_t *localtime(const time_t *time)
{
static tm_t date;
unsigned int a, b, c, d, e, f;
time_t t = *time;
// Negative Unix time values are not supported
if (t < 1) {
t = 0;
}
//Retrieve hours, minutes and seconds
date.tm_sec = (int)(t % 60);
t /= 60;
date.tm_min = (int)(t % 60);
t /= 60;
date.tm_hour = (int)(t % 24);
t /= 24;
// Convert Unix time to date
a = (unsigned int)((4 * t + 102032) / 146097 + 15);
b = (unsigned int)(t + 2442113 + a - (a / 4));
c = (20 * b - 2442) / 7305;
d = b - 365 * c - (c / 4);
e = d * 1000 / 30601;
f = d - e * 30 - e * 601 / 1000;
// January and February are counted as months 13 and 14 of the previous year
if (e <= 13) {
c -= 4716;
e -= 1;
} else {
c -= 4715;
e -= 13;
}
//Retrieve year, month and day
date.tm_year = (int)c;
date.tm_mon = (int)e;
date.tm_mday = (int)f;
// Calculate day of week.
date.tm_wday = day_of_week(date.tm_year, date.tm_mon, date.tm_mday);
return &date;
}
+105
View File
@@ -0,0 +1,105 @@
/// MentOS, The Mentoring Operating system project
/// @file vscanf.c
/// @brief Reading formatting routines.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "vfs.h"
#include "ctype.h"
#include "string.h"
#include "debug.h"
#include "stdio.h"
static int vsscanf(const char *buf, const char *s, va_list ap)
{
int count = 0, noassign = 0, width = 0, base = 0;
const char *tc;
char tmp[BUFSIZ];
while (*s && *buf) {
while (isspace(*s))
++s;
if (*s == '%') {
++s;
for (; *s; ++s) {
if (strchr("dibouxcsefg%", *s))
break;
if (*s == '*')
noassign = 1;
else if (isdigit(*s)) {
for (tc = s; isdigit(*s); ++s)
;
strncpy(tmp, tc, s - tc);
tmp[s - tc] = '\0';
width = strtol(tmp, NULL, 10);
--s;
}
}
if (*s == 's') {
while (isspace(*buf))
++buf;
if (!width)
width = strcspn(buf, " \t\n\r\f\v");
if (!noassign) {
char *string = va_arg(ap, char *);
strncpy(string, buf, width);
string[width] = '\0';
}
buf += width;
} else if (*s == 'c') {
while (isspace(*buf))
++buf;
if (!width)
width = 1;
if (!noassign) {
strncpy(va_arg(ap, char *), buf, width);
}
buf += width;
} else if (strchr("duxob", *s)) {
while (isspace(*buf))
++buf;
if (*s == 'd' || *s == 'u')
base = 10;
else if (*s == 'x')
base = 16;
else if (*s == 'o')
base = 8;
else if (*s == 'b')
base = 2;
if (!width) {
if (isspace(*(s + 1)) || *(s + 1) == 0)
width = strcspn(buf, " \t\n\r\f\v");
else
width = strchr(buf, *(s + 1)) - buf;
}
strncpy(tmp, buf, width);
tmp[width] = '\0';
buf += width;
if (!noassign)
*va_arg(ap, unsigned int *) = strtol(tmp, NULL, base);
}
if (!noassign)
++count;
width = noassign = 0;
++s;
} else {
while (isspace(*buf))
++buf;
if (*s != *buf)
break;
else
++s, ++buf;
}
}
return (count);
}
int sscanf(const char *buf, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int count = vsscanf(buf, fmt, ap);
va_end(ap);
return count;
}
+675
View File
@@ -0,0 +1,675 @@
/// MentOS, The Mentoring Operating system project
/// @file vsprintf.c
/// @brief Print formatting routines.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "math.h"
#include "ctype.h"
#include "string.h"
#include "stdarg.h"
#include "stdbool.h"
#include "stdint.h"
#include "stdio.h"
#include "video.h"
#include "fcvt.h"
/// Size of the buffer used to call cvt functions.
#define CVTBUFSIZE 500
#define FLAGS_ZEROPAD (1U << 0U) ///< Fill zeros before the number.
#define FLAGS_LEFT (1U << 1U) ///< Left align the value.
#define FLAGS_PLUS (1U << 2U) ///< Print the plus sign.
#define FLAGS_SPACE (1U << 3U) ///< If positive add a space instead of the plus sign.
#define FLAGS_HASH (1U << 4U) ///< Preceed with 0x or 0X, %x or %X respectively.
#define FLAGS_UPPERCASE (1U << 5U) ///< Print uppercase.
#define FLAGS_SIGN (1U << 6U) ///< Print the sign.
/// The list of digits.
static char *_digits = "0123456789abcdefghijklmnopqrstuvwxyz";
/// The list of uppercase digits.
static char *_upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/// @brief Returns the index of the first non-integer character.
static inline int skip_atoi(const char **s)
{
int i = 0;
while (isdigit(**s))
i = i * 10 + *((*s)++) - '0';
return i;
}
static char *number(char *str, long num, int base, int size, int32_t precision, unsigned flags)
{
char c, tmp[66] = { 0 };
char *dig = _digits;
if (flags & FLAGS_UPPERCASE) {
dig = _upper_digits;
}
if (flags & FLAGS_LEFT) {
flags &= ~FLAGS_ZEROPAD;
}
if (base < 2 || base > 36) {
return 0;
}
c = (flags & FLAGS_ZEROPAD) ? '0' : ' ';
// --------------------------------
// Set the sign.
// --------------------------------
char sign = 0;
if (flags & FLAGS_SIGN) {
if (num < 0) {
sign = '-';
num = -num;
size--;
} else if (flags & FLAGS_PLUS) {
sign = '+';
size--;
} else if (flags & FLAGS_SPACE) {
sign = ' ';
size--;
}
}
// Sice I've removed the sign (if negative), i can transform it to unsigned.
uint32_t uns_num = (uint32_t)num;
if (flags & FLAGS_HASH) {
if (base == 16) {
size -= 2;
} else if (base == 8) {
size--;
}
}
int32_t i = 0;
if (uns_num == 0) {
tmp[i++] = '0';
} else {
while (uns_num != 0) {
tmp[i++] = dig[((unsigned long)uns_num) % (unsigned)base];
uns_num = ((unsigned long)uns_num) / (unsigned)base;
}
}
if (i > precision) {
precision = i;
}
size -= precision;
if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) {
while (size-- > 0)
*str++ = ' ';
}
if (sign) {
*str++ = sign;
}
if (flags & FLAGS_HASH) {
if (base == 8)
*str++ = '0';
else if (base == 16) {
*str++ = '0';
*str++ = _digits[33];
}
}
if (!(flags & FLAGS_LEFT)) {
while (size-- > 0) {
*str++ = c;
}
}
while (i < precision--) {
*str++ = '0';
}
while (i-- > 0) {
*str++ = tmp[i];
}
while (size-- > 0) {
*str++ = ' ';
}
return str;
}
static char *eaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags)
{
(void)precision;
char tmp[24];
char *dig = _digits;
int i, len;
if (flags & FLAGS_UPPERCASE) {
dig = _upper_digits;
}
len = 0;
for (i = 0; i < 6; i++) {
if (i != 0) {
tmp[len++] = ':';
}
tmp[len++] = dig[addr[i] >> 4];
tmp[len++] = dig[addr[i] & 0x0F];
}
if (!(flags & FLAGS_LEFT)) {
while (len < size--) {
*str++ = ' ';
}
}
for (i = 0; i < len; ++i) {
*str++ = tmp[i];
}
while (len < size--) {
*str++ = ' ';
}
return str;
}
static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags)
{
(void)precision;
char tmp[24];
int i, n, len;
len = 0;
for (i = 0; i < 4; i++) {
if (i != 0) {
tmp[len++] = '.';
}
n = addr[i];
if (n == 0) {
tmp[len++] = _digits[0];
} else {
if (n >= 100) {
tmp[len++] = _digits[n / 100];
n = n % 100;
tmp[len++] = _digits[n / 10];
n = n % 10;
} else if (n >= 10) {
tmp[len++] = _digits[n / 10];
n = n % 10;
}
tmp[len++] = _digits[n];
}
}
if (!(flags & FLAGS_LEFT)) {
while (len < size--) {
*str++ = ' ';
}
}
for (i = 0; i < len; ++i) {
*str++ = tmp[i];
}
while (len < size--) {
*str++ = ' ';
}
return str;
}
static void cfltcvt(double value, char *buffer, char fmt, int precision)
{
int decpt, sign, exp, pos;
char cvtbuf[CVTBUFSIZE];
char *digits = cvtbuf;
int capexp = 0;
int magnitude;
if (fmt == 'G' || fmt == 'E') {
capexp = 1;
fmt += 'a' - 'A';
}
if (fmt == 'g') {
ecvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE);
magnitude = decpt - 1;
if (magnitude < -4 || magnitude > precision - 1) {
fmt = 'e';
precision -= 1;
} else {
fmt = 'f';
precision -= decpt;
}
}
if (fmt == 'e') {
ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf, CVTBUFSIZE);
if (sign) {
*buffer++ = '-';
}
*buffer++ = *digits;
if (precision > 0) {
*buffer++ = '.';
}
memcpy(buffer, digits + 1, precision);
buffer += precision;
*buffer++ = capexp ? 'E' : 'e';
if (decpt == 0) {
if (value == 0.0) {
exp = 0;
} else {
exp = -1;
}
} else {
exp = decpt - 1;
}
if (exp < 0) {
*buffer++ = '-';
exp = -exp;
} else {
*buffer++ = '+';
}
buffer[2] = (char)((exp % 10) + '0');
exp = exp / 10;
buffer[1] = (char)((exp % 10) + '0');
exp = exp / 10;
buffer[0] = (char)((exp % 10) + '0');
buffer += 3;
} else if (fmt == 'f') {
fcvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE);
if (sign) {
*buffer++ = '-';
}
if (*digits) {
if (decpt <= 0) {
*buffer++ = '0';
*buffer++ = '.';
for (pos = 0; pos < -decpt; pos++) {
*buffer++ = '0';
}
while (*digits) {
*buffer++ = *digits++;
}
} else {
pos = 0;
while (*digits) {
if (pos++ == decpt) {
*buffer++ = '.';
}
*buffer++ = *digits++;
}
}
} else {
*buffer++ = '0';
if (precision > 0) {
*buffer++ = '.';
for (pos = 0; pos < precision; pos++) {
*buffer++ = '0';
}
}
}
}
*buffer = '\0';
}
static void forcdecpt(char *buffer)
{
while (*buffer) {
if (*buffer == '.') {
return;
}
if (*buffer == 'e' || *buffer == 'E') {
break;
}
buffer++;
}
if (*buffer) {
long n = (long)strlen(buffer);
while (n > 0) {
buffer[n + 1] = buffer[n];
n--;
}
*buffer = '.';
} else {
*buffer++ = '.';
*buffer = '\0';
}
}
static void cropzeros(char *buffer)
{
char *stop;
while (*buffer && *buffer != '.') {
buffer++;
}
if (*buffer++) {
while (*buffer && *buffer != 'e' && *buffer != 'E') {
buffer++;
}
stop = buffer--;
while (*buffer == '0') {
buffer--;
}
if (*buffer == '.') {
buffer--;
}
while ((*++buffer = *stop++)) {}
}
}
static char *flt(char *str, double num, int size, int precision, char fmt, unsigned flags)
{
char tmp[80];
char c, sign;
int n, i;
// Left align means no zero padding.
if (flags & FLAGS_LEFT)
flags &= ~FLAGS_ZEROPAD;
// Determine padding and sign char.
c = (flags & FLAGS_ZEROPAD) ? '0' : ' ';
sign = 0;
if (flags & FLAGS_SIGN) {
if (num < 0.0) {
sign = '-';
num = -num;
size--;
} else if (flags & FLAGS_PLUS) {
sign = '+';
size--;
} else if (flags & FLAGS_SPACE) {
sign = ' ';
size--;
}
}
// Compute the precision value.
if (precision < 0) {
// Default precision: 6.
precision = 6;
} else if (precision == 0 && fmt == 'g') {
// ANSI specified.
precision = 1;
}
// Convert floating point number to text.
cfltcvt(num, tmp, fmt, precision);
// '#' and precision == 0 means force a decimal point.
if ((flags & FLAGS_HASH) && precision == 0) {
forcdecpt(tmp);
}
// 'g' format means crop zero unless '#' given.
if (fmt == 'g' && !(flags & FLAGS_HASH)) {
cropzeros(tmp);
}
n = strlen(tmp);
// Output number with alignment and padding.
size -= n;
if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) {
while (size-- > 0) {
*str++ = ' ';
}
}
if (sign) {
*str++ = sign;
}
if (!(flags & FLAGS_LEFT)) {
while (size-- > 0) {
*str++ = c;
}
}
for (i = 0; i < n; i++) {
*str++ = tmp[i];
}
while (size-- > 0) {
*str++ = ' ';
}
return str;
}
int vsprintf(char *str, const char *fmt, va_list args)
{
int base;
char *tmp;
char *s;
// Flags to number().
unsigned flags;
// 'h', 'l', or 'L' for integer fields.
char qualifier;
for (tmp = str; *fmt; fmt++) {
if (*fmt != '%') {
*tmp++ = *fmt;
continue;
}
// Process flags-
flags = 0;
repeat:
// This also skips first '%'.
fmt++;
switch (*fmt) {
case '-':
flags |= FLAGS_LEFT;
goto repeat;
case '+':
flags |= FLAGS_PLUS;
goto repeat;
case ' ':
flags |= FLAGS_SPACE;
goto repeat;
case '#':
flags |= FLAGS_HASH;
goto repeat;
case '0':
flags |= FLAGS_ZEROPAD;
goto repeat;
}
// Get the width of the output field.
int32_t field_width;
field_width = -1;
if (isdigit(*fmt)) {
field_width = skip_atoi(&fmt);
} else if (*fmt == '*') {
fmt++;
field_width = va_arg(args, int32_t);
if (field_width < 0) {
field_width = -field_width;
flags |= FLAGS_LEFT;
}
}
/* Get the precision, thus the minimum number of digits for
* integers; max number of chars for from string.
*/
int32_t precision = -1;
if (*fmt == '.') {
++fmt;
if (isdigit(*fmt)) {
precision = skip_atoi(&fmt);
} else if (*fmt == '*') {
++fmt;
precision = va_arg(args, int);
}
if (precision < 0) {
precision = 0;
}
}
// Get the conversion qualifier.
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
qualifier = *fmt;
fmt++;
}
// Default base.
base = 10;
switch (*fmt) {
case 'c':
if (!(flags & FLAGS_LEFT)) {
while (--field_width > 0) {
*tmp++ = ' ';
}
}
*tmp++ = va_arg(args, char);
while (--field_width > 0) {
*tmp++ = ' ';
}
continue;
case 's':
s = va_arg(args, char *);
if (!s) {
s = "<NULL>";
}
int32_t len = (int32_t)strnlen(s, (uint32_t)precision);
if (!(flags & FLAGS_LEFT)) {
while (len < field_width--) {
*tmp++ = ' ';
}
}
int32_t it;
for (it = 0; it < len; ++it) {
*tmp++ = *s++;
}
while (len < field_width--) {
*tmp++ = ' ';
}
continue;
case 'p':
if (field_width == -1) {
field_width = 2 * sizeof(void *);
flags |= FLAGS_ZEROPAD;
}
tmp = number(tmp, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags);
continue;
case 'n':
if (qualifier == 'l') {
long *ip = va_arg(args, long *);
*ip = (tmp - str);
} else {
int *ip = va_arg(args, int *);
*ip = (tmp - str);
}
continue;
case 'A':
flags |= FLAGS_UPPERCASE;
break;
case 'a':
if (qualifier == 'l')
tmp = eaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags);
else
tmp = iaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags);
continue;
// Integer number formats - set up the flags and "break".
case 'o':
base = 8;
break;
case 'X':
flags |= FLAGS_UPPERCASE;
break;
case 'x':
base = 16;
break;
case 'd':
case 'i':
flags |= FLAGS_SIGN;
case 'u':
break;
case 'E':
case 'G':
case 'e':
case 'f':
case 'g':
tmp = flt(tmp, va_arg(args, double), field_width, precision, *fmt, flags | FLAGS_SIGN);
continue;
default:
if (*fmt != '%')
*tmp++ = '%';
if (*fmt)
*tmp++ = *fmt;
else
--fmt;
continue;
}
if (flags & FLAGS_SIGN) {
long num;
if (qualifier == 'l') {
num = va_arg(args, long);
} else if (qualifier == 'h') {
num = va_arg(args, short);
} else {
num = va_arg(args, int);
}
tmp = number(tmp, num, base, field_width, precision, flags);
} else {
unsigned long num;
if (qualifier == 'l') {
num = va_arg(args, unsigned long);
} else if (qualifier == 'h') {
num = va_arg(args, unsigned short);
} else {
num = va_arg(args, unsigned int);
}
tmp = number(tmp, num, base, field_width, precision, flags);
}
}
*tmp = '\0';
return tmp - str;
}
int printf(const char *format, ...)
{
char buffer[4096];
va_list ap;
int len;
// Start variabile argument's list.
va_start(ap, format);
len = vsprintf(buffer, format, ap);
va_end(ap);
video_puts(buffer);
return len;
}
int sprintf(char *str, const char *fmt, ...)
{
va_list args;
int len;
va_start(args, fmt);
len = vsprintf(str, fmt, args);
va_end(args);
return len;
}