Update MentOs code to the latest development version.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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 == ' ');
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user