From 8021cff1225b0bf4771ff6386cadb27d83bd8953 Mon Sep 17 00:00:00 2001 From: Jordan Bancino Date: Sun, 14 May 2023 19:35:23 +0000 Subject: [PATCH] Make Telodendria use Cytoplasm. This commit removes all the duplicate code and makes Telodendria use the new Cytoplasm library. --- Cytoplasm/LICENSE.txt | 1 - Cytoplasm/make.sh | 4 + src/Array.c | 339 --------- src/Base64.c | 244 ------- src/Cron.c | 249 ------- src/Db.c | 968 ------------------------- src/HashMap.c | 401 ----------- src/HeaderParser.c | 664 ----------------- src/Http.c | 642 ----------------- src/HttpClient.c | 298 -------- src/HttpRouter.c | 296 -------- src/HttpServer.c | 753 -------------------- src/Io.c | 212 ------ src/Io/IoFd.c | 95 --- src/Io/IoFile.c | 91 --- src/Json.c | 1384 ------------------------------------ src/Log.c | 388 ---------- src/Memory.c | 493 ------------- src/Queue.c | 176 ----- src/Rand.c | 172 ----- src/Sha2.c | 238 ------- src/Str.c | 297 -------- src/Stream.c | 657 ----------------- src/Tls.c | 85 --- src/Tls/TlsLibreSSL.c | 247 ------- src/Tls/TlsOpenSSL.c | 296 -------- src/Uri.c | 73 -- src/Util.c | 246 ------- src/include/Array.h | 164 ----- src/include/Base64.h | 99 --- src/include/Cron.h | 139 ---- src/include/Db.h | 169 ----- src/include/HashMap.h | 167 ----- src/include/HeaderParser.h | 125 ---- src/include/Http.h | 211 ------ src/include/HttpClient.h | 104 --- src/include/HttpRouter.h | 91 --- src/include/HttpServer.h | 234 ------ src/include/Int.h | 125 ---- src/include/Io.h | 222 ------ src/include/Json.h | 322 --------- src/include/Log.h | 196 ----- src/include/Memory.h | 225 ------ src/include/Queue.h | 105 --- src/include/Rand.h | 81 --- src/include/Sha2.h | 50 -- src/include/Str.h | 113 --- src/include/Stream.h | 223 ------ src/include/Tls.h | 109 --- src/include/Uri.h | 69 -- src/include/Util.h | 105 --- tools/bin/td | 72 +- tools/env.sh | 5 +- tools/src/hdoc.c | 520 -------------- tools/src/intcheck.c | 48 -- 55 files changed, 34 insertions(+), 14068 deletions(-) delete mode 100644 src/Array.c delete mode 100644 src/Base64.c delete mode 100644 src/Cron.c delete mode 100644 src/Db.c delete mode 100644 src/HashMap.c delete mode 100644 src/HeaderParser.c delete mode 100644 src/Http.c delete mode 100644 src/HttpClient.c delete mode 100644 src/HttpRouter.c delete mode 100644 src/HttpServer.c delete mode 100644 src/Io.c delete mode 100644 src/Io/IoFd.c delete mode 100644 src/Io/IoFile.c delete mode 100644 src/Json.c delete mode 100644 src/Log.c delete mode 100644 src/Memory.c delete mode 100644 src/Queue.c delete mode 100644 src/Rand.c delete mode 100644 src/Sha2.c delete mode 100644 src/Str.c delete mode 100644 src/Stream.c delete mode 100644 src/Tls.c delete mode 100644 src/Tls/TlsLibreSSL.c delete mode 100644 src/Tls/TlsOpenSSL.c delete mode 100644 src/Uri.c delete mode 100644 src/Util.c delete mode 100644 src/include/Array.h delete mode 100644 src/include/Base64.h delete mode 100644 src/include/Cron.h delete mode 100644 src/include/Db.h delete mode 100644 src/include/HashMap.h delete mode 100644 src/include/HeaderParser.h delete mode 100644 src/include/Http.h delete mode 100644 src/include/HttpClient.h delete mode 100644 src/include/HttpRouter.h delete mode 100644 src/include/HttpServer.h delete mode 100644 src/include/Int.h delete mode 100644 src/include/Io.h delete mode 100644 src/include/Json.h delete mode 100644 src/include/Log.h delete mode 100644 src/include/Memory.h delete mode 100644 src/include/Queue.h delete mode 100644 src/include/Rand.h delete mode 100644 src/include/Sha2.h delete mode 100644 src/include/Str.h delete mode 100644 src/include/Stream.h delete mode 100644 src/include/Tls.h delete mode 100644 src/include/Uri.h delete mode 100644 src/include/Util.h delete mode 100644 tools/src/hdoc.c delete mode 100644 tools/src/intcheck.c diff --git a/Cytoplasm/LICENSE.txt b/Cytoplasm/LICENSE.txt index 9d1c610..90f6e6d 100644 --- a/Cytoplasm/LICENSE.txt +++ b/Cytoplasm/LICENSE.txt @@ -1,5 +1,4 @@ /* - * Cytoplasm (libcytoplasm) * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> * * Permission is hereby granted, free of charge, to any person diff --git a/Cytoplasm/make.sh b/Cytoplasm/make.sh index c87a3e2..37e86df 100755 --- a/Cytoplasm/make.sh +++ b/Cytoplasm/make.sh @@ -122,6 +122,10 @@ recipe_docs() { fi } +recipe_libs() { + echo "-lm -pthread ${TLS_LIBS}" +} + recipe_build() { mkdir -p "${BUILD}" ${OUT}/{bin,lib} cd "${SRC}" diff --git a/src/Array.c b/src/Array.c deleted file mode 100644 index 948d9d9..0000000 --- a/src/Array.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#ifndef ARRAY_BLOCK -#define ARRAY_BLOCK 16 -#endif - -#include -#include - -struct Array -{ - void **entries; /* An array of void pointers, to - * store any data */ - size_t allocated; /* Elements allocated on the heap */ - size_t size; /* Elements actually filled */ -}; - -int -ArrayAdd(Array * array, void *value) -{ - if (!array) - { - return 0; - } - - return ArrayInsert(array, array->size, value); -} - -Array * -ArrayCreate(void) -{ - Array *array = Malloc(sizeof(Array)); - - if (!array) - { - return NULL; - } - - array->size = 0; - array->allocated = ARRAY_BLOCK; - array->entries = Malloc(sizeof(void *) * ARRAY_BLOCK); - - if (!array->entries) - { - Free(array); - return NULL; - } - - return array; -} - -void * -ArrayDelete(Array * array, size_t index) -{ - size_t i; - void *element; - - if (!array || array->size <= index) - { - return NULL; - } - - element = array->entries[index]; - - for (i = index; i < array->size - 1; i++) - { - array->entries[i] = array->entries[i + 1]; - } - - array->size--; - - return element; -} - -void -ArrayFree(Array * array) -{ - if (array) - { - Free(array->entries); - Free(array); - } -} - -void * -ArrayGet(Array * array, size_t index) -{ - if (!array) - { - return NULL; - } - - if (index >= array->size) - { - return NULL; - } - - return array->entries[index]; -} - - -extern int -ArrayInsert(Array * array, size_t index, void *value) -{ - size_t i; - - if (!array || !value || index > array->size) - { - return 0; - } - - if (array->size >= array->allocated) - { - void **tmp; - size_t newSize = array->allocated + ARRAY_BLOCK; - - tmp = array->entries; - - array->entries = Realloc(array->entries, - sizeof(void *) * newSize); - - if (!array->entries) - { - array->entries = tmp; - return 0; - } - - array->allocated = newSize; - } - - for (i = array->size; i > index; i--) - { - array->entries[i] = array->entries[i - 1]; - } - - array->size++; - - array->entries[index] = value; - - return 1; -} - -extern void * -ArraySet(Array * array, size_t index, void *value) -{ - void *oldValue; - - if (!value) - { - return ArrayDelete(array, index); - } - - if (!array) - { - return NULL; - } - - if (index >= array->size) - { - return NULL; - } - - oldValue = array->entries[index]; - array->entries[index] = value; - - return oldValue; -} - -size_t -ArraySize(Array * array) -{ - if (!array) - { - return 0; - } - - return array->size; -} - -int -ArrayTrim(Array * array) -{ - void **tmp; - - if (!array) - { - return 0; - } - - tmp = array->entries; - - array->entries = Realloc(array->entries, - sizeof(void *) * array->size); - - if (!array->entries) - { - array->entries = tmp; - return 0; - } - - return 1; -} - -static void -ArraySwap(Array * array, size_t i, size_t j) -{ - void *p = array->entries[i]; - - array->entries[i] = array->entries[j]; - array->entries[j] = p; -} - -static size_t -ArrayPartition(Array * array, size_t low, size_t high, int (*compare) (void *, void *)) -{ - void *pivot = array->entries[high]; - size_t i = low - 1; - size_t j; - - for (j = low; j <= high - 1; j++) - { - if (compare(array->entries[j], pivot) < 0) - { - i++; - ArraySwap(array, i, j); - } - } - ArraySwap(array, i + 1, high); - return i + 1; -} - -static void -ArrayQuickSort(Array * array, size_t low, size_t high, int (*compare) (void *, void *)) -{ - if (low < high) - { - size_t pi = ArrayPartition(array, low, high, compare); - - ArrayQuickSort(array, low, pi - 1, compare); - ArrayQuickSort(array, pi + 1, high, compare); - } -} - -void -ArraySort(Array * array, int (*compare) (void *, void *)) -{ - if (!array) - { - return; - } - ArrayQuickSort(array, 0, array->size, compare); -} - -/* Even though the following operations could be done using only the - * public Array API defined above, I opted for low-level struct - * manipulation because it allows much more efficient copying; we only - * allocate what we for sure need upfront, and don't have to - * re-allocate during the operation. */ - -Array * -ArrayFromVarArgs(size_t n, va_list ap) -{ - size_t i; - Array *arr = Malloc(sizeof(Array)); - - if (!arr) - { - return NULL; - } - - arr->size = n; - arr->allocated = n; - arr->entries = Malloc(sizeof(void *) * arr->allocated); - - if (!arr->entries) - { - Free(arr); - return NULL; - } - - for (i = 0; i < n; i++) - { - arr->entries[i] = va_arg(ap, void *); - } - - return arr; -} - -Array * -ArrayDuplicate(Array * arr) -{ - size_t i; - Array *arr2 = Malloc(sizeof(Array)); - - if (!arr2) - { - return NULL; - } - - arr2->size = arr->size; - arr2->allocated = arr->size; - arr2->entries = Malloc(sizeof(void *) * arr->allocated); - - if (!arr2->entries) - { - Free(arr2); - return NULL; - } - - for (i = 0; i < arr2->size; i++) - { - arr2->entries[i] = arr->entries[i]; - } - - return arr2; -} diff --git a/src/Base64.c b/src/Base64.c deleted file mode 100644 index fc43be8..0000000 --- a/src/Base64.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include - -static const char Base64EncodeMap[] = -"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -static const int Base64DecodeMap[] = { - 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, - 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, - 43, 44, 45, 46, 47, 48, 49, 50, 51 -}; - -size_t -Base64EncodedSize(size_t inputSize) -{ - size_t size = inputSize; - - if (inputSize % 3) - { - size += 3 - (inputSize % 3); - } - - size /= 3; - size *= 4; - - return size; -} - -size_t -Base64DecodedSize(const char *base64, size_t len) -{ - size_t ret; - size_t i; - - if (!base64) - { - return 0; - } - - ret = len / 4 * 3; - - for (i = len; i > 0; i--) - { - if (base64[i] == '=') - { - ret--; - } - else - { - break; - } - } - - return ret; -} - -char * -Base64Encode(const char *input, size_t len) -{ - char *out; - size_t outLen; - size_t i, j, v; - - if (!input || !len) - { - return NULL; - } - - outLen = Base64EncodedSize(len); - out = Malloc(outLen + 1); - if (!out) - { - return NULL; - } - out[outLen] = '\0'; - - for (i = 0, j = 0; i < len; i += 3, j += 4) - { - v = input[i]; - v = i + 1 < len ? v << 8 | input[i + 1] : v << 8; - v = i + 2 < len ? v << 8 | input[i + 2] : v << 8; - - out[j] = Base64EncodeMap[(v >> 18) & 0x3F]; - out[j + 1] = Base64EncodeMap[(v >> 12) & 0x3F]; - - if (i + 1 < len) - { - out[j + 2] = Base64EncodeMap[(v >> 6) & 0x3F]; - } - else - { - out[j + 2] = '='; - } - if (i + 2 < len) - { - out[j + 3] = Base64EncodeMap[v & 0x3F]; - } - else - { - out[j + 3] = '='; - } - } - - return out; -} - -static int -Base64IsValidChar(char c) -{ - return (c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c == '+') || - (c == '/') || - (c == '='); -} - -char * -Base64Decode(const char *input, size_t len) -{ - size_t i, j; - int v; - size_t outLen; - char *out; - - if (!input) - { - return NULL; - } - - outLen = Base64DecodedSize(input, len); - if (len % 4) - { - /* Invalid length; must have incorrect padding */ - return NULL; - } - - /* Scan for invalid characters. */ - for (i = 0; i < len; i++) - { - if (!Base64IsValidChar(input[i])) - { - return NULL; - } - } - - out = Malloc(outLen + 1); - if (!out) - { - return NULL; - } - - out[outLen] = '\0'; - - for (i = 0, j = 0; i < len; i += 4, j += 3) - { - v = Base64DecodeMap[input[i] - 43]; - v = (v << 6) | Base64DecodeMap[input[i + 1] - 43]; - v = input[i + 2] == '=' ? v << 6 : (v << 6) | Base64DecodeMap[input[i + 2] - 43]; - v = input[i + 3] == '=' ? v << 6 : (v << 6) | Base64DecodeMap[input[i + 3] - 43]; - - out[j] = (v >> 16) & 0xFF; - if (input[i + 2] != '=') - out[j + 1] = (v >> 8) & 0xFF; - if (input[i + 3] != '=') - out[j + 2] = v & 0xFF; - } - - return out; -} - -extern void -Base64Unpad(char *base64, size_t length) -{ - if (!base64) - { - return; - } - - while (base64[length - 1] == '=') - { - length--; - } - - base64[length] = '\0'; -} - -extern int -Base64Pad(char **base64Ptr, size_t length) -{ - char *tmp; - size_t newSize; - size_t i; - - if (length % 4 == 0) - { - return length; /* Success: no padding needed */ - } - - newSize = length + (4 - (length % 4)); - - tmp = Realloc(*base64Ptr, newSize + 100);; - if (!tmp) - { - return 0; /* Memory error */ - } - *base64Ptr = tmp; - - for (i = length; i < newSize; i++) - { - (*base64Ptr)[i] = '='; - } - - (*base64Ptr)[newSize] = '\0'; - - return newSize; -} diff --git a/src/Cron.c b/src/Cron.c deleted file mode 100644 index b6abdc6..0000000 --- a/src/Cron.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include - -#include - -struct Cron -{ - unsigned long tick; - Array *jobs; - pthread_mutex_t lock; - volatile unsigned int stop:1; - pthread_t thread; -}; - -typedef struct Job -{ - unsigned long interval; - unsigned long lastExec; - JobFunc *func; - void *args; -} Job; - -static Job * -JobCreate(long interval, JobFunc * func, void *args) -{ - Job *job; - - if (!func) - { - return NULL; - } - - job = Malloc(sizeof(Job)); - if (!job) - { - return NULL; - } - - job->interval = interval; - job->lastExec = 0; - job->func = func; - job->args = args; - - return job; -} - -static void * -CronThread(void *args) -{ - Cron *cron = args; - - while (!cron->stop) - { - size_t i; - unsigned long ts; /* tick start */ - unsigned long te; /* tick end */ - - pthread_mutex_lock(&cron->lock); - - ts = UtilServerTs(); - - for (i = 0; i < ArraySize(cron->jobs); i++) - { - Job *job = ArrayGet(cron->jobs, i); - - if (ts - job->lastExec > job->interval) - { - job->func(job->args); - job->lastExec = ts; - } - - if (!job->interval) - { - ArrayDelete(cron->jobs, i); - Free(job); - } - } - te = UtilServerTs(); - - pthread_mutex_unlock(&cron->lock); - - /* Only sleep if the jobs didn't overrun the tick */ - if (cron->tick > (te - ts)) - { - const unsigned long microTick = 100; - unsigned long remainingTick = cron->tick - (te - ts); - - /* Only sleep for microTick ms at a time because if the job - * scheduler is supposed to stop before the tick is up, we - * don't want to be stuck in a long sleep */ - while (remainingTick >= microTick && !cron->stop) - { - UtilSleepMillis(microTick); - remainingTick -= microTick; - } - - if (remainingTick && !cron->stop) - { - UtilSleepMillis(remainingTick); - } - } - } - - return NULL; -} - -Cron * -CronCreate(unsigned long tick) -{ - Cron *cron = Malloc(sizeof(Cron)); - - if (!cron) - { - return NULL; - } - - cron->jobs = ArrayCreate(); - if (!cron->jobs) - { - Free(cron); - return NULL; - } - - cron->tick = tick; - cron->stop = 1; - - pthread_mutex_init(&cron->lock, NULL); - - return cron; -} - -void -CronOnce(Cron * cron, JobFunc * func, void *args) -{ - Job *job; - - if (!cron || !func) - { - return; - } - - job = JobCreate(0, func, args); - if (!job) - { - return; - } - - pthread_mutex_lock(&cron->lock); - ArrayAdd(cron->jobs, job); - pthread_mutex_unlock(&cron->lock); -} - -void -CronEvery(Cron * cron, unsigned long interval, JobFunc * func, void *args) -{ - Job *job; - - if (!cron || !func) - { - return; - } - - job = JobCreate(interval, func, args); - if (!job) - { - return; - } - - pthread_mutex_lock(&cron->lock); - ArrayAdd(cron->jobs, job); - pthread_mutex_unlock(&cron->lock); -} - -void -CronStart(Cron * cron) -{ - if (!cron || !cron->stop) - { - return; - } - - cron->stop = 0; - - pthread_create(&cron->thread, NULL, CronThread, cron); -} - -void -CronStop(Cron * cron) -{ - if (!cron || cron->stop) - { - return; - } - - cron->stop = 1; - - pthread_join(cron->thread, NULL); -} - -void -CronFree(Cron * cron) -{ - size_t i; - - if (!cron) - { - return; - } - - CronStop(cron); - - pthread_mutex_lock(&cron->lock); - for (i = 0; i < ArraySize(cron->jobs); i++) - { - Free(ArrayGet(cron->jobs, i)); - } - - ArrayFree(cron->jobs); - pthread_mutex_unlock(&cron->lock); - pthread_mutex_destroy(&cron->lock); - - Free(cron); -} diff --git a/src/Db.c b/src/Db.c deleted file mode 100644 index e777f63..0000000 --- a/src/Db.c +++ /dev/null @@ -1,968 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include - -struct Db -{ - char *dir; - pthread_mutex_t lock; - - size_t cacheSize; - size_t maxCache; - HashMap *cache; - - /* - * The cache uses a double linked list (see DbRef - * below) to know which objects are most and least - * recently used. The following diagram helps me - * know what way all the pointers go, because it - * can get very confusing sometimes. For example, - * there's nothing stopping "next" from pointing to - * least recent, and "prev" from pointing to most - * recent, so hopefully this clarifies the pointer - * terminology used when dealing with the linked - * list: - * - * mostRecent leastRecent - * | prev prev | prev - * +---+ ---> +---+ ---> +---+ ---> NULL - * |ref| |ref| |ref| - * NULL <--- +---+ <--- +---+ <--- +---+ - * next next next - */ - DbRef *mostRecent; - DbRef *leastRecent; -}; - -struct DbRef -{ - HashMap *json; - - unsigned long ts; - size_t size; - - Array *name; - - DbRef *prev; - DbRef *next; - - int fd; - Stream *stream; -}; - -static void -StringArrayFree(Array * arr) -{ - size_t i; - - for (i = 0; i < ArraySize(arr); i++) - { - Free(ArrayGet(arr, i)); - } - - ArrayFree(arr); -} - -static ssize_t DbComputeSize(HashMap *); - -static ssize_t -DbComputeSizeOfValue(JsonValue * val) -{ - MemoryInfo *a; - ssize_t total = 0; - - size_t i; - - union - { - char *str; - Array *arr; - } u; - - if (!val) - { - return -1; - } - - a = MemoryInfoGet(val); - if (a) - { - total += MemoryInfoGetSize(a); - } - - switch (JsonValueType(val)) - { - case JSON_OBJECT: - total += DbComputeSize(JsonValueAsObject(val)); - break; - case JSON_ARRAY: - u.arr = JsonValueAsArray(val); - a = MemoryInfoGet(u.arr); - - if (a) - { - total += MemoryInfoGetSize(a); - } - - for (i = 0; i < ArraySize(u.arr); i++) - { - total += DbComputeSizeOfValue(ArrayGet(u.arr, i)); - } - break; - case JSON_STRING: - u.str = JsonValueAsString(val); - a = MemoryInfoGet(u.str); - if (a) - { - total += MemoryInfoGetSize(a); - } - break; - case JSON_NULL: - case JSON_INTEGER: - case JSON_FLOAT: - case JSON_BOOLEAN: - default: - /* These don't use any extra heap space */ - break; - } - return total; -} - -static ssize_t -DbComputeSize(HashMap * json) -{ - char *key; - JsonValue *val; - MemoryInfo *a; - size_t total; - - if (!json) - { - return -1; - } - - total = 0; - - a = MemoryInfoGet(json); - if (a) - { - total += MemoryInfoGetSize(a); - } - - while (HashMapIterate(json, &key, (void **) &val)) - { - a = MemoryInfoGet(key); - if (a) - { - total += MemoryInfoGetSize(a); - } - - total += DbComputeSizeOfValue(val); - } - - return total; -} - -static char * -DbHashKey(Array * args) -{ - size_t i; - char *str = NULL; - - for (i = 0; i < ArraySize(args); i++) - { - char *tmp = StrConcat(2, str, ArrayGet(args, i)); - - Free(str); - str = tmp; - } - - return str; -} - -static char * -DbDirName(Db * db, Array * args, size_t strip) -{ - size_t i; - char *str = StrConcat(2, db->dir, "/"); - - for (i = 0; i < ArraySize(args) - strip; i++) - { - char *tmp; - - tmp = StrConcat(3, str, ArrayGet(args, i), "/"); - - Free(str); - - str = tmp; - } - - return str; -} - -static char * -DbFileName(Db * db, Array * args) -{ - size_t i; - char *str = StrConcat(2, db->dir, "/"); - - for (i = 0; i < ArraySize(args); i++) - { - char *tmp; - char *arg = StrDuplicate(ArrayGet(args, i)); - size_t j = 0; - - /* Sanitize name to prevent directory traversal attacks */ - while (arg[j]) - { - switch (arg[j]) - { - case '/': - arg[j] = '_'; - break; - case '.': - arg[j] = '-'; - break; - default: - break; - } - j++; - } - - tmp = StrConcat(3, str, arg, - (i < ArraySize(args) - 1) ? "/" : ".json"); - - Free(arg); - Free(str); - - str = tmp; - } - - return str; -} - -static void -DbCacheEvict(Db * db) -{ - DbRef *ref = db->leastRecent; - DbRef *tmp; - - while (ref && db->cacheSize > db->maxCache) - { - char *hash; - - JsonFree(ref->json); - - hash = DbHashKey(ref->name); - HashMapDelete(db->cache, hash); - Free(hash); - - StringArrayFree(ref->name); - - db->cacheSize -= ref->size; - - if (ref->next) - { - ref->next->prev = ref->prev; - } - else - { - db->mostRecent = ref->prev; - } - - if (ref->prev) - { - ref->prev->next = ref->next; - } - else - { - db->leastRecent = ref->next; - } - - tmp = ref->next; - Free(ref); - - ref = tmp; - } -} - -Db * -DbOpen(char *dir, size_t cache) -{ - Db *db; - pthread_mutexattr_t attr; - - if (!dir) - { - return NULL; - } - - db = Malloc(sizeof(Db)); - if (!db) - { - return NULL; - } - - db->dir = dir; - db->maxCache = cache; - - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&db->lock, &attr); - pthread_mutexattr_destroy(&attr); - - db->mostRecent = NULL; - db->leastRecent = NULL; - db->cacheSize = 0; - - if (db->maxCache) - { - db->cache = HashMapCreate(); - if (!db->cache) - { - return NULL; - } - } - else - { - db->cache = NULL; - } - - return db; -} - -void -DbMaxCacheSet(Db * db, size_t cache) -{ - if (!db) - { - return; - } - - pthread_mutex_lock(&db->lock); - - db->maxCache = cache; - if (db->maxCache && !db->cache) - { - db->cache = HashMapCreate(); - db->cacheSize = 0; - } - - DbCacheEvict(db); - - pthread_mutex_unlock(&db->lock); -} - -void -DbClose(Db * db) -{ - if (!db) - { - return; - } - - pthread_mutex_lock(&db->lock); - - DbMaxCacheSet(db, 0); - DbCacheEvict(db); - HashMapFree(db->cache); - - pthread_mutex_unlock(&db->lock); - pthread_mutex_destroy(&db->lock); - - Free(db); -} - -static DbRef * -DbLockFromArr(Db * db, Array * args) -{ - char *file; - char *hash; - DbRef *ref; - struct flock lock; - - int fd; - Stream *stream; - - if (!db || !args) - { - return NULL; - } - - ref = NULL; - hash = NULL; - - pthread_mutex_lock(&db->lock); - - /* Check if the item is in the cache */ - hash = DbHashKey(args); - ref = HashMapGet(db->cache, hash); - file = DbFileName(db, args); - - fd = open(file, O_RDWR); - if (fd == -1) - { - if (ref) - { - HashMapDelete(db->cache, hash); - JsonFree(ref->json); - StringArrayFree(ref->name); - - db->cacheSize -= ref->size; - - if (ref->next) - { - ref->next->prev = ref->prev; - } - else - { - db->mostRecent = ref->prev; - } - - if (ref->prev) - { - ref->prev->next = ref->next; - } - else - { - db->leastRecent = ref->next; - } - - if (!db->leastRecent) - { - db->leastRecent = db->mostRecent; - } - - Free(ref); - } - ref = NULL; - goto finish; - } - - stream = StreamFd(fd); - - lock.l_start = 0; - lock.l_len = 0; - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - - /* Lock the file on the disk */ - if (fcntl(fd, F_SETLK, &lock) < 0) - { - StreamClose(stream); - ref = NULL; - goto finish; - } - - if (ref) /* In cache */ - { - unsigned long diskTs = UtilLastModified(file); - - ref->fd = fd; - ref->stream = stream; - - if (diskTs > ref->ts) - { - /* File was modified on disk since it was cached */ - HashMap *json = JsonDecode(ref->stream); - - if (!json) - { - StreamClose(ref->stream); - ref = NULL; - goto finish; - } - - JsonFree(ref->json); - ref->json = json; - ref->ts = diskTs; - ref->size = DbComputeSize(ref->json); - } - - /* Float this ref to mostRecent */ - if (ref->next) - { - ref->next->prev = ref->prev; - - if (!ref->prev) - { - db->leastRecent = ref->next; - } - else - { - ref->prev->next = ref->next; - } - - ref->prev = db->mostRecent; - ref->next = NULL; - if (db->mostRecent) - { - db->mostRecent->next = ref; - } - db->mostRecent = ref; - } - - /* If there is no least recent, this is the only thing in the - * cache, so it is also least recent. */ - if (!db->leastRecent) - { - db->leastRecent = ref; - } - - /* The file on disk may be larger than what we have in memory, - * which may require items in cache to be evicted. */ - DbCacheEvict(db); - } - else - { - Array *name; - size_t i; - - /* Not in cache; load from disk */ - - ref = Malloc(sizeof(DbRef)); - if (!ref) - { - StreamClose(stream); - goto finish; - } - - ref->json = JsonDecode(stream); - - if (!ref->json) - { - Free(ref); - StreamClose(stream); - ref = NULL; - goto finish; - } - - ref->fd = fd; - ref->stream = stream; - - name = ArrayCreate(); - for (i = 0; i < ArraySize(args); i++) - { - ArrayAdd(name, StrDuplicate(ArrayGet(args, i))); - } - ref->name = name; - - if (db->cache) - { - ref->ts = UtilServerTs(); - ref->size = DbComputeSize(ref->json); - HashMapSet(db->cache, hash, ref); - db->cacheSize += ref->size; - - ref->next = NULL; - ref->prev = db->mostRecent; - if (db->mostRecent) - { - db->mostRecent->next = ref; - } - db->mostRecent = ref; - - if (!db->leastRecent) - { - db->leastRecent = ref; - } - - /* Adding this item to the cache may case it to grow too - * large, requiring some items to be evicted */ - DbCacheEvict(db); - } - } - -finish: - if (!ref) - { - pthread_mutex_unlock(&db->lock); - } - - Free(file); - Free(hash); - - return ref; -} - -DbRef * -DbCreate(Db * db, size_t nArgs,...) -{ - Stream *fp; - char *file; - char *dir; - va_list ap; - Array *args; - DbRef *ret; - - if (!db) - { - return NULL; - } - - va_start(ap, nArgs); - args = ArrayFromVarArgs(nArgs, ap); - va_end(ap); - - if (!args) - { - return NULL; - } - - pthread_mutex_lock(&db->lock); - - file = DbFileName(db, args); - - if (UtilLastModified(file)) - { - Free(file); - ArrayFree(args); - pthread_mutex_unlock(&db->lock); - return NULL; - } - - dir = DbDirName(db, args, 1); - if (UtilMkdir(dir, 0750) < 0) - { - Free(file); - ArrayFree(args); - Free(dir); - pthread_mutex_unlock(&db->lock); - return NULL; - } - - Free(dir); - - fp = StreamOpen(file, "w"); - Free(file); - if (!fp) - { - ArrayFree(args); - pthread_mutex_unlock(&db->lock); - return NULL; - } - - StreamPuts(fp, "{}"); - StreamClose(fp); - - /* DbLockFromArr() will lock again for us */ - pthread_mutex_unlock(&db->lock); - - ret = DbLockFromArr(db, args); - - ArrayFree(args); - - return ret; -} - -int -DbDelete(Db * db, size_t nArgs,...) -{ - va_list ap; - Array *args; - char *file; - char *hash; - int ret = 1; - DbRef *ref; - - if (!db) - { - return 0; - } - - va_start(ap, nArgs); - args = ArrayFromVarArgs(nArgs, ap); - va_end(ap); - - pthread_mutex_lock(&db->lock); - - hash = DbHashKey(args); - file = DbFileName(db, args); - - ref = HashMapGet(db->cache, hash); - if (ref) - { - HashMapDelete(db->cache, hash); - JsonFree(ref->json); - StringArrayFree(ref->name); - - db->cacheSize -= ref->size; - - if (ref->next) - { - ref->next->prev = ref->prev; - } - else - { - db->mostRecent = ref->prev; - } - - if (ref->prev) - { - ref->prev->next = ref->next; - } - else - { - db->leastRecent = ref->next; - } - - if (!db->leastRecent) - { - db->leastRecent = db->mostRecent; - } - - Free(ref); - } - - Free(hash); - - if (UtilLastModified(file)) - { - ret = remove(file) == 0; - } - - pthread_mutex_unlock(&db->lock); - - ArrayFree(args); - Free(file); - return ret; -} - -DbRef * -DbLock(Db * db, size_t nArgs,...) -{ - va_list ap; - Array *args; - DbRef *ret; - - va_start(ap, nArgs); - args = ArrayFromVarArgs(nArgs, ap); - va_end(ap); - - if (!args) - { - return NULL; - } - - ret = DbLockFromArr(db, args); - - ArrayFree(args); - - return ret; -} - -int -DbUnlock(Db * db, DbRef * ref) -{ - int destroy; - - if (!db || !ref) - { - return 0; - } - - lseek(ref->fd, 0L, SEEK_SET); - if (ftruncate(ref->fd, 0) < 0) - { - pthread_mutex_unlock(&db->lock); - Log(LOG_ERR, "Failed to truncate file on disk."); - Log(LOG_ERR, "Error on fd %d: %s", ref->fd, strerror(errno)); - return 0; - } - - JsonEncode(ref->json, ref->stream, JSON_DEFAULT); - StreamClose(ref->stream); - - if (db->cache) - { - char *key = DbHashKey(ref->name); - - if (HashMapGet(db->cache, key)) - { - db->cacheSize -= ref->size; - ref->size = DbComputeSize(ref->json); - db->cacheSize += ref->size; - - /* If this ref has grown significantly since we last - * computed its size, it may have filled the cache and - * require some items to be evicted. */ - DbCacheEvict(db); - - destroy = 0; - } - else - { - destroy = 1; - } - - Free(key); - } - else - { - destroy = 1; - } - - if (destroy) - { - JsonFree(ref->json); - StringArrayFree(ref->name); - - Free(ref); - } - - pthread_mutex_unlock(&db->lock); - return 1; -} - -int -DbExists(Db * db, size_t nArgs,...) -{ - va_list ap; - Array *args; - char *file; - int ret; - - va_start(ap, nArgs); - args = ArrayFromVarArgs(nArgs, ap); - va_end(ap); - - if (!args) - { - return 0; - } - - pthread_mutex_lock(&db->lock); - - file = DbFileName(db, args); - ret = UtilLastModified(file); - - pthread_mutex_unlock(&db->lock); - - Free(file); - ArrayFree(args); - - return ret; -} - -Array * -DbList(Db * db, size_t nArgs,...) -{ - Array *result; - Array *path; - DIR *files; - struct dirent *file; - char *dir; - va_list ap; - - if (!db || !nArgs) - { - return NULL; - } - - result = ArrayCreate(); - if (!result) - { - return NULL; - } - - va_start(ap, nArgs); - path = ArrayFromVarArgs(nArgs, ap); - dir = DbDirName(db, path, 0); - - pthread_mutex_lock(&db->lock); - - files = opendir(dir); - if (!files) - { - ArrayFree(path); - ArrayFree(result); - Free(dir); - pthread_mutex_unlock(&db->lock); - return NULL; - } - while ((file = readdir(files))) - { - size_t namlen = strlen(file->d_name); - - if (namlen > 5) - { - int nameOffset = namlen - 5; - - if (StrEquals(file->d_name + nameOffset, ".json")) - { - file->d_name[nameOffset] = '\0'; - ArrayAdd(result, StrDuplicate(file->d_name)); - } - } - } - closedir(files); - - ArrayFree(path); - Free(dir); - pthread_mutex_unlock(&db->lock); - - return result; -} - -void -DbListFree(Array * arr) -{ - StringArrayFree(arr); -} - -HashMap * -DbJson(DbRef * ref) -{ - return ref ? ref->json : NULL; -} - -int -DbJsonSet(DbRef * ref, HashMap * json) -{ - if (!ref || !json) - { - return 0; - } - - JsonFree(ref->json); - ref->json = JsonDuplicate(json); - return 1; -} diff --git a/src/HashMap.c b/src/HashMap.c deleted file mode 100644 index 620092d..0000000 --- a/src/HashMap.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include - -#include -#include - -typedef struct HashMapBucket -{ - unsigned long hash; - char *key; - void *value; -} HashMapBucket; - -struct HashMap -{ - size_t count; - size_t capacity; - HashMapBucket **entries; - - unsigned long (*hashFunc) (const char *); - - float maxLoad; - size_t iterator; -}; - -static unsigned long -HashMapHashKey(const char *key) -{ - unsigned long hash = 2166136261u; - size_t i = 0; - - while (key[i]) - { - hash ^= (unsigned char) key[i]; - hash *= 16777619; - - i++; - } - - return hash; -} - -static int -HashMapGrow(HashMap * map) -{ - size_t oldCapacity; - size_t i; - HashMapBucket **newEntries; - - if (!map) - { - return 0; - } - - oldCapacity = map->capacity; - map->capacity *= 2; - - newEntries = Malloc(map->capacity * sizeof(HashMapBucket *)); - if (!newEntries) - { - map->capacity /= 2; - return 0; - } - - memset(newEntries, 0, map->capacity * sizeof(HashMapBucket *)); - - for (i = 0; i < oldCapacity; i++) - { - /* If there is a value here, and it isn't a tombstone */ - if (map->entries[i] && map->entries[i]->hash) - { - /* Copy it to the new entries array */ - size_t index = map->entries[i]->hash % map->capacity; - - for (;;) - { - if (newEntries[index]) - { - if (!newEntries[index]->hash) - { - Free(newEntries[index]); - newEntries[index] = map->entries[i]; - break; - } - } - else - { - newEntries[index] = map->entries[i]; - break; - } - - index = (index + 1) % map->capacity; - } - } - else - { - /* Either NULL or a tombstone */ - Free(map->entries[i]); - } - } - - Free(map->entries); - map->entries = newEntries; - return 1; -} - -HashMap * -HashMapCreate(void) -{ - HashMap *map = Malloc(sizeof(HashMap)); - - if (!map) - { - return NULL; - } - - map->maxLoad = 0.75; - map->count = 0; - map->capacity = 16; - map->iterator = 0; - map->hashFunc = HashMapHashKey; - - map->entries = Malloc(map->capacity * sizeof(HashMapBucket *)); - if (!map->entries) - { - Free(map); - return NULL; - } - - memset(map->entries, 0, map->capacity * sizeof(HashMapBucket *)); - - return map; -} - -void * -HashMapDelete(HashMap * map, const char *key) -{ - unsigned long hash; - size_t index; - - if (!map || !key) - { - return NULL; - } - - hash = map->hashFunc(key); - index = hash % map->capacity; - - for (;;) - { - HashMapBucket *bucket = map->entries[index]; - - if (!bucket) - { - break; - } - - if (bucket->hash == hash) - { - bucket->hash = 0; - return bucket->value; - } - - index = (index + 1) % map->capacity; - } - - return NULL; -} - -void -HashMapFree(HashMap * map) -{ - if (map) - { - size_t i; - - for (i = 0; i < map->capacity; i++) - { - if (map->entries[i]) - { - Free(map->entries[i]->key); - Free(map->entries[i]); - } - } - Free(map->entries); - Free(map); - } -} - -void * -HashMapGet(HashMap * map, const char *key) -{ - unsigned long hash; - size_t index; - - if (!map || !key) - { - return NULL; - } - - hash = map->hashFunc(key); - index = hash % map->capacity; - - for (;;) - { - HashMapBucket *bucket = map->entries[index]; - - if (!bucket) - { - break; - } - - if (bucket->hash == hash) - { - return bucket->value; - } - - index = (index + 1) % map->capacity; - } - - return NULL; -} - -int -HashMapIterateReentrant(HashMap * map, char **key, void **value, size_t * i) -{ - if (!map) - { - return 0; - } - - if (*i >= map->capacity) - { - *i = 0; - *key = NULL; - *value = NULL; - return 0; - } - - while (*i < map->capacity) - { - HashMapBucket *bucket = map->entries[*i]; - - *i = *i + 1; - - if (bucket && bucket->hash) - { - *key = bucket->key; - *value = bucket->value; - return 1; - } - } - - *i = 0; - return 0; -} - -int -HashMapIterate(HashMap * map, char **key, void **value) -{ - if (!map) - { - return 0; - } - else - { - return HashMapIterateReentrant(map, key, value, &map->iterator); - } -} - -void -HashMapMaxLoadSet(HashMap * map, float load) -{ - if (!map || (load > 1.0 || load <= 0)) - { - return; - } - - map->maxLoad = load; -} - -void -HashMapFunctionSet(HashMap * map, unsigned long (*hashFunc) (const char *)) -{ - if (!map || !hashFunc) - { - return; - } - - map->hashFunc = hashFunc; -} - -void * -HashMapSet(HashMap * map, char *key, void *value) -{ - unsigned long hash; - size_t index; - - if (!map || !key || !value) - { - return NULL; - } - - key = StrDuplicate(key); - if (!key) - { - return NULL; - } - - if (map->count + 1 > map->capacity * map->maxLoad) - { - HashMapGrow(map); - } - - hash = map->hashFunc(key); - index = hash % map->capacity; - - for (;;) - { - HashMapBucket *bucket = map->entries[index]; - - if (!bucket) - { - bucket = Malloc(sizeof(HashMapBucket)); - if (!bucket) - { - break; - } - - bucket->hash = hash; - bucket->key = key; - bucket->value = value; - map->entries[index] = bucket; - map->count++; - break; - } - - if (!bucket->hash) - { - bucket->hash = hash; - Free(bucket->key); - bucket->key = key; - bucket->value = value; - break; - } - - if (bucket->hash == hash) - { - void *oldValue = bucket->value; - - Free(bucket->key); - bucket->key = key; - - bucket->value = value; - return oldValue; - } - - index = (index + 1) % map->capacity; - } - - return NULL; -} - -void -HashMapIterateFree(char *key, void *value) -{ - if (key) - { - Free(key); - } - - if (value) - { - Free(value); - } -} diff --git a/src/HeaderParser.c b/src/HeaderParser.c deleted file mode 100644 index 9f03ac5..0000000 --- a/src/HeaderParser.c +++ /dev/null @@ -1,664 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include - -#include -#include - -static int -HeaderConsumeWhitespace(HeaderExpr * expr) -{ - int c; - - while (1) - { - c = StreamGetc(expr->state.stream); - - if (StreamEof(expr->state.stream) || StreamError(expr->state.stream)) - { - expr->type = HP_EOF; - expr->data.error.msg = "End of stream reached."; - expr->data.error.lineNo = expr->state.lineNo; - break; - } - - if (isspace(c)) - { - if (c == '\n') - { - expr->state.lineNo++; - } - } - else - { - break; - } - } - - return c; -} - -static char * -HeaderConsumeWord(HeaderExpr * expr) -{ - char *str = Malloc(16 * sizeof(char)); - int len = 16; - int i; - int c; - - if (!str) - { - return NULL; - } - - c = HeaderConsumeWhitespace(expr); - - i = 0; - str[i] = c; - i++; - - while (!isspace(c = StreamGetc(expr->state.stream))) - { - if (i >= len) - { - len *= 2; - str = Realloc(str, len * sizeof(char)); - } - - str[i] = c; - i++; - } - - if (i >= len) - { - len++; - str = Realloc(str, len * sizeof(char)); - } - - str[i] = '\0'; - - if (c != EOF) - { - StreamUngetc(expr->state.stream, c); - } - - return str; -} - -static char * -HeaderConsumeAlnum(HeaderExpr * expr) -{ - char *str = Malloc(16 * sizeof(char)); - int len = 16; - int i; - int c; - - if (!str) - { - return NULL; - } - - c = HeaderConsumeWhitespace(expr); - - i = 0; - str[i] = c; - i++; - - while (isalnum(c = StreamGetc(expr->state.stream))) - { - if (i >= len) - { - len *= 2; - str = Realloc(str, len * sizeof(char)); - } - - str[i] = c; - i++; - } - - if (i >= len) - { - len++; - str = Realloc(str, len * sizeof(char)); - } - - str[i] = '\0'; - - if (c != EOF) - { - StreamUngetc(expr->state.stream, c); - } - - return str; -} - -static char * -HeaderConsumeArg(HeaderExpr * expr) -{ - char *str = Malloc(16 * sizeof(char)); - int len = 16; - int i; - int c; - int block = 0; - - if (!str) - { - return NULL; - } - - c = HeaderConsumeWhitespace(expr); - - i = 0; - str[i] = c; - i++; - - while (((c = StreamGetc(expr->state.stream)) != ',' && c != ')') || block > 0) - { - if (i >= len) - { - len *= 2; - str = Realloc(str, len * sizeof(char)); - } - - str[i] = c; - i++; - - if (c == '(') - { - block++; - } - else if (c == ')') - { - block--; - } - else if (c == '\n') - { - expr->state.lineNo++; - } - } - - if (i >= len) - { - len++; - str = Realloc(str, len * sizeof(char)); - } - - str[i] = '\0'; - - if (c != EOF) - { - StreamUngetc(expr->state.stream, c); - } - - return str; -} - -void -HeaderParse(Stream * stream, HeaderExpr * expr) -{ - int c; - - if (!expr) - { - return; - } - - if (!stream) - { - expr->type = HP_PARSE_ERROR; - expr->data.error.msg = "NULL pointer to stream."; - expr->data.error.lineNo = -1; - return; - } - - if (expr->type == HP_DECLARATION && expr->data.declaration.args) - { - size_t i; - - for (i = 0; i < ArraySize(expr->data.declaration.args); i++) - { - Free(ArrayGet(expr->data.declaration.args, i)); - } - - ArrayFree(expr->data.declaration.args); - } - - expr->state.stream = stream; - if (!expr->state.lineNo) - { - expr->state.lineNo = 1; - } - - c = HeaderConsumeWhitespace(expr); - - if (StreamEof(stream) || StreamError(stream)) - { - expr->type = HP_EOF; - expr->data.error.msg = "End of stream reached."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - if (c == '/') - { - int i = 0; - - c = StreamGetc(expr->state.stream); - if (c != '*') - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Expected comment opening."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - expr->type = HP_COMMENT; - while (1) - { - if (i >= HEADER_EXPR_MAX - 1) - { - expr->type = HP_PARSE_ERROR; - expr->data.error.msg = "Memory limit exceeded while parsing comment."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - c = StreamGetc(expr->state.stream); - - if (StreamEof(expr->state.stream) || StreamError(expr->state.stream)) - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Unterminated comment."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - if (c == '*') - { - c = StreamGetc(expr->state.stream); - if (c == '/') - { - expr->data.text[i] = '\0'; - break; - } - else - { - expr->data.text[i] = '*'; - i++; - expr->data.text[i] = c; - i++; - if (c == '\n') - { - expr->state.lineNo++; - } - } - } - else - { - expr->data.text[i] = c; - i++; - - if (c == '\n') - { - expr->state.lineNo++; - } - } - } - } - else if (c == '#') - { - int i = 0; - char *word; - - expr->type = HP_PREPROCESSOR_DIRECTIVE; - expr->data.text[i] = '#'; - i++; - - word = HeaderConsumeWord(expr); - - strncpy(expr->data.text + i, word, HEADER_EXPR_MAX - i - 1); - i += strlen(word); - - if (StrEquals(word, "include") || - StrEquals(word, "undef") || - StrEquals(word, "ifdef") || - StrEquals(word, "ifndef")) - { - /* Read one more word */ - Free(word); - word = HeaderConsumeWord(expr); - - if (i + strlen(word) + 1 >= HEADER_EXPR_MAX) - { - expr->type = HP_PARSE_ERROR; - expr->data.error.msg = "Memory limit reached parsing preprocessor directive."; - expr->data.error.lineNo = expr->state.lineNo; - } - else - { - strncpy(expr->data.text + i + 1, word, HEADER_EXPR_MAX - i - 1); - expr->data.text[i] = ' '; - } - - Free(word); - } - else if (StrEquals(word, "define") || - StrEquals(word, "if") || - StrEquals(word, "elif") || - StrEquals(word, "error")) - { - int pC = 0; - - Free(word); - expr->data.text[i] = ' '; - i++; - - while (1) - { - if (i >= HEADER_EXPR_MAX - 1) - { - expr->type = HP_PARSE_ERROR; - expr->data.error.msg = "Memory limit reached parsing preprocessor directive."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - c = StreamGetc(expr->state.stream); - - if (StreamEof(expr->state.stream) || StreamError(expr->state.stream)) - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Unterminated preprocessor directive."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - if (c == '\n') - { - expr->state.lineNo++; - if (pC != '\\') - { - expr->data.text[i] = '\0'; - break; - } - } - - expr->data.text[i] = c; - i++; - - pC = c; - } - } - else if (StrEquals(word, "else") || - StrEquals(word, "endif")) - { - /* Read no more words, that's the whole directive */ - } - else - { - Free(word); - - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Unknown preprocessor directive."; - expr->data.error.lineNo = expr->state.lineNo; - } - } - else - { - char *word; - - StreamUngetc(expr->state.stream, c); - word = HeaderConsumeWord(expr); - - if (StrEquals(word, "typedef")) - { - int block = 0; - int i = 0; - - expr->type = HP_TYPEDEF; - strncpy(expr->data.text, word, HEADER_EXPR_MAX - 1); - i += strlen(word); - expr->data.text[i] = ' '; - i++; - - while (1) - { - if (i >= HEADER_EXPR_MAX - 1) - { - expr->type = HP_PARSE_ERROR; - expr->data.error.msg = "Memory limit exceeded while parsing typedef."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - c = StreamGetc(expr->state.stream); - - if (StreamEof(expr->state.stream) || StreamError(expr->state.stream)) - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Unterminated typedef."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - expr->data.text[i] = c; - i++; - - if (c == '{') - { - block++; - } - else if (c == '}') - { - block--; - } - else if (c == '\n') - { - expr->state.lineNo++; - } - - if (block <= 0 && c == ';') - { - expr->data.text[i] = '\0'; - break; - } - } - } - else if (StrEquals(word, "extern")) - { - int wordLimit = sizeof(expr->data.declaration.returnType) - 8; - int wordLen; - - Free(word); - - word = HeaderConsumeWord(expr); - wordLen = strlen(word); - if (wordLen > wordLimit) - { - expr->type = HP_PARSE_ERROR; - expr->data.error.msg = "Return of declaration exceeds length limit."; - expr->data.error.lineNo = expr->state.lineNo; - } - else - { - int i = wordLen; - - expr->type = HP_GLOBAL; - strncpy(expr->data.global.type, word, wordLimit); - - if (StrEquals(word, "struct") || - StrEquals(word, "enum") || - StrEquals(word, "const") || - StrEquals(word, "unsigned")) - { - Free(word); - word = HeaderConsumeWord(expr); - wordLen = strlen(word); - expr->data.global.type[i] = ' '; - - strncpy(expr->data.global.type + i + 1, word, wordLen + 1); - i += wordLen + 1; - } - - Free(word); - - c = HeaderConsumeWhitespace(expr); - if (c == '*') - { - expr->data.global.type[i] = ' '; - - i++; - expr->data.global.type[i] = '*'; - - i++; - while ((c = HeaderConsumeWhitespace(expr)) == '*') - { - expr->data.global.type[i] = c; - - i++; - } - } - - StreamUngetc(expr->state.stream, c); - word = HeaderConsumeAlnum(expr); - - wordLen = strlen(word); - wordLimit = sizeof(expr->data.declaration.name) - 1; - - if (wordLen > wordLimit) - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Function name too long."; - expr->data.error.lineNo = expr->state.lineNo; - } - else - { - strncpy(expr->data.global.name, word, wordLimit); - Free(word); - word = NULL; - - c = HeaderConsumeWhitespace(expr); - - if (c == ';') - { - /* That's the end of the global. */ - } - else if (c == '[') - { - /* Looks like we have an array. Slurp all the - * dimensions */ - int i = wordLen; - - expr->data.global.name[i] = '['; - - i++; - - while (1) - { - if (i >= HEADER_EXPR_MAX - wordLen) - { - expr->type = HP_PARSE_ERROR; - expr->data.error.msg = "Memory limit exceeded while parsing global array."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - c = StreamGetc(expr->state.stream); - - if (StreamEof(expr->state.stream) || StreamError(expr->state.stream)) - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Unterminated global array."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - if (c == ';') - { - expr->data.global.name[i] = '\0'; - - break; - } - else - { - expr->data.global.name[i] = c; - - i++; - } - } - } - else if (c == '(') - { - expr->type = HP_DECLARATION; - expr->data.declaration.args = ArrayCreate(); - do - { - word = HeaderConsumeArg(expr); - ArrayAdd(expr->data.declaration.args, word); - word = NULL; - } - while ((!StreamEof(expr->state.stream)) && ((c = HeaderConsumeWhitespace(expr)) != ')')); - - if (StreamEof(expr->state.stream)) - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "End of file reached before ')'."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - c = HeaderConsumeWhitespace(expr); - if (c != ';') - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Expected ';'."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - } - else - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Expected ';', '[', or '('"; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - } - } - } - else - { - /* Cope with preprocessor macro expansions at the top - * level. */ - expr->type = HP_UNKNOWN; - strncpy(expr->data.text, word, HEADER_EXPR_MAX); - } - - Free(word); - } -} diff --git a/src/Http.c b/src/Http.c deleted file mode 100644 index f3df30d..0000000 --- a/src/Http.c +++ /dev/null @@ -1,642 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifndef TELODENDRIA_STRING_CHUNK -#define TELODENDRIA_STRING_CHUNK 64 -#endif - -const char * -HttpRequestMethodToString(const HttpRequestMethod method) -{ - switch (method) - { - case HTTP_GET: - return "GET"; - case HTTP_HEAD: - return "HEAD"; - case HTTP_POST: - return "POST"; - case HTTP_PUT: - return "PUT"; - case HTTP_DELETE: - return "DELETE"; - case HTTP_CONNECT: - return "CONNECT"; - case HTTP_OPTIONS: - return "OPTIONS"; - case HTTP_TRACE: - return "TRACE"; - case HTTP_PATCH: - return "PATCH"; - default: - return NULL; - } -} - -HttpRequestMethod -HttpRequestMethodFromString(const char *str) -{ - if (StrEquals(str, "GET")) - { - return HTTP_GET; - } - - if (StrEquals(str, "HEAD")) - { - return HTTP_HEAD; - } - - if (StrEquals(str, "POST")) - { - return HTTP_POST; - } - - if (StrEquals(str, "PUT")) - { - return HTTP_PUT; - } - - if (StrEquals(str, "DELETE")) - { - return HTTP_DELETE; - } - - if (StrEquals(str, "CONNECT")) - { - return HTTP_CONNECT; - } - - if (StrEquals(str, "OPTIONS")) - { - return HTTP_OPTIONS; - } - - if (StrEquals(str, "TRACE")) - { - return HTTP_TRACE; - } - - if (StrEquals(str, "PATCH")) - { - return HTTP_PATCH; - } - - return HTTP_METHOD_UNKNOWN; -} - -const char * -HttpStatusToString(const HttpStatus status) -{ - switch (status) - { - case HTTP_CONTINUE: - return "Continue"; - case HTTP_SWITCHING_PROTOCOLS: - return "Switching Protocols"; - case HTTP_EARLY_HINTS: - return "Early Hints"; - case HTTP_OK: - return "Ok"; - case HTTP_CREATED: - return "Created"; - case HTTP_ACCEPTED: - return "Accepted"; - case HTTP_NON_AUTHORITATIVE_INFORMATION: - return "Non-Authoritative Information"; - case HTTP_NO_CONTENT: - return "No Content"; - case HTTP_RESET_CONTENT: - return "Reset Content"; - case HTTP_PARTIAL_CONTENT: - return "Partial Content"; - case HTTP_MULTIPLE_CHOICES: - return "Multiple Choices"; - case HTTP_MOVED_PERMANENTLY: - return "Moved Permanently"; - case HTTP_FOUND: - return "Found"; - case HTTP_SEE_OTHER: - return "See Other"; - case HTTP_NOT_MODIFIED: - return "Not Modified"; - case HTTP_TEMPORARY_REDIRECT: - return "Temporary Redirect"; - case HTTP_PERMANENT_REDIRECT: - return "Permanent Redirect"; - case HTTP_BAD_REQUEST: - return "Bad Request"; - case HTTP_UNAUTHORIZED: - return "Unauthorized"; - case HTTP_FORBIDDEN: - return "Forbidden"; - case HTTP_NOT_FOUND: - return "Not Found"; - case HTTP_METHOD_NOT_ALLOWED: - return "Method Not Allowed"; - case HTTP_NOT_ACCEPTABLE: - return "Not Acceptable"; - case HTTP_PROXY_AUTH_REQUIRED: - return "Proxy Authentication Required"; - case HTTP_REQUEST_TIMEOUT: - return "Request Timeout"; - case HTTP_CONFLICT: - return "Conflict"; - case HTTP_GONE: - return "Gone"; - case HTTP_LENGTH_REQUIRED: - return "Length Required"; - case HTTP_PRECONDITION_FAILED: - return "Precondition Failed"; - case HTTP_PAYLOAD_TOO_LARGE: - return "Payload Too Large"; - case HTTP_URI_TOO_LONG: - return "URI Too Long"; - case HTTP_UNSUPPORTED_MEDIA_TYPE: - return "Unsupported Media Type"; - case HTTP_RANGE_NOT_SATISFIABLE: - return "Range Not Satisfiable"; - case HTTP_EXPECTATION_FAILED: - return "Expectation Failed"; - case HTTP_TEAPOT: - return "I'm a Teapot"; - case HTTP_UPGRADE_REQUIRED: - return "Upgrade Required"; - case HTTP_PRECONDITION_REQUIRED: - return "Precondition Required"; - case HTTP_TOO_MANY_REQUESTS: - return "Too Many Requests"; - case HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE: - return "Request Header Fields Too Large"; - case HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - return "Unavailable For Legal Reasons"; - case HTTP_INTERNAL_SERVER_ERROR: - return "Internal Server Error"; - case HTTP_NOT_IMPLEMENTED: - return "Not Implemented"; - case HTTP_BAD_GATEWAY: - return "Bad Gateway"; - case HTTP_SERVICE_UNAVAILABLE: - return "Service Unavailable"; - case HTTP_GATEWAY_TIMEOUT: - return "Gateway Timeout"; - case HTTP_VERSION_NOT_SUPPORTED: - return "Version Not Supported"; - case HTTP_VARIANT_ALSO_NEGOTIATES: - return "Variant Also Negotiates"; - case HTTP_NOT_EXTENDED: - return "Not Extended"; - case HTTP_NETWORK_AUTH_REQUIRED: - return "Network Authentication Required"; - default: - return NULL; - } -} - -char * -HttpUrlEncode(char *str) -{ - size_t size; - size_t len; - char *encoded; - - if (!str) - { - return NULL; - } - - size = TELODENDRIA_STRING_CHUNK; - len = 0; - encoded = Malloc(size); - if (!encoded) - { - return NULL; - } - - while (*str) - { - char c = *str; - - if (len >= size - 4) - { - char *tmp; - - size += TELODENDRIA_STRING_CHUNK; - tmp = Realloc(encoded, size); - if (!tmp) - { - Free(encoded); - return NULL; - } - - encoded = tmp; - } - - /* Control characters and extended characters */ - if (c <= 0x1F || c >= 0x7F) - { - goto percentEncode; - } - - /* Reserved and unsafe characters */ - switch (c) - { - case '$': - case '&': - case '+': - case ',': - case '/': - case ':': - case ';': - case '=': - case '?': - case '@': - case ' ': - case '"': - case '<': - case '>': - case '#': - case '%': - case '{': - case '}': - case '|': - case '\\': - case '^': - case '~': - case '[': - case ']': - case '`': - goto percentEncode; - break; - default: - encoded[len] = c; - len++; - str++; - continue; - } - -percentEncode: - encoded[len] = '%'; - len++; - snprintf(encoded + len, 3, "%2X", c); - len += 2; - - str++; - } - - encoded[len] = '\0'; - return encoded; -} - -char * -HttpUrlDecode(char *str) -{ - size_t i; - size_t inputLen; - char *decoded; - - if (!str) - { - return NULL; - } - - i = 0; - inputLen = strlen(str); - decoded = Malloc(inputLen + 1); - - if (!decoded) - { - return NULL; - } - - while (*str) - { - char c = *str; - - if (c == '%') - { - unsigned int d; - - str++; - - if (sscanf(str, "%2X", &d) != 1) - { - /* Decoding error */ - Free(decoded); - return NULL; - } - - if (!d) - { - /* Null character given, don't put that in the string. */ - continue; - } - - c = (char) d; - - str++; - } - - decoded[i] = c; - i++; - - str++; - } - - decoded[i] = '\0'; - - return decoded; -} - -HashMap * -HttpParamDecode(char *in) -{ - HashMap *params; - - if (!in) - { - return NULL; - } - - params = HashMapCreate(); - if (!params) - { - return NULL; - } - - while (*in) - { - char *buf; - size_t allocated; - size_t len; - - char *decKey; - char *decVal; - - /* Read in key */ - - allocated = TELODENDRIA_STRING_CHUNK; - buf = Malloc(allocated); - len = 0; - - while (*in && *in != '=') - { - if (len >= allocated - 1) - { - allocated += TELODENDRIA_STRING_CHUNK; - buf = Realloc(buf, allocated); - } - - buf[len] = *in; - len++; - in++; - } - - buf[len] = '\0'; - - /* Sanity check */ - if (*in != '=') - { - /* Malformed param */ - Free(buf); - HashMapFree(params); - return NULL; - } - - in++; - - /* Decode key */ - decKey = HttpUrlDecode(buf); - Free(buf); - - if (!decKey) - { - /* Decoding error */ - HashMapFree(params); - return NULL; - } - - /* Read in value */ - allocated = TELODENDRIA_STRING_CHUNK; - buf = Malloc(allocated); - len = 0; - - while (*in && *in != '&') - { - if (len >= allocated - 1) - { - allocated += TELODENDRIA_STRING_CHUNK; - buf = Realloc(buf, allocated); - } - - buf[len] = *in; - len++; - in++; - } - - buf[len] = '\0'; - - /* Decode value */ - decVal = HttpUrlDecode(buf); - Free(buf); - - if (!decVal) - { - /* Decoding error */ - HashMapFree(params); - return NULL; - } - - buf = HashMapSet(params, decKey, decVal); - if (buf) - { - Free(buf); - } - Free(decKey); - - if (*in == '&') - { - in++; - continue; - } - else - { - break; - } - } - - return params; -} - -char * -HttpParamEncode(HashMap * params) -{ - char *key; - char *val; - char *out = NULL; - - if (!params || !out) - { - return NULL; - } - - while (HashMapIterate(params, &key, (void *) &val)) - { - char *encKey; - char *encVal; - - encKey = HttpUrlEncode(key); - encVal = HttpUrlEncode(val); - - if (!encKey || !encVal) - { - /* Memory error */ - Free(encKey); - Free(encVal); - return NULL; - } - - /* TODO */ - - Free(encKey); - Free(encVal); - } - - return out; -} - -HashMap * -HttpParseHeaders(Stream * fp) -{ - HashMap *headers; - - char *line; - ssize_t lineLen; - size_t lineSize; - - char *headerKey; - char *headerValue; - - if (!fp) - { - return NULL; - } - - - headers = HashMapCreate(); - if (!headers) - { - return NULL; - } - - line = NULL; - lineLen = 0; - - while ((lineLen = UtilGetLine(&line, &lineSize, fp)) != -1) - { - char *headerPtr; - - ssize_t i; - size_t len; - - if (StrEquals(line, "\r\n") || StrEquals(line, "\n")) - { - break; - } - - for (i = 0; i < lineLen; i++) - { - if (line[i] == ':') - { - line[i] = '\0'; - break; - } - - line[i] = tolower((unsigned char) line[i]); - } - - len = i + 1; - headerKey = Malloc(len * sizeof(char)); - if (!headerKey) - { - goto error; - } - - strncpy(headerKey, line, len); - - headerPtr = line + i + 1; - - while (isspace((unsigned char) *headerPtr)) - { - headerPtr++; - } - - for (i = lineLen - 1; i > (line + lineLen) - headerPtr; i--) - { - if (!isspace((unsigned char) line[i])) - { - break; - } - line[i] = '\0'; - } - - len = strlen(headerPtr) + 1; - headerValue = Malloc(len * sizeof(char)); - if (!headerValue) - { - Free(headerKey); - goto error; - } - - strncpy(headerValue, headerPtr, len); - - HashMapSet(headers, headerKey, headerValue); - Free(headerKey); - } - - Free(line); - return headers; - -error: - Free(line); - - while (HashMapIterate(headers, &headerKey, (void **) &headerValue)) - { - Free(headerValue); - } - - HashMapFree(headers); - - return NULL; -} diff --git a/src/HttpClient.c b/src/HttpClient.c deleted file mode 100644 index 7874966..0000000 --- a/src/HttpClient.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -struct HttpClientContext -{ - HashMap *responseHeaders; - Stream *stream; -}; - -HttpClientContext * -HttpRequest(HttpRequestMethod method, int flags, unsigned short port, char *host, char *path) -{ - HttpClientContext *context; - - int sd = -1; - struct addrinfo hints, *res, *res0; - int error; - - char serv[8]; - - if (!method || !host || !path) - { - return NULL; - } - -#ifndef TLS_IMPL - if (flags & HTTP_FLAG_TLS) - { - return NULL; - } -#endif - - if (!port) - { - if (flags & HTTP_FLAG_TLS) - { - strcpy(serv, "https"); - } - else - { - strcpy(serv, "www"); - } - } - else - { - snprintf(serv, sizeof(serv), "%hu", port); - } - - - context = Malloc(sizeof(HttpClientContext)); - if (!context) - { - return NULL; - } - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - error = getaddrinfo(host, serv, &hints, &res0); - - if (error) - { - Free(context); - return NULL; - } - - for (res = res0; res; res = res->ai_next) - { - sd = socket(res->ai_family, res->ai_socktype, - res->ai_protocol); - - if (sd < 0) - { - continue; - } - - if (connect(sd, res->ai_addr, res->ai_addrlen) < 0) - { - close(sd); - sd = -1; - continue; - } - - break; - } - - if (sd < 0) - { - Free(context); - return NULL; - } - - freeaddrinfo(res0); - -#ifdef TLS_IMPL - if (flags & HTTP_FLAG_TLS) - { - context->stream = TlsClientStream(sd, host); - } - else - { - context->stream = StreamFd(sd); - } -#else - context->stream = StreamFd(sd); -#endif - - if (!context->stream) - { - Free(context); - close(sd); - return NULL; - } - - StreamPrintf(context->stream, "%s %s HTTP/1.0\r\n", - HttpRequestMethodToString(method), path); - - HttpRequestHeader(context, "Connection", "close"); - HttpRequestHeader(context, "User-Agent", "Telodendria/" TELODENDRIA_VERSION); - HttpRequestHeader(context, "Host", host); - - return context; -} - -void -HttpRequestHeader(HttpClientContext * context, char *key, char *val) -{ - if (!context || !key || !val) - { - return; - } - - StreamPrintf(context->stream, "%s: %s\r\n", key, val); -} - -void -HttpRequestSendHeaders(HttpClientContext * context) -{ - if (!context) - { - return; - } - - StreamPuts(context->stream, "\r\n"); - StreamFlush(context->stream); -} - -HttpStatus -HttpRequestSend(HttpClientContext * context) -{ - HttpStatus status; - - char *line = NULL; - ssize_t lineLen; - size_t lineSize = 0; - char *tmp; - - if (!context) - { - return 0; - } - - StreamFlush(context->stream); - - lineLen = UtilGetLine(&line, &lineSize, context->stream); - - while (lineLen == -1 && errno == EAGAIN) - { - StreamClearError(context->stream); - lineLen = UtilGetLine(&line, &lineSize, context->stream); - } - - if (lineLen == -1) - { - return 0; - } - - /* Line must contain at least "HTTP/x.x xxx" */ - if (lineLen < 12) - { - return 0; - } - - if (!(strncmp(line, "HTTP/1.0", 8) == 0 || - strncmp(line, "HTTP/1.1", 8) == 0)) - { - return 0; - } - - tmp = line + 9; - - while (isspace((unsigned char) *tmp) && *tmp != '\0') - { - tmp++; - } - - if (!*tmp) - { - return 0; - } - - status = atoi(tmp); - - if (!status) - { - return 0; - } - - context->responseHeaders = HttpParseHeaders(context->stream); - if (!context->responseHeaders) - { - return 0; - } - - return status; -} - -HashMap * -HttpResponseHeaders(HttpClientContext * context) -{ - if (!context) - { - return NULL; - } - - return context->responseHeaders; -} - -Stream * -HttpClientStream(HttpClientContext * context) -{ - if (!context) - { - return NULL; - } - - return context->stream; -} - -void -HttpClientContextFree(HttpClientContext * context) -{ - char *key; - void *val; - - if (!context) - { - return; - } - - while (HashMapIterate(context->responseHeaders, &key, &val)) - { - Free(val); - } - - HashMapFree(context->responseHeaders); - - StreamClose(context->stream); - Free(context); -} diff --git a/src/HttpRouter.c b/src/HttpRouter.c deleted file mode 100644 index 87ddae0..0000000 --- a/src/HttpRouter.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include - -#include -#include - -#define REG_FLAGS (REG_EXTENDED) -#define REG_MAX_SUB 8 - -typedef struct RouteNode -{ - HttpRouteFunc *exec; - HashMap *children; - - regex_t regex; -} RouteNode; - -struct HttpRouter -{ - RouteNode *root; -}; - -static RouteNode * -RouteNodeCreate(char *regex, HttpRouteFunc * exec) -{ - RouteNode *node; - - if (!regex) - { - return NULL; - } - - node = Malloc(sizeof(RouteNode)); - - if (!node) - { - return NULL; - } - - node->children = HashMapCreate(); - if (!node->children) - { - Free(node); - return NULL; - } - - /* Force the regex to match the entire path part exactly. */ - regex = StrConcat(3, "^", regex, "$"); - if (!regex) - { - Free(node); - return NULL; - } - - if (regcomp(&node->regex, regex, REG_FLAGS) != 0) - { - HashMapFree(node->children); - Free(node); - Free(regex); - return NULL; - } - - node->exec = exec; - - Free(regex); - return node; -} - -static void -RouteNodeFree(RouteNode * node) -{ - char *key; - RouteNode *val; - - if (!node) - { - return; - } - - while (HashMapIterate(node->children, &key, (void **) &val)) - { - RouteNodeFree(val); - } - - HashMapFree(node->children); - - regfree(&node->regex); - - Free(node); -} - -HttpRouter * -HttpRouterCreate(void) -{ - HttpRouter *router = Malloc(sizeof(HttpRouter)); - - if (!router) - { - return NULL; - } - - router->root = RouteNodeCreate("/", NULL); - - return router; -} - -void -HttpRouterFree(HttpRouter * router) -{ - if (!router) - { - return; - } - - RouteNodeFree(router->root); - Free(router); -} - -int -HttpRouterAdd(HttpRouter * router, char *regPath, HttpRouteFunc * exec) -{ - RouteNode *node; - char *pathPart; - char *tmp; - - if (!router || !regPath || !exec) - { - return 0; - } - - if (StrEquals(regPath, "/")) - { - router->root->exec = exec; - return 1; - } - - regPath = StrDuplicate(regPath); - if (!regPath) - { - return 0; - } - - tmp = regPath; - node = router->root; - - while ((pathPart = strtok_r(tmp, "/", &tmp))) - { - RouteNode *tNode = HashMapGet(node->children, pathPart); - - if (!tNode) - { - tNode = RouteNodeCreate(pathPart, NULL); - RouteNodeFree(HashMapSet(node->children, pathPart, tNode)); - } - - node = tNode; - } - - node->exec = exec; - - Free(regPath); - - return 1; -} - -int -HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret) -{ - RouteNode *node; - char *pathPart; - char *tmp; - HttpRouteFunc *exec = NULL; - Array *matches; - size_t i; - int retval; - - if (!router || !path) - { - return 0; - } - - matches = ArrayCreate(); - if (!matches) - { - return 0; - } - - node = router->root; - - if (StrEquals(path, "/")) - { - exec = node->exec; - } - else - { - path = StrDuplicate(path); - tmp = path; - while ((pathPart = strtok_r(tmp, "/", &tmp))) - { - char *key; - RouteNode *val = NULL; - - regmatch_t pmatch[REG_MAX_SUB]; - - i = 0; - - while (HashMapIterateReentrant(node->children, &key, (void **) &val, &i)) - { - if (regexec(&val->regex, pathPart, REG_MAX_SUB, pmatch, 0) == 0) - { - break; - } - - val = NULL; - } - - if (!val) - { - exec = NULL; - break; - } - - node = val; - exec = node->exec; - - /* If we want to pass an arg, the match must be in parens */ - if (val->regex.re_nsub) - { - /* pmatch[0] is the whole string, not the first - * subexpression */ - for (i = 1; i < REG_MAX_SUB; i++) - { - if (pmatch[i].rm_so == -1) - { - break; - } - - ArrayAdd(matches, StrSubstr(pathPart, pmatch[i].rm_so, pmatch[i].rm_eo)); - } - } - } - Free(path); - } - - if (!exec) - { - retval = 0; - goto finish; - } - - if (ret) - { - *ret = exec(matches, args); - } - else - { - exec(matches, args); - } - - retval = 1; - -finish: - for (i = 0; i < ArraySize(matches); i++) - { - Free(ArrayGet(matches, i)); - } - ArrayFree(matches); - - return retval; -} diff --git a/src/HttpServer.c b/src/HttpServer.c deleted file mode 100644 index e620983..0000000 --- a/src/HttpServer.c +++ /dev/null @@ -1,753 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -static const char ENABLE = 1; - -struct HttpServer -{ - HttpServerConfig config; - int sd; - pthread_t socketThread; - - volatile unsigned int stop:1; - volatile unsigned int isRunning:1; - - Queue *connQueue; - pthread_mutex_t connQueueMutex; - - Array *threadPool; -}; - -struct HttpServerContext -{ - HashMap *requestHeaders; - HttpRequestMethod requestMethod; - char *requestPath; - HashMap *requestParams; - - HashMap *responseHeaders; - HttpStatus responseStatus; - - Stream *stream; -}; - -typedef struct HttpServerWorkerThreadArgs -{ - HttpServer *server; - int id; - pthread_t thread; -} HttpServerWorkerThreadArgs; - -static HttpServerContext * -HttpServerContextCreate(HttpRequestMethod requestMethod, - char *requestPath, HashMap * requestParams, Stream * stream) -{ - HttpServerContext *c; - - c = Malloc(sizeof(HttpServerContext)); - if (!c) - { - return NULL; - } - - c->responseHeaders = HashMapCreate(); - if (!c->responseHeaders) - { - Free(c->requestHeaders); - Free(c); - return NULL; - } - - c->requestMethod = requestMethod; - c->requestPath = requestPath; - c->requestParams = requestParams; - c->stream = stream; - c->responseStatus = HTTP_OK; - - return c; -} - -static void -HttpServerContextFree(HttpServerContext * c) -{ - char *key; - void *val; - - if (!c) - { - return; - } - - while (HashMapIterate(c->requestHeaders, &key, &val)) - { - Free(val); - } - HashMapFree(c->requestHeaders); - - while (HashMapIterate(c->responseHeaders, &key, &val)) - { - /* - * These are generated by code. As such, they may be either - * on the heap, or on the stack, depending on how they were - * added. - * - * Basically, if the memory API knows about a pointer, then - * it can be freed. If it doesn't know about a pointer, skip - * freeing it because it's probably a stack pointer. - */ - - if (MemoryInfoGet(val)) - { - Free(val); - } - } - - HashMapFree(c->responseHeaders); - - while (HashMapIterate(c->requestParams, &key, &val)) - { - Free(val); - } - - HashMapFree(c->requestParams); - - Free(c->requestPath); - StreamClose(c->stream); - - Free(c); -} - -HashMap * -HttpRequestHeaders(HttpServerContext * c) -{ - if (!c) - { - return NULL; - } - - return c->requestHeaders; -} - -HttpRequestMethod -HttpRequestMethodGet(HttpServerContext * c) -{ - if (!c) - { - return HTTP_METHOD_UNKNOWN; - } - - return c->requestMethod; -} - -char * -HttpRequestPath(HttpServerContext * c) -{ - if (!c) - { - return NULL; - } - - return c->requestPath; -} - -HashMap * -HttpRequestParams(HttpServerContext * c) -{ - if (!c) - { - return NULL; - } - - return c->requestParams; -} - -char * -HttpResponseHeader(HttpServerContext * c, char *key, char *val) -{ - if (!c) - { - return NULL; - } - - return HashMapSet(c->responseHeaders, key, val); -} - -void -HttpResponseStatus(HttpServerContext * c, HttpStatus status) -{ - if (!c) - { - return; - } - - c->responseStatus = status; -} - -HttpStatus -HttpResponseStatusGet(HttpServerContext * c) -{ - if (!c) - { - return HTTP_STATUS_UNKNOWN; - } - - return c->responseStatus; -} - -Stream * -HttpServerStream(HttpServerContext * c) -{ - if (!c) - { - return NULL; - } - - return c->stream; -} - -void -HttpSendHeaders(HttpServerContext * c) -{ - Stream *fp = c->stream; - - char *key; - char *val; - - StreamPrintf(fp, "HTTP/1.0 %d %s\n", c->responseStatus, HttpStatusToString(c->responseStatus)); - - while (HashMapIterate(c->responseHeaders, &key, (void **) &val)) - { - StreamPrintf(fp, "%s: %s\n", key, val); - } - - StreamPuts(fp, "\n"); -} - -static Stream * -DequeueConnection(HttpServer * server) -{ - Stream *fp; - - if (!server) - { - return NULL; - } - - pthread_mutex_lock(&server->connQueueMutex); - fp = QueuePop(server->connQueue); - pthread_mutex_unlock(&server->connQueueMutex); - - return fp; -} - -HttpServer * -HttpServerCreate(HttpServerConfig * config) -{ - HttpServer *server; - struct sockaddr_in sa; - - if (!config) - { - return NULL; - } - - if (!config->handler) - { - return NULL; - } - -#ifndef TLS_IMPL - if (config->flags & HTTP_FLAG_TLS) - { - return NULL; - } -#endif - - server = Malloc(sizeof(HttpServer)); - if (!server) - { - goto error; - } - - memset(server, 0, sizeof(HttpServer)); - - server->config = *config; - server->config.tlsCert = StrDuplicate(config->tlsCert); - server->config.tlsKey = StrDuplicate(config->tlsKey); - - server->threadPool = ArrayCreate(); - if (!server->threadPool) - { - goto error; - } - - server->connQueue = QueueCreate(config->maxConnections); - if (!server->connQueue) - { - goto error; - } - - if (pthread_mutex_init(&server->connQueueMutex, NULL) != 0) - { - goto error; - } - - server->sd = socket(AF_INET, SOCK_STREAM, 0); - - if (server->sd < 0) - { - goto error; - } - - if (fcntl(server->sd, F_SETFL, O_NONBLOCK) == -1) - { - goto error; - } - - if (setsockopt(server->sd, SOL_SOCKET, SO_REUSEADDR, &ENABLE, sizeof(int)) < 0) - { - goto error; - } - -#ifdef SO_REUSEPORT - if (setsockopt(server->sd, SOL_SOCKET, SO_REUSEPORT, &ENABLE, sizeof(int)) < 0) - { - goto error; - } -#endif - - memset(&sa, 0, sizeof(struct sockaddr_in)); - - sa.sin_family = AF_INET; - sa.sin_port = htons(config->port); - sa.sin_addr.s_addr = htonl(INADDR_ANY); - - if (bind(server->sd, (struct sockaddr *) & sa, sizeof(sa)) < 0) - { - goto error; - } - - if (listen(server->sd, config->maxConnections) < 0) - { - goto error; - } - - server->stop = 0; - server->isRunning = 0; - - return server; - -error: - if (server) - { - if (server->connQueue) - { - QueueFree(server->connQueue); - } - - pthread_mutex_destroy(&server->connQueueMutex); - - if (server->threadPool) - { - ArrayFree(server->threadPool); - } - - if (server->sd) - { - close(server->sd); - } - - Free(server); - } - return NULL; -} - -HttpServerConfig * -HttpServerConfigGet(HttpServer * server) -{ - if (!server) - { - return NULL; - } - - return &server->config; -} - -void -HttpServerFree(HttpServer * server) -{ - if (!server) - { - return; - } - - close(server->sd); - QueueFree(server->connQueue); - pthread_mutex_destroy(&server->connQueueMutex); - ArrayFree(server->threadPool); - Free(server->config.tlsCert); - Free(server->config.tlsKey); - Free(server); -} - -static void * -HttpServerWorkerThread(void *args) -{ - HttpServerWorkerThreadArgs *wArgs = (HttpServerWorkerThreadArgs *) args; - HttpServer *server = wArgs->server; - - while (!server->stop) - { - Stream *fp; - HttpServerContext *context; - - char *line = NULL; - size_t lineSize = 0; - ssize_t lineLen = 0; - - char *requestMethodPtr; - char *pathPtr; - char *requestPath; - char *requestProtocol; - - HashMap *requestParams; - ssize_t requestPathLen; - - ssize_t i = 0; - HttpRequestMethod requestMethod; - - long firstRead; - - fp = DequeueConnection(server); - - if (!fp) - { - /* Block for 1 millisecond before continuing so we don't - * murder the CPU if the queue is empty. */ - UtilSleepMillis(1); - continue; - } - - /* Get the first line of the request. - * - * Every once in a while, we're too fast for the client. When this - * happens, UtilGetLine() sets errno to EAGAIN. If we get - * EAGAIN, then clear the error on the stream and try again - * after a few ms. This is typically more than enough time for - * the client to send data. */ - firstRead = UtilServerTs(); - while ((lineLen = UtilGetLine(&line, &lineSize, fp)) == -1 - && errno == EAGAIN) - { - StreamClearError(fp); - - /* If the server is stopped, or it's been a while, just - * give up so we aren't wasting a thread on this client. */ - if (server->stop || (UtilServerTs() - firstRead) > 1000 * 30) - { - goto finish; - } - - UtilSleepMillis(5); - } - - if (lineLen == -1) - { - goto bad_request; - } - - requestMethodPtr = line; - for (i = 0; i < lineLen; i++) - { - if (line[i] == ' ') - { - line[i] = '\0'; - break; - } - } - - if (i == lineLen) - { - goto bad_request; - } - - requestMethod = HttpRequestMethodFromString(requestMethodPtr); - if (requestMethod == HTTP_METHOD_UNKNOWN) - { - goto bad_request; - } - - pathPtr = line + i + 1; - - for (i = 0; i < (line + lineLen) - pathPtr; i++) - { - if (pathPtr[i] == ' ') - { - pathPtr[i] = '\0'; - break; - } - } - - requestPathLen = i; - requestPath = Malloc(((requestPathLen + 1) * sizeof(char))); - strncpy(requestPath, pathPtr, requestPathLen + 1); - - requestProtocol = &pathPtr[i + 1]; - line[lineLen - 2] = '\0'; /* Get rid of \r and \n */ - - if (!StrEquals(requestProtocol, "HTTP/1.1") && !StrEquals(requestProtocol, "HTTP/1.0")) - { - Free(requestPath); - goto bad_request; - } - - /* Find request params */ - for (i = 0; i < requestPathLen; i++) - { - if (requestPath[i] == '?') - { - break; - } - } - - requestPath[i] = '\0'; - requestParams = (i == requestPathLen) ? NULL : HttpParamDecode(requestPath + i + 1); - - context = HttpServerContextCreate(requestMethod, requestPath, requestParams, fp); - if (!context) - { - Free(requestPath); - goto internal_error; - } - - context->requestHeaders = HttpParseHeaders(fp); - if (!context->requestHeaders) - { - goto internal_error; - } - - server->config.handler(context, server->config.handlerArgs); - - HttpServerContextFree(context); - fp = NULL; /* The above call will close this - * Stream */ - goto finish; - -internal_error: - StreamPuts(fp, "HTTP/1.0 500 Internal Server Error\n"); - StreamPuts(fp, "Connection: close\n"); - goto finish; - -bad_request: - StreamPuts(fp, "HTTP/1.0 400 Bad Request\n"); - StreamPuts(fp, "Connection: close\n"); - goto finish; - -finish: - Free(line); - if (fp) - { - StreamClose(fp); - } - } - - return NULL; -} - -static void * -HttpServerEventThread(void *args) -{ - HttpServer *server = (HttpServer *) args; - struct pollfd pollFds[1]; - Stream *fp; - size_t i; - - server->isRunning = 1; - server->stop = 0; - - pollFds[0].fd = server->sd; - pollFds[0].events = POLLIN; - - for (i = 0; i < server->config.threads; i++) - { - HttpServerWorkerThreadArgs *workerThread = Malloc(sizeof(HttpServerWorkerThreadArgs)); - - if (!workerThread) - { - /* TODO: Make the event thread return an error to the main - * thread */ - return NULL; - } - - workerThread->server = server; - workerThread->id = i; - - if (pthread_create(&workerThread->thread, NULL, HttpServerWorkerThread, workerThread) != 0) - { - /* TODO: Make the event thread return an error to the main - * thread */ - return NULL; - } - - ArrayAdd(server->threadPool, workerThread); - } - - while (!server->stop) - { - struct sockaddr_storage addr; - socklen_t addrLen = sizeof(addr); - int connFd; - int pollResult; - - - pollResult = poll(pollFds, 1, 500); - - if (pollResult < 0) - { - /* The poll either timed out, or was interrupted. */ - continue; - } - - pthread_mutex_lock(&server->connQueueMutex); - - /* Don't even accept connections if the queue is full. */ - if (!QueueFull(server->connQueue)) - { - connFd = accept(server->sd, (struct sockaddr *) & addr, &addrLen); - - if (connFd < 0) - { - pthread_mutex_unlock(&server->connQueueMutex); - continue; - } - -#ifdef TLS_IMPL - if (server->config.flags & HTTP_FLAG_TLS) - { - fp = TlsServerStream(connFd, server->config.tlsCert, server->config.tlsKey); - } - else - { - fp = StreamFd(connFd); - } -#else - fp = StreamFd(connFd); -#endif - - if (!fp) - { - pthread_mutex_unlock(&server->connQueueMutex); - close(connFd); - continue; - } - - QueuePush(server->connQueue, fp); - } - pthread_mutex_unlock(&server->connQueueMutex); - } - - for (i = 0; i < server->config.threads; i++) - { - HttpServerWorkerThreadArgs *workerThread = ArrayGet(server->threadPool, i); - - pthread_join(workerThread->thread, NULL); - Free(workerThread); - } - - while ((fp = DequeueConnection(server))) - { - StreamClose(fp); - } - - server->isRunning = 0; - - return NULL; -} - -int -HttpServerStart(HttpServer * server) -{ - if (!server) - { - return 0; - } - - if (server->isRunning) - { - return 1; - } - - if (pthread_create(&server->socketThread, NULL, HttpServerEventThread, server) != 0) - { - return 0; - } - - return 1; -} - -void -HttpServerJoin(HttpServer * server) -{ - if (!server) - { - return; - } - - pthread_join(server->socketThread, NULL); -} - -void -HttpServerStop(HttpServer * server) -{ - if (!server) - { - return; - } - - server->stop = 1; -} diff --git a/src/Io.c b/src/Io.c deleted file mode 100644 index 54d250c..0000000 --- a/src/Io.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include - -#include -#include -#include -#include - -struct Io -{ - IoFunctions io; - void *cookie; -}; - -Io * -IoCreate(void *cookie, IoFunctions funcs) -{ - Io *io; - - /* Must have at least read or write */ - if (!funcs.read && !funcs.write) - { - return NULL; - } - - io = Malloc(sizeof(Io)); - - if (!io) - { - return NULL; - } - - io->cookie = cookie; - - io->io.read = funcs.read; - io->io.write = funcs.write; - io->io.seek = funcs.seek; - io->io.close = funcs.close; - - return io; -} - -ssize_t -IoRead(Io * io, void *buf, size_t nBytes) -{ - if (!io || !io->io.read) - { - errno = EBADF; - return -1; - } - - return io->io.read(io->cookie, buf, nBytes); -} - -ssize_t -IoWrite(Io * io, void *buf, size_t nBytes) -{ - if (!io || !io->io.write) - { - errno = EBADF; - return -1; - } - - return io->io.write(io->cookie, buf, nBytes); -} - -off_t -IoSeek(Io * io, off_t offset, int whence) -{ - if (!io) - { - errno = EBADF; - return -1; - } - - if (!io->io.seek) - { - errno = EINVAL; - return -1; - } - - return io->io.seek(io->cookie, offset, whence); -} - -int -IoClose(Io * io) -{ - int ret; - - if (!io) - { - errno = EBADF; - return -1; - } - - if (io->io.close) - { - ret = io->io.close(io->cookie); - } - else - { - ret = 0; - } - - Free(io); - - return ret; -} - -int -IoVprintf(Io * io, const char *fmt, va_list ap) -{ - char *buf = NULL; - size_t bufSize = 0; - FILE *fp; - - int ret; - - if (!io || !fmt) - { - return -1; - } - - fp = open_memstream(&buf, &bufSize); - if (!fp) - { - return -1; - } - - ret = vfprintf(fp, fmt, ap); - fclose(fp); - - if (ret >= 0) - { - ret = IoWrite(io, buf, bufSize); - } - - free(buf); /* Allocated by stdlib, not Memory - * API */ - return ret; -} - -int -IoPrintf(Io * io, const char *fmt,...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = IoVprintf(io, fmt, ap); - va_end(ap); - - return ret; -} - -ssize_t -IoCopy(Io * in, Io * out) -{ - ssize_t nBytes = 0; - char buf[IO_BUFFER]; - ssize_t rRes; - ssize_t wRes; - - if (!in || !out) - { - errno = EBADF; - return -1; - } - - while ((rRes = IoRead(in, &buf, IO_BUFFER)) != 0) - { - if (rRes == -1) - { - return -1; - } - - wRes = IoWrite(out, &buf, rRes); - - if (wRes == -1) - { - return -1; - } - - nBytes += wRes; - } - - return nBytes; -} diff --git a/src/Io/IoFd.c b/src/Io/IoFd.c deleted file mode 100644 index eaa1539..0000000 --- a/src/Io/IoFd.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include - -#include - -static ssize_t -IoReadFd(void *cookie, void *buf, size_t nBytes) -{ - int fd = *((int *) cookie); - - return read(fd, buf, nBytes); -} - -static ssize_t -IoWriteFd(void *cookie, void *buf, size_t nBytes) -{ - int fd = *((int *) cookie); - - return write(fd, buf, nBytes); -} - -static off_t -IoSeekFd(void *cookie, off_t offset, int whence) -{ - int fd = *((int *) cookie); - - return lseek(fd, offset, whence); -} - -static int -IoCloseFd(void *cookie) -{ - int fd = *((int *) cookie); - - Free(cookie); - return close(fd); -} - -Io * -IoFd(int fd) -{ - int *cookie = Malloc(sizeof(int)); - IoFunctions f; - - if (!cookie) - { - return NULL; - } - - *cookie = fd; - - f.read = IoReadFd; - f.write = IoWriteFd; - f.seek = IoSeekFd; - f.close = IoCloseFd; - - return IoCreate(cookie, f); -} - -Io * -IoOpen(const char *path, int flags, mode_t mode) -{ - int fd = open(path, flags, mode); - - if (fd == -1) - { - return NULL; - } - - return IoFd(fd); -} diff --git a/src/Io/IoFile.c b/src/Io/IoFile.c deleted file mode 100644 index cdd40ab..0000000 --- a/src/Io/IoFile.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include - -static ssize_t -IoReadFile(void *cookie, void *buf, size_t nBytes) -{ - FILE *fp = cookie; - - return fread(buf, 1, nBytes, fp); -} - -static ssize_t -IoWriteFile(void *cookie, void *buf, size_t nBytes) -{ - FILE *fp = cookie; - size_t res = fwrite(buf, 1, nBytes, fp); - - /* - * fwrite() may be buffered on some platforms, but at this low level, - * it should not be; buffering happens in Stream, not Io. - */ - fflush(fp); - - return res; -} - -static off_t -IoSeekFile(void *cookie, off_t offset, int whence) -{ - FILE *fp = cookie; - off_t ret = fseeko(fp, offset, whence); - - if (ret > -1) - { - return ftello(fp); - } - else - { - return ret; - } -} - -static int -IoCloseFile(void *cookie) -{ - FILE *fp = cookie; - - return fclose(fp); -} - -Io * -IoFile(FILE * fp) -{ - IoFunctions f; - - if (!fp) - { - return NULL; - } - - f.read = IoReadFile; - f.write = IoWriteFile; - f.seek = IoSeekFile; - f.close = IoCloseFile; - - return IoCreate(fp, f); -} diff --git a/src/Json.c b/src/Json.c deleted file mode 100644 index a9f8ce9..0000000 --- a/src/Json.c +++ /dev/null @@ -1,1384 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct JsonValue -{ - JsonType type; - union - { - HashMap *object; - Array *array; - char *string; - long integer; - double floating; - int boolean:1; - } as; -}; - -typedef enum JsonToken -{ - TOKEN_UNKNOWN, - TOKEN_COLON, - TOKEN_COMMA, - TOKEN_OBJECT_OPEN, - TOKEN_OBJECT_CLOSE, - TOKEN_ARRAY_OPEN, - TOKEN_ARRAY_CLOSE, - TOKEN_STRING, - TOKEN_INTEGER, - TOKEN_FLOAT, - TOKEN_BOOLEAN, - TOKEN_NULL, - TOKEN_EOF -} JsonToken; - -typedef struct JsonParserState -{ - Stream *stream; - - JsonToken tokenType; - char *token; - size_t tokenLen; -} JsonParserState; - -JsonType -JsonValueType(JsonValue * value) -{ - if (!value) - { - return JSON_NULL; - } - - return value->type; -} - -static JsonValue * -JsonValueAllocate(void) -{ - return Malloc(sizeof(JsonValue)); -} - -JsonValue * -JsonValueObject(HashMap * object) -{ - JsonValue *value; - - if (!object) - { - return NULL; - } - - value = JsonValueAllocate(); - if (!value) - { - return NULL; - } - - value->type = JSON_OBJECT; - value->as.object = object; - - return value; -} - -HashMap * -JsonValueAsObject(JsonValue * value) -{ - if (!value || value->type != JSON_OBJECT) - { - return NULL; - } - - return value->as.object; -} - - -JsonValue * -JsonValueArray(Array * array) -{ - JsonValue *value; - - if (!array) - { - return NULL; - } - - value = JsonValueAllocate(); - if (!value) - { - return NULL; - } - - value->type = JSON_ARRAY; - value->as.array = array; - - return value; -} - -Array * -JsonValueAsArray(JsonValue * value) -{ - if (!value || value->type != JSON_ARRAY) - { - return NULL; - } - - return value->as.array; -} - - -JsonValue * -JsonValueString(char *string) -{ - JsonValue *value; - - if (!string) - { - return NULL; - } - - value = JsonValueAllocate(); - if (!value) - { - return NULL; - } - - value->type = JSON_STRING; - - value->as.string = StrDuplicate(string); - if (!value->as.string) - { - Free(value); - return NULL; - } - - return value; -} - -char * -JsonValueAsString(JsonValue * value) -{ - if (!value || value->type != JSON_STRING) - { - return NULL; - } - - return value->as.string; -} - -JsonValue * -JsonValueInteger(long integer) -{ - JsonValue *value; - - value = JsonValueAllocate(); - if (!value) - { - return 0; - } - - value->type = JSON_INTEGER; - value->as.integer = integer; - - return value; -} - -long -JsonValueAsInteger(JsonValue * value) -{ - if (!value || value->type != JSON_INTEGER) - { - return 0; - } - - return value->as.integer; -} - - -JsonValue * -JsonValueFloat(double floating) -{ - JsonValue *value; - - value = JsonValueAllocate(); - if (!value) - { - return NULL; - } - - value->type = JSON_FLOAT; - value->as.floating = floating; - - return value; -} - -double -JsonValueAsFloat(JsonValue * value) -{ - if (!value || value->type != JSON_FLOAT) - { - return 0; - } - - return value->as.floating; -} - -JsonValue * -JsonValueBoolean(int boolean) -{ - JsonValue *value; - - value = JsonValueAllocate(); - if (!value) - { - return NULL; - } - - value->type = JSON_BOOLEAN; - value->as.boolean = boolean; - - return value; -} - -int -JsonValueAsBoolean(JsonValue * value) -{ - if (!value || value->type != JSON_BOOLEAN) - { - return 0; - } - - return value->as.boolean; -} - -JsonValue * -JsonValueNull(void) -{ - JsonValue *value; - - value = JsonValueAllocate(); - if (!value) - { - return NULL; - } - - value->type = JSON_NULL; - - return value; -} - -void -JsonValueFree(JsonValue * value) -{ - size_t i; - Array *arr; - - if (!value) - { - return; - } - - switch (value->type) - { - case JSON_OBJECT: - JsonFree(value->as.object); - break; - case JSON_ARRAY: - arr = value->as.array; - for (i = 0; i < ArraySize(arr); i++) - { - JsonValueFree((JsonValue *) ArrayGet(arr, i)); - } - ArrayFree(arr); - break; - case JSON_STRING: - Free(value->as.string); - break; - default: - break; - } - - Free(value); -} - -int -JsonEncodeString(const char *str, Stream * out) -{ - size_t i; - char c; - int length = 0; - - StreamPutc(out, '"'); - length++; - - i = 0; - while ((c = str[i]) != '\0') - { - switch (c) - { - case '\\': - case '"': - case '/': - StreamPutc(out, '\\'); - StreamPutc(out, c); - length += 2; - break; - case '\b': - StreamPuts(out, "\\b"); - length += 2; - break; - case '\t': - StreamPuts(out, "\\t"); - length += 2; - break; - case '\n': - StreamPuts(out, "\\n"); - length += 2; - break; - case '\f': - StreamPuts(out, "\\f"); - length += 2; - break; - case '\r': - StreamPuts(out, "\\r"); - length += 2; - break; - default: /* Assume UTF-8 input */ - /* - * RFC 4627: "All Unicode characters may be placed - * within the quotation marks except for the characters - * that must be escaped: quotation mark, reverse solidus, - * and the control characters (U+0000 through U+001F)." - * - * This technically covers the above cases for backspace, - * tab, newline, feed, and carriage return characters, - * but we can save bytes if we encode those as their - * more human-readable representation. - */ - if (c <= 0x001F) - { - length += StreamPrintf(out, "\\u%04x", c); - } - else - { - StreamPutc(out, c); - length++; - } - break; - } - i++; - } - - StreamPutc(out, '"'); - length++; - - return length; -} - -static char * -JsonDecodeString(Stream * in) -{ - const size_t strBlockSize = 16; - - size_t i; - size_t len; - size_t allocated; - char *str; - int c; - char a[5]; - - unsigned long utf8; - char *utf8Ptr; - - len = 0; - allocated = strBlockSize; - - str = Malloc(allocated * sizeof(char)); - if (!str) - { - return NULL; - } - - while ((c = StreamGetc(in)) != EOF) - { - if (c <= 0x001F) - { - /* Bad byte; these must be escaped */ - Free(str); - return NULL; - } - - switch (c) - { - case '"': - if (len >= allocated) - { - char *tmp; - - allocated += 1; - tmp = Realloc(str, allocated * sizeof(char)); - if (!tmp) - { - Free(str); - return NULL; - } - - str = tmp; - } - str[len] = '\0'; - return str; - break; - case '\\': - c = StreamGetc(in); - switch (c) - { - case '\\': - case '"': - case '/': - a[0] = c; - a[1] = '\0'; - break; - case 'b': - a[0] = '\b'; - a[1] = '\0'; - break; - case 't': - a[0] = '\t'; - a[1] = '\0'; - break; - case 'n': - a[0] = '\n'; - a[1] = '\0'; - break; - case 'f': - a[0] = '\f'; - a[1] = '\0'; - break; - case 'r': - a[0] = '\r'; - a[1] = '\0'; - break; - case 'u': - /* Read 4 characters into a */ - if (!StreamGets(in, a, sizeof(a))) - { - Free(str); - return NULL; - } - /* Interpret characters as a hex number */ - if (sscanf(a, "%04lx", &utf8) != 1) - { - /* Bad hex value */ - Free(str); - return NULL; - } - - if (utf8 == 0) - { - /* - * We read in a 0000, null. There is no - * circumstance in which putting a null - * character into our buffer will end well. - * - * There's also really no legitimate use - * for the null character in our JSON anyway; - * it's likely an attempted exploit. - * - * So lets just strip it out. Don't even - * include it in the string. There should be - * no harm in ignoring it. - */ - continue; - } - - /* Encode the 4-byte UTF-8 buffer into a series - * of 1-byte characters */ - utf8Ptr = StrUtf8Encode(utf8); - if (!utf8Ptr) - { - /* Mem error */ - Free(str); - return NULL; - } - - /* Move the output of StrUtf8Encode() into our - * local buffer */ - strncpy(a, utf8Ptr, sizeof(a) - 1); - Free(utf8Ptr); - break; - default: - /* Bad escape value */ - Free(str); - return NULL; - } - break; - default: - a[0] = c; - a[1] = '\0'; - break; - } - - /* Append buffer a */ - i = 0; - while (a[i] != '\0') - { - if (len >= allocated) - { - char *tmp; - - allocated += strBlockSize; - tmp = Realloc(str, allocated * sizeof(char)); - if (!tmp) - { - Free(str); - return NULL; - } - - str = tmp; - } - - str[len] = a[i]; - len++; - i++; - } - } - - Free(str); - return NULL; -} - -int -JsonEncodeValue(JsonValue * value, Stream * out, int level) -{ - size_t i; - size_t len; - Array *arr; - int length = 0; - - switch (value->type) - { - case JSON_OBJECT: - length += JsonEncode(value->as.object, out, level >= 0 ? level : level); - break; - case JSON_ARRAY: - arr = value->as.array; - len = ArraySize(arr); - - StreamPutc(out, '['); - length++; - for (i = 0; i < len; i++) - { - if (level >= 0) - { - length += StreamPrintf(out, "\n%*s", level + 2, ""); - } - length += JsonEncodeValue(ArrayGet(arr, i), out, level >= 0 ? level + 2 : level); - if (i < len - 1) - { - StreamPutc(out, ','); - length++; - } - } - - if (level >= 0) - { - length += StreamPrintf(out, "\n%*s", level, ""); - } - StreamPutc(out, ']'); - length++; - break; - case JSON_STRING: - length += JsonEncodeString(value->as.string, out); - break; - case JSON_INTEGER: - length += StreamPrintf(out, "%ld", value->as.integer); - break; - case JSON_FLOAT: - length += StreamPrintf(out, "%f", value->as.floating); - break; - case JSON_BOOLEAN: - if (value->as.boolean) - { - StreamPuts(out, "true"); - length += 4; - } - else - { - StreamPuts(out, "false"); - length += 5; - } - break; - case JSON_NULL: - StreamPuts(out, "null"); - length += 4; - break; - default: - return -1; - } - - return length; -} - -int -JsonEncode(HashMap * object, Stream * out, int level) -{ - size_t index; - size_t count; - char *key; - JsonValue *value; - int length; - - if (!object) - { - return -1; - } - - count = 0; - while (HashMapIterate(object, &key, (void **) &value)) - { - count++; - } - - /* The total number of bytes written */ - length = 0; - - StreamPutc(out, '{'); - length++; - - if (level >= 0) - { - StreamPutc(out, '\n'); - length++; - } - - index = 0; - while (HashMapIterate(object, &key, (void **) &value)) - { - if (level >= 0) - { - StreamPrintf(out, "%*s", level + 2, ""); - length += level + 2; - } - - length += JsonEncodeString(key, out); - - StreamPutc(out, ':'); - length++; - if (level >= 0) - { - StreamPutc(out, ' '); - length++; - } - - length += JsonEncodeValue(value, out, level >= 0 ? level + 2 : level); - - if (index < count - 1) - { - StreamPutc(out, ','); - length++; - } - - if (level >= 0) - { - StreamPutc(out, '\n'); - length++; - } - - index++; - } - - if (level >= 0) - { - StreamPrintf(out, "%*s", level, ""); - length += level; - } - StreamPutc(out, '}'); - length++; - - return length; -} - -void -JsonFree(HashMap * object) -{ - char *key; - JsonValue *value; - - if (!object) - { - return; - } - - while (HashMapIterate(object, &key, (void **) &value)) - { - JsonValueFree(value); - } - - HashMapFree(object); -} - -JsonValue * -JsonValueDuplicate(JsonValue * val) -{ - JsonValue *new; - size_t i; - - if (!val) - { - return NULL; - } - - new = JsonValueAllocate(); - if (!new) - { - return NULL; - } - - new->type = val->type; - - switch (val->type) - { - case JSON_OBJECT: - new->as.object = JsonDuplicate(val->as.object); - break; - case JSON_ARRAY: - new->as.array = ArrayCreate(); - for (i = 0; i < ArraySize(val->as.array); i++) - { - ArrayAdd(new->as.array, JsonValueDuplicate(ArrayGet(val->as.array, i))); - } - break; - case JSON_STRING: - new->as.string = StrDuplicate(val->as.string); - break; - case JSON_INTEGER: - case JSON_FLOAT: - case JSON_BOOLEAN: - /* These are by value, not by reference */ - new->as = val->as; - case JSON_NULL: - default: - break; - } - - return new; -} - -HashMap * -JsonDuplicate(HashMap * object) -{ - HashMap *new; - char *key; - JsonValue *val; - - if (!object) - { - return NULL; - } - - new = HashMapCreate(); - if (!new) - { - return NULL; - } - - while (HashMapIterate(object, &key, (void **) &val)) - { - HashMapSet(new, key, JsonValueDuplicate(val)); - } - - return new; -} - -static int -JsonConsumeWhitespace(JsonParserState * state) -{ - static const int nRetries = 5; - static const int delay = 2; - - int c; - int tries = 0; - int readFlg = 0; - - while (1) - { - c = StreamGetc(state->stream); - - if (StreamEof(state->stream)) - { - break; - } - - if (StreamError(state->stream)) - { - if (errno == EAGAIN) - { - StreamClearError(state->stream); - tries++; - - if (tries >= nRetries || readFlg) - { - break; - } - else - { - UtilSleepMillis(delay); - continue; - } - } - else - { - break; - } - } - - /* As soon as we've successfully read a byte, treat future - * EAGAINs as EOF, because some clients don't properly shutdown - * their sockets. */ - readFlg = 1; - tries = 0; - - if (!isspace(c)) - { - break; - } - } - - return c; -} - -static void -JsonTokenSeek(JsonParserState * state) -{ - int c = JsonConsumeWhitespace(state); - - if (StreamEof(state->stream)) - { - state->tokenType = TOKEN_EOF; - return; - } - - if (state->token) - { - Free(state->token); - state->token = NULL; - } - - switch (c) - { - case ':': - state->tokenType = TOKEN_COLON; - break; - case ',': - state->tokenType = TOKEN_COMMA; - break; - case '{': - state->tokenType = TOKEN_OBJECT_OPEN; - break; - case '}': - state->tokenType = TOKEN_OBJECT_CLOSE; - break; - case '[': - state->tokenType = TOKEN_ARRAY_OPEN; - break; - case ']': - state->tokenType = TOKEN_ARRAY_CLOSE; - break; - case '"': - state->token = JsonDecodeString(state->stream); - if (!state->token) - { - state->tokenType = TOKEN_EOF; - return; - } - state->tokenType = TOKEN_STRING; - state->tokenLen = strlen(state->token); - break; - default: - if (c == '-' || isdigit(c)) - { - int isFloat = 0; - size_t allocated = 16; - - state->tokenLen = 1; - state->token = Malloc(allocated); - if (!state->token) - { - state->tokenType = TOKEN_EOF; - return; - } - state->token[0] = c; - - while ((c = StreamGetc(state->stream)) != EOF) - { - if (c == '.') - { - if (state->tokenLen > 1 && !isFloat) - { - isFloat = 1; - } - else - { - state->tokenType = TOKEN_UNKNOWN; - return; - } - } - else if (!isdigit(c)) - { - StreamUngetc(state->stream, c); - break; - } - - if (state->tokenLen >= allocated) - { - char *tmp; - - allocated += 16; - - tmp = Realloc(state->token, allocated); - if (!tmp) - { - state->tokenType = TOKEN_EOF; - return; - } - - state->token = tmp; - } - - state->token[state->tokenLen] = c; - state->tokenLen++; - } - - if (state->token[state->tokenLen - 1] == '.') - { - state->tokenType = TOKEN_UNKNOWN; - return; - } - - state->token[state->tokenLen] = '\0'; - if (isFloat) - { - state->tokenType = TOKEN_FLOAT; - } - else - { - state->tokenType = TOKEN_INTEGER; - } - } - else - { - state->tokenLen = 8; - state->token = Malloc(state->tokenLen); - if (!state->token) - { - state->tokenType = TOKEN_EOF; - return; - } - - state->token[0] = c; - - switch (c) - { - case 't': - if (!StreamGets(state->stream, state->token + 1, 4)) - { - state->tokenType = TOKEN_EOF; - Free(state->token); - state->token = NULL; - return; - } - - if (StrEquals("true", state->token)) - { - state->tokenType = TOKEN_BOOLEAN; - state->tokenLen = 5; - } - else - { - state->tokenType = TOKEN_UNKNOWN; - Free(state->token); - state->token = NULL; - } - break; - case 'f': - if (!StreamGets(state->stream, state->token + 1, 5)) - { - state->tokenType = TOKEN_EOF; - Free(state->token); - state->token = NULL; - return; - } - - if (StrEquals("false", state->token)) - { - state->tokenType = TOKEN_BOOLEAN; - state->tokenLen = 6; - } - else - { - state->tokenType = TOKEN_UNKNOWN; - Free(state->token); - state->token = NULL; - } - break; - case 'n': - if (!StreamGets(state->stream, state->token + 1, 4)) - { - state->tokenType = TOKEN_EOF; - Free(state->token); - state->token = NULL; - return; - } - - if (StrEquals("null", state->token)) - { - state->tokenType = TOKEN_NULL; - } - else - { - state->tokenType = TOKEN_UNKNOWN; - Free(state->token); - state->token = NULL; - } - break; - default: - state->tokenType = TOKEN_UNKNOWN; - Free(state->token); - state->token = NULL; - break; - } - } - } -} - -static int -JsonExpect(JsonParserState * state, JsonToken token) -{ - return state->tokenType == token; -} - -static Array * - JsonDecodeArray(JsonParserState *); - -static HashMap * - JsonDecodeObject(JsonParserState *); - -static JsonValue * -JsonDecodeValue(JsonParserState * state) -{ - JsonValue *value; - char *strValue; - - switch (state->tokenType) - { - case TOKEN_OBJECT_OPEN: - value = JsonValueObject(JsonDecodeObject(state)); - break; - case TOKEN_ARRAY_OPEN: - value = JsonValueArray(JsonDecodeArray(state)); - break; - case TOKEN_STRING: - strValue = Malloc(state->tokenLen + 1); - if (!strValue) - { - return NULL; - } - strncpy(strValue, state->token, state->tokenLen + 1); - value = JsonValueString(strValue); - Free(strValue); - break; - case TOKEN_INTEGER: - value = JsonValueInteger(atol(state->token)); - break; - case TOKEN_FLOAT: - value = JsonValueFloat(atof(state->token)); - break; - case TOKEN_BOOLEAN: - value = JsonValueBoolean(state->token[0] == 't'); - break; - case TOKEN_NULL: - value = JsonValueNull(); - break; - default: - value = NULL; - break; - } - - return value; -} - -static HashMap * -JsonDecodeObject(JsonParserState * state) -{ - HashMap *obj = HashMapCreate(); - int comma = 0; - - if (!obj) - { - return NULL; - } - - do - { - JsonTokenSeek(state); - if (JsonExpect(state, TOKEN_STRING)) - { - char *key = Malloc(state->tokenLen + 1); - JsonValue *value; - - if (!key) - { - goto error; - } - strncpy(key, state->token, state->tokenLen + 1); - - JsonTokenSeek(state); - if (!JsonExpect(state, TOKEN_COLON)) - { - Free(key); - goto error; - } - - JsonTokenSeek(state); - value = JsonDecodeValue(state); - - if (!value) - { - Free(key); - goto error; - } - - /* If there's an existing value at this key, discard it. */ - JsonValueFree(HashMapSet(obj, key, value)); - Free(key); - - JsonTokenSeek(state); - - if (JsonExpect(state, TOKEN_COMMA)) - { - comma = 1; - continue; - } - - if (JsonExpect(state, TOKEN_OBJECT_CLOSE)) - { - break; - } - - goto error; - } - else if (!comma && JsonExpect(state, TOKEN_OBJECT_CLOSE)) - { - break; - } - else - { - goto error; - } - } while (!JsonExpect(state, TOKEN_EOF)); - - return obj; -error: - JsonFree(obj); - return NULL; -} - -static Array * -JsonDecodeArray(JsonParserState * state) -{ - Array *arr = ArrayCreate(); - size_t i; - int comma = 0; - - if (!arr) - { - return NULL; - } - - do - { - JsonValue *value; - - JsonTokenSeek(state); - - if (!comma && JsonExpect(state, TOKEN_ARRAY_CLOSE)) - { - break; - } - - value = JsonDecodeValue(state); - - if (!value) - { - goto error; - } - - ArrayAdd(arr, value); - - JsonTokenSeek(state); - - if (JsonExpect(state, TOKEN_COMMA)) - { - comma = 1; - continue; - } - - if (JsonExpect(state, TOKEN_ARRAY_CLOSE)) - { - break; - } - - goto error; - } while (!JsonExpect(state, TOKEN_EOF)); - - return arr; -error: - for (i = 0; i < ArraySize(arr); i++) - { - JsonValueFree((JsonValue *) ArrayGet(arr, i)); - } - ArrayFree(arr); - return NULL; -} - -HashMap * -JsonDecode(Stream * stream) -{ - HashMap *result; - - JsonParserState state; - - state.stream = stream; - state.token = NULL; - - JsonTokenSeek(&state); - if (!JsonExpect(&state, TOKEN_OBJECT_OPEN)) - { - return NULL; - } - - result = JsonDecodeObject(&state); - - if (state.token) - { - Free(state.token); - } - - return result; -} - -JsonValue * -JsonGet(HashMap * json, size_t nArgs,...) -{ - va_list argp; - - HashMap *tmp = json; - JsonValue *val = NULL; - size_t i; - - if (!json || !nArgs) - { - return NULL; - } - - va_start(argp, nArgs); - for (i = 0; i < nArgs - 1; i++) - { - char *key = va_arg(argp, char *); - - val = HashMapGet(tmp, key); - if (!val) - { - goto finish; - } - - if (JsonValueType(val) != JSON_OBJECT) - { - val = NULL; - goto finish; - } - - tmp = JsonValueAsObject(val); - } - - val = HashMapGet(tmp, va_arg(argp, char *)); - -finish: - va_end(argp); - return val; -} - -JsonValue * -JsonSet(HashMap * json, JsonValue * newVal, size_t nArgs,...) -{ - HashMap *tmp = json; - JsonValue *val = NULL; - size_t i; - - va_list argp; - - if (!json || !newVal || !nArgs) - { - return NULL; - } - - va_start(argp, nArgs); - - for (i = 0; i < nArgs - 1; i++) - { - char *key = va_arg(argp, char *); - - val = HashMapGet(tmp, key); - if (!val) - { - val = JsonValueObject(HashMapCreate()); - HashMapSet(tmp, key, val); - } - - if (JsonValueType(val) != JSON_OBJECT) - { - val = NULL; - goto finish; - } - - tmp = JsonValueAsObject(val); - } - - val = HashMapSet(tmp, va_arg(argp, char *), newVal); - -finish: - va_end(argp); - return val; -} diff --git a/src/Log.c b/src/Log.c deleted file mode 100644 index f24ee23..0000000 --- a/src/Log.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include - -#include -#include -#include -#include -#include -#include - -#define LOG_TSBUFFER 64 - -struct LogConfig -{ - int level; - size_t indent; - Stream *out; - int flags; - char *tsFmt; - - pthread_mutex_t lock; -}; - -LogConfig *globalConfig = NULL; - -LogConfig * -LogConfigCreate(void) -{ - LogConfig *config; - - config = Malloc(sizeof(LogConfig)); - - if (!config) - { - return NULL; - } - - memset(config, 0, sizeof(LogConfig)); - - LogConfigLevelSet(config, LOG_INFO); - LogConfigIndentSet(config, 0); - LogConfigOutputSet(config, NULL); /* Will set to stdout */ - LogConfigFlagSet(config, LOG_FLAG_COLOR); - LogConfigTimeStampFormatSet(config, "%y-%m-%d %H:%M:%S"); - - return config; -} - -LogConfig * -LogConfigGlobal(void) -{ - if (!globalConfig) - { - globalConfig = LogConfigCreate(); - } - - return globalConfig; -} - -void -LogConfigFlagClear(LogConfig * config, int flags) -{ - if (!config) - { - return; - } - - config->flags &= ~flags; -} - -static int -LogConfigFlagGet(LogConfig * config, int flags) -{ - if (!config) - { - return 0; - } - - return config->flags & flags; -} - -void -LogConfigFlagSet(LogConfig * config, int flags) -{ - if (!config) - { - return; - } - - config->flags |= flags; -} - -void -LogConfigFree(LogConfig * config) -{ - if (!config) - { - return; - } - - Free(config); - - if (config == globalConfig) - { - globalConfig = NULL; - } -} - -void -LogConfigIndent(LogConfig * config) -{ - if (config) - { - config->indent += 2; - } -} - -void -LogConfigIndentSet(LogConfig * config, size_t indent) -{ - if (!config) - { - return; - } - - config->indent = indent; -} - -int -LogConfigLevelGet(LogConfig * config) -{ - if (!config) - { - return -1; - } - - return config->level; -} - -void -LogConfigLevelSet(LogConfig * config, int level) -{ - if (!config) - { - return; - } - - switch (level) - { - case LOG_ERR: - case LOG_WARNING: - case LOG_INFO: - case LOG_DEBUG: - config->level = level; - default: - break; - } -} - -void -LogConfigOutputSet(LogConfig * config, Stream * out) -{ - if (!config) - { - return; - } - - if (out) - { - config->out = out; - } - else - { - config->out = StreamStdout(); - } - -} - -void -LogConfigTimeStampFormatSet(LogConfig * config, char *tsFmt) -{ - if (config) - { - config->tsFmt = tsFmt; - } -} - -void -LogConfigUnindent(LogConfig * config) -{ - if (config && config->indent >= 2) - { - config->indent -= 2; - } -} - -void -Logv(LogConfig * config, int level, const char *msg, va_list argp) -{ - size_t i; - int doColor; - char indicator; - - /* - * Only proceed if we have a config and its log level is set to a - * value that permits us to log. This is as close as we can get - * to a no-op function if we aren't logging anything, without doing - * some crazy macro magic. - */ - if (!config || level > config->level) - { - return; - } - - /* Misconfiguration */ - if (!config->out) - { - return; - } - - pthread_mutex_lock(&config->lock); - - if (LogConfigFlagGet(config, LOG_FLAG_SYSLOG)) - { - /* No further print logic is needed; syslog will handle it all - * for us. */ - vsyslog(level, msg, argp); - pthread_mutex_unlock(&config->lock); - return; - } - - doColor = LogConfigFlagGet(config, LOG_FLAG_COLOR) - && isatty(StreamFileno(config->out)); - - if (doColor) - { - char *ansi; - - switch (level) - { - case LOG_EMERG: - case LOG_ALERT: - case LOG_CRIT: - case LOG_ERR: - /* Bold Red */ - ansi = "\033[1;31m"; - break; - case LOG_WARNING: - /* Bold Yellow */ - ansi = "\033[1;33m"; - break; - case LOG_NOTICE: - /* Bold Magenta */ - ansi = "\033[1;35m"; - break; - case LOG_INFO: - /* Bold Green */ - ansi = "\033[1;32m"; - break; - case LOG_DEBUG: - /* Bold Blue */ - ansi = "\033[1;34m"; - break; - default: - ansi = ""; - break; - } - - StreamPuts(config->out, ansi); - } - - StreamPutc(config->out, '['); - - if (config->tsFmt) - { - time_t timer = time(NULL); - struct tm *timeInfo = localtime(&timer); - char tsBuffer[LOG_TSBUFFER]; - - int tsLength = strftime(tsBuffer, LOG_TSBUFFER, config->tsFmt, - timeInfo); - - if (tsLength) - { - StreamPuts(config->out, tsBuffer); - if (!isspace((unsigned char) tsBuffer[tsLength - 1])) - { - StreamPutc(config->out, ' '); - } - } - } - - switch (level) - { - case LOG_EMERG: - indicator = '#'; - break; - case LOG_ALERT: - indicator = '@'; - break; - case LOG_CRIT: - indicator = 'X'; - break; - case LOG_ERR: - indicator = 'x'; - break; - case LOG_WARNING: - indicator = '!'; - break; - case LOG_NOTICE: - indicator = '~'; - break; - case LOG_INFO: - indicator = '>'; - break; - case LOG_DEBUG: - indicator = '*'; - break; - default: - indicator = '?'; - break; - } - - StreamPrintf(config->out, "%c]", indicator); - - if (doColor) - { - /* ANSI Reset */ - StreamPuts(config->out, "\033[0m"); - } - - StreamPutc(config->out, ' '); - for (i = 0; i < config->indent; i++) - { - StreamPutc(config->out, ' '); - } - - StreamVprintf(config->out, msg, argp); - StreamPutc(config->out, '\n'); - - StreamFlush(config->out); - - pthread_mutex_unlock(&config->lock); -} - -void -LogTo(LogConfig * config, int level, const char *fmt,...) -{ - va_list argp; - - va_start(argp, fmt); - Logv(config, level, fmt, argp); - va_end(argp); -} - -extern void -Log(int level, const char *fmt,...) -{ - va_list argp; - - va_start(argp, fmt); - Logv(LogConfigGlobal(), level, fmt, argp); - va_end(argp); -} diff --git a/src/Memory.c b/src/Memory.c deleted file mode 100644 index 09963fd..0000000 --- a/src/Memory.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include -#include - -#ifndef MEMORY_TABLE_CHUNK -#define MEMORY_TABLE_CHUNK 256 -#endif - -#ifndef MEMORY_HEXDUMP_WIDTH -#define MEMORY_HEXDUMP_WIDTH 16 -#endif - -struct MemoryInfo -{ - size_t size; - const char *file; - int line; - void *pointer; -}; - -static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; -static void (*hook) (MemoryAction, MemoryInfo *, void *) = NULL; -static void *hookArgs = NULL; - -static MemoryInfo **allocations = NULL; -static size_t allocationsSize = 0; -static size_t allocationsLen = 0; - -static size_t -MemoryHash(void *p) -{ - return (((size_t) p) >> 2 * 7) % allocationsSize; -} - -static int -MemoryInsert(MemoryInfo * a) -{ - size_t hash; - - if (!allocations) - { - allocationsSize = MEMORY_TABLE_CHUNK; - allocations = calloc(allocationsSize, sizeof(void *)); - if (!allocations) - { - return 0; - } - } - - /* If the next insertion would cause the table to be at least 3/4 - * full, re-allocate and re-hash. */ - if ((allocationsLen + 1) >= ((allocationsSize * 3) >> 2)) - { - size_t i; - size_t tmpAllocationsSize = allocationsSize; - MemoryInfo **tmpAllocations; - - allocationsSize += MEMORY_TABLE_CHUNK; - tmpAllocations = calloc(allocationsSize, sizeof(void *)); - - if (!tmpAllocations) - { - return 0; - } - - for (i = 0; i < tmpAllocationsSize; i++) - { - if (allocations[i]) - { - hash = MemoryHash(allocations[i]->pointer); - - while (tmpAllocations[hash]) - { - hash = (hash + 1) % allocationsSize; - } - - tmpAllocations[hash] = allocations[i]; - } - } - - free(allocations); - allocations = tmpAllocations; - } - - hash = MemoryHash(a->pointer); - - while (allocations[hash]) - { - hash = (hash + 1) % allocationsSize; - } - - allocations[hash] = a; - allocationsLen++; - - return 1; -} - -static void -MemoryDelete(MemoryInfo * a) -{ - size_t hash = MemoryHash(a->pointer); - size_t count = 0; - - while (count <= allocationsSize) - { - if (allocations[hash] && allocations[hash] == a) - { - allocations[hash] = NULL; - allocationsLen--; - return; - } - else - { - hash = (hash + 1) % allocationsSize; - count++; - } - } -} - -void * -MemoryAllocate(size_t size, const char *file, int line) -{ - void *p; - MemoryInfo *a; - - pthread_mutex_lock(&lock); - - p = malloc(size); - if (!p) - { - pthread_mutex_unlock(&lock); - return NULL; - } - - a = malloc(sizeof(MemoryInfo)); - if (!a) - { - free(p); - pthread_mutex_unlock(&lock); - return NULL; - } - - a->size = size; - a->file = file; - a->line = line; - a->pointer = p; - - if (!MemoryInsert(a)) - { - free(a); - free(p); - pthread_mutex_unlock(&lock); - return NULL; - } - - if (hook) - { - hook(MEMORY_ALLOCATE, a, hookArgs); - } - - pthread_mutex_unlock(&lock); - return p; -} - -void * -MemoryReallocate(void *p, size_t size, const char *file, int line) -{ - MemoryInfo *a; - void *new = NULL; - - if (!p) - { - return MemoryAllocate(size, file, line); - } - - a = MemoryInfoGet(p); - if (a) - { - pthread_mutex_lock(&lock); - new = realloc(a->pointer, size); - if (new) - { - MemoryDelete(a); - a->size = size; - a->file = file; - a->line = line; - - a->pointer = new; - MemoryInsert(a); - - if (hook) - { - hook(MEMORY_REALLOCATE, a, hookArgs); - } - - } - pthread_mutex_unlock(&lock); - } - else if (hook) - { - a = malloc(sizeof(MemoryInfo)); - if (a) - { - a->size = 0; - a->file = file; - a->line = line; - a->pointer = p; - hook(MEMORY_BAD_POINTER, a, hookArgs); - free(a); - } - } - - - return new; -} - -void -MemoryFree(void *p, const char *file, int line) -{ - MemoryInfo *a; - - if (!p) - { - return; - } - - a = MemoryInfoGet(p); - if (a) - { - pthread_mutex_lock(&lock); - if (hook) - { - a->file = file; - a->line = line; - hook(MEMORY_FREE, a, hookArgs); - } - MemoryDelete(a); - free(a->pointer); - free(a); - - pthread_mutex_unlock(&lock); - } - else if (hook) - { - a = malloc(sizeof(MemoryInfo)); - if (a) - { - a->file = file; - a->line = line; - a->size = 0; - a->pointer = p; - hook(MEMORY_BAD_POINTER, a, hookArgs); - free(a); - } - } -} - -size_t -MemoryAllocated(void) -{ - size_t i; - size_t total = 0; - - pthread_mutex_lock(&lock); - - for (i = 0; i < allocationsSize; i++) - { - if (allocations[i]) - { - total += allocations[i]->size; - } - } - - pthread_mutex_unlock(&lock); - - return total; -} - -void -MemoryFreeAll(void) -{ - size_t i; - - pthread_mutex_lock(&lock); - - for (i = 0; i < allocationsSize; i++) - { - if (allocations[i]) - { - free(allocations[i]->pointer); - free(allocations[i]); - } - } - - free(allocations); - allocations = NULL; - allocationsSize = 0; - allocationsLen = 0; - - pthread_mutex_unlock(&lock); -} - -MemoryInfo * -MemoryInfoGet(void *p) -{ - size_t hash, count; - - pthread_mutex_lock(&lock); - - hash = MemoryHash(p); - - count = 0; - while (count <= allocationsSize) - { - if (!allocations[hash] || allocations[hash]->pointer != p) - { - hash = (hash + 1) % allocationsSize; - count++; - } - else - { - pthread_mutex_unlock(&lock); - return allocations[hash]; - } - } - - pthread_mutex_unlock(&lock); - return NULL; -} - -size_t -MemoryInfoGetSize(MemoryInfo * a) -{ - if (!a) - { - return 0; - } - - return a->size; -} - -const char * -MemoryInfoGetFile(MemoryInfo * a) -{ - if (!a) - { - return NULL; - } - - return a->file; -} - -int -MemoryInfoGetLine(MemoryInfo * a) -{ - if (!a) - { - return -1; - } - - return a->line; -} - -void * -MemoryInfoGetPointer(MemoryInfo * a) -{ - if (!a) - { - return NULL; - } - - return a->pointer; -} - -void - MemoryIterate(void (*iterFunc) (MemoryInfo *, void *), void *args) -{ - size_t i; - - pthread_mutex_lock(&lock); - - for (i = 0; i < allocationsSize; i++) - { - if (allocations[i]) - { - iterFunc(allocations[i], args); - } - } - - pthread_mutex_unlock(&lock); -} - -void - MemoryHook(void (*memHook) (MemoryAction, MemoryInfo *, void *), void *args) -{ - pthread_mutex_lock(&lock); - hook = memHook; - hookArgs = args; - pthread_mutex_unlock(&lock); -} - -void - MemoryHexDump(MemoryInfo * info, void (*printFunc) (size_t, char *, char *, void *), void *args) -{ - char hexBuf[(MEMORY_HEXDUMP_WIDTH * 2) + MEMORY_HEXDUMP_WIDTH + 1]; - char asciiBuf[MEMORY_HEXDUMP_WIDTH + 1]; - size_t pI = 0; - size_t hI = 0; - size_t aI = 0; - const unsigned char *pc; - - if (!info || !printFunc) - { - return; - } - - pc = MemoryInfoGetPointer(info); - - for (pI = 0; pI < MemoryInfoGetSize(info); pI++) - { - if (pI > 0 && pI % MEMORY_HEXDUMP_WIDTH == 0) - { - hexBuf[hI - 1] = '\0'; - asciiBuf[aI] = '\0'; - - printFunc(pI - MEMORY_HEXDUMP_WIDTH, hexBuf, asciiBuf, args); - - snprintf(hexBuf, 4, "%02x ", pc[pI]); - hI = 3; - - asciiBuf[0] = isprint(pc[pI]) ? pc[pI] : '.'; - asciiBuf[1] = '\0'; - aI = 1; - } - else - { - asciiBuf[aI] = isprint(pc[pI]) ? pc[pI] : '.'; - aI++; - - snprintf(hexBuf + hI, 4, "%02x ", pc[pI]); - hI += 3; - } - } - - hexBuf[hI] = '\0'; - hI--; - - while (hI < sizeof(hexBuf) - 2) - { - hexBuf[hI] = ' '; - hI++; - } - - while (aI < sizeof(asciiBuf) - 1) - { - asciiBuf[aI] = ' '; - aI++; - } - - hexBuf[hI] = '\0'; - asciiBuf[aI] = '\0'; - - printFunc(pI - ((pI % MEMORY_HEXDUMP_WIDTH) ? - (pI % MEMORY_HEXDUMP_WIDTH) : MEMORY_HEXDUMP_WIDTH), - hexBuf, asciiBuf, args); - printFunc(pI, NULL, NULL, args); -} diff --git a/src/Queue.c b/src/Queue.c deleted file mode 100644 index dbb0595..0000000 --- a/src/Queue.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include - -struct Queue -{ - void **items; - size_t size; - size_t front; - size_t rear; -}; - -Queue * -QueueCreate(size_t size) -{ - Queue *q; - - if (!size) - { - /* Can't have a queue of length zero */ - return NULL; - } - - q = Malloc(sizeof(Queue)); - if (!q) - { - return NULL; - } - - q->items = Malloc(size * sizeof(void *)); - if (!q->items) - { - Free(q); - return NULL; - } - - q->size = size; - q->front = size + 1; - q->rear = size + 1; - - return q; -} - -void -QueueFree(Queue * q) -{ - if (q) - { - Free(q->items); - } - - Free(q); -} - -int -QueueFull(Queue * q) -{ - if (!q) - { - return 0; - } - - return ((q->front == q->rear + 1) || (q->front == 0 && q->rear == q->size - 1)); -} - -int -QueueEmpty(Queue * q) -{ - if (!q) - { - return 0; - } - - return q->front == q->size + 1; -} - -int -QueuePush(Queue * q, void *element) -{ - if (!q || !element) - { - return 0; - } - - if (QueueFull(q)) - { - return 0; - } - - if (q->front == q->size + 1) - { - q->front = 0; - } - - if (q->rear == q->size + 1) - { - q->rear = 0; - } - else - { - q->rear = (q->rear + 1) % q->size; - } - - q->items[q->rear] = element; - - return 1; -} - -void * -QueuePop(Queue * q) -{ - void *element; - - if (!q) - { - return NULL; - } - - if (QueueEmpty(q)) - { - return NULL; - } - - element = q->items[q->front]; - - if (q->front == q->rear) - { - q->front = q->size + 1; - q->rear = q->size + 1; - } - else - { - q->front = (q->front + 1) % q->size; - } - - return element; -} - -void * -QueuePeek(Queue * q) -{ - if (!q) - { - return NULL; - } - - if (QueueEmpty(q)) - { - return NULL; - } - - return q->items[q->front]; -} diff --git a/src/Rand.c b/src/Rand.c deleted file mode 100644 index 9236e4d..0000000 --- a/src/Rand.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include - -#include -#include -#include - -#define RAND_STATE_VECTOR_LENGTH 624 -#define RAND_STATE_VECTOR_M 397 - -#define RAND_UPPER_MASK 0x80000000 -#define RAND_LOWER_MASK 0x7FFFFFFF -#define RAND_TEMPER_B 0x9D2C5680 -#define RAND_TEMPER_C 0xEFC60000 - -typedef struct RandState -{ - UInt32 mt[RAND_STATE_VECTOR_LENGTH]; - int index; -} RandState; - -static void -RandSeed(RandState * state, UInt32 seed) -{ - state->mt[0] = seed & 0xFFFFFFFF; - - for (state->index = 1; state->index < RAND_STATE_VECTOR_LENGTH; state->index++) - { - state->mt[state->index] = (6069 * state->mt[state->index - 1]) & 0xFFFFFFFF; - } -} - -static UInt32 -RandGenerate(RandState * state) -{ - static const UInt32 mag[2] = {0x0, 0x9908B0DF}; - - UInt32 result; - - if (state->index >= RAND_STATE_VECTOR_LENGTH || state->index < 0) - { - int kk; - - if (state->index >= RAND_STATE_VECTOR_LENGTH + 1 || state->index < 0) - { - RandSeed(state, 4357); - } - - for (kk = 0; kk < RAND_STATE_VECTOR_LENGTH - RAND_STATE_VECTOR_M; kk++) - { - result = (state->mt[kk] & RAND_UPPER_MASK) | (state->mt[kk + 1] & RAND_LOWER_MASK); - state->mt[kk] = state->mt[kk + RAND_STATE_VECTOR_M] ^ (result >> 1) ^ mag[result & 0x1]; - } - - for (; kk < RAND_STATE_VECTOR_LENGTH - 1; kk++) - { - result = (state->mt[kk] & RAND_UPPER_MASK) | (state->mt[kk + 1] & RAND_LOWER_MASK); - state->mt[kk] = state->mt[kk + (RAND_STATE_VECTOR_M - RAND_STATE_VECTOR_LENGTH)] ^ (result >> 1) ^ mag[result & 0x1]; - } - - result = (state->mt[RAND_STATE_VECTOR_LENGTH - 1] & RAND_UPPER_MASK) | (state->mt[0] & RAND_LOWER_MASK); - state->mt[RAND_STATE_VECTOR_LENGTH - 1] = state->mt[RAND_STATE_VECTOR_M - 1] ^ (result >> 1) ^ mag[result & 0x1]; - state->index = 0; - } - - result = state->mt[state->index++]; - result ^= (result >> 11); - result ^= (result << 7) & RAND_TEMPER_B; - result ^= (result << 15) & RAND_TEMPER_C; - result ^= (result >> 18); - - return result; -} - -static void -RandDestructor(void *p) -{ - Free(p); -} - -/* Generate random numbers using rejection sampling. The basic idea is - * to "reroll" if a number happens to be outside the range. However - * this could be extremely inefficient. - * - * Another idea would just be to "reroll" if the generated number ends up - * in the previously "biased" range, and THEN do a modulo. - * - * This would be far more efficient for small values of max, and fixes the - * bias issue. */ - -/* This algorithm therefore computes N random numbers generally in O(N) - * time, while being less biased. */ -void -RandIntN(int *buf, size_t size, unsigned int max) -{ - static pthread_key_t stateKey; - static int createdKey = 0; - - /* Limit the range to banish all previously biased results */ - const int allowed = RAND_MAX - RAND_MAX % max; - - RandState *state; - int tmp; - size_t i; - - if (!createdKey) - { - pthread_key_create(&stateKey, RandDestructor); - createdKey = 1; - } - - state = pthread_getspecific(stateKey); - - if (!state) - { - /* Generate a seed from the system time, PID, and TID */ - UInt32 seed = UtilServerTs() ^ getpid() ^ (unsigned long) pthread_self(); - - state = Malloc(sizeof(RandState)); - RandSeed(state, seed); - - pthread_setspecific(stateKey, state); - } - - /* Generate {size} random numbers. */ - for (i = 0; i < size; i++) - { - /* Most of the time, this will take about 1 loop */ - do - { - tmp = RandGenerate(state); - } while (tmp > allowed); - - buf[i] = tmp % max; - } -} - -/* Generate just 1 random number */ -int -RandInt(unsigned int max) -{ - int val = 0; - - RandIntN(&val, 1, max); - return val; -} diff --git a/src/Sha2.c b/src/Sha2.c deleted file mode 100644 index 613d420..0000000 --- a/src/Sha2.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include -#include -#include - -#include -#include - -#include - -#define GET_UINT32(x) \ - (((UInt32)(x)[0] << 24) | \ - ((UInt32)(x)[1] << 16) | \ - ((UInt32)(x)[2] << 8) | \ - ((UInt32)(x)[3])) - -#define PUT_UINT32(dst, x) { \ - (dst)[0] = (x) >> 24; \ - (dst)[1] = (x) >> 16; \ - (dst)[2] = (x) >> 8; \ - (dst)[3] = (x); \ -} - -#define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) - -#define S0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ (x >> 3)) -#define S1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ (x >> 10)) - -#define T0(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) -#define T1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) - -#define CH(a, b, c) (((a) & (b)) ^ ((~(a)) & (c))) -#define MAJ(a, b, c) (((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))) -#define WW(i) (w[i] = w[i - 16] + S0(w[i - 15]) + w[i - 7] + S1(w[i - 2])) - -#define ROUND(a, b, c, d, e, f, g, h, k, w) { \ - UInt32 tmp0 = h + T0(e) + CH(e, f, g) + k + w; \ - UInt32 tmp1 = T1(a) + MAJ(a, b, c); \ - h = tmp0 + tmp1; \ - d += tmp0; \ -} - -typedef struct Sha256Context -{ - size_t length; - UInt32 state[8]; - size_t bufLen; - unsigned char buffer[64]; -} Sha256Context; - -static void -Sha256Chunk(Sha256Context * context, unsigned char chunk[64]) -{ - const UInt32 rk[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - }; - - UInt32 w[64]; - UInt32 a, b, c, d, e, f, g, h; - - int i; - - for (i = 0; i < 16; i++) - { - w[i] = GET_UINT32(&chunk[4 * i]); - } - - a = context->state[0]; - b = context->state[1]; - c = context->state[2]; - d = context->state[3]; - e = context->state[4]; - f = context->state[5]; - g = context->state[6]; - h = context->state[7]; - - for (i = 0; i < 16; i += 8) - { - ROUND(a, b, c, d, e, f, g, h, rk[i], w[i]); - ROUND(h, a, b, c, d, e, f, g, rk[i + 1], w[i + 1]); - ROUND(g, h, a, b, c, d, e, f, rk[i + 2], w[i + 2]); - ROUND(f, g, h, a, b, c, d, e, rk[i + 3], w[i + 3]); - ROUND(e, f, g, h, a, b, c, d, rk[i + 4], w[i + 4]); - ROUND(d, e, f, g, h, a, b, c, rk[i + 5], w[i + 5]); - ROUND(c, d, e, f, g, h, a, b, rk[i + 6], w[i + 6]); - ROUND(b, c, d, e, f, g, h, a, rk[i + 7], w[i + 7]); - } - - for (i = 16; i < 64; i += 8) - { - ROUND(a, b, c, d, e, f, g, h, rk[i], WW(i)); - ROUND(h, a, b, c, d, e, f, g, rk[i + 1], WW(i + 1)); - ROUND(g, h, a, b, c, d, e, f, rk[i + 2], WW(i + 2)); - ROUND(f, g, h, a, b, c, d, e, rk[i + 3], WW(i + 3)); - ROUND(e, f, g, h, a, b, c, d, rk[i + 4], WW(i + 4)); - ROUND(d, e, f, g, h, a, b, c, rk[i + 5], WW(i + 5)); - ROUND(c, d, e, f, g, h, a, b, rk[i + 6], WW(i + 6)); - ROUND(b, c, d, e, f, g, h, a, rk[i + 7], WW(i + 7)); - } - - context->state[0] += a; - context->state[1] += b; - context->state[2] += c; - context->state[3] += d; - context->state[4] += e; - context->state[5] += f; - context->state[6] += g; - context->state[7] += h; -} - -static void -Sha256Process(Sha256Context * context, unsigned char *data, size_t length) -{ - context->length += length; - - if (context->bufLen && context->bufLen + length >= 64) - { - int len = 64 - context->bufLen; - - memcpy(context->buffer + context->bufLen, data, len); - Sha256Chunk(context, context->buffer); - data += len; - length -= len; - context->bufLen = 0; - } - - while (length >= 64) - { - Sha256Chunk(context, data); - data += 64; - length -= 64; - } - - if (length) - { - memcpy(context->buffer + context->bufLen, data, length); - context->bufLen += length; - } -} - -char * -Sha256(char *str) -{ - Sha256Context context; - size_t i; - unsigned char out[32]; - char *outStr; - - unsigned char fill[64]; - UInt32 fillLen; - unsigned char buf[8]; - UInt32 hiLen; - UInt32 loLen; - - if (!str) - { - return NULL; - } - - outStr = Malloc(65); - if (!outStr) - { - return NULL; - } - - context.state[0] = 0x6a09e667; - context.state[1] = 0xbb67ae85; - context.state[2] = 0x3c6ef372; - context.state[3] = 0xa54ff53a; - context.state[4] = 0x510e527f; - context.state[5] = 0x9b05688c; - context.state[6] = 0x1f83d9ab; - context.state[7] = 0x5be0cd19; - - context.bufLen = 0; - context.length = 0; - memset(context.buffer, 0, 64); - - Sha256Process(&context, (unsigned char *) str, strlen(str)); - - memset(fill, 0, 64); - fill[0] = 0x80; - - fillLen = (context.bufLen < 56) ? 56 - context.bufLen : 120 - context.bufLen; - hiLen = (UInt32) (context.length >> 29); - loLen = (UInt32) (context.length << 3); - - PUT_UINT32(&buf[0], hiLen); - PUT_UINT32(&buf[4], loLen); - - Sha256Process(&context, fill, fillLen); - Sha256Process(&context, buf, 8); - - for (i = 0; i < 8; i++) - { - PUT_UINT32(&out[4 * i], context.state[i]); - } - - /* Convert to string */ - for (i = 0; i < 32; i++) - { - snprintf(outStr + (2 * i), 3, "%02x", out[i]); - } - - return outStr; -} diff --git a/src/Str.c b/src/Str.c deleted file mode 100644 index 8bd8c73..0000000 --- a/src/Str.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -char * -StrUtf8Encode(unsigned long utf8) -{ - char *str; - - str = Malloc(5 * sizeof(char)); - if (!str) - { - return NULL; - } - - if (utf8 <= 0x7F) /* Plain ASCII */ - { - str[0] = (char) utf8; - str[1] = '\0'; - } - else if (utf8 <= 0x07FF) /* 2-byte */ - { - str[0] = (char) (((utf8 >> 6) & 0x1F) | 0xC0); - str[1] = (char) (((utf8 >> 0) & 0x3F) | 0x80); - str[2] = '\0'; - } - else if (utf8 <= 0xFFFF) /* 3-byte */ - { - str[0] = (char) (((utf8 >> 12) & 0x0F) | 0xE0); - str[1] = (char) (((utf8 >> 6) & 0x3F) | 0x80); - str[2] = (char) (((utf8 >> 0) & 0x3F) | 0x80); - str[3] = '\0'; - } - else if (utf8 <= 0x10FFFF) /* 4-byte */ - { - str[0] = (char) (((utf8 >> 18) & 0x07) | 0xF0); - str[1] = (char) (((utf8 >> 12) & 0x3F) | 0x80); - str[2] = (char) (((utf8 >> 6) & 0x3F) | 0x80); - str[3] = (char) (((utf8 >> 0) & 0x3F) | 0x80); - str[4] = '\0'; - } - else - { - /* Send replacement character */ - str[0] = (char) 0xEF; - str[1] = (char) 0xBF; - str[2] = (char) 0xBD; - str[3] = '\0'; - } - - return str; -} - -char * -StrDuplicate(const char *inStr) -{ - size_t len; - char *outStr; - - if (!inStr) - { - return NULL; - } - - len = strlen(inStr); - outStr = Malloc(len + 1); /* For the null terminator */ - if (!outStr) - { - return NULL; - } - - strncpy(outStr, inStr, len + 1); - - return outStr; -} - -char * -StrSubstr(const char *inStr, size_t start, size_t end) -{ - size_t len; - size_t i; - size_t j; - - char *outStr; - - if (!inStr) - { - return NULL; - } - - if (start >= end) - { - return NULL; - } - - len = end - start; - - outStr = Malloc(len + 1); - if (!outStr) - { - return NULL; - } - - j = 0; - for (i = start; i < end; i++) - { - if (inStr[i] == '\0') - { - break; - } - - outStr[j] = inStr[i]; - j++; - } - - outStr[j] = '\0'; - - return outStr; -} - -char * -StrConcat(size_t nStr,...) -{ - va_list argp; - char *str; - char *strp; - size_t strLen = 0; - size_t i; - - va_start(argp, nStr); - for (i = 0; i < nStr; i++) - { - char *argStr = va_arg(argp, char *); - - if (argStr) - { - strLen += strlen(argStr); - } - } - va_end(argp); - - str = Malloc(strLen + 1); - strp = str; - - va_start(argp, nStr); - - for (i = 0; i < nStr; i++) - { - /* Manually copy chars instead of using strcopy() so we don't - * have to call strlen() on the strings again, and we aren't - * writing useless null chars. */ - - char *argStr = va_arg(argp, char *); - - if (argStr) - { - while (*argStr) - { - *strp = *argStr; - strp++; - argStr++; - } - } - } - - va_end(argp); - str[strLen] = '\0'; - return str; -} - -int -StrBlank(const char *str) -{ - int blank = 1; - size_t i = 0; - - while (str[i]) - { - blank &= isspace((unsigned char) str[i]); - /* No need to continue if we don't have a blank */ - if (!blank) - { - break; - } - i++; - } - return blank; -} - -char * -StrRandom(size_t len) -{ - static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - char *str; - int *nums; - size_t i; - - if (!len) - { - return NULL; - } - - str = Malloc(len + 1); - - if (!str) - { - return NULL; - } - - nums = Malloc(len * sizeof(int)); - if (!nums) - { - Free(str); - return NULL; - } - - /* TODO: This seems slow. */ - RandIntN(nums, len, sizeof(charset) - 1); - for (i = 0; i < len; i++) - { - str[i] = charset[nums[i]]; - } - - Free(nums); - str[len] = '\0'; - return str; -} - -char * -StrInt(long i) -{ - char *str; - int len; - - len = snprintf(NULL, 0, "%ld", i); - str = Malloc(len + 1); - if (!str) - { - return NULL; - } - - snprintf(str, len + 1, "%ld", i); - - return str; -} - -int -StrEquals(const char *str1, const char *str2) -{ - /* Both strings are NULL, they're equal */ - if (!str1 && !str2) - { - return 1; - } - - /* One or the other is NULL, they're not equal */ - if (!str1 || !str2) - { - return 0; - } - - /* Neither are NULL, do a regular string comparison */ - return strcmp(str1, str2) == 0; -} diff --git a/src/Stream.c b/src/Stream.c deleted file mode 100644 index 2cb299f..0000000 --- a/src/Stream.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifndef STREAM_RETRIES -#define STREAM_RETRIES 10 -#endif - -#ifndef STREAM_DELAY -#define STREAM_DELAY 2 -#endif - -#define STREAM_EOF (1 << 0) -#define STREAM_ERR (1 << 1) -#define STREAM_TTY (1 << 2) - -struct Stream -{ - Io *io; - - char *rBuf; - size_t rLen; - size_t rOff; - - char *wBuf; - size_t wLen; - - char *ugBuf; - size_t ugSize; - size_t ugLen; - - int flags; - - int fd; -}; - -Stream * -StreamIo(Io * io) -{ - Stream *stream; - - if (!io) - { - return NULL; - } - - stream = Malloc(sizeof(Stream)); - if (!stream) - { - return NULL; - } - - memset(stream, 0, sizeof(Stream)); - stream->io = io; - stream->fd = -1; - - return stream; -} - -Stream * -StreamFd(int fd) -{ - Io *io = IoFd(fd); - Stream *stream; - - if (!io) - { - return NULL; - } - - stream = StreamIo(io); - if (!stream) - { - return NULL; - } - - stream->fd = fd; - - if (isatty(stream->fd)) - { - stream->flags |= STREAM_TTY; - } - - return stream; -} - -Stream * -StreamFile(FILE * fp) -{ - Io *io = IoFile(fp); - Stream *stream; - - if (!io) - { - return NULL; - } - - stream = StreamIo(io); - if (!stream) - { - return NULL; - } - - stream->fd = fileno(fp); - - if (isatty(stream->fd)) - { - stream->flags |= STREAM_TTY; - } - - return stream; -} - -Stream * -StreamOpen(const char *path, const char *mode) -{ - FILE *fp = fopen(path, mode); - - if (!fp) - { - return NULL; - } - - return StreamFile(fp); -} - -Stream * -StreamStdout(void) -{ - static Stream *stdOut = NULL; - - if (!stdOut) - { - stdOut = StreamFd(STDOUT_FILENO); - } - - return stdOut; -} - -Stream * -StreamStderr(void) -{ - static Stream *stdErr = NULL; - - if (!stdErr) - { - stdErr = StreamFd(STDERR_FILENO); - } - - return stdErr; -} - -Stream * -StreamStdin(void) -{ - static Stream *stdIn = NULL; - - if (!stdIn) - { - stdIn = StreamFd(STDIN_FILENO); - } - - return stdIn; -} - -int -StreamClose(Stream * stream) -{ - int ret = 0; - - if (!stream) - { - errno = EBADF; - return EOF; - } - - if (stream->rBuf) - { - Free(stream->rBuf); - } - - if (stream->wBuf) - { - ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen); - - Free(stream->wBuf); - - if (writeRes == -1) - { - ret = EOF; - } - } - - if (stream->ugBuf) - { - Free(stream->ugBuf); - } - - ret = IoClose(stream->io); - Free(stream); - - return ret; -} - -int -StreamVprintf(Stream * stream, const char *fmt, va_list ap) -{ - /* This might look like very similar code to IoVprintf(), but I - * chose not to defer to IoVprintf() because that would require us - * to immediately flush the buffer, since the Io API is unbuffered. - * StreamPuts() uses StreamPutc() under the hood, which is - * buffered. It therefore allows us to finish filling the buffer - * and then only flush it when necessary, preventing superfluous - * writes. */ - - char *buf = NULL; - size_t bufSize = 0; - FILE *fp; - - int ret; - - if (!fmt) - { - return -1; - } - - fp = open_memstream(&buf, &bufSize); - if (!fp) - { - return -1; - } - - ret = vfprintf(fp, fmt, ap); - fclose(fp); - - if (ret >= 0 && stream) - { - if (StreamPuts(stream, buf) < 0) - { - ret = -1; - }; - } - - free(buf); /* Allocated by stdlib, not Memory - * API */ - - return ret; -} - -int -StreamPrintf(Stream * stream, const char *fmt,...) -{ - int ret; - va_list ap; - - va_start(ap, fmt); - ret = StreamVprintf(stream, fmt, ap); - va_end(ap); - - return ret; -} - -int -StreamGetc(Stream * stream) -{ - int c; - - if (!stream) - { - errno = EBADF; - return EOF; - } - - /* Empty the ungetc stack first */ - if (stream->ugLen) - { - c = stream->ugBuf[stream->ugLen - 1]; - stream->ugLen--; - return c; - } - - if (stream->flags & STREAM_EOF) - { - return EOF; - } - - if (!stream->rBuf) - { - /* No buffer allocated yet */ - stream->rBuf = Malloc(IO_BUFFER); - if (!stream->rBuf) - { - stream->flags |= STREAM_ERR; - return EOF; - } - - stream->rOff = 0; - stream->rLen = 0; - } - - if (stream->rOff >= stream->rLen) - { - /* We read through the entire buffer; get a new one */ - ssize_t readRes = IoRead(stream->io, stream->rBuf, IO_BUFFER); - - if (readRes == 0) - { - stream->flags |= STREAM_EOF; - return EOF; - } - - if (readRes == -1) - { - stream->flags |= STREAM_ERR; - return EOF; - } - - stream->rOff = 0; - stream->rLen = readRes; - } - - /* Read the character in the buffer and advance the offset */ - c = stream->rBuf[stream->rOff]; - stream->rOff++; - - return c; -} - -int -StreamUngetc(Stream * stream, int c) -{ - if (!stream) - { - errno = EBADF; - return EOF; - } - - if (!stream->ugBuf) - { - stream->ugSize = IO_BUFFER; - stream->ugBuf = Malloc(stream->ugSize); - - if (!stream->ugBuf) - { - stream->flags |= STREAM_ERR; - return EOF; - } - } - - if (stream->ugLen >= stream->ugSize) - { - char *new; - - stream->ugSize += IO_BUFFER; - new = Realloc(stream->ugBuf, stream->ugSize); - if (!new) - { - stream->flags |= STREAM_ERR; - Free(stream->ugBuf); - stream->ugBuf = NULL; - return EOF; - } - - Free(stream->ugBuf); - stream->ugBuf = new; - } - - stream->ugBuf[stream->ugLen] = c; - stream->ugLen++; - - return c; -} - -int -StreamPutc(Stream * stream, int c) -{ - if (!stream) - { - errno = EBADF; - return EOF; - } - - if (!stream->wBuf) - { - stream->wBuf = Malloc(IO_BUFFER); - if (!stream->wBuf) - { - stream->flags |= STREAM_ERR; - return EOF; - } - } - - if (stream->wLen == IO_BUFFER) - { - /* Buffer full; write it */ - ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen); - - if (writeRes == -1) - { - stream->flags |= STREAM_ERR; - return EOF; - } - - stream->wLen = 0; - } - - stream->wBuf[stream->wLen] = c; - stream->wLen++; - - if (stream->flags & STREAM_TTY && c == '\n') - { - /* Newline encountered on a TTY; write now. This fixes some - * strange behavior on certain TTYs where a newline is written - * to the screen upon flush even when no newline exists in the - * stream. We just flush on newlines, but only if we're - * directly writing to a TTY. */ - ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen); - - if (writeRes == -1) - { - stream->flags |= STREAM_ERR; - return EOF; - } - - stream->wLen = 0; - } - - return c; -} - -int -StreamPuts(Stream * stream, char *str) -{ - int ret = 0; - - if (!stream) - { - errno = EBADF; - return -1; - } - - while (*str) - { - if (StreamPutc(stream, *str) == EOF) - { - ret = -1; - break; - } - - str++; - } - - return ret; -} - -char * -StreamGets(Stream * stream, char *str, int size) -{ - int i; - - if (!stream) - { - errno = EBADF; - return NULL; - } - - if (size <= 0) - { - errno = EINVAL; - return NULL; - } - - for (i = 0; i < size - 1; i++) - { - int c = StreamGetc(stream); - - if (StreamEof(stream) || StreamError(stream)) - { - break; - } - - str[i] = c; - - if (c == '\n') - { - i++; - break; - } - } - - str[i] = '\0'; - - return str; -} - -off_t -StreamSeek(Stream * stream, off_t offset, int whence) -{ - off_t result; - - if (!stream) - { - errno = EBADF; - return -1; - } - - result = IoSeek(stream->io, offset, whence); - if (result < 0) - { - return result; - } - - /* Successful seek; clear the buffers */ - stream->rOff = 0; - stream->wLen = 0; - stream->ugLen = 0; - - return result; -} - -int -StreamEof(Stream * stream) -{ - return stream && (stream->flags & STREAM_EOF); -} - -int -StreamError(Stream * stream) -{ - return stream && (stream->flags & STREAM_ERR); -} - -void -StreamClearError(Stream * stream) -{ - if (stream) - { - stream->flags &= ~STREAM_ERR; - } -} - -int -StreamFlush(Stream * stream) -{ - if (!stream) - { - errno = EBADF; - return EOF; - } - - if (stream->wLen) - { - ssize_t writeRes = IoWrite(stream->io, stream->wBuf, stream->wLen); - - if (writeRes == -1) - { - stream->flags |= STREAM_ERR; - return EOF; - } - - stream->wLen = 0; - } - - return 0; -} - -ssize_t -StreamCopy(Stream * in, Stream * out) -{ - ssize_t nBytes = 0; - int c; - int tries = 0; - int readFlg = 0; - - while (1) - { - c = StreamGetc(in); - - if (StreamEof(in)) - { - break; - } - - if (StreamError(in)) - { - if (errno == EAGAIN) - { - StreamClearError(in); - tries++; - - if (tries >= STREAM_RETRIES || readFlg) - { - break; - } - else - { - UtilSleepMillis(STREAM_DELAY); - continue; - } - } - else - { - break; - } - } - - /* As soon as we've successfully read a byte, treat future - * EAGAINs as EOF, because somebody might have forgotten to - * close their stream. */ - - readFlg = 1; - tries = 0; - - StreamPutc(out, c); - nBytes++; - } - - StreamFlush(out); - return nBytes; -} - -int -StreamFileno(Stream * stream) -{ - return stream ? stream->fd : -1; -} diff --git a/src/Tls.c b/src/Tls.c deleted file mode 100644 index d3ad08f..0000000 --- a/src/Tls.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#ifdef TLS_IMPL - -#include -#include - -Stream * -TlsClientStream(int fd, const char *serverName) -{ - Io *io; - void *cookie; - IoFunctions funcs; - - cookie = TlsInitClient(fd, serverName); - if (!cookie) - { - return NULL; - } - - funcs.read = TlsRead; - funcs.write = TlsWrite; - funcs.seek = NULL; - funcs.close = TlsClose; - - io = IoCreate(cookie, funcs); - if (!io) - { - return NULL; - } - - return StreamIo(io); -} - -Stream * -TlsServerStream(int fd, const char *crt, const char *key) -{ - Io *io; - void *cookie; - IoFunctions funcs; - - cookie = TlsInitServer(fd, crt, key); - if (!cookie) - { - return NULL; - } - - funcs.read = TlsRead; - funcs.write = TlsWrite; - funcs.seek = NULL; - funcs.close = TlsClose; - - io = IoCreate(cookie, funcs); - if (!io) - { - return NULL; - } - - return StreamIo(io); -} - -#endif diff --git a/src/Tls/TlsLibreSSL.c b/src/Tls/TlsLibreSSL.c deleted file mode 100644 index 27b9fa7..0000000 --- a/src/Tls/TlsLibreSSL.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#if TLS_IMPL == TLS_LIBRESSL - -#include -#include - -#include - -#include /* LibreSSL TLS */ - -typedef struct LibreSSLCookie -{ - int fd; - struct tls *ctx; - struct tls *cctx; - struct tls_config *cfg; -} LibreSSLCookie; - -void * -TlsInitClient(int fd, const char *serverName) -{ - LibreSSLCookie *cookie = Malloc(sizeof(LibreSSLCookie)); - - if (!cookie) - { - return NULL; - } - - cookie->ctx = tls_client(); - cookie->cctx = NULL; - cookie->cfg = tls_config_new(); - cookie->fd = fd; - - - if (!cookie->ctx || !cookie->cfg) - { - goto error; - } - - if (tls_config_set_ca_file(cookie->cfg, tls_default_ca_cert_file()) == -1) - { - goto error; - } - - if (tls_configure(cookie->ctx, cookie->cfg) == -1) - { - goto error; - } - - if (tls_connect_socket(cookie->ctx, fd, serverName) == -1) - { - goto error; - } - - if (tls_handshake(cookie->ctx) == -1) - { - goto error; - } - - return cookie; - -error: - if (cookie->ctx) - { - if (tls_error(cookie->ctx)) - { - Log(LOG_ERR, "TlsInitClient(): %s", tls_error(cookie->ctx)); - } - - tls_free(cookie->ctx); - } - - if (cookie->cfg) - { - tls_config_free(cookie->cfg); - } - - Free(cookie); - - return NULL; -} - -void * -TlsInitServer(int fd, const char *crt, const char *key) -{ - LibreSSLCookie *cookie = Malloc(sizeof(LibreSSLCookie)); - - if (!cookie) - { - return NULL; - } - - cookie->ctx = tls_server(); - cookie->cctx = NULL; - cookie->cfg = tls_config_new(); - cookie->fd = fd; - - if (!cookie->ctx || !cookie->cfg) - { - goto error; - } - - if (tls_config_set_cert_file(cookie->cfg, crt) == -1) - { - goto error; - } - - if (tls_config_set_key_file(cookie->cfg, key) == -1) - { - goto error; - } - - if (tls_configure(cookie->ctx, cookie->cfg) == -1) - { - goto error; - } - - if (tls_accept_fds(cookie->ctx, &cookie->cctx, fd, fd) == -1) - { - goto error; - } - - if (tls_handshake(cookie->cctx) == -1) - { - goto error; - } - - return cookie; - -error: - if (cookie->ctx) - { - if (tls_error(cookie->ctx)) - { - Log(LOG_ERR, "TlsInitServer(): %s", tls_error(cookie->ctx)); - } - tls_free(cookie->ctx); - } - - if (cookie->cctx) - { - if (tls_error(cookie->cctx)) - { - Log(LOG_ERR, "TlsInitServer(): %s", tls_error(cookie->cctx)); - } - - tls_free(cookie->cctx); - } - - if (cookie->cfg) - { - tls_config_free(cookie->cfg); - } - - Free(cookie); - - return NULL; -} - -ssize_t -TlsRead(void *cookie, void *buf, size_t nBytes) -{ - LibreSSLCookie *tls = cookie; - struct tls *ctx = tls->cctx ? tls->cctx : tls->ctx; - ssize_t ret = tls_read(ctx, buf, nBytes); - - if (ret == -1) - { - errno = EIO; - } - else if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) - { - errno = EAGAIN; - ret = -1; - } - - return ret; -} - -ssize_t -TlsWrite(void *cookie, void *buf, size_t nBytes) -{ - LibreSSLCookie *tls = cookie; - struct tls *ctx = tls->cctx ? tls->cctx : tls->ctx; - ssize_t ret = tls_write(ctx, buf, nBytes); - - if (ret == -1) - { - errno = EIO; - } - else if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) - { - errno = EAGAIN; - ret = -1; - } - - return ret; -} - -int -TlsClose(void *cookie) -{ - LibreSSLCookie *tls = cookie; - - int tlsRet = tls_close(tls->cctx ? tls->cctx : tls->ctx); - int sdRet; - - if (tls->cctx) - { - tls_free(tls->cctx); - } - - tls_free(tls->ctx); - tls_config_free(tls->cfg); - - sdRet = close(tls->fd); - - Free(tls); - - return (tlsRet == -1 || sdRet == -1) ? -1 : 0; -} - -#endif diff --git a/src/Tls/TlsOpenSSL.c b/src/Tls/TlsOpenSSL.c deleted file mode 100644 index c2e950e..0000000 --- a/src/Tls/TlsOpenSSL.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#if TLS_IMPL == TLS_OPENSSL - -#include -#include - -#include - -#include -#include - -typedef struct OpenSSLCookie -{ - int fd; - const SSL_METHOD *method; - SSL_CTX *ctx; - SSL *ssl; -} OpenSSLCookie; - -static char * -SSLErrorString(int err) -{ - switch (err) - { - case SSL_ERROR_NONE: - return "No error."; - case SSL_ERROR_ZERO_RETURN: - return "The TLS/SSL connection has been closed."; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_CONNECT: - case SSL_ERROR_WANT_ACCEPT: - return "The operation did not complete."; - case SSL_ERROR_WANT_X509_LOOKUP: - return "X509 lookup failed."; - case SSL_ERROR_SYSCALL: - return "I/O Error."; - case SSL_ERROR_SSL: - return "SSL library error."; - } - return NULL; -} - -void * -TlsInitClient(int fd, const char *serverName) -{ - OpenSSLCookie *cookie; - char errorStr[256]; - - /* - * TODO: Seems odd that this isn't needed to make the - * connection... we should figure out how to verify the - * certificate matches the server we think we're - * connecting to. - */ - (void) serverName; - - cookie = Malloc(sizeof(OpenSSLCookie)); - if (!cookie) - { - return NULL; - } - - memset(cookie, 0, sizeof(OpenSSLCookie)); - - cookie->method = TLS_client_method(); - cookie->ctx = SSL_CTX_new(cookie->method); - if (!cookie->ctx) - { - goto error; - } - - cookie->ssl = SSL_new(cookie->ctx); - if (!cookie->ssl) - { - goto error; - } - - if (!SSL_set_fd(cookie->ssl, fd)) - { - goto error; - } - - if (SSL_connect(cookie->ssl) <= 0) - { - goto error; - } - - return cookie; - -error: - Log(LOG_ERR, "TlsClientInit(): %s", ERR_error_string(ERR_get_error(), errorStr)); - - if (cookie->ssl) - { - SSL_shutdown(cookie->ssl); - SSL_free(cookie->ssl); - } - - close(cookie->fd); - - if (cookie->ctx) - { - SSL_CTX_free(cookie->ctx); - } - - return NULL; -} - -void * -TlsInitServer(int fd, const char *crt, const char *key) -{ - OpenSSLCookie *cookie; - char errorStr[256]; - int acceptRet = 0; - - cookie = Malloc(sizeof(OpenSSLCookie)); - if (!cookie) - { - return NULL; - } - - memset(cookie, 0, sizeof(OpenSSLCookie)); - - cookie->method = TLS_server_method(); - cookie->ctx = SSL_CTX_new(cookie->method); - if (!cookie->ctx) - { - Log(LOG_ERR, "TlsInitServer(): Unable to create SSL Context."); - goto error; - } - - if (SSL_CTX_use_certificate_file(cookie->ctx, crt, SSL_FILETYPE_PEM) <= 0) - { - Log(LOG_ERR, "TlsInitServer(): Unable to set certificate file: %s", crt); - goto error; - } - - if (SSL_CTX_use_PrivateKey_file(cookie->ctx, key, SSL_FILETYPE_PEM) <= 0) - { - Log(LOG_ERR, "TlsInitServer(): Unable to set key file."); - goto error; - } - - cookie->ssl = SSL_new(cookie->ctx); - if (!cookie->ssl) - { - Log(LOG_ERR, "TlsInitServer(): Unable to create SSL object."); - goto error; - } - - if (!SSL_set_fd(cookie->ssl, fd)) - { - Log(LOG_ERR, "TlsInitServer(): Unable to set file descriptor."); - goto error; - } - - while ((acceptRet = SSL_accept(cookie->ssl)) <= 0) - { - switch (SSL_get_error(cookie->ssl, acceptRet)) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_CONNECT: - case SSL_ERROR_WANT_ACCEPT: - continue; - default: - Log(LOG_ERR, "TlsInitServer(): Unable to accept connection."); - goto error; - } - } - - return cookie; - -error: - Log(LOG_ERR, "TlsServerInit(): %s", SSLErrorString(SSL_get_error(cookie->ssl, acceptRet))); - Log(LOG_ERR, "TlsServerInit(): %s", ERR_error_string(ERR_get_error(), errorStr)); - - if (cookie->ssl) - { - SSL_shutdown(cookie->ssl); - SSL_free(cookie->ssl); - } - - close(cookie->fd); - - if (cookie->ctx) - { - SSL_CTX_free(cookie->ctx); - } - - Free(cookie); - - return NULL; -} - -ssize_t -TlsRead(void *cookie, void *buf, size_t nBytes) -{ - OpenSSLCookie *ssl = cookie; - int ret = SSL_read(ssl->ssl, buf, nBytes); - - if (ret <= 0) - { - switch (SSL_get_error(ssl->ssl, ret)) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_CONNECT: - case SSL_ERROR_WANT_ACCEPT: - case SSL_ERROR_WANT_X509_LOOKUP: - errno = EAGAIN; - break; - case SSL_ERROR_ZERO_RETURN: - ret = 0; - break; - default: - errno = EIO; - break; - } - ret = -1; - } - - return ret; -} - -ssize_t -TlsWrite(void *cookie, void *buf, size_t nBytes) -{ - OpenSSLCookie *ssl = cookie; - int ret = SSL_write(ssl->ssl, buf, nBytes); - - if (ret <= 0) - { - switch (SSL_get_error(ssl->ssl, ret)) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_CONNECT: - case SSL_ERROR_WANT_ACCEPT: - case SSL_ERROR_WANT_X509_LOOKUP: - errno = EAGAIN; - break; - case SSL_ERROR_ZERO_RETURN: - ret = 0; - break; - default: - errno = EIO; - break; - } - ret = -1; - } - - return ret; -} - -int -TlsClose(void *cookie) -{ - OpenSSLCookie *ssl = cookie; - - SSL_shutdown(ssl->ssl); - SSL_free(ssl->ssl); - close(ssl->fd); - SSL_CTX_free(ssl->ctx); - - Free(ssl); - - return 0; -} - -#endif diff --git a/src/Uri.c b/src/Uri.c deleted file mode 100644 index ca1ad96..0000000 --- a/src/Uri.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include - -#include - -Uri * -UriParse(const char *str) -{ - Uri *uri; - int res; - - if (!str) - { - return NULL; - } - - uri = Malloc(sizeof(Uri)); - if (!uri) - { - return NULL; - } - - memset(uri, 0, sizeof(Uri)); - - res = sscanf(str, "%7[^:]://%127[^:]:%hu%255[^\n]", uri->proto, uri->host, &uri->port, uri->path) == 4 - || sscanf(str, "%7[^:]://%127[^/]%255[^\n]", uri->proto, uri->host, uri->path) == 3 - || sscanf(str, "%7[^:]://%127[^:]:%hu[^\n]", uri->proto, uri->host, &uri->port) == 3 - || sscanf(str, "%7[^:]://%127[^\n]", uri->proto, uri->host) == 2; - - if (!res) - { - Free(uri); - return NULL; - } - - if (!uri->path[0]) - { - strcpy(uri->path, "/"); - } - - return uri; -} - -void -UriFree(Uri * uri) -{ - Free(uri); -} diff --git a/src/Util.c b/src/Util.c deleted file mode 100644 index 8a1b62d..0000000 --- a/src/Util.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#ifndef PATH_MAX -#define PATH_MAX 256 -#endif - -#ifndef SSIZE_MAX -#define SSIZE_MAX LONG_MAX -#endif - -unsigned long -UtilServerTs(void) -{ - struct timeval tv; - unsigned long ts; - - gettimeofday(&tv, NULL); - ts = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); - - return ts; -} - -unsigned long -UtilLastModified(char *path) -{ - struct stat st; - unsigned long ts; - - if (stat(path, &st) == 0) - { - ts = (st.st_mtim.tv_sec * 1000) + (st.st_mtim.tv_nsec / 1000000); - return ts; - } - else - { - return 0; - } -} - -int -UtilMkdir(const char *dir, const mode_t mode) -{ - char tmp[PATH_MAX]; - char *p = NULL; - - struct stat st; - - size_t len; - - len = strnlen(dir, PATH_MAX); - if (!len || len == PATH_MAX) - { - errno = ENAMETOOLONG; - return -1; - } - - memcpy(tmp, dir, len); - tmp[len] = '\0'; - - if (tmp[len - 1] == '/') - { - tmp[len - 1] = '\0'; - } - - if (stat(tmp, &st) == 0 && S_ISDIR(st.st_mode)) - { - return 0; - } - - for (p = tmp + 1; *p; p++) - { - if (*p == '/') - { - *p = 0; - - if (stat(tmp, &st) != 0) - { - if (mkdir(tmp, mode) < 0) - { - /* errno already set by mkdir() */ - return -1; - } - } - else if (!S_ISDIR(st.st_mode)) - { - errno = ENOTDIR; - return -1; - } - - *p = '/'; - } - } - - if (stat(tmp, &st) != 0) - { - if (mkdir(tmp, mode) < 0) - { - /* errno already set by mkdir() */ - return -1; - } - } - else if (!S_ISDIR(st.st_mode)) - { - errno = ENOTDIR; - return -1; - } - - return 0; -} - -int -UtilSleepMillis(long ms) -{ - struct timespec ts; - int res; - - ts.tv_sec = ms / 1000; - ts.tv_nsec = (ms % 1000) * 1000000; - - res = nanosleep(&ts, &ts); - - return res; -} - -ssize_t -UtilGetDelim(char **linePtr, size_t * n, int delim, Stream * stream) -{ - char *curPos, *newLinePtr; - size_t newLinePtrLen; - int c; - - if (!linePtr || !n || !stream) - { - errno = EINVAL; - return -1; - } - - if (*linePtr == NULL) - { - *n = 128; - - if (!(*linePtr = Malloc(*n))) - { - errno = ENOMEM; - return -1; - } - } - - curPos = *linePtr; - - while (1) - { - c = StreamGetc(stream); - - if (StreamError(stream) || (c == EOF && curPos == *linePtr)) - { - return -1; - } - - if (c == EOF) - { - break; - } - - if ((*linePtr + *n - curPos) < 2) - { - if (SSIZE_MAX / 2 < *n) - { -#ifdef EOVERFLOW - errno = EOVERFLOW; -#else - errno = ERANGE; -#endif - return -1; - } - - newLinePtrLen = *n * 2; - - if (!(newLinePtr = Realloc(*linePtr, newLinePtrLen))) - { - errno = ENOMEM; - return -1; - } - - curPos = newLinePtr + (curPos - *linePtr); - *linePtr = newLinePtr; - *n = newLinePtrLen; - } - - *curPos++ = (char) c; - - if (c == delim) - { - break; - } - } - - *curPos = '\0'; - return (ssize_t) (curPos - *linePtr); -} - -ssize_t -UtilGetLine(char **linePtr, size_t * n, Stream * stream) -{ - return UtilGetDelim(linePtr, n, '\n', stream); -} diff --git a/src/include/Array.h b/src/include/Array.h deleted file mode 100644 index 3de66e8..0000000 --- a/src/include/Array.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef TELODENDRIA_ARRAY_H -#define TELODENDRIA_ARRAY_H - -/*** - * @Nm Array - * @Nd A simple dynamic array data structure. - * @Dd November 24 2022 - * @Xr HashMap Queue - * - * These functions implement a simple array data structure that is - * automatically resized as necessary when new values are added. This - * implementation does not actually store the values of the items in it; - * it only stores pointers to the data. As such, you will still have to - * manually maintain all your data. The advantage of this is that these - * functions don't have to copy data, and thus don't care how big the - * data is. Furthermore, arbitrary data can be stored in the array. - * .Pp - * This array implementation is optimized for storage space and - * appending. Deletions are expensive in that all the items of the list - * above a deletion are moved down to fill the hole where the deletion - * occurred. Insertions are also expensive in that all the elements - * above the given index must be shifted up to make room for the new - * element. - * .Pp - * Due to these design choices, this array implementation is best - * suited to linear writing, and then linear or random reading. - */ - -#include -#include - -/** - * The functions in this API operate on an array structure which is - * opaque to the caller. - */ -typedef struct Array Array; - -/** - * Allocate a new array. This function returns a pointer that can be - * used with the other functions in this API, or NULL if there was an - * error allocating memory for the array. - */ -extern Array * ArrayCreate(void); - -/** - * Deallocate an array. Note that this function does not free any of - * the values stored in the array; it is the caller's job to manage the - * memory for each item. Typically, the caller would iterate over all - * the items in the array and free them before freeing the array. - */ -extern void ArrayFree(Array *); - -/** - * Get the size, in number of elements, of the given array. - */ -extern size_t ArraySize(Array *); - -/** - * Get the element at the specified index from the specified array. - * This function will return NULL if the array is NULL, or the index - * is out of bounds. Otherwise, it will return a pointer to a value - * put into the array using - * .Fn ArrayInsert - * or - * .Fn ArraySet . - */ -extern void * ArrayGet(Array *, size_t); - -/** - * Insert the specified element at the specified index in the specified - * array. This function will shift the element currently at that index, - * and any elements after it before inserting the given element. - * .Pp - * This function returns a boolean value indicating whether or not it - * suceeded. - */ -extern int ArrayInsert(Array *, size_t, void *); - -/** - * Set the value at the specified index in the specified array to the - * specified value. This function will return the old value at that - * index, if any. - */ -extern void * ArraySet(Array *, size_t, void *); - -/** - * Append the specified element to the end of the specified array. This - * function uses - * .Fn ArrayInsert - * under the hood to insert an element at the end. It thus has the same - * return value as - * .Fn ArrayInsert . - */ -extern int ArrayAdd(Array *, void *); - -/** - * Remove the element at the specified index from the specified array. - * This function returns the element removed, if any. - */ -extern void * ArrayDelete(Array *, size_t); - -/** - * Sort the specified array using the specified sort function. The - * sort function compares two elements. It takes two void pointers as - * parameters, and returns an integer. The return value indicates to - * the sorting algorithm how the elements relate to each other. A - * return value of 0 indicates that the elements are identical. A - * return value greater than 0 indicates that the first item is - * ``bigger'' than the second item and should thus appear after it in - * the array. A return value less than 0 indicates the opposite: the - * second element should appear after the first in the array. - */ -extern void ArraySort(Array *, int (*) (void *, void *)); - -/** - * If possible, reduce the amount of memory allocated to this array - * by calling - * .Fn Realloc - * on the internal structure to perfectly fit the elements in the - * array. This function is intended to be used by functions that return - * relatively read-only arrays that will be long-lived. - */ -extern int ArrayTrim(Array *); - -/** - * Convert a variadic arguments list into an Array. In most cases, the - * Array API is much easier to work with than - * .Fn va_arg - * and friends. - */ -extern Array * ArrayFromVarArgs(size_t, va_list); - -/** - * Duplicate an existing array. Note that arrays only hold pointers to - * their data, not the data itself, so the duplicated array will point - * to the same places in memory as the original array. - */ -extern Array * ArrayDuplicate(Array *); - -#endif /* TELODENDRIA_ARRAY_H */ diff --git a/src/include/Base64.h b/src/include/Base64.h deleted file mode 100644 index 93509ea..0000000 --- a/src/include/Base64.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef TELODENDRIA_BASE64_H -#define TELODENDRIA_BASE64_H - -/*** - * @Nm Base64 - * @Nd A simple base64 encoder/decoder with unpadded base64 support. - * @Dd September 30 2022 - * @Xr Sha2 - * - * This is an efficient yet simple base64 encoding and decoding API - * that supports regular base64, as well as the Matrix specification's - * extension to base64, called ``unpadded base64.'' This API provides - * the ability to convert between the two, instead of just implementing - * unpadded base64. - */ - -#include - -/** - * This function computes the amount of bytes needed to store a message - * of the specified number of bytes as base64. - */ -extern size_t - Base64EncodedSize(size_t); - -/** - * This function computes the amount of bytes needed to store a decoded - * representation of the encoded message. It takes a pointer to the - * encoded string because it must read a few bytes off the end in order - * to accurately compute the size. - */ -extern size_t - Base64DecodedSize(const char *, size_t); - -/** - * Encode the specified number of bytes from the specified buffer as - * base64. This function returns a string on the heap that should be - * freed with - * .Fn Free , - * or NULL if a memory allocation error ocurred. - */ -extern char * - Base64Encode(const char *, size_t); - -/** - * Decode the specified number of bytes from the specified buffer of - * base64. This function returns a string on the heap that should be - * freed with - * .Fn Free , - * or NULL if a memory allocation error occured. - */ -extern char * - Base64Decode(const char *, size_t); - -/** - * Remove the padding from a specified base64 string. This function - * modifies the specified string in place. It thus has no return value - * because it cannot fail. If the passed pointer is invalid, the - * behavior is undefined. - */ -extern void - Base64Unpad(char *, size_t); - -/** - * Add padding to an unpadded base64 string. This function takes a - * pointer to a pointer because it may be necessary to grow the memory - * allocated to the string. This function returns a boolean value - * indicating whether the pad operation was successful. In practice, - * this means it will only fail if a bigger string is necessary, but it - * could not be automatically allocated on the heap. - */ -extern int - Base64Pad(char **, size_t); - -#endif /* TELODENDRIA_BASE64_H */ diff --git a/src/include/Cron.h b/src/include/Cron.h deleted file mode 100644 index 8cf0336..0000000 --- a/src/include/Cron.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_CRON_H -#define TELODENDRIA_CRON_H - -/*** - * @Nm Cron - * @Nd Basic periodic job scheduler. - * @Dd December 24 2022 - * - * This is an extremely basic job scheduler. So basic, in fact, that - * it currently runs all jobs on a single thread, which means that it - * is intended for short-lived jobs. In the future, it might be - * extended to support a one-thread-per-job model, but for now, jobs - * should take into consideration the fact that they are sharing their - * thread, so they should not be long-running. - * .Pp - * .Nm - * works by ``ticking'' at an interval defined by the caller of - * .Fn CronCreate . - * At each tick, all the registered jobs are queried, and if they are - * due to run again, their function is executed. As much as possible, - * .Nm - * tries to tick at constant intervals, however it is possible that one - * or more jobs may overrun the tick duration. If this happens, - * .Nm - * ticks again immediately after all the jobs for the previous tick - * have completed. This is in an effort to compensate for lost time, - * however it is important to note that when jobs overrun the tick - * interval, the interval is pushed back by the amount that it was - * overrun. Because of this, - * .Nm - * is best suited for scheduling jobs that should happen - * ``aproximately'' every so often; it is not a real-time scheduler - * by any means. - */ - -/** - * All functions defined here operate on a structure opaque to the - * caller. - */ -typedef struct Cron Cron; - -/** - * A job function is a function that takes a void pointer and returns - * nothing. The pointer is passed when the job is scheduled, and - * is expected to remain valid until the job is no longer registered. - * The pointer is passed each time the job executes. - */ -typedef void (JobFunc) (void *); - -/** - * Create a new - * .Nm - * object that all other functions operate on. Like most of the other - * APIs in this project, it must be freed with - * .Fn CronFree - * when it is no longer needed. - * .Pp - * This function takes the tick interval in milliseconds. - */ -extern Cron * - CronCreate(unsigned long); - -/** - * Schedule a one-off job to be executed only at the next tick, and - * then immediately discarded. This is useful for scheduling tasks that - * only have to happen once, or very infrequently depending on - * conditions other than the current time, but don't have to happen - * immediately. The caller simply indicates that it wishes for the task - * to execute at some time in the future. How far into the future this - * practically ends up being is determined by how long it takes for - * other registered jobs to finish, and what the tick interval is. - * .Pp - * This function takes a job function and a pointer to pass to that - * function when it is executed. - */ -extern void - CronOnce(Cron *, JobFunc *, void *); - -/** - * Schedule a repetitive task to be executed at aproximately the given - * interval. As stated above, this is as fuzzy interval; depending on - * the jobs being run and the tick interval, tasks may not execute at - * exactly the scheduled time, but they will eventually execute. - * .Pp - * This function takes an interval in milliseconds, a job function, - * and a pointer to pass to that function when it is executed. - */ -extern void - CronEvery(Cron *, unsigned long, JobFunc *, void *); - -/** - * Start ticking the clock and executing registered jobs. - */ -extern void - CronStart(Cron *); - -/** - * Stop ticking the clock. Jobs that are already executing will - * continue to execute, but when they are finished, no new jobs will - * be executed until - * .Fn CronStart - * is called. - */ -extern void - CronStop(Cron *); - -/** - * Discard all job references and free all memory associated with the - * given - * .Nm Cron - * instance. - */ -extern void - CronFree(Cron *); - -#endif /* TELODENDRIA_CRON_H */ diff --git a/src/include/Db.h b/src/include/Db.h deleted file mode 100644 index 7446264..0000000 --- a/src/include/Db.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_DB_H -#define TELODENDRIA_DB_H - -/*** - * @Nm Db - * @Nd A minimal flat-file database with mutex locking and cache. - * @Dd April 27 2023 - * @Xr Json - * - * Telodendria operates on a flat-file database instead of a - * traditional relational database. This greatly simplifies the - * persistent storage code, and creates a relatively basic API, - * described here. - */ - -#include - -#include -#include - -/** - * All functions in this API operate on a database structure that is - * opaque to the caller. - */ -typedef struct Db Db; - -/** - * When an object is locked, a reference is returned. This reference - * is owned by the current thread, and the database is inaccessible to - * other threads until all references have been returned to the - * database. - * .Pp - * This reference is opaque, but can be manipulated by the functions - * defined here. - */ -typedef struct DbRef DbRef; - -/** - * Open a data directory. This function takes a path to open, and a - * cache size in bytes. If the cache size is 0, then caching is - * disabled and objects are loaded off the disk every time they are - * locked. Otherwise, objects are stored in the cache, and they are - * evicted in a least-recently-used manner. - */ -extern Db * DbOpen(char *, size_t); - -/** - * Close the database. This function will flush anything in the cache - * to the disk, and then close the data directory. It assumes that - * all references have been unlocked. If a reference has not been - * unlocked, undefined behavior results. - */ -extern void DbClose(Db *); - -/** - * Set the maximum cache size allowed before - * .Nm - * starts evicting old objects. If this is set to 0, everything in the - * cache is immediately evicted and caching is disabled. If the - * database was opened with a cache size of 0, setting this will - * initialize the cache, and subsequent calls to - * .Fn DbLock - * will begin caching objects. - */ -extern void DbMaxCacheSet(Db *, size_t); - -/** - * Create a new object in the database with the specified name. This - * function will fail if the object already exists in the database. It - * takes a variable number of C strings, with the exact number being - * specified by the second parameter. These C strings are used to - * generate a filesystem path at which to store the object. These paths - * ensure each object is uniquely identifiable, and provides semantic - * meaning to an object. - */ -extern DbRef * DbCreate(Db *, size_t,...); - -/** - * Lock an existing object in the database. This function will fail - * if the object does not exist. It takes a variable number of C - * strings, with the exact number being specified by the second - * parameter. These C strings are used to generate the filesystem path - * at which to load the object. These paths ensure each object is - * uniquely identifiable, and provides semantic meaning to an object. - */ -extern DbRef * DbLock(Db *, size_t,...); - -/** - * Immediately and permanently remove an object from the database. - * This function assumes the object is not locked, otherwise undefined - * behavior will result. - */ -extern int DbDelete(Db *, size_t,...); - -/** - * Unlock an object and return it back to the database. This function - * immediately syncs the object to the filesystem. The cache is a - * read cache; writes are always immediate to ensure data integrity in - * the event of a system failure. - */ -extern int DbUnlock(Db *, DbRef *); - -/** - * Check the existence of the given database object in a more efficient - * manner than attempting to lock it with - * .Fn DbLock . - * This function does not lock the object, nor does it load it into - * memory if it exists. - */ -extern int DbExists(Db *, size_t,...); - -/** - * List all of the objects at a given path. Unlike the other varargs - * functions, this one does not take a path to a specific object; it - * takes a directory to be iterated, where each path part is its own - * C string. Note that the resulting list only contains the objects - * in the specified directory, it does not list any subdirectories. - * .Pp - * The array returned is an array of C strings containing the object - * name. - */ -extern Array * DbList(Db *, size_t,...); - -/** - * Free the list returned by - * .Fn DbListFree . - */ -extern void DbListFree(Array *); - -/** - * Convert a database reference into JSON that can be manipulated. - * At this time, the database actually stores objects as JSON on the - * disk, so this function just returns an internal pointer, but in the - * future it may have to be generated by decompressing a binary blob, - * or something of that nature. - */ -extern HashMap * DbJson(DbRef *); - -/** - * Free the existing JSON associated with the given reference, and - * replace it with new JSON. This is more efficient than duplicating - * a separate object into the database reference. - */ -extern int DbJsonSet(DbRef *, HashMap *); - -#endif diff --git a/src/include/HashMap.h b/src/include/HashMap.h deleted file mode 100644 index f3b7a83..0000000 --- a/src/include/HashMap.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef TELODENDRIA_HASHMAP_H -#define TELODENDRIA_HASHMAP_H - -/*** - * @Nm HashMap - * @Nd A simple hash map implementation. - * @Dd October 11 2022 - * @Xr Array Queue - * - * This is the public interface for Telodendria's hash map - * implementation. This hash map is designed to be simple, - * well-documented, and generally readable and understandable, yet also - * performant enough to be useful, because it is used extensively - * throughout the project. - * .Pp - * Fundamentally, this is an entirely generic map implementation. It - * can be used for many general purposes, but it is designed only to - * implement the features Telodendria needs to be functional. One - * example of a Telodendria-specific feature is that keys cannot be - * arbitrary data; they are NULL-terminated C strings. - */ - -#include - -/** - * These functions operate on an opaque structure, which the caller - * has no knowledge about. - */ -typedef struct HashMap HashMap; - -/** - * Create a new hash map that is ready to be used with the rest of the - * functions defined here. - */ -extern HashMap * HashMapCreate(void); - -/** - * Free the specified hash map such that it becomes invalid and any - * future use results in undefined behavior. Note that this function - * does not free the values stored in the hash map, but since it stores - * the keys internally, it will free the keys. You should use - * .Fn HashMapIterate - * to free the values stored in this map appropriately before calling - * this function. - */ -extern void HashMapFree(HashMap *); - -/** - * Control the maximum load of the hash map before it is expanded. - * When the hash map reaches the given capacity, it is grown. You - * don't want to only grow hash maps when they are full, because that - * makes them perform very poorly. The maximum load value is a - * percentage of how full the hash map is, and it should be between - * 0 and 1, where 0 means that no elements will cause the map to be - * expanded, and 1 means that the hash map must be completely full - * before it is expanded. The default maximum load on a new hash map - * is 0.75, which should be good enough for most purposes, however, - * this function exists specifically so that the maximum load can be - * fine-tuned. - */ -extern void HashMapMaxLoadSet(HashMap *, float); - -/** - * Use a custom hashing function with the given hash map. New hash - * maps have a sane hashing function that should work okay for most - * use cases, but if you have a better hashing function, it can be - * specified this way. Do not change the hash function after keys have - * been added; doing so results in undefined behavior. Only set a new - * hash function immediately after constructing a new hash map, before - * anything has been added to it. - * .Pp - * The hash function takes a pointer to a C string, and is expected - * to return a fairly unique numerical hash value which will be - * converted into an array index. - */ -extern void -HashMapFunctionSet(HashMap *, unsigned long (*) (const char *)); - -/** - * Set the given string key to the given value. Note that the key is - * copied into the hash map's own memory space, but the value is not. - * It is the caller's job to ensure that the value pointer remains - * valid for the life of the hash map, and are freed when no longer - * needed. - */ -extern void * HashMapSet(HashMap *, char *, void *); - -/** - * Retrieve the value for the given key, or return NULL if no such - * key exists in the hash map. - */ -extern void * HashMapGet(HashMap *, const char *); - -/** - * Remove a value specified by the given key from the hash map, and - * return it to the caller to deal with. This function returns NULL - * if no such key exists. - */ -extern void * HashMapDelete(HashMap *, const char *); - -/** - * Iterate over all the keys and values of a hash map. This function - * works very similarly to - * .Xr getopt 3 , - * where calls are repeatedly made in a while loop until there are no - * more items to go over. The difference is that this function does not - * rely on globals; it takes pointer pointers, and stores all - * necessary state inside the hash map itself. - * .Pp - * Note that this function is not thread-safe; two threads cannot be - * iterating over any given hash map at the same time, though they - * can each be iterating over different hash maps. - * .Pp - * This function can be tricky to use in some scenarios, as it - * continues where it left off on each call, until there are no more - * elements to go through in the hash map. If you are not iterating - * over the entire map in one go, and happen to break the loop, then - * the next time you attempt to iterate the hash map, you'll start - * somewhere in the middle, which is most likely not the intended - * behavior. Thus, it is always recommended to iterate over the entire - * hash map if you're going to use this function. - * .Pp - * Also note that the behavior of this function is undefined if - * insertions or deletions occur during the iteration. This - * functionality has not been tested, and will likely not work. - */ -extern int HashMapIterate(HashMap *, char **, void **); - -/** - * A reentrant version of - * .Fn HashMapIterate - * that allows the caller to overcome the flaws of that function by - * storing the cursor outside of the hash map structure itself. This - * allows multiple threads to iterate over the same hash map at the - * same time, and it allows the iteration to be halted midway through - * without causing any unintended side effects. - * .Pp - * The cursor should be initialized to 0 at the start of iteration. - */ -extern int -HashMapIterateReentrant(HashMap *, char **, void **, size_t *); - -#endif /* TELODENDRIA_HASHMAP_H */ diff --git a/src/include/HeaderParser.h b/src/include/HeaderParser.h deleted file mode 100644 index c5a7370..0000000 --- a/src/include/HeaderParser.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_HEADERPARSER_H -#define TELODENDRIA_HEADERPARSER_H - -/*** - * @Nm HeaderParser - * @Nd Parse simple C header files. - * @Dd April 29 2023 - * - * .Nm - * is an extremely simple parser that lacks most of the functionality - * one would expect from a C code parser. It simply maps a stream - * of tokens into a few categories, parsing major ``milestones'' in - * a header, without actually understanding any of the details. - * .Pp - * This exists because it is used to generate man pages from headers. - * See - * .Xr hdoc 1 - * for example usage of this parser. - */ - -#include -#include - -#define HEADER_EXPR_MAX 4096 - -/** - * Headers are parsed as expressions. These are the expressions that - * this parser recognizes. - */ -typedef enum HeaderExprType -{ - HP_COMMENT, - HP_PREPROCESSOR_DIRECTIVE, - HP_TYPEDEF, - HP_DECLARATION, - HP_GLOBAL, - HP_UNKNOWN, - HP_SYNTAX_ERROR, - HP_PARSE_ERROR, - HP_EOF -} HeaderExprType; - -/** - * A representation of a function declaration. - */ -typedef struct HeaderDeclaration -{ - char returnType[64]; - char name[32]; /* Enforced by ANSI C */ - Array *args; -} HeaderDeclaration; - -/** - * A global variable declaration. The type must be of the same size - * as the function declaration's return type due to the way parsing - * them is implemented. - */ -typedef struct HeaderGlobal -{ - char type[64]; - char name[HEADER_EXPR_MAX - 64]; -} HeaderGlobal; - -/** - * A representation of a single header expression. Note that that state - * structure is entirely internally managed, so it should not be - * accessed or manipulated by functions outside the functions defined - * here. - * .Pp - * The type field should be used to determine which field in the data - * union is valid. - */ -typedef struct HeaderExpr -{ - HeaderExprType type; - union - { - char text[HEADER_EXPR_MAX]; - HeaderDeclaration declaration; - HeaderGlobal global; - struct - { - int lineNo; - char *msg; - } error; - } data; - - struct - { - Stream *stream; - int lineNo; - } state; -} HeaderExpr; - -/** - * Parse the next expression into the given header expression structure. - * To parse an entire C header, this function should be called in a - * loop until the type of the expression is HP_EOF. - */ -extern void HeaderParse(Stream *, HeaderExpr *); - -#endif /* TELODENDRIA_HEADERPARSER_H */ diff --git a/src/include/Http.h b/src/include/Http.h deleted file mode 100644 index 233ba93..0000000 --- a/src/include/Http.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_HTTP_H -#define TELODENDRIA_HTTP_H - -/*** - * @Nm Http - * @Nd Encode and decode various parts of the HTTP protocol. - * @Dd March 12 2023 - * @Xr HttpClient HttpServer HashMap Queue Memory - * - * .Nm - * is a collection of utility functions and type definitions that are - * useful for dealing with HTTP. HTTP is not a complex protocol, but - * this API makes it a lot easier to work with. - * .Pp - * Note that this API doesn't target any particular HTTP version, but - * it is currently used with HTTP 1.0 clients and servers, and - * therefore may be lacking functionality added in later HTTP versions. - */ - -#include - -#include -#include - -#define HTTP_FLAG_NONE 0 -#define HTTP_FLAG_TLS (1 << 0) - -/** - * The request methods defined by the HTTP standard. These numeric - * constants should be preferred to strings when building HTTP APIs - * because they are more efficient. - */ -typedef enum HttpRequestMethod -{ - HTTP_METHOD_UNKNOWN, - HTTP_GET, - HTTP_HEAD, - HTTP_POST, - HTTP_PUT, - HTTP_DELETE, - HTTP_CONNECT, - HTTP_OPTIONS, - HTTP_TRACE, - HTTP_PATCH -} HttpRequestMethod; - -/** - * An enumeration that corresponds to the actual integer values of the - * valid HTTP response codes. - */ -typedef enum HttpStatus -{ - HTTP_STATUS_UNKNOWN = 0, - - /* Informational responses */ - HTTP_CONTINUE = 100, - HTTP_SWITCHING_PROTOCOLS = 101, - HTTP_EARLY_HINTS = 103, - - /* Successful responses */ - HTTP_OK = 200, - HTTP_CREATED = 201, - HTTP_ACCEPTED = 202, - HTTP_NON_AUTHORITATIVE_INFORMATION = 203, - HTTP_NO_CONTENT = 204, - HTTP_RESET_CONTENT = 205, - HTTP_PARTIAL_CONTENT = 206, - - /* Redirection messages */ - HTTP_MULTIPLE_CHOICES = 300, - HTTP_MOVED_PERMANENTLY = 301, - HTTP_FOUND = 302, - HTTP_SEE_OTHER = 303, - HTTP_NOT_MODIFIED = 304, - HTTP_TEMPORARY_REDIRECT = 307, - HTTP_PERMANENT_REDIRECT = 308, - - /* Client error messages */ - HTTP_BAD_REQUEST = 400, - HTTP_UNAUTHORIZED = 401, - HTTP_FORBIDDEN = 403, - HTTP_NOT_FOUND = 404, - HTTP_METHOD_NOT_ALLOWED = 405, - HTTP_NOT_ACCEPTABLE = 406, - HTTP_PROXY_AUTH_REQUIRED = 407, - HTTP_REQUEST_TIMEOUT = 408, - HTTP_CONFLICT = 409, - HTTP_GONE = 410, - HTTP_LENGTH_REQUIRED = 411, - HTTP_PRECONDITION_FAILED = 412, - HTTP_PAYLOAD_TOO_LARGE = 413, - HTTP_URI_TOO_LONG = 414, - HTTP_UNSUPPORTED_MEDIA_TYPE = 415, - HTTP_RANGE_NOT_SATISFIABLE = 416, - HTTP_EXPECTATION_FAILED = 417, - HTTP_TEAPOT = 418, - HTTP_UPGRADE_REQUIRED = 426, - HTTP_PRECONDITION_REQUIRED = 428, - HTTP_TOO_MANY_REQUESTS = 429, - HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, - HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, - - /* Server error responses */ - HTTP_INTERNAL_SERVER_ERROR = 500, - HTTP_NOT_IMPLEMENTED = 501, - HTTP_BAD_GATEWAY = 502, - HTTP_SERVICE_UNAVAILABLE = 503, - HTTP_GATEWAY_TIMEOUT = 504, - HTTP_VERSION_NOT_SUPPORTED = 505, - HTTP_VARIANT_ALSO_NEGOTIATES = 506, - HTTP_NOT_EXTENDED = 510, - HTTP_NETWORK_AUTH_REQUIRED = 511 -} HttpStatus; - -/** - * Convert an HTTP status enumeration value into a string description - * of the status, which is to be used in server response to a client, - * or a client response to a user. For example, calling - * .Fn HttpStatusToString "HTTP_GATEWAY_TIMEOUT" - * (or - * .Fn HttpStatusToString "504" ) - * produces the string "Gateway Timeout". Note that the returned - * pointers point to static space, so their manipulation is forbidden. - */ -extern const char * HttpStatusToString(const HttpStatus); - -/** - * Convert a string into a numeric code that can be used throughout - * the code of a program in an efficient manner. See the definition - * of HttpRequestMethod. This function does case-sensitive matching, - * and does not trim or otherwise process the input string. - */ -extern HttpRequestMethod HttpRequestMethodFromString(const char *); - -/** - * Convert a numeric code as defined by HttpRequestMethod into a - * string that can be sent to a server. Note that the returned pointers - * point to static space, so their manipulation is forbidden. - */ -extern const char * HttpRequestMethodToString(const HttpRequestMethod); - -/** - * Encode a C string such that it can safely appear in a URL by - * performing the necessary percent escaping. A new string on the - * heap is returned. It should be freed with - * .Fn Free , - * defined in the - * .Xr Memory 3 - * API. - */ -extern char * HttpUrlEncode(char *); - -/** - * Decode a percent-encoded string into a C string, ignoring encoded - * null characters entirely, because those would do nothing but cause - * problems. - */ -extern char * HttpUrlDecode(char *); - -/** - * Decode an encoded parameter string in the form of - * ``key=val&key2=val2'' into a hash map whose values are C strings. - * This function properly decodes keys and values using the functions - * defined above. - */ -extern HashMap * HttpParamDecode(char *); - -/** - * Encode a hash map whose values are strings as an HTTP parameter - * string suitable for GET or POST requests. - */ -extern char * HttpParamEncode(HashMap *); - -/** - * Read HTTP headers from a stream and return a hash map whose values - * are strings. All keys are lowercased to make querying them - * consistent and not dependent on the case that was read from the - * stream. This is useful for both client and server code, since the - * headers are in the same format. This function should be used after - * parsing the HTTP status line, because it does not parse that line. - * It will stop when it encounters the first blank line, which - * indicates that the body is beginning. After this function completes, - * the body may be immediately read from the stream without any - * additional processing. - */ -extern HashMap * HttpParseHeaders(Stream *); - -#endif diff --git a/src/include/HttpClient.h b/src/include/HttpClient.h deleted file mode 100644 index 5da1b8f..0000000 --- a/src/include/HttpClient.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_HTTPCLIENT_H -#define TELODENDRIA_HTTPCLIENT_H - -/*** - * @Nm HttpClient - * @Nd Extremely simple HTTP client. - * @Dd April 29 2023 - * @Xr Http HttpServer Tls - * - * .Nm - * HttpClient - * builds on the HTTP API to provide a simple yet functional HTTP - * client. It aims at being easy to use and minimal, yet also - * efficient. - */ - -#include - -#include -#include - -/** - * A server response is represented by a client context. It is - * opaque, so the functions defined in this API should be used to - * fetch data from and manipulate it. - */ -typedef struct HttpClientContext HttpClientContext; - -/** - * Make an HTTP request. This function takes the request method, - * any flags defined in the HTTP API, the port, hostname, and path, - * all in that order. It returns NULL if there was an error making - * the request. Otherwise it returns a client context. Note that this - * function does not actually send any data, it simply makes the - * connection. Use - * .Fn HttpRequestHeader - * to add headers to the request. Then, send headers with - * .Fn HttpRequestSendHeaders . - * Finally, the request body, if any, can be written to the output - * stream, and then the request can be fully sent using - * .Fn HttpRequestSend . - */ -extern HttpClientContext * - HttpRequest(HttpRequestMethod, int, unsigned short, char *, char *); - -/** - * Set a request header to send to the server when making the - * request. - */ -extern void HttpRequestHeader(HttpClientContext *, char *, char *); - -/** - * Send the request headers to the server. This must be called before - * the request body can be written or a response body can be read. - */ -extern void HttpRequestSendHeaders(HttpClientContext *); - -/** - * Flush the request stream to the server. This function should be - * called before the response body is read. - */ -extern HttpStatus HttpRequestSend(HttpClientContext *); - -/** - * Get the headers returned by the server. - */ -extern HashMap * HttpResponseHeaders(HttpClientContext *); - -/** - * Get the stream used to write the request body and read the - * response body. - */ -extern Stream * HttpClientStream(HttpClientContext *); - -/** - * Free all memory associated with the client context. This closes the - * connection, if it was still open. - */ -extern void HttpClientContextFree(HttpClientContext *); - -#endif /* TELODENDRIA_HTTPCLIENT_H */ diff --git a/src/include/HttpRouter.h b/src/include/HttpRouter.h deleted file mode 100644 index bd8b74f..0000000 --- a/src/include/HttpRouter.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_HTTPROUTER_H -#define TELODENDRIA_HTTPROUTER_H - -/*** - * @Nm HttpRouter - * @Nd Simple HTTP request router with regular expression support. - * @Dd April 29 2023 - * @Xr HttpServer Http - * - * .Nm - * provides a simple mechanism for assigning functions to an HTTP - * request path. It is a simple tree data structure that parses the - * registered request paths and maps functions onto each part of the - * path. Then, requests can be easily routed to their appropriate - * handler functions. - */ - -#include - -/** - * The router structure is opaque and thus managed entirely by the - * functions defined in this API. - */ -typedef struct HttpRouter HttpRouter; - -/** - * A function written to handle an HTTP request takes an array - * consisting of the matched path parts in the order they appear in - * the path, and a pointer to caller-provided arguments, if any. - * It returns a pointer that the caller is assumed to know how to - * handle. - */ -typedef void *(HttpRouteFunc) (Array *, void *); - -/** - * Create a new empty routing tree. - */ -extern HttpRouter * HttpRouterCreate(void); - -/** - * Free all the memory associated with the given routing tree. - */ -extern void HttpRouterFree(HttpRouter *); - -/** - * Register the specified route function to be executed upon requests - * for the specified HTTP path. The path is parsed by splitting at - * each path separator. Each part of the path is a regular expression - * that matches the entire path part. A regular expression cannot - * match more than one path part. This allows for paths like - * .Pa /some/path/(.*)/parts - * to work as one would expect. - */ -extern int HttpRouterAdd(HttpRouter *, char *, HttpRouteFunc *); - -/** - * Route the specified request path using the specified routing - * tree. This function will parse the path and match it to the - * appropriate route handler function. The return value is a boolean - * value that indicates whether or not an appropriate route function - * was found. If an appropriate function was found, then the void - * pointer is passed to it as arguments that it is expected to know - * how to handle, and the pointer to a void pointer is where the - * route function's response will be placed. - */ -extern int HttpRouterRoute(HttpRouter *, char *, void *, void **); - -#endif /* TELODENDRIA_HTTPROUTER_H */ diff --git a/src/include/HttpServer.h b/src/include/HttpServer.h deleted file mode 100644 index 37c6ef1..0000000 --- a/src/include/HttpServer.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_HTTPSERVER_H -#define TELODENDRIA_HTTPSERVER_H - -/*** - * @Nm HttpServer - * @Nd Extremely simple HTTP server. - * @Dd December 13 2022 - * @Xr Http HttpClient - * - * .Nm - * builds on the - * .Xr Http 3 - * API, and provides a very simple, yet very functional API for - * creating an HTTP server. It aims at being easy to use and minimal, - * yet also efficient. It uses non-blocking I/O, is fully - * multi-threaded, and is very configurable. It can be set up in just - * two function calls and minimal supporting code. - * .Pp - * This API should be familar to those that have dealt with the HTTP - * server libraries of other programming languages, particularly Java. - * In fact, much of the terminology used in this API came from Java, - * and you'll notice that the way responses are sent and received very - * closely resembles Java. - */ - -#include - -#include - -#include -#include - -/** - * The functions on this API operate on an opaque structure. - */ -typedef struct HttpServer HttpServer; - -/** - * Each request receives a context structure. It is opaque, so the - * functions defined in this API should be used to fetch data from - * it. These functions allow the handler to figure out the context of - * the request, which includes the path requested, any parameters, - * and the headers and method used to make the request. The context - * also provides the means by which the handler responds to the - * request, allowing it to set the status code, headers, and body. - */ -typedef struct HttpServerContext HttpServerContext; - -/** - * The request handler function is executed when an HTTP request is - * received. It takes a request context, and a pointer as specified - * in the server configuration. - */ -typedef void (HttpHandler) (HttpServerContext *, void *); - -/** - * The number of arguments to - * .Fn HttpServerCreate - * has grown so large that arguments are now stuffed into a - * configuration structure, which is in turn passed to - * .Fn HttpServerCreate . - * This configuration is copied by value into the internal - * structures of the server. It is copied with very minimal - * validation, so ensure that all values are sensible. It may - * make sense to use - * .Fn memset - * to zero out everything in here before assigning values. - */ -typedef struct HttpServerConfig -{ - unsigned short port; - unsigned int threads; - unsigned int maxConnections; - - int flags; /* Http(3) flags */ - char *tlsCert; /* File path */ - char *tlsKey; /* File path */ - - HttpHandler *handler; - void *handlerArgs; -} HttpServerConfig; - -/** - * Create a new HTTP server using the specified configuration. - * This will set up all internal structures used by the server, - * and bind the socket and start listening for connections. However, - * it will not start accepting connections. - */ -extern HttpServer * HttpServerCreate(HttpServerConfig *); - -/** - * Retrieve the configuration that was used to instantiate the given - * server. Note that this configuration is not necessarily the exact - * one that was provided; even though its values are the same, it - * should be treated as an entirely separate configuration with no - * connection to the original. - */ -extern HttpServerConfig * HttpServerConfigGet(HttpServer *); - -/** - * Free the resources associated with the given HTTP server. Note that - * the server can only be freed after it has been stopped. Calling this - * function while the server is still running results in undefined - * behavior. - */ -extern void HttpServerFree(HttpServer *); - -/** - * Attempt to start the HTTP server, and return immediately with the - * status. This API is fully multi-threaded and asynchronous, so the - * caller can continue working while the HTTP server is running in a - * separate thread and managing a pool of threads to handle responses. - */ -extern int HttpServerStart(HttpServer *); - -/** - * Typically, at some point after calling - * .Fn HttpServerStart , - * the program will have no more work to do, so it will want to wait - * for the HTTP server to finish. This is accomplished via this - * function, which joins the HTTP worker thread to the calling thread, - * pausing the calling thread until the HTTP server has stopped. - */ -extern void HttpServerJoin(HttpServer *); - -/** - * Stop the HTTP server. Only the execution of this function will - * cause the proper shutdown of the HTTP server. If the main program - * is joined to the HTTP thread, then either another thread or a - * signal handler will have to stop the server using this function. - * The typical use case is to install a signal handler that executes - * this function on a global HTTP server. - */ -extern void HttpServerStop(HttpServer *); - -/** - * Get the request headers for the request represented by the given - * context. The data in the returned hash map should be treated as - * read only and should not be freed; it is managed entirely by the - * server. - */ -extern HashMap * HttpRequestHeaders(HttpServerContext *); - -/** - * Get the request method used to make the request represented by - * the given context. - */ -extern HttpRequestMethod HttpRequestMethodGet(HttpServerContext *); - -/** - * Get the request path for the request represented by the given - * context. The return value of this function should be treated as - * read-only, and should not be freed; it is managed entirely by the - * server. - */ -extern char * HttpRequestPath(HttpServerContext *); - -/** - * Retrieve the parsed GET parameters for the request represented by - * the given context. The returned hash map should be treated as - * read-only, and should not be freed; it is managed entirely by the - * server. - */ -extern HashMap * HttpRequestParams(HttpServerContext *); - -/** - * Set a response header to return to the client. The old value for - * the given header is returned, if any, otherwise NULL is returned. - */ -extern char * HttpResponseHeader(HttpServerContext *, char *, char *); - -/** - * Set the response status to return to the client. - */ -extern void HttpResponseStatus(HttpServerContext *, HttpStatus); - -/** - * Get the current response status that will be sent to the client - * making the request represented by the given context. - */ -extern HttpStatus HttpResponseStatusGet(HttpServerContext *); - -/** - * Send the response headers to the client that made the request - * represented by the specified context. This function must be called - * before the response body can be written, otherwise a malformed - * response will be sent. - */ -extern void HttpSendHeaders(HttpServerContext *); - -/** - * Get a stream that is both readable and writable. Reading from the - * stream reads the request body that the client sent, if there is one. - * Note that the rquest headers have already been read, so the stream - * is correctly positioned at the beginning of the body of the request. - * .Fn HttpSendHeaders - * must be called before the stream is written, otherwise a malformed - * HTTP response will be sent. An HTTP handler should properly set all - * the headers it itends to send, send those headers, and then write - * the response body to this stream. - * .Pp - * Note that the stream does not need to be closed by the HTTP - * handler; in fact doing so results in undefined behavior. The stream - * is managed entirely by the server itself, so it will close it when - * necessary. This allows the underlying protocol to differ: for - * instance, an HTTP/1.1 connection may stay for multiple requests and - * responses. - */ -extern Stream * HttpServerStream(HttpServerContext *); - -#endif /* TELODENDRIA_HTTPSERVER_H */ diff --git a/src/include/Int.h b/src/include/Int.h deleted file mode 100644 index 2b29bac..0000000 --- a/src/include/Int.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_INT_H -#define TELODENDRIA_INT_H - -/*** - * @Nm Int - * @Nd Fixed-width integer types. - * @Dd April 27 2023 - * - * This header provides cross-platform, fixed-width integer types. - * Specifically, it uses preprocessor magic to define the following - * types: - * .Bl -bullet -offset indent - * .It - * Int8 and UInt8 - * .It - * Int16 and UInt16 - * .It - * Int32 and UInt32 - * .El - * .Pp - * Note that there is no 64-bit integer type, because the ANSI C - * standard makes no guarantee that such a type will exist, even - * though it does on most platforms. - * .Pp - * The reason Telodendria provides its own header for this is - * because ANSI C does not define fixed-width types, and while it - * should be safe to rely on C99 fixed-width types in most cases, - * there may be cases where even that is not possible. - * - * @ignore-typedefs - */ - -#include - -#define BIT64_MAX 18446744073709551615 -#define BIT32_MAX 4294967295 -#define BIT16_MAX 65535 -#define BIT8_MAX 255 - -#ifndef UCHAR_MAX -#error Size of char data type is unknown. Define UCHAR_MAX. -#endif - -#ifndef USHRT_MAX -#error Size of short data type is unknown. Define USHRT_MAX. -#endif - -#ifndef UINT_MAX -#error Size of int data type is unknown. Define UINT_MAX. -#endif - -#ifndef ULONG_MAX -#error Size of long data type is unknown. Define ULONG_MAX. -#endif - -#if UCHAR_MAX == BIT8_MAX -typedef signed char Int8; -typedef unsigned char UInt8; - -#else -#error Unable to determine suitable data type for 8-bit integers. -#endif - -#if UINT_MAX == BIT16_MAX -typedef signed int Int16; -typedef unsigned int UInt16; - -#elif USHRT_MAX == BIT16_MAX -typedef signed short Int16; -typedef unsigned short UInt16; - -#elif UCHAR_MAX == BIT16_MAX -typedef signed char Int16; -typedef unsigned char UInt16; - -#else -#error Unable to determine suitable data type for 16-bit integers. -#endif - -#if ULONG_MAX == BIT32_MAX -typedef signed long Int32; -typedef unsigned long UInt32; - -#elif UINT_MAX == BIT32_MAX -typedef signed int Int32; -typedef unsigned int UInt32; - -#elif USHRT_MAX == BIT32_MAX -typedef signed short Int32; -typedef unsigned short UInt32; - -#elif UCHAR_MAX == BIT32_MAX -typedef signed char Int32; -typedef unsigned char UInt32; - -#else -#error Unable to determine suitable data type for 32-bit integers. -#endif - -/* The ANSI C standard only guarantees a data size of up to 32 bits. */ - -#endif diff --git a/src/include/Io.h b/src/include/Io.h deleted file mode 100644 index e722890..0000000 --- a/src/include/Io.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_IO_H -#define TELODENDRIA_IO_H - -/*** - * @Nm Io - * @Nd Source/sink-agnostic I/O for implementing custom streams. - * @Dd April 29 2023 - * @Xr Stream Tls - * - * Many systems provide platform-specific means of implementing custom - * streams using file pointers. However, POSIX does not define a way - * of programmatically creating custom streams. - * .Nm - * therefore fills this gap in POSIX by mimicking all of the - * functionality of these platform-specific functions, but in pure - * POSIX C. It defines a number of callback funtions to be executed - * in place of the standard POSIX I/O functions, which are used to - * implement arbitrary streams that may not be to a file or socket. - * Additionally, streams can now be pipelined; the sink of one stream - * may be the source of another lower-level stream. Additionally, all - * streams, regardless of their source or sink, share the same API, so - * streams can be handled in a much more generic manner. This allows - * the HTTP client and server libraries to seemlessly support TLS and - * plain connections without having to handle each separately. - * .Pp - * .Nm - * was heavily inspired by GNU's - * .Fn fopencookie - * and BSD's - * .Fn funopen . - * It aims to combine the best of both of these functions into a single - * API that is intuitive and easy to use. - */ - -#include -#include -#include -#include - -#ifndef IO_BUFFER -#define IO_BUFFER 4096 -#endif - -/** - * An opaque structure analogous to a POSIX file descriptor. - */ -typedef struct Io Io; - -/** - * Read input from the source of a stream. This function should - * attempt to read the specified number of bytes of data from the - * given cookie into the given buffer. It should behave identically - * to the POSIX - * .Xr read 2 - * system call, except instead of using an integer descriptor as the - * first parameter, a pointer to an implementation-defined cookie - * stores any information the function needs to read from the source. - */ -typedef ssize_t (IoReadFunc) (void *, void *, size_t); - -/** - * Write output to a sink. This function should attempt to write the - * specified number of bytes of data from the given buffer into the - * stream described by the given cookie. It should behave identically - * to the POSIX - * .Xr write 2 - * system call, except instead of using an integer descriptor as the - * first parameter, a pointer to an implementation-defined cookie - * stores any information the function needs to write to the sink. - */ -typedef ssize_t (IoWriteFunc) (void *, void *, size_t); - -/** - * Repositions the offset of the stream described by the specified - * cookie. This function should behave identically to the POSIX - * .Xr lseek 2 - * system call, except instead of using an integer descriptor as the - * first parameter, a pointer to an implementation-defined cookie - * stores any information the function needs to seek the stream. - */ -typedef off_t (IoSeekFunc) (void *, off_t, int); - -/** - * Close the given stream, making future reads or writes result in - * undefined behavior. This function should also free all memory - * associated with the cookie. It should behave identically to the - * .Xr close 2 - * system call, except instead of using an integer descriptor for the - * parameter, a pointer to an implementation-defined cookie stores any - * information the function needs to close the stream. - */ -typedef int (IoCloseFunc) (void *); - -/** - * A simple mechanism for grouping together a set of stream functions, - * to be passed to - * .Fn IoCreate . - */ -typedef struct IoFunctions -{ - IoReadFunc *read; - IoWriteFunc *write; - IoSeekFunc *seek; - IoCloseFunc *close; -} IoFunctions; - -/** - * Create a new stream using the specified cookie and the specified - * I/O functions. - */ -extern Io * IoCreate(void *, IoFunctions); - -/** - * Read the specified number of bytes from the specified stream into - * the specified buffer. This calls the stream's underlying IoReadFunc, - * which should behave identically to the POSIX - * .Xr read 2 - * system call. - */ -extern ssize_t IoRead(Io *, void *, size_t); - -/** - * Write the specified number of bytes from the specified stream into - * the specified buffer. This calls the stream's underlying - * IoWriteFunc, which should behave identically to the POSIX - * .Xr write 2 - * system call. - */ -extern ssize_t IoWrite(Io *, void *, size_t); - -/** - * Seek the specified stream using the specified offset and whence - * value. This calls the stream's underlying IoSeekFunc, which should - * behave identically to the POSIX - * .Xr lseek 2 - * system call. - */ -extern off_t IoSeek(Io *, off_t, int); - -/** - * Close the specified stream. This calls the stream's underlying - * IoCloseFunc, which should behave identically to the POSIX - * .Xr close 2 - * system call. - */ -extern int IoClose(Io *); - -/** - * Print a formatted string to the given stream. This is a - * re-implementation of the standard library function - * .Xr vfprintf 3 , - * and behaves identically. - */ -extern int IoVprintf(Io *, const char *, va_list); - -/** - * Print a formatted string to the given stream. This is a - * re-implementation of the standard library function - * .Xr fprintf 3 , - * and behaves identically. - */ -extern int IoPrintf(Io *, const char *,...); - -/** - * Read all the bytes from the first stream and write them to the - * second stream. Neither stream is closed upon the completion of this - * function. This can be used for quick and convenient buffered - * copying of data from one stream into another. - */ -extern ssize_t IoCopy(Io *, Io *); - -/** - * Wrap a POSIX file descriptor to take advantage of this API. The - * common use case for this function is when a regular file descriptor - * needs to be accessed by an application that uses this API to also - * access non-POSIX streams. - */ -extern Io * IoFd(int); - -/** - * Open or create a file for reading or writing. The specified file - * name is opened for reading or writing as specified by the given - * flags and mode. This function is a simple convenience wrapper around - * the POSIX - * .Xr open 2 - * system call that passes the opened file descriptor into - * .Fn IoFd . - */ -extern Io * IoOpen(const char *, int, mode_t); - -/** - * Wrap a standard C file pointer to take advantage of this API. The - * common use case for this function is when a regular C file pointer - * needs to be accessed by an application that uses this API to also - * access custom streams. - */ -extern Io * IoFile(FILE *); - -#endif /* TELODENDRIA_IO_H */ diff --git a/src/include/Json.h b/src/include/Json.h deleted file mode 100644 index e2442dd..0000000 --- a/src/include/Json.h +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef TELODENDRIA_JSON_H -#define TELODENDRIA_JSON_H - -/*** - * @Nm Json - * @Nd A fully-featured JSON API. - * @Dd March 12 2023 - * @Xr HashMap Array Stream - * - * .Nm - * is a fully-featured JSON API for C using the array and hash map - * APIs. It can parse JSON, ans serialize an in-memory structure to - * JSON. It build on the foundation of Array and HashMap because that's - * all JSON really is, just arrays and maps. - * .Nm - * also provides a structure for encapsulating an arbitrary value and - * identifying its type, making it easy for a strictly-typed language - * like C to work with loosely-typed JSON data. - * .Nm - * is very strict and tries to adhere as closely as possible to the - * proper definition of JSON. It will fail on syntax errors of any - * kind, which is fine for a Matrix homeserver that can just return - * M_BAD_JSON if anything in here fails, but this behavior may not be - * suitable for other purposes. - * .Pp - * This JSON implementation focuses primarily on serialization and - * deserialization to and from streams. It does not provide facilities - * for handling JSON strings; it only writes JSON to output streams, - * and reads them from input streams. Of course, you can use the - * POSIX - * .Xr fmemopen 3 - * and - * .Xr open_memstream 3 - * functions if you want to deal with JSON strings, but JSON is - * intended to be an exchange format. Data should be converted to JSON - * right when it is leaving the program, and converted from JSON to the - * in-memory format as soon as it is coming in. - * .Pp - * JSON objects are represented as hash maps consisting entirely of - * JsonValue structures, and arrays are represented as arrays - * consisting entirely of JsonValue structures. When generating a - * JSON object, any attempt to stuff a value into a hash map or array - * without first encoding it as a JsonValue will result in undefined - * behavior. - */ - -#include -#include -#include - -#include -#include - -#define JSON_DEFAULT -1 -#define JSON_PRETTY 0 - -/** - * This opaque structure encapsulates all the possible types that can - * be stored in JSON. It is managed entirely by the functions defined - * in this API. It is important to note that strings, integers, floats, - * booleans, and the NULL value are all stored by value, but objects - * and arrays are stored by reference. That is, it doesn't store these - * itself, just pointers to them, however, the data - * .Em is - * freed when using - * .Fn JsonFree . - */ -typedef struct JsonValue JsonValue; - -/** - * These are the types that can be used to identify a JsonValue - * and act on it accordingly. - */ -typedef enum JsonType -{ - JSON_NULL, /* Maps to a C NULL */ - JSON_OBJECT, /* Maps to a HashMap of JsonValues */ - JSON_ARRAY, /* Maps to an Array of JsonValues */ - JSON_STRING, /* Maps to a null-terminated C string */ - JSON_INTEGER, /* Maps to a C long */ - JSON_FLOAT, /* Maps to a C double */ - JSON_BOOLEAN /* Maps to a C integer of either 0 or 1 */ -} JsonType; - -/** - * Determine the type of the specified JSON value. - */ -extern JsonType JsonValueType(JsonValue *); - -/** - * Encode a JSON object as a JSON value that can be added to another - * object, or an array. - */ -extern JsonValue * JsonValueObject(HashMap *); - -/** - * Unwrap a JSON value that represents an object. This function will - * return NULL if the value is not actually an object. - */ -extern HashMap * JsonValueAsObject(JsonValue *); - -/** - * Encode a JSON array as a JSON value that can be added to an object - * or another array. - */ -extern JsonValue * JsonValueArray(Array *); - -/** - * Unwrap a JSON value that represents an array. This function will - * return NULL if the value is not actually an array. - */ -extern Array * JsonValueAsArray(JsonValue *); - -/** - * Encode a C string as a JSON value that can be added to an object or - * an array. - */ -extern JsonValue * JsonValueString(char *); - -/** - * Unwrap a JSON value that represents a string. This function will - * return NULL if the value is not actually a string. - */ -extern char * JsonValueAsString(JsonValue *); - -/** - * Encode a number as a JSON value that can be added to an object or - * an array. - */ -extern JsonValue * JsonValueInteger(long); - -/** - * Unwrap a JSON value that represents a number. This function will - * return 0 if the value is not actually a number, which may be - * misleading. Check the type of the value before making assumptions - * about its value. - */ -extern long JsonValueAsInteger(JsonValue *); - -/** - * Encode a floating point number as a JSON value that can be added - * to an object or an array. - */ -extern JsonValue * JsonValueFloat(double); - -/** - * Unwrap a JSON value that represents a floating point number. This - * function will return 0 if the value is not actually a floating - * point number, which may be misleading. Check the type of the value - * before making assumptions about its type. - */ -extern double JsonValueAsFloat(JsonValue *); - -/** - * Encode a C integer according to the way C treats integers in boolean - * expressions as a JSON value that can be added to an object or an - * array. - */ -extern JsonValue * JsonValueBoolean(int); - -/** - * Unwrap a JSON value that represents a boolean. This function will - * return 0 if the value is not actually a boolean, which may be - * misleading. Check the type of the value before making assumptions - * about its type. - */ -extern int JsonValueAsBoolean(JsonValue *); - -/** - * This is a special case that represents a JSON null. Because the - * Array and HashMap APIs do not accept NULL values, this function - * should be used to represent NULL in JSON. Even though a small - * amount of memory is allocated just to be a placeholder for nothing, - * this keeps the APIs clean. - */ -extern JsonValue * JsonValueNull(void); - -/** - * Free the memory being used by a JSON value. Note that this will - * recursively free all Arrays, HashMaps, and other JsonValues that are - * reachable from the given value, including any strings attached to - * this value. - */ -extern void JsonValueFree(JsonValue *); - -/** - * Recursively duplicate the given JSON value. This returns a new - * JSON value that is completely identical to the specified value, but - * in no way connected to it. - */ -extern JsonValue * JsonValueDuplicate(JsonValue *); - -/** - * Recursively duplicate the given JSON object. This returns a new - * JSON object that is completely identical to the specified object, - * but in no way connect to it. - */ -extern HashMap * JsonDuplicate(HashMap *); - -/** - * Recursively free a JSON object by iterating over all of its values - * and freeing them using - * .Fn JsonValueFree . - */ -extern void JsonFree(HashMap *); - -/** - * Encode the given string in such a way that it can be safely - * embedded in a JSON stream. This entails: - * .Bl -bullet -offset indent - * .It - * Escaping quotes, backslashes, and other special characters using - * their backslash escape. - * .It - * Encoding bytes that are not UTF-8 using escapes. - * .It - * Wrapping the entire string in double quotes. - * .El - * .Pp - * This function is only provided via the public - * .Nm - * API so that it is accessible to custom JSON encoders, such as the - * CanonicalJson encoder. This will typically be used for encoding - * object keys; to encode values, just use - * .Fn JsonEncodeValue . - * .Pp - * This function returns the number of bytes written to the stream, - * or if the stream is NULL, the number of bytes that would have - * been written. - */ -extern int JsonEncodeString(const char *, Stream *); - -/** - * Serialize a JSON value as it would appear in JSON output. This is - * a recursive function that also encodes all child values reachable - * from the given value. This function is exposed via the public - * .Nm - * API so that it is accessible to custom JSON encoders. Normal users - * that are not writing custom encoders should in most cases just use - * .Fn JsonEncode - * to encode an entire object. - * .Pp - * The third parameter is an integer that represents the indent level - * of the value to be printed, or a negative number if pretty-printing - * should be disabled and JSON should be printed as minimized as - * possible. To pretty-print a JSON object, set this to - * .Va JSON_PRETTY . - * To get minified output, set it to - * .Va JSON_DEFAULT . - * .Pp - * This function returns the number of bytes written to the stream, - * or if the stream is NULL, the number of bytes that would have - * been written. - */ -extern int JsonEncodeValue(JsonValue *, Stream *, int); - -/** - * Encode a JSON object as it would appear in JSON output, writing it - * to the given output stream. This function is recursive; it will - * serialize everything accessible from the passed object. The third - * parameter has the same behavior as described above. - * .Pp - * This function returns the number of bytes written to the stream, - * or if the stream is NULL, the number of bytes that would have - * been written. - */ -extern int JsonEncode(HashMap *, Stream *, int); - -/** - * Decode a JSON object from the given input stream and parse it into - * a hash map of JSON values. - */ -extern HashMap * JsonDecode(Stream *); - -/** - * A convenience function that allows the caller to retrieve and - * arbitrarily deep keys within a JSON object. It takes a root JSON - * object, the number of levels deep to go, and then that number of - * keys as a varargs list. All keys must have objects as values, with - * the exception of the last one, which is the value that will be - * returned. Otherwise, NULL indicates the specified path doas not - * exist. - */ -extern JsonValue * JsonGet(HashMap *, size_t,...); - -/** - * A convenience function that allows the caller to set arbitrarily - * deep keys within a JSON object. It takes a root JSON object, the - * number of levels deep to go, and then that number of keys as a - * varargs list. All keys must have object as values, with the - * exception of the last one, which is the value that will be set. - * The value currently at that key, if any, will be returned. - * This function will create any intermediate objects as necessary to - * set the proper key. - */ -extern JsonValue * JsonSet(HashMap *, JsonValue *, size_t,...); - -#endif /* TELODENDRIA_JSON_H */ diff --git a/src/include/Log.h b/src/include/Log.h deleted file mode 100644 index ea10dd1..0000000 --- a/src/include/Log.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef TELODENDRIA_LOG_H -#define TELODENDRIA_LOG_H - -/*** - * @Nm Log - * @Nd A simple logging framework for logging to multiple destinations. - * @Dd April 27 2023 - * @Xr Stream - * - * .Nm - * is a simple C logging library that allows for colorful outputs, - * timestamps, and custom log levels. It also features the ability to - * have multiple logs open at one time, although Telodendria primarily - * utilizes the global log. All logs are thread safe. - */ - -#include -#include -#include - -#include - -#define LOG_FLAG_COLOR (1 << 0) -#define LOG_FLAG_SYSLOG (1 << 1) - -/** - * A log is defined as a configuration that describes the properties - * of the log. This opaque structure can be manipulated by the - * functions defined in this API. - */ -typedef struct LogConfig LogConfig; - -/** - * Create a new log configuration with sane defaults that can be used - * immediately with the logging functions. - */ -extern LogConfig * LogConfigCreate(void); - -/** - * Get the global log configuration, creating a new one with - * .Fn LogConfigCreate - * if necessary. - */ -extern LogConfig * LogConfigGlobal(void); - -/** - * Free the given log configuration. Note that this does not close the - * underlying stream associated with the log, if there is one. Also - * note that to avoid memory leaks, the global log configuration must - * also be freed, but it cannot be used after it is freed. - */ -extern void LogConfigFree(LogConfig *); - -/** - * Set the current log level on the specified log configuration. - * This indicates that only messages at or above this level should be - * logged; all others are silently discarded. The passed log level - * should be one of the log levels defined by - * .Xr syslog 3 . - * Refer to that page for a complete list of acceptable log levels, - * and note that passing an invalid log level will result in undefined - * behavior. - */ -extern void LogConfigLevelSet(LogConfig *, int); - -/** - * Cause the log output to be indented two more spaces than it was - * previously. This can be helpful when generating stack traces or - * other hierarchical output. This is a simple convenience wrapper - * around - * .Fn LogConfigIndentSet . - */ -extern void LogConfigIndent(LogConfig *); - -/** - * Cause the log output to be indented two less spaces than it was - * previously. This is a simple convenience wrapper around - * .Fn LogConfigIndentSet . - */ -extern void LogConfigUnindent(LogConfig *); - -/** - * Indent future log output using the specified config by some - * arbitrary amount. - */ -extern void LogConfigIndentSet(LogConfig *, size_t); - -/** - * Set the file stream that logging output should be written to. This - * defaults to standard output, but it can be set to standard error, - * or any other arbitrary stream. Passing a NULL value for the stream - * pointer sets the log output to the standard output. Note that the - * output stream is only used if - * .Va LOG_FLAG_SYSLOG - * is not set. - */ -extern void LogConfigOutputSet(LogConfig *, Stream *); - -/** - * Set a number of boolean options on a log configuration. This - * function uses bitwise operators, so multiple options can be set with - * a single function call using bitwise OR operators. The flags are - * defined as preprocessor macros, and are as follows: - * .Bl -tag -width Ds - * .It LOG_FLAG_COLOR - * When set, enable color-coded output on TTYs. Note that colors are - * implemented as ANSI escape sequences, and are not written to file - * streams that are not actually connected to a TTY, to prevent those - * sequences from being written to a file. - * .Xr isatty 3 - * is checked before writing any terminal sequences. - * .It LOG_FLAG_SYSLOG - * When set, log output to the syslog using - * .Xr syslog 3 , - * instead of logging to the file set by - * .Fn LogConfigOutputSet . - * This flag always overrides the stream set by that function, - * regardless of when it was set, even if it was set after this flag - * was set. - * .El - */ -extern void LogConfigFlagSet(LogConfig *, int); - -/** - * Clear a boolean flag from the specified log format. See above for - * the list of flags. - */ -extern void LogConfigFlagClear(LogConfig *, int); - -/** - * Set a custom timestamp to be prepended to each message if the - * output is not going to the system log. Consult your system's - * documentation for - * .Xr strftime 3 . - * A value of NULL disables the timestamp output before messages. - */ -extern void LogConfigTimeStampFormatSet(LogConfig *, char *); - -/** - * This function does the actual logging of messages using a - * specified configuration. It takes the configuration, the log - * level, a format string, and then a list of arguments, all in that - * order. This function only logs messages if their level is above - * or equal to the currently configured log level, making it easy to - * turn some messages on or off. - * .Pp - * This function has the same usage as - * .Xr vprintf 3 . - * Consult that page for the list of format specifiers and their - * arguments. This function is typically not used directly, see the - * other log functions for the most common use cases. - */ -extern void Logv(LogConfig *, int, const char *, va_list); - -/** - * Log a message using - * .Fn Logv . - * with the specified configuration. This function has the same usage - * as - * .Xr printf 3 . - */ -extern void LogTo(LogConfig *, int, const char *, ...); - -/** - * Log a message to the global log using - * .Fn Logv . - * This function has the same usage as - * .Xr printf 3 . - */ -extern void Log(int, const char *, ...); - -#endif diff --git a/src/include/Memory.h b/src/include/Memory.h deleted file mode 100644 index 40318a9..0000000 --- a/src/include/Memory.h +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_MEMORY_H -#define TELODENDRIA_MEMORY_H - -/*** - * @Nm Memory - * @Nd Smart memory management. - * @Dd January 9 2023 - * - * .Nm - * is an API that allows for smart memory management and profiling. It - * wraps the standard library functions - * .Xr malloc 3 , - * .Xr realloc 3 , - * and - * .Xr free 3 , - * and offers identical semantics, while providing functionality that - * the standard library doesn't have, such as getting statistics on the - * total memory allocated on the heap, and getting the size of a block - * given a pointer. Additionally, thanks to preprocessor macros, the - * exact file and line number at which an allocation, re-allocation, or - * free occured can be obtained given a pointer. Finally, all the - * blocks allocated on the heap can be iterated and evaluated, and a - * callback function can be executed every time a memory operation - * occurs. - * .Pp - * In the future, this API could include a garbage collector that - * automatically frees memory it detects as being no longer in use. - * However, this feature does not yet exist. - * .Pp - * A number of macros are available, which make the - * .Nm - * API much easier to use. They are as follows: - * .Bl -bullet -offset indent - * .It - * .Fn Malloc "x" - * .It - * .Fn Realloc "x" "y" - * .It - * .Fn Free "x" - * .El - * .Pp - * These macros expand to - * .Fn MemoryAllocate , - * .Fn MemoryReallocate , - * and - * .Fn MemoryFree - * with the second and third parameters set to __FILE__ and __LINE__. - * This allows - * .Nm - * to be used exactly how the standard library functions would be - * used. In fact, the functions to which these macros expand are not - * intended to be used directly; for the best results, use these - * macros. - */ -#include - -/** - * These values are passed into the memory hook function to indicate - * the action that just happened. - */ -typedef enum MemoryAction -{ - MEMORY_ALLOCATE, - MEMORY_REALLOCATE, - MEMORY_FREE, - MEMORY_BAD_POINTER -} MemoryAction; - -#define Malloc(x) MemoryAllocate(x, __FILE__, __LINE__) -#define Realloc(x, s) MemoryReallocate(x, s, __FILE__, __LINE__) -#define Free(x) MemoryFree(x, __FILE__, __LINE__) - -/** - * The memory information is opaque, but can be accessed using the - * functions defined by this API. - */ -typedef struct MemoryInfo MemoryInfo; - -/** - * Allocate the specified number of bytes on the heap. This function - * has the same semantics as - * .Xr malloc 3 , - * except that it takes the file name and line number at which the - * allocation occurred. - */ -extern void * MemoryAllocate(size_t, const char *, int); - -/** - * Change the size of the object pointed to by the given pointer - * to the given number of bytes. This function has the same semantics - * as - * .Xr realloc 3 , - * except that it takes the file name and line number at which the - * reallocation occurred. - */ -extern void * MemoryReallocate(void *, size_t, const char *, int); - -/** - * Free the memory at the given pointer. This function has the same - * semantics as - * .Xr free 3 , - * except that it takes the file name and line number at which the - * free occurred. - */ -extern void MemoryFree(void *, const char *, int); - -/** - * Get the total number of bytes that the program has allocated on the - * heap. This operation iterates over all heap allocations made with - * .Fn MemoryAllocate - * and then returns a total count, in bytes. - */ -extern size_t MemoryAllocated(void); - -/** - * Iterate over all heap allocations made with - * .Fn MemoryAllocate - * and call - * .Fn MemoryFree - * on them. This function immediately invalidates all pointers to - * blocks on the heap, and any subsequent attempt to read or write to - * data on the heap will result in undefined behavior. This is - * typically called at the end of the program, just before exit. - */ -extern void MemoryFreeAll(void); - -/** - * Fetch information about an allocation. This function takes a raw - * pointer, and if - * . Nm - * knows about the pointer, it returns a structure that can be used - * to obtain information about the block of memory that the pointer - * points to. - */ -extern MemoryInfo * MemoryInfoGet(void *); - -/** - * Get the size in bytes of the block of memory represented by the - * specified memory info structure. - */ -extern size_t MemoryInfoGetSize(MemoryInfo *); - -/** - * Get the file name in which the block of memory represented by the - * specified memory info structure was allocated. - */ -extern const char * MemoryInfoGetFile(MemoryInfo *); - -/** - * Get the line number on which the block of memory represented by the - * specified memory info structure was allocated. - */ -extern int MemoryInfoGetLine(MemoryInfo *); - -/** - * Get a pointer to the block of memory represented by the specified - * memory info structure. - */ -extern void * MemoryInfoGetPointer(MemoryInfo *); - -/** - * This function takes a pointer to a function that takes the memory - * info structure, as well as a void pointer for caller-provided - * arguments. It iterates over all the heap memory currently allocated - * at the time of calling, executing the function on each allocation. - */ -extern void MemoryIterate(void (*) (MemoryInfo *, void *), void *); - -/** - * Specify a function to be executed whenever a memory operation - * occurs. The MemoryAction argument specifies the operation that - * occurred on the block of memory represented by the memory info - * structure. The function also takes a void pointer to caller-provided - * arguments. - */ -extern void MemoryHook(void (*) (MemoryAction, MemoryInfo *, void *), void *); - -/** - * Read over the block of memory represented by the given memory info - * structure and generate a hexadecimal and ASCII string for each - * chunk of the block. This function takes a callback function that - * takes the following parameters in order: - * .Bl -bullet -offset indent - * .It - * The current offset from the beginning of the block of memory in - * bytes. - * .It - * A null-terminated string containing the next 16 bytes of the block - * encoded as space-separated hex values. - * .It - * A null-terminated string containing the ASCII representation of the - * same 16 bytes of memory. This ASCII representation is safe to print - * to a terminal or other text device, because non-printable characters - * are encoded as a . (period). - * .It - * Caller-passed pointer. - * .El - */ -extern void -MemoryHexDump(MemoryInfo *, void (*) (size_t, char *, char *, void *), void *); - -#endif diff --git a/src/include/Queue.h b/src/include/Queue.h deleted file mode 100644 index d3973e1..0000000 --- a/src/include/Queue.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_QUEUE_H -#define TELODENDRIA_QUEUE_H - -/*** - * @Nm Queue - * @Nd A simple static queue data structure. - * @Dd November 25 2022 - * @Xr Array HashMap - * - * .Nm - * implements a simple queue data structure that is statically sized. - * This implementation does not actually store the values of the items - * in it; it only stores pointers to the data. As such, you will have - * to manually maintain data and make sure it remains valid as long as - * it is in the queue. The advantage of this is that - * .Nm - * doesn't have to copy data, and thus doesn't care how big the data - * is. Furthermore, any arbitrary data can be stored in the queue. - * .Pp - * This queue implementation operates on the heap. It is a circular - * queue, and it does not grow as it is used. Once the size is set, - * the queue never gets any bigger. - */ - -#include - -/** - * These functions operate on a queue structure that is opaque to the - * caller. - */ -typedef struct Queue Queue; - -/** - * Allocate a new queue that is able to store the specified number of - * items in it. - */ -extern Queue * QueueCreate(size_t); - -/** - * Free the memory associated with the specified queue structure. Note - * that this function does not free any of the values stored in the - * queue; it is the caller's job to manage memory for each item. - * Typically, the caller would dequeue all the items in the queue and - * deal with them before freeing the queue itself. - */ -extern void QueueFree(Queue *); - -/** - * Push an element into the queue. This function returns a boolean - * value indicating whether or not the push succeeded. Pushing items - * into the queue will fail if the queue is full. - */ -extern int QueuePush(Queue *, void *); - -/** - * Pop an element out of the queue. This function returns NULL if the - * queue is empty. Otherwise, it returns a pointer to the item that is - * next up in the queue. - */ -extern void * QueuePop(Queue *); - -/** - * Retrieve a pointer to the item that is next up in the queue without - * actually discarding it, such that the next call to - * .Fn QueuePeek - * or - * .Fn QueuePop - * will return the same pointer. - */ -extern void * QueuePeek(Queue *); - -/** - * Determine whether or not the queue is full. - */ -extern int QueueFull(Queue *); - -/** - * Determine whether or not the queue is empty. - */ -extern int QueueEmpty(Queue *); - -#endif diff --git a/src/include/Rand.h b/src/include/Rand.h deleted file mode 100644 index 6733bda..0000000 --- a/src/include/Rand.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef TELODENDRIA_RAND_H -#define TELODENDRIA_RAND_H - -/*** - * @Nm Rand - * @Nd Thread-safe random numbers. - * @Dd February 16 2023 - * @Xr Util - * - * .Nm - * is used for generating random numbers in a thread-safe way. - * Currently, one generator state is shared across all threads, which - * means that only one thread can generate random numbers at a time. - * This state is protected with a mutex to guarantee this behavior. - * In the future, a seed pool may be maintained to allow multiple - * threads to generate random numbers at the same time. - * .Pp - * The generator state is seeded on the first call to a function that - * needs it. The seed is determined by the current timestamp, the ID - * of the process, and the thread ID. These should all be sufficiently - * random sources, so the seed should be secure enough. - * .Pp - * .Nm - * currently uses a simple Mersenne Twister algorithm to generate - * random numbers. This algorithm was chosen because it is extremely - * popular and widespread. While it is likely not cryptographically - * secure, and does suffer some unfortunate pitfalls, this algorithm - * has stood the test of time and is simple enough to implement, so - * it was chosen over the alternatives. - * .Pp - * .Nm - * does not use any random number generator functions from the C - * standard library, since these are often flawed. - */ - -#include - -/** - * Generate a single random integer between 0 and the passed value. - */ -extern int RandInt(unsigned int); - -/** - * Generate the number of integers specified by the second argument - * storing them into the buffer pointed to in the first argument. - * Ensure that each number is between 0 and the third argument. - * .Pp - * This function allows a caller to get multiple random numbers at once - * in a more efficient manner than repeatedly calling - * .Fn RandInt , - * since each call to these functions - * has to lock and unlock a mutex. It is therefore better to obtain - * multiple random numbers in one pass if multiple are needed. - */ -extern void RandIntN(int *, size_t, unsigned int); - -#endif /* TELODENDRIA_RAND_H */ diff --git a/src/include/Sha2.h b/src/include/Sha2.h deleted file mode 100644 index 2701993..0000000 --- a/src/include/Sha2.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef TELODENDRIA_SHA2_H -#define TELODENDRIA_SHA2_H - -/*** - * @Nm Sha2 - * @Nd A simple implementation of the SHA2 hashing functions. - * @Dd December 19 2022 - * @Xr Memory Base64 - * - * This API defines simple functions for computing SHA2 hashes. - * At the moment, it only defines - * .Fn Sha256 , - * which computes the SHA-256 hash of the given C string. It is - * not trivial to implement SHA-512 in ANSI C due to the lack of - * a 64-bit integer type, so that hash function has been omitted. - */ - -/** - * This function takes a pointer to a NULL-terminated C string, and - * returns a string allocated on the heap using the Memory API, or - * NULL if there was an error allocating memory. The returned string - * should be freed when it is no longer needed. - */ -extern char * Sha256(char *); - -#endif /* TELODENDRIA_SHA2_H */ diff --git a/src/include/Str.h b/src/include/Str.h deleted file mode 100644 index a848b9e..0000000 --- a/src/include/Str.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_STR_H -#define TELODENDRIA_STR_H - -/*** - * @Nm Str - * @Nd Functions for creating and manipulating strings. - * @Dd February 15 2023 - * @Xr Memory - * - * .Nm - * provides string-related functions. It is called - * .Nm , - * not String, because some platforms (Windows) do not have - * case-sensitive filesystems, which poses a problem since - * .Pa string.h - * is a standard library header. - */ - -#include - -/** - * Take a UTF-8 codepoint and encode it into a string buffer containing - * between 1 and 4 bytes. The string buffer is allocated on the heap, - * so it should be freed when it is no longer needed. - */ -extern char * StrUtf8Encode(unsigned long); - -/** - * Duplicate a null-terminated string, returning a new string on the - * heap. This is useful when a function takes in a string that it needs - * to store for long amounts of time, even perhaps after the original - * string is gone. - */ -extern char * StrDuplicate(const char *); - -/** - * Extract part of a null-terminated string, returning a new string on - * the heap containing only the requested subsection. Like the - * substring functions included with most programming languages, the - * starting index is inclusive, and the ending index is exclusive. - */ -extern char * StrSubstr(const char *, size_t, size_t); - -/** - * A varargs function that takes a number of null-terminated strings - * specified by the first argument, and returns a new string that - * contains their concatenation. It works similarly to - * .Xr strcat 3 , - * but it takes care of allocating memory big enough to hold all the - * strings. Any string in the list may be NULL. If a NULL pointer is - * passed, it is treated like an empty string. - */ -extern char * StrConcat(size_t,...); - -/** - * Return a boolean value indicating whether or not the null-terminated - * string consists only of blank characters, as determined by - * .Xr isblank 3 . - */ -extern int StrBlank(const char *str); - -/** - * Generate a string of the specified length, containing random - * lowercase and uppercase letters. - */ -extern char * StrRandom(size_t); - -/** - * Convert the specified integer into a string, returning the string - * on the heap, or NULL if there was a memory allocation error. The - * returned string should be freed by the caller after it is no longer - * needed. - */ -extern char * StrInt(long); - -/** - * Compare two strings and determine whether or not they are equal. - * This is the most common use case of strcmp() in Telodendria, but - * strcmp() doesn't like NULL pointers, so these have to be checked - * explicitly and can cause problems if they aren't. This function, - * on the other hand, makes NULL pointers special cases. If both - * arguments are NULL, then they are considered equal. If only one - * argument is NULL, they are considered not equal. Otherwise, if - * no arguments are NULL, a regular strcmp() takes place and this - * function returns a boolean value indicating whether or not - * strcmp() returned 0. - */ -extern int StrEquals(const char *, const char *); - -#endif /* TELODENDRIA_STR_H */ diff --git a/src/include/Stream.h b/src/include/Stream.h deleted file mode 100644 index 894ebf9..0000000 --- a/src/include/Stream.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_STREAM_H -#define TELODENDRIA_STREAM_H - -/*** - * @Nm Stream - * @Nd An abstraction over the Io API that implements standard C I/O. - * @Dd April 29 2023 - * @Xr Io - * - * .Nm - * implements an abstraction layer over the Io API. This layer buffers - * I/O and makes it much easier to work with, mimicking the standard - * C library and offering some more convenience features. - */ - -#include - -#include - -/** - * An opaque structure analogous to C's FILE pointers. - */ -typedef struct Stream Stream; - -/** - * Create a new stream using the specified Io for underlying I/O - * operations. - */ -extern Stream * StreamIo(Io * io); - -/** - * Create a new stream using the specified POSIX file descriptor. - * This is a convenience function for calling - * .Fn IoFd - * and then passing the result into - * .Fn StreamIo . - */ -extern Stream * StreamFd(int); - -/** - * Create a new stream using the specified C FILE pointer. This is a - * convenience function for calling - * .Fn IoFile - * and then passing the result into - * .Fn StreamIo . - */ -extern Stream * StreamFile(FILE *); - -/** - * Create a new stream using the specified path and mode. This is a - * convenience function for calling - * .Xr fopen 3 - * and then passing the result into - * .Fn StreamFile . - */ -extern Stream * StreamOpen(const char *, const char *); - -/** - * Get a stream that writes to the standard output. - */ -extern Stream * StreamStdout(void); - -/** - * Get a stream that writes to the standard error. - */ -extern Stream * StreamStderr(void); - -/** - * Get a stream that reads from the standard input. - */ -extern Stream * StreamStdin(void); - -/** - * Close the stream. This flushes the buffers and closes the underlying - * Io. It is analogous to the standard - * .Xr fclose 3 - * function. - */ -extern int StreamClose(Stream *); - -/** - * Print a formatted string. This function is analogous to the standard - * .Xr vfprintf 3 - * function. - */ -extern int StreamVprintf(Stream *, const char *, va_list); - -/** - * Print a formatted string. This function is analogous to the - * standard - * .Xr fprintf 3 - * function. - */ -extern int StreamPrintf(Stream *, const char *,...); - -/** - * Get a single character from a stream. This function is analogous to - * the standard - * .Xr fgetc 3 - * function. - */ -extern int StreamGetc(Stream *); - -/** - * Push a character back onto the input stream. This function is - * analogous to the standard - * .Xr ungetc 3 - * function. - */ -extern int StreamUngetc(Stream *, int); - -/** - * Write a single character to the stream. This function is analogous - * to the standard - * .Xr fputc 3 - * function. - */ -extern int StreamPutc(Stream *, int); - -/** - * Write a null-terminated string to the stream. This function is - * analogous to the standard - * .Xr fputs 3 - * function. - */ -extern int StreamPuts(Stream *, char *); - -/** - * Read at most the specified number of characters minus 1 from the - * specified stream and store them at the memory located at the - * specified pointer. This function is analogous to the standard - * .Xr fgets 3 - * function. - */ -extern char * StreamGets(Stream *, char *, int); - -/** - * Set the file position indicator for the specified stream. This - * function is analogous to the standard - * .Xr fseeko - * function. - */ -extern off_t StreamSeek(Stream *, off_t, int); - -/** - * Test the end-of-file indicator for the given stream, returning a - * boolean value indicating whether or not it is set. This is analogous - * to the standard - * .Xr feof 3 - * function. - */ -extern int StreamEof(Stream *); - -/** - * Test the stream for an error condition, returning a boolean value - * indicating whether or not one is present. This is analogous to the - * standard - * .Xr ferror 3 - * function. - */ -extern int StreamError(Stream *); - -/** - * Clear the error condition associated with the given stream, allowing - * future reads or writes to potentially be successful. This functio - * is analogous to the standard - * .Xr clearerr 3 - * function. - */ -extern void StreamClearError(Stream *); - -/** - * Flush all buffered data using the streams underlying write function. - * This function is analogous to the standard - * .Xr fflush 3 - * function. - */ -extern int StreamFlush(Stream *); - -/** - * Read all the bytes from the first stream and write them to the - * second stream. This is analogous to - * .Fn IoCopy , - * but it uses the internal buffers of the streams. It is probably - * less efficient than doing a - * .Fn IoCopy - * instead, but it is more convenient. - */ -extern ssize_t StreamCopy(Stream *, Stream *); - -/** - * Get the file descriptor associated with the given stream, or -1 if - * the stream is not associated with any file descriptor. This function - * is analogous to the standard - * .Xr fileno 3 - * function. - */ -extern int StreamFileno(Stream *); - -#endif /* TELODENDRIA_STREAM_H */ diff --git a/src/include/Tls.h b/src/include/Tls.h deleted file mode 100644 index c36b574..0000000 --- a/src/include/Tls.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_TLS_H -#define TELODENDRIA_TLS_H - -/*** - * @Nm Tls - * @Nd Interface to platform-dependent TLS libraries. - * @Dd April 29 2023 - * @Xr Stream Io - * - * .Nm - * provides an interface to platform-dependent TLS libraries. It allows - * Telodendria to support any TLS library with no changes to existing - * code. Support for additional TLS libraries is added by creating a - * new compilation unit that implements all the functions here, with - * the exception of a few, which are noted. - * .Pp - * Currently, Telodendria has support for the following TLS libraries: - * .Bl -bullet -offset indent - * .It - * LibreSSL - * .It - * OpenSSL - * .El - */ - -#include - -#define TLS_LIBRESSL 2 -#define TLS_OPENSSL 3 - -/** - * Create a new TLS client stream using the given file descriptor and - * the given server hostname. The hostname should be used to verify - * that the server actually is who it says it is. - * .Pp - * This function does not need to be implemented by the individual - * TLS support stubs. - */ -extern Stream * TlsClientStream(int, const char *); - -/** - * Create a new TLS server stream using the given certificate and key - * file, in the format natively supported by the TLS library. - * .Pp - * This function does not need to be implemented by the individual - * TLS support stubs. - */ -extern Stream * TlsServerStream(int, const char *, const char *); - -/** - * Initialize a cookie that stores information about the given client - * connection. This cookie will be passed into the other functions - * defined by this API. - */ -extern void * TlsInitClient(int, const char *); - -/** - * Initialize a cookie that stores information about the given - * server connection. This cookie will be passed into the other - * functions defined by this API. - */ -extern void * TlsInitServer(int, const char *, const char *); - -/** - * Read from a TLS stream, decrypting it and storing the result in the - * specified buffer. This function takes the cookie, buffer, and - * number of decrypted bytes to read into it. See the documentation for - * .Fn IoRead . - */ -extern ssize_t TlsRead(void *, void *, size_t); - -/** - * Write to a TLS stream, encrypting the buffer. This function takes - * the cookie, buffer, and number of unencrypted bytes to write to - * the stream. See the documentation for - * .Fn IoWrite . - */ -extern ssize_t TlsWrite(void *, void *, size_t); - -/** - * Close the TLS stream, also freeing all memory associated with the - * cookie. - */ -extern int TlsClose(void *); - -#endif /* TELODENDRIA_TLS_H */ diff --git a/src/include/Uri.h b/src/include/Uri.h deleted file mode 100644 index 4f7cb83..0000000 --- a/src/include/Uri.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef TELODENDRIA_URI_H -#define TELODENDRIA_URI_H - -/*** - * @Nm Uri - * @Nd Parse a URI. Typically used to parse HTTP(s) URLs. - * @Dd April 29 2023 - * @Xr Http - * - * .Nm - * provides a simple mechanism for parsing URIs. This is an extremely - * basic parser that (ab)uses - * .Xr sscanf 3 - * to parse URIs, so it may not be the most reliable, but it should - * work in most cases and on reasonable URIs that aren't too long, as - * the _MAX definitions are modest. - */ - -#define URI_PROTO_MAX 8 -#define URI_HOST_MAX 128 -#define URI_PATH_MAX 256 - -/** - * The parsed URI is stored in this structure. - */ -typedef struct Uri -{ - char proto[URI_PROTO_MAX]; - char host[URI_HOST_MAX]; - char path[URI_PATH_MAX]; - unsigned short port; -} Uri; - -/** - * Parse a URI string into the Uri structure as described above, or - * return NULL if there was a parsing error. - */ -extern Uri * UriParse(const char *); - -/** - * Free the memory associated with a Uri structure returned by - * .Fn UriParse . - */ -extern void UriFree(Uri *); - -#endif /* TELODENDRIA_URI_H */ diff --git a/src/include/Util.h b/src/include/Util.h deleted file mode 100644 index 73b5dfb..0000000 --- a/src/include/Util.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef TELODENDRIA_UTIL_H -#define TELODENDRIA_UTIL_H - -/*** - * @Nm Util - * @Nd Some misc. helper functions that don't need their own headers. - * @Dd February 15 2023 - * - * This header holds a number of random functions related to strings, - * time, the filesystem, and other simple tasks that don't require a - * full separate API. For the most part, the functions here are - * entirely standalone, depending only on POSIX functions, however - * there are a few that depend explicitly on a few other APIs. Those - * are noted. - */ - -#include -#include -#include - -#include - -/** - * Get the current timestamp in milliseconds since the Unix epoch. This - * uses - * .Xr gettimeofday 2 - * and time_t, and converts it to a single number, which is then - * returned to the caller. - * .Pp - * A note on the 2038 problem: as long as sizeof(long) >= 8, that is, - * as long as the long data type is 64 bits or more, then everything - * should be fine. On most, if not, all, 64-bit systems, long is 64 - * bits. I would expect Telodendria to break for 32 bit systems - * eventually, but we should have a ways to go before that happens. - * I didn't want to try to hack together some system to store larger - * numbers than the architecture supports. But we can always - * re-evaluate over the next few years. - */ -extern unsigned long UtilServerTs(void); - -/** - * Use - * .Xr stat 2 - * to get the last modified time of the given file, or zero if there - * was an error getting the last modified time of a file. This is - * primarily useful for caching file data. - */ -extern unsigned long UtilLastModified(char *); - -/** - * This function behaves just like the system call - * .Xr mkdir 2 , - * but it creates any intermediate directories as necessary, unlike - * .Xr mkdir 2 . - */ -extern int UtilMkdir(const char *, const mode_t); - -/** - * Sleep the calling thread for the given number of milliseconds. - * POSIX does not have a very friendly way to sleep, so this wraps - * .Xr nanosleep 2 - * to make its usage much, much simpler. - */ -extern int UtilSleepMillis(long); - -/** - * This function works identically to the POSIX - * .Xr getdelim 3 , - * except that it assumes pointers were allocated with the Memory API - * and it reads from a Stream instead of a file pointer. - */ -extern ssize_t UtilGetDelim(char **, size_t *, int, Stream *); - -/** - * This function is just a special case of - * .Fn UtilGetDelim - * that sets the delimiter to the newline character. - */ -extern ssize_t UtilGetLine(char **, size_t *, Stream *); - -#endif /* TELODENDRIA_UTIL_H */ diff --git a/tools/bin/td b/tools/bin/td index dbcddd8..37c6064 100644 --- a/tools/bin/td +++ b/tools/bin/td @@ -11,6 +11,17 @@ # handle building the code, it also handles formatting it, as well # as generating patch files. +addprefix() { + prefix="$1" + shift + for thing in "$@"; do + echo "${prefix}${thing}" + done + + unset prefix + unset thing +} + if [ -z "$TELODENDRIA_ENV" ]; then . tools/env.sh fi @@ -27,38 +38,18 @@ fi : "${CVS_TAG:=Telodendria-$(echo $TELODENDRIA_VERSION | sed 's/\./_/g')}" : "${DEFINES:=-D_DEFAULT_SOURCE -DTELODENDRIA_VERSION=\"${TELODENDRIA_VERSION}-$(uname)\"}" +: "${INCLUDES:=src/include Cytoplasm/src/include}" : "${CC:=cc}" : "${AR:=ar}" : "${CFLAGS:=-Wall -Wextra -pedantic -std=c89 -O3 -pipe}" : "${STATIC:=-static -Wl,-static}" : "${LD_EXTRA:=-flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections}" -: "${LDFLAGS:=-lm -pthread}" : "${PROG:=telodendria}" . "$(pwd)/tools/lib/common.sh" -if [ -n "$TLS_IMPL" ]; then - case "$TLS_IMPL" in - "LIBRESSL") - TLS_LIBS="-ltls -lcrypto -lssl" - ;; - "OPENSSL") - TLS_LIBS="-lcrypto -lssl" - ;; - *) - echo "Unrecognized TLS implementation: ${TLS_IMPL}" - echo "Consult src/include/Tls.h for supported implementations." - echo "Note that the TLS_ prefix is omitted in TLS_IMPL." - exit 1 - ;; - esac - - DEFINES="${DEFINES} -DTLS_IMPL=TLS_${TLS_IMPL}" - LDFLAGS="${LDFLAGS} ${TLS_LIBS}" -fi - -CFLAGS="${CFLAGS} ${DEFINES} ${INCLUDES}" +CFLAGS="${CFLAGS} ${DEFINES} $(addprefix -I$(pwd)/ ${INCLUDES})" MAIN="Main" @@ -132,15 +123,25 @@ recipe_docs() { fi } +recipe_cytoplasm() { + cd Cytoplasm + export TLS_IMPL + sh make.sh + CYTOPLASM_FLAGS="-LCytoplasm/out/lib -lcytoplasm $(sh make.sh libs)" + cd - > /dev/null +} + # Build the source code, and generate the 'build/telodendria' # binary. recipe_build() { + recipe_cytoplasm + cd src mkdir -p ../build echo "CC = ${CC}" echo "CFLAGS = ${CFLAGS}" - echo "LDFLAGS = ${LDFLAGS} ${STATIC}" + echo "LDFLAGS = ${CYTOPLASM_FLAGS} ${LDFLAGS} ${STATIC}" echo do_rebuild=0 @@ -156,7 +157,7 @@ recipe_build() { echo "CC $(basename $obj)" obj_dir=$(dirname "../$obj") mkdir -p "$obj_dir" - if ! $CC $CFLAGS -fPIC -Iinclude -c -o "../$obj" "$src"; then + if ! $CC $CFLAGS -fPIC -c -o "../$obj" "$src"; then exit 1 fi do_rebuild=1 @@ -165,28 +166,13 @@ recipe_build() { cd .. - if [ $do_rebuild -eq 1 ] || [ ! -f "build/lib${PROG}.a" ]; then - echo "AR lib${PROG}.a" - if ! $AR rcs "build/lib${PROG}.a" $objs; then - exit 1 - fi - fi - - if [ $do_rebuild -eq 1 ] || [ ! -f "build/lib${PROG}.so" ]; then - echo "LD lib${PROG}.so" - if ! $CC -shared -o "build/lib${PROG}.so" $objs ${LDFLAGS}; then - exit 1 - fi - fi - if [ $do_rebuild -eq 1 ] || [ ! -f "build/$PROG" ]; then echo "LD $PROG" - if ! $CC -o "build/$PROG" "build/$MAIN.o" -Lbuild -ltelodendria ${LDFLAGS} ${STATIC}; then + if ! $CC -o "build/$PROG" $objs "build/$MAIN.o" ${CYTOPLASM_FLAGS} ${LDFLAGS} ${STATIC}; then exit 1 fi fi - for src in $(find tools/src -name '*.c'); do out=$(basename "$src" .c) out="build/tools/$out" @@ -194,16 +180,12 @@ recipe_build() { if [ $(mod_time "$src") -ge $(mod_time "$out") ] || [ $do_rebuild -eq 1 ]; then echo "CC $(basename $out)" mkdir -p "$(dirname $out)" - if ! $CC $CFLAGS -Isrc/include -o "$out" "$src" -Lbuild -ltelodendria ${LDFLAGS} ${STATIC}; then + if ! $CC $CFLAGS -o "$out" "$src" ${CYTOPLASM_FLAGS} ${LDFLAGS} ${STATIC}; then exit 1 fi fi done - if ! intcheck; then - exit 1 - fi - recipe_docs } diff --git a/tools/env.sh b/tools/env.sh index dfa4355..9f57642 100644 --- a/tools/env.sh +++ b/tools/env.sh @@ -31,8 +31,9 @@ if which makewhatis 2>&1 > /dev/null; then makewhatis "$(pwd)/man" fi -export PATH="$(pwd)/tools/bin:$(pwd)/build/tools:$PATH" -export MANPATH="$(pwd)/man:$(pwd)/build/man:$MANPATH" +export LD_LIBRARY_PATH="$(pwd)/Cytoplasm/out/lib" +export PATH="$(pwd)/tools/bin:$(pwd)/build/tools:$(pwd)/Cytoplasm/out/bin:$PATH" +export MANPATH="$(pwd)/man:$(pwd)/build/man:$(pwd)/Cytoplasm/out/man:$MANPATH" if [ "$(uname)" = "OpenBSD" ]; then # Other platforms use different MALLOC_OPTIONS diff --git a/tools/src/hdoc.c b/tools/src/hdoc.c deleted file mode 100644 index f89ab5b..0000000 --- a/tools/src/hdoc.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -typedef struct DocDecl -{ - char docs[HEADER_EXPR_MAX]; - HeaderDeclaration decl; -} DocDecl; - -typedef struct DocTypedef -{ - char docs[HEADER_EXPR_MAX]; - char text[HEADER_EXPR_MAX]; -} DocTypedef; - -typedef struct DocGlobal -{ - char docs[HEADER_EXPR_MAX]; - HeaderGlobal global; -} DocGlobal; - -static void -ParseMainBlock(HashMap * registers, Array * descr, char *comment) -{ - char *line = strtok(comment, "\n"); - - while (line) - { - while (*line && (isspace(*line) || *line == '*')) - { - line++; - } - - if (!*line) - { - goto next; - } - - if (*line == '@') - { - int i = 0; - - line++; - - while (!isspace(line[i])) - { - i++; - } - - line[i] = '\0'; - - Free(HashMapSet(registers, line, StrDuplicate(line + i + 1))); - } - else - { - ArrayAdd(descr, StrDuplicate(line)); - } - -next: - line = strtok(NULL, "\n"); - } -} - -int -main(int argc, char **argv) -{ - HeaderExpr expr; - size_t i; - char *val; - int exit = EXIT_SUCCESS; - - HashMap *registers = HashMapCreate(); - Array *descr = ArrayCreate(); - - Array *declarations = ArrayCreate(); - DocDecl *decl = NULL; - - Array *typedefs = ArrayCreate(); - DocTypedef *type = NULL; - - Array *globals = ArrayCreate(); - DocGlobal *global = NULL; - - char comment[HEADER_EXPR_MAX]; - int isDocumented = 0; - - Stream *in = NULL; - Stream *out = NULL; - - int opt; - - while ((opt = getopt(argc, argv, "i:o:D:")) != -1) - { - switch (opt) - { - case 'i': - if (in) - { - break; - } - - if (StrEquals(optarg, "-")) - { - in = StreamStdin(); - } - else - { - int len = strlen(optarg); - - in = StreamOpen(optarg, "r"); - if (!in) - { - StreamPrintf(StreamStderr(), "Error: %s:%s", - optarg, strerror(errno)); - exit = EXIT_FAILURE; - goto finish; - } - - while (optarg[len - 1] != '.') - { - optarg[len - 1] = '\0'; - len--; - } - - optarg[len - 1] = '\0'; - len--; - - HashMapSet(registers, "Nm", StrDuplicate(optarg)); - } - break; - case 'o': - if (out) - { - break; - } - - if (StrEquals(optarg, "-")) - { - out = StreamStdout(); - } - else - { - out = StreamOpen(optarg, "w"); - if (!out) - { - StreamPrintf(StreamStderr(), "Error: %s:%s", - optarg, strerror(errno)); - exit = EXIT_FAILURE; - goto finish; - } - } - break; - case 'D': - val = optarg; - while (*val && *val != '=') - { - val++; - } - if (!*val || *val != '=') - { - StreamPrintf(StreamStderr(), "Bad register definition: %s", - optarg); - exit = EXIT_FAILURE; - goto finish; - } - - *val = '\0'; - val++; - HashMapSet(registers, optarg, StrDuplicate(val)); - break; - } - } - - if (!in) - { - in = StreamStdin(); - } - - if (!out) - { - out = StreamStdout(); - } - - memset(&expr, 0, sizeof(expr)); - - while (1) - { - HeaderParse(in, &expr); - - switch (expr.type) - { - case HP_PREPROCESSOR_DIRECTIVE: - /* Ignore */ - break; - case HP_EOF: - /* Done parsing */ - goto last; - case HP_PARSE_ERROR: - case HP_SYNTAX_ERROR: - StreamPrintf(StreamStderr(), "Parse Error: (line %d) %s\n", - expr.data.error.lineNo, expr.data.error.msg); - exit = EXIT_FAILURE; - goto finish; - case HP_COMMENT: - if (expr.data.text[0] != '*') - { - break; - } - - if (strncmp(expr.data.text, "**", 2) == 0) - { - ParseMainBlock(registers, descr, expr.data.text); - } - else - { - strncpy(comment, expr.data.text, sizeof(comment)); - isDocumented = 1; - } - break; - case HP_TYPEDEF: - if (HashMapGet(registers, "ignore-typedefs")) - { - break; - } - - if (!isDocumented) - { - StreamPrintf(StreamStderr(), - "Error: Undocumented typedef:\n%s\n", - expr.data.text); - exit = EXIT_FAILURE; - goto finish; - } - else - { - type = Malloc(sizeof(DocTypedef)); - strncpy(type->docs, comment, sizeof(type->docs)); - strncpy(type->text, expr.data.text, sizeof(type->text)); - ArrayAdd(typedefs, type); - isDocumented = 0; - } - break; - case HP_DECLARATION: - if (!isDocumented) - { - StreamPrintf(StreamStderr(), - "Error: %s() is undocumented.\n", - expr.data.declaration.name); - exit = EXIT_FAILURE; - goto finish; - } - else - { - decl = Malloc(sizeof(DocDecl)); - decl->decl = expr.data.declaration; - decl->decl.args = ArrayCreate(); - strncpy(decl->docs, comment, sizeof(decl->docs)); - for (i = 0; i < ArraySize(expr.data.declaration.args); i++) - { - ArrayAdd(decl->decl.args, StrDuplicate(ArrayGet(expr.data.declaration.args, i))); - } - ArrayAdd(declarations, decl); - isDocumented = 0; - } - break; - case HP_GLOBAL: - if (!isDocumented) - { - StreamPrintf(StreamStderr(), - "Error: Global %s is undocumented.\n", - expr.data.global.name); - exit = EXIT_FAILURE; - goto finish; - } - else - { - global = Malloc(sizeof(DocGlobal)); - global->global = expr.data.global; - - strncpy(global->docs, comment, sizeof(global->docs)); - ArrayAdd(globals, global); - isDocumented = 0; - } - break; - case HP_UNKNOWN: - if (HashMapGet(registers, "suppress-warnings")) - { - break; - } - StreamPrintf(StreamStderr(), "Warning: Unknown expression: %s\n", - expr.data.text); - break; - default: - StreamPrintf(StreamStderr(), "Unknown header type: %d\n", expr.type); - StreamPrintf(StreamStderr(), "This is a programming error.\n"); - exit = EXIT_FAILURE; - goto finish; - } - } - -last: - val = HashMapGet(registers, "Nm"); - if (!val) - { - HashMapSet(registers, "Nm", StrDuplicate("Unnamed")); - } - - val = HashMapGet(registers, "Dd"); - if (!val) - { - time_t currentTime; - struct tm *timeInfo; - char tsBuf[1024]; - - currentTime = time(NULL); - timeInfo = localtime(¤tTime); - strftime(tsBuf, sizeof(tsBuf), "%B %d %Y", timeInfo); - - val = tsBuf; - } - StreamPrintf(out, ".Dd $%s: %s $\n", "Mdocdate", val); - - val = HashMapGet(registers, "Os"); - if (val) - { - StreamPrintf(out, ".Os %s\n", val); - } - - val = HashMapGet(registers, "Nm"); - StreamPrintf(out, ".Dt %s 3\n", val); - StreamPrintf(out, ".Sh NAME\n"); - StreamPrintf(out, ".Nm %s\n", val); - - val = HashMapGet(registers, "Nd"); - if (!val) - { - val = "No Description."; - } - StreamPrintf(out, ".Nd %s\n", val); - - StreamPrintf(out, ".Sh SYNOPSIS\n"); - val = HashMapGet(registers, "Nm"); - StreamPrintf(out, ".In %s.h\n", val); - for (i = 0; i < ArraySize(declarations); i++) - { - size_t j; - - decl = ArrayGet(declarations, i); - StreamPrintf(out, ".Ft %s\n", decl->decl.returnType); - StreamPrintf(out, ".Fn %s ", decl->decl.name); - for (j = 0; j < ArraySize(decl->decl.args); j++) - { - StreamPrintf(out, "\"%s\" ", ArrayGet(decl->decl.args, j)); - } - StreamPutc(out, '\n'); - } - - if (ArraySize(globals)) - { - StreamPrintf(out, ".Sh GLOBALS\n"); - for (i = 0; i < ArraySize(globals); i++) - { - char *line; - global = ArrayGet(globals, i); - - StreamPrintf(out, ".Ss %s %s\n", global->global.type, global->global.name); - - line = strtok(global->docs, "\n"); - while (line) - { - while (*line && (isspace(*line) || *line == '*')) - { - line++; - } - - if (*line) - { - StreamPrintf(out, "%s\n", line); - } - - line = strtok(NULL, "\n"); - } - } - } - - if (ArraySize(typedefs)) - { - StreamPrintf(out, ".Sh TYPE DECLARATIONS\n"); - for (i = 0; i < ArraySize(typedefs); i++) - { - char *line; - - type = ArrayGet(typedefs, i); - StreamPrintf(out, ".Bd -literal -offset indent\n"); - StreamPrintf(out, "%s\n", type->text); - StreamPrintf(out, ".Ed\n.Pp\n"); - - line = strtok(type->docs, "\n"); - while (line) - { - while (*line && (isspace(*line) || *line == '*')) - { - line++; - } - - if (*line) - { - StreamPrintf(out, "%s\n", line); - } - - line = strtok(NULL, "\n"); - } - } - } - - StreamPrintf(out, ".Sh DESCRIPTION\n"); - for (i = 0; i < ArraySize(descr); i++) - { - StreamPrintf(out, "%s\n", ArrayGet(descr, i)); - } - - for (i = 0; i < ArraySize(declarations); i++) - { - size_t j; - char *line; - - decl = ArrayGet(declarations, i); - StreamPrintf(out, ".Ss %s %s(", - decl->decl.returnType, decl->decl.name); - for (j = 0; j < ArraySize(decl->decl.args); j++) - { - StreamPrintf(out, "%s", ArrayGet(decl->decl.args, j)); - if (j < ArraySize(decl->decl.args) - 1) - { - StreamPuts(out, ", "); - } - } - StreamPuts(out, ")\n"); - - line = strtok(decl->docs, "\n"); - while (line) - { - while (*line && (isspace(*line) || *line == '*')) - { - line++; - } - - if (*line) - { - StreamPrintf(out, "%s\n", line); - } - - line = strtok(NULL, "\n"); - } - } - - val = HashMapGet(registers, "Xr"); - if (val) - { - char *xr = strtok(val, " "); - - StreamPrintf(out, ".Sh SEE ALSO\n"); - while (xr) - { - if (*xr) - { - StreamPrintf(out, ".Xr %s 3 ", xr); - } - - xr = strtok(NULL, " "); - - if (xr) - { - StreamPutc(out, ','); - } - StreamPutc(out, '\n'); - } - } - -finish: - StreamClose(in); - StreamClose(out); - StreamClose(StreamStderr()); - - MemoryFreeAll(); - return exit; -} diff --git a/tools/src/intcheck.c b/tools/src/intcheck.c deleted file mode 100644 index 1060126..0000000 --- a/tools/src/intcheck.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include - -#include - -#define ASSERT_SIZE(type, size) \ - if ((sizeof(type) * 8) != size) \ - { \ - fputs(#type " is not " #size " bits.\n", stderr); \ - return 1; \ - } - -int -main(void) -{ - ASSERT_SIZE(Int8, 8); - ASSERT_SIZE(UInt8, 8); - - ASSERT_SIZE(Int16, 16); - ASSERT_SIZE(UInt16, 16); - - ASSERT_SIZE(Int32, 32); - ASSERT_SIZE(UInt32, 32); - - return 0; -}