diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbd6f91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# VSCodium +.cache/ + +# Meson +build/ +debug/ diff --git a/gcovr.cfg b/gcovr.cfg new file mode 100644 index 0000000..f6c4ea7 --- /dev/null +++ b/gcovr.cfg @@ -0,0 +1 @@ +exclude = .*tests/.+ diff --git a/include/ksr/arrays.h b/include/ksr/arrays.h new file mode 100644 index 0000000..a8c69cd --- /dev/null +++ b/include/ksr/arrays.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include + +/* + * KSRARRAY DEFAULT SIZES. + */ + +#define KSRARRAY_TINY_SIZE 2 +#define KSRARRAY_SMALL_SIZE 8 +#define KSRARRAY_BASIC_SIZE 16 +#define KSRARRAY_BIG_SIZE 64 +#define KSRARRAY_HUGE_SIZE 128 + +/** + * ksrarray structure. + */ +typedef struct { + void **data; // array data. + size_t length; // currently used size. + + size_t _allocated_size; // currently allocated size. +} ksrarray; + +/** + * Create a ksrarray with the given start size. + * @param start_size - the allocated size at start. + */ +ksrarray* ksrarray_new(const size_t start_size); +/** + * Create a tiny sized ksrarray. + */ +static inline ksrarray* ksrarray_new_tiny() +{ return ksrarray_new(KSRARRAY_TINY_SIZE); } +/** + * Create a small sized ksrarray. + */ +static inline ksrarray* ksrarray_new_small() +{ return ksrarray_new(KSRARRAY_SMALL_SIZE); } +/** + * Create a normal sized ksrarray. + */ +static inline ksrarray* ksrarray_new_basic() +{ return ksrarray_new(KSRARRAY_BASIC_SIZE); } +/** + * Create a big sized ksrarray. + */ +static inline ksrarray* ksrarray_new_big() +{ return ksrarray_new(KSRARRAY_BIG_SIZE); } +/** + * Create a huge sized ksrarray. + */ +static inline ksrarray* ksrarray_new_huge() +{ return ksrarray_new(KSRARRAY_HUGE_SIZE); } + +/** + * Push a new element at the end of a ksrarray. + * @param array - the ksrarray in which to push. + * @param element - the element to push. + */ +void ksrarray_push(ksrarray *array, void *element); + +/** + * Remove an element from a ksrarray. + * @param array - the ksrarray in which to remove. + * @param index - the index of the element to remove. + */ +bool ksrarray_remove(ksrarray *array, const size_t index); + +/** + * Free a ksrarray. + * /!\ This will NOT free the elements, just the container and its internal data. + */ +static inline void ksrarray_free(ksrarray *array) +{ + free(array->data); + free(array); +} + +/** + * Get the iterator variable name, used in ksrarray_foreach. + */ +#define _ksrarray_get_iter_name(a, b) b ## _iter +/** + * For each element of the given ksrarray, do... + */ +#define ksrarray_foreach(ksrarray, element) \ + for(size_t _ksrarray_get_iter_name(ksrarray, element) = 0; \ + (element = ksrarray.data[_ksrarray_get_iter_name(ksrarray, element)]) || true, \ + _ksrarray_get_iter_name(ksrarray, element) < ksrarray.length; \ + _ksrarray_get_iter_name(ksrarray, element)++) diff --git a/include/ksr/buffers.h b/include/ksr/buffers.h new file mode 100644 index 0000000..2bed038 --- /dev/null +++ b/include/ksr/buffers.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +/** + * ksrbuffer structure. + */ +typedef struct { + char *bytes; + size_t length; +} ksrbuffer; + +/** + * Create a buffer of given size. + */ +ksrbuffer* ksrbuffer_new(const size_t buffer_size); + +/** + * Create a buffer of given size. + */ +ksrbuffer* ksrbuffer_new_empty(const size_t buffer_size); + +/** + * Create a buffer from given size and content. + */ +ksrbuffer* ksrbuffer_new_from_content(const size_t buffer_size, const char *buffer_content); + +/** + * Create a buffer of default buffer size. + */ +static inline ksrbuffer* ksrbuffer_new_default() +{ + return ksrbuffer_new(BUFSIZ); +} + +/** + * Free a ksrbuffer and its content. + */ +static inline void ksrbuffer_free(ksrbuffer *buffer) +{ + free(buffer->bytes); + free(buffer); +} diff --git a/include/ksr/files.h b/include/ksr/files.h new file mode 100644 index 0000000..f18a4b8 --- /dev/null +++ b/include/ksr/files.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +/** + * Expand the path in parameter. + * If the path expands to multiple paths, return the first one. + */ +char* path_exp_simple(const char* path); + +/** + * Make all directories to the file in argument. + * @param path - will create the directories for this path. + * @param mode - permissions of created directories. + */ +int mkpath(const char *path, const mode_t mode); + +/** + * Read the file. + * @param file - the opened file stream. + * @return - the buffer of the read file. + */ +ksrbuffer* read_file(FILE *file); +/** + * Read the file from its path. + * @param file_path - path of the file to read. + * @return - the buffer of the read file. + */ +ksrbuffer* read_file_from_path(const char *file_path); + +/** + * Write a buffer into a file. + * @param file_path - path of the file to read. + * @return - true if the writing is successful, false otherwise. + */ +bool write_file(const char *file_path, const ksrbuffer *buffer); diff --git a/include/ksr/logging.h b/include/ksr/logging.h new file mode 100644 index 0000000..d0a308e --- /dev/null +++ b/include/ksr/logging.h @@ -0,0 +1,11 @@ +#pragma once + +#define TERMSTYLE_RESET "\033[0m" +#define TERMSTYLE_BLACK "\033[30m" +#define TERMSTYLE_RED "\033[31m" +#define TERMSTYLE_GREEN "\033[32m" +#define TERMSTYLE_YELLOW "\033[33m" +#define TERMSTYLE_BLUE "\033[34m" +#define TERMSTYLE_MAGENTA "\033[35m" +#define TERMSTYLE_CYAN "\033[36m" +#define TERMSTYLE_WHITE "\033[37m" diff --git a/include/ksr/promises.h b/include/ksr/promises.h new file mode 100644 index 0000000..2211fca --- /dev/null +++ b/include/ksr/promises.h @@ -0,0 +1,114 @@ +#pragma once + +#include +#include +#include + +// sleep delay of promise await function, in µs. +#define KSRPROMISES_AWAIT_SLEEP_DELAY 50*1000 + +/** + * KSR promise. + */ +typedef struct { + pthread_t thread; + ksrarray *then_callbacks; // array of then callbacks. + ksrarray *catch_callbacks; // array of catch callbacks. + + bool successful; // determine if the promise has been fulfilled or not. + void *result; // the promised result. + + int error_code; + const char *error_message; +} ksrpromise; + +// type of promise execution function. +typedef void (*ksrpromise_execution_f)(ksrpromise *promise, void *userdata); +// type of then callback. +typedef void (*ksrpromise_then_callback_f)(void *result, void *userdata); +// type of catch callback. +typedef void (*ksrpromise_catch_callback_f)(int code, const char *message, void *userdata); + +/** + * Create a new promise. + * @param exec - promise execution function. + * @param data - custom user data. + * @return - the new promise. + */ +ksrpromise* ksrpromise_new(ksrpromise_execution_f exec, void *userdata); + +/** + * Resolve the given promise. + * @param promise - the promise to resolve. + * @param result - the promised result. + */ +void ksrpromise_resolve(ksrpromise *promise, void *result); + +/** + * Reject the given promise. + * @param promise - the promise to resolve. + * @param code - the error code. + * @param message - the error message. + */ +void ksrpromise_reject(ksrpromise *promise, int code, const char *message); + +/** + * Await a given promise. + * @param promise - the promise to await. + * @return - the promised result. + */ +void* ksrpromise_await(ksrpromise *promise); + +/** + * Set a new callback to call on promise resolution. + * @param promise - the promise for which to set a resolution callback. + * @param callback - the callback to set. + */ +void ksrpromise_then(ksrpromise *promise, ksrpromise_then_callback_f, void *userdata); + +/** + * Set a new callback to call on promise rejection. + * @param promise - the promise for which to set a rejection callback. + * @param callback - a callback to set. + */ +void ksrpromise_catch(ksrpromise *promise, ksrpromise_catch_callback_f, void *userdata); + +/** + * Determine if the given promise is successful or not. + * @param - the promise to check. + */ +static inline bool ksrpromise_is_successful(const ksrpromise *promise) +{ + return promise->successful; +} +/** + * Determine if the given promise is failed or not. + * @param - the promise to check. + */ +static inline bool ksrpromise_is_failed(const ksrpromise *promise) +{ + return !!promise->error_code; +} +/** + * Determine if the given promise is running or not. + * @param - the promise to check. + */ +static inline bool ksrpromise_is_running(const ksrpromise *promise) +{ + return !ksrpromise_is_successful(promise) && !ksrpromise_is_failed(promise); +} + +/** + * Free the promise. + * /!\ The promise is freed automatically on rejection or resolution. + * @param promise - the promise to free. + */ +static inline void ksrpromise_free(ksrpromise *promise) +{ + // free arrays of callbacks. + ksrarray_free(promise->then_callbacks); + ksrarray_free(promise->catch_callbacks); + + // free promise data. + free(promise); +} diff --git a/include/ksr/regex.h b/include/ksr/regex.h new file mode 100644 index 0000000..4665252 --- /dev/null +++ b/include/ksr/regex.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +/** + * Regex structure. + */ +typedef struct { + regex_t expr; + unsigned nmatches; + char **matches; +} ksregex; + +/** + * Create a ksregex from an expression. + * @param expression - the expression to compile. + * @param nmatches - the number of matches groups to get, 0 if you want none. + * @param flags - regex flags. + * @return - the created regex, NULL if an error happen. + */ +ksregex* ksregex_new(const char *expression, unsigned nmatches, int flags); +/** + * Create a ksregex from an expression with 0 match group. + * @param expression - the expression to compile. + * @param flags - regex flags. + */ +static inline ksregex* ksregex_new_nogroup(const char *expression, int flags) +{ return ksregex_new(expression, 0, flags); } + +/** + * Try to match the regex with a tested string. + * @param regex - the regex to match. + * @param tested - the string that should match the regex. + * @return - true if the string matches, false otherwise. + */ +bool ksregex_matches(ksregex *regex, const char *tested); + +/** + * Free ksregex matches. + */ +static inline void _ksregex_free_matches(ksregex *regex) +{ + if (regex->matches) + { + // free all matches. + for (unsigned i = 0; i < regex->nmatches; i++) free(regex->matches[i]); + // free matches array. + free(regex->matches); + regex->matches = NULL; + } +} + +/** + * Free a ksregex. + */ +static inline void ksregex_free(ksregex *regex) +{ + // free matches groups. + _ksregex_free_matches(regex); + + free(regex); // free regex data. +} diff --git a/include/ksr/string.h b/include/ksr/string.h new file mode 100644 index 0000000..e52a530 --- /dev/null +++ b/include/ksr/string.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +/** + * Check if two strings are equal. + * @param a - the first string to check. + * @param b - the second string to check. + */ +static inline bool str_equals(const char *a, const char *b) +{ return 0 == strcmp(a, b); } + +/** + * Convert a string to lowercase. + */ +char* to_lowercase(const char* str); diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..b6ee5ce --- /dev/null +++ b/meson.build @@ -0,0 +1,42 @@ +project('ksr', 'c', version : '0.1', default_options : ['warning_level=3']) +c = meson.get_compiler('c') + +# declare the dependencies of the library. +deps = [ dependency('jemalloc') ] +# add pthread dependency +deps += c.find_library('pthread') + +# includes +include_dirs = [ 'include' ] + +# source files. +libsrc = [ + 'src/arrays.c', + 'src/buffers.c', + 'src/promises.c', + 'src/regex.c', + 'src/files.c', + 'src/string.c', +] + +# library target. +libksr = library('ksr', libsrc, dependencies : deps, include_directories : include_dirs, install : true) + +# add the library to the dependencies of the executables. +deps += declare_dependency(link_with : libksr) + +# test executables. +test_ksrarrays = executable('test_ksrarrays', [ 'tests/ksrarrays.c' ], dependencies : deps, include_directories : include_dirs, install : false) +test_ksrbuffers = executable('test_ksrbuffers', [ 'tests/ksrbuffers.c' ], dependencies : deps, include_directories : include_dirs, install : false) +test_ksrpromises = executable('test_ksrpromises', [ 'tests/ksrpromises.c' ], dependencies : deps, include_directories : include_dirs, install : false) +test_ksregex = executable('test_ksregex', [ 'tests/ksregex.c' ], dependencies : deps, include_directories : include_dirs, install : false) +test_ksrstring = executable('test_ksrstring', [ 'tests/ksrstring.c' ], dependencies : deps, include_directories : include_dirs, install : false) +test_ksrfiles = executable('test_ksrfiles', [ 'tests/ksrfiles.c' ], dependencies : deps, include_directories : include_dirs, install : false) + +# tests. +test('ksrarrays', test_ksrarrays) +test('ksrbuffers', test_ksrbuffers) +test('ksrpromises', test_ksrpromises) +test('ksregex', test_ksregex) +test('ksrfiles', test_ksrfiles) +test('ksrstring', test_ksrstring) diff --git a/src/arrays.c b/src/arrays.c new file mode 100644 index 0000000..58087b7 --- /dev/null +++ b/src/arrays.c @@ -0,0 +1,41 @@ +#include + +ksrarray* ksrarray_new(const size_t start_size) +{ + // allocate array. + ksrarray *array = malloc(sizeof(ksrarray)); + + // initialize array fields. + array->_allocated_size = start_size; + array->length = 0; + // allocate array from start size. + array->data = malloc(sizeof(void*) * array->_allocated_size); + + return array; // return initialized array. +} + +void ksrarray_push(ksrarray *array, void *element) +{ + if (array->length == array->_allocated_size) + { // cannot add a new element without allocating more memory. + array->_allocated_size *= 2; // allocate two times bigger space. + array->data = realloc(array->data, sizeof(void*) * array->_allocated_size); // reallocate array. + } + + // push the element at the end of the array. + array->data[array->length++] = element; +} + +bool ksrarray_remove(ksrarray *array, const size_t index) +{ + if (index >= array->length) + // the index is not associated with a value, cannot delete it. + return false; + + array->length--; // length is decreasing because we are removing a value. + for (size_t i = index; i < array->length; i++) + // moving each value left. + array->data[i] = array->data[i+1]; + + return true; +} diff --git a/src/buffers.c b/src/buffers.c new file mode 100644 index 0000000..a7caddb --- /dev/null +++ b/src/buffers.c @@ -0,0 +1,36 @@ +#include +#include + +ksrbuffer* ksrbuffer_new(const size_t buffer_size) +{ + // allocate buffer. + ksrbuffer *buffer = malloc(sizeof(ksrbuffer)); + + buffer->length = buffer_size; + // allocate buffer bytes. + buffer->bytes = malloc(buffer->length); + + return buffer; // return allocated buffer. +} + +ksrbuffer* ksrbuffer_new_empty(const size_t buffer_size) +{ + // create buffer. + ksrbuffer *buffer = ksrbuffer_new(buffer_size); + + // cleanup allocated memory. + memset(buffer->bytes, 0, buffer->length); + + return buffer; // return created buffer. +} + +ksrbuffer* ksrbuffer_new_from_content(const size_t buffer_size, const char *buffer_content) +{ + // create buffer. + ksrbuffer *buffer = ksrbuffer_new(buffer_size); + + // cleanup allocated memory. + memcpy(buffer->bytes, buffer_content, buffer->length); + + return buffer; // return created buffer. +} diff --git a/src/files.c b/src/files.c new file mode 100644 index 0000000..28ac17d --- /dev/null +++ b/src/files.c @@ -0,0 +1,115 @@ +#include "ksr/buffers.h" +#include + +#include +#include +#include +#include +#include +#include + +char* path_exp_simple(const char* path) +{ + // expanding the path. + wordexp_t exp; + wordexp(path, &exp, 0); + // copying the first expanded path. + char *expanded_path = strdup(exp.we_wordv[0]); + wordfree(&exp); // freeing the path expansion. + + return expanded_path; // returning the first expanded path. +} + +int mkpath(const char *path, const mode_t mode) +{ + // getting the current directory path to create. + char *fullpath = strdup(path); + char *dirpath = dirname(fullpath); + + int res; // result of mkpath / mkdir. + DIR *dir = opendir(dirpath); // trying to open the directory, to see if it exists. + if (dir) // the parent directory exists, closing it. + closedir(dir); + else + { // the parent directory does not exist, creating it recursively. + res = mkpath(dirpath, mode); + if (res != 0) goto err; // returning the error instantly if there is one. + } + + // creating the current directory. + res = mkdir(dirpath, mode); + if (errno == EEXIST) + // the directory exists, everything have been created correctly. + res = 0; + + err: + free(fullpath); + return res; // if an error happened while creating, returning it. +} + +ksrbuffer* read_file(FILE *file) +{ + if (file) + { // the file stream is open. + // searching the end of the file. + fseek(file, 0, SEEK_END); + long filesize = ftell(file); // getting file size. + // getting back to the beginning of the file. + fseek(file, 0, SEEK_SET); + + // allocating a buffer corresponding to the file size, plus the end of string char. + ksrbuffer *buffer = ksrbuffer_new(filesize + 1); + + // reading the file into the buffer. + fread(buffer->bytes, 1, buffer->length, file); + buffer->bytes[buffer->length - 1] = 0; // 0-terminated string. + + return buffer; // returning allocated buffer. + } + else // the file is not readable, just returning NULL as we cannot read it. + return NULL; +} +ksrbuffer* read_file_from_path(const char *file_path) +{ + // expanding the file path. + char *fullpath = path_exp_simple(file_path); + + // opening file. + FILE *file = fopen(fullpath, "r"); + + free(fullpath); // freeing used full path. + + if (file) + { // the file is readable. + // reading file stream. + ksrbuffer *buffer = read_file(file); + fclose(file); // closing the read file. + + return buffer; // returning allocated buffer. + } + else // the file is not readable, just returning NULL as we cannot read it. + return NULL; +} + +bool write_file(const char *file_path, const ksrbuffer *buffer) +{ + // expanding the file path. + char *fullpath = path_exp_simple(file_path); + + // creating directories if it is necessary. + mkpath(fullpath, 0755); + // opening file. + FILE *file = fopen(fullpath, "w"); + + free(fullpath); // free full path to file. + + if (file) + { // the file is writable. + // write buffer bytes to the file. + fwrite(buffer->bytes, 1, buffer->length, file); + fclose(file); // closing file stream. + return true; // returning true because the file have been written. + } + else + return false; // returning false because the file could not be written. +} diff --git a/src/promises.c b/src/promises.c new file mode 100644 index 0000000..b76ce1f --- /dev/null +++ b/src/promises.c @@ -0,0 +1,168 @@ +#include "ksr/arrays.h" +#include +#include +#include + +// global variable indicating if the program has been interrupted or not. +bool interrupted = false; + +/** + * Function launched on signal reception. + * @param signal - the received signal. + */ +void onsigint(/*int signal*/) +{ + //if (signal == SIGINT) + // the program is now interrupted. + interrupted = true; +} + +/** + * Promise thread structure. + */ +typedef struct { + ksrpromise *promise; + ksrpromise_execution_f exec; + void *data; +} promise_thread_data; + +/** + * Promise then user callback. + */ +typedef struct { + ksrpromise_then_callback_f callback; + void *userdata; +} promise_then_user_callback; +/** + * Promise catch user callback. + */ +typedef struct { + ksrpromise_catch_callback_f callback; + void *userdata; +} promise_catch_user_callback; + +/** + * The main promise thread function. + * @param data - thread data. + */ +void* promise_thread(void *data) +{ + // reading thread data. + promise_thread_data *thread_data = data; + + // executing the promise. + thread_data->exec(thread_data->promise, thread_data->data); + + return NULL; // this thread has no result. +} + +ksrpromise* ksrpromise_new(ksrpromise_execution_f exec, void *data) +{ + // allocate the promise. + ksrpromise *promise = malloc(sizeof(ksrpromise)); + promise->then_callbacks = ksrarray_new_tiny(); + promise->catch_callbacks = ksrarray_new_tiny(); + promise->successful = false; + promise->result = NULL; + promise->error_code = 0; + promise->error_message = NULL; + + // allocate thread data. + promise_thread_data *thread_data = malloc(sizeof(promise_thread_data)); + // assign thread data. + thread_data->promise = promise; + thread_data->exec = exec; + thread_data->data = data; + + // initialize and run a new thread. + pthread_create(&promise->thread, NULL, promise_thread, thread_data); + + return promise; // returning the created promise. +} + +/** + * Run then callbacks for the given promise. + * @param promise - the promise for which to run then callbacks. + */ +void ksrpromise_run_then_callbacks(ksrpromise *promise) +{ + promise_then_user_callback *user_callback; // initialize user callback pointer. + for (size_t i = 0; i < promise->then_callbacks->length; i++) + { // for each then callback, execute it. + user_callback = promise->then_callbacks->data[i]; // getting callback. + (user_callback->callback)(promise->result, user_callback->userdata); // executing callback with promised result. + } +} + +void ksrpromise_resolve(ksrpromise *promise, void *result) +{ + // save success and result. + promise->successful = true; + promise->result = result; + + // running then callbacks. + ksrpromise_run_then_callbacks(promise); +} + +/** + * Run reject callbacks for the given promise. + * @param promise - the promise for which to run reject callbacks. + */ +void ksrpromise_run_catch_callbacks(ksrpromise *promise) +{ + promise_catch_user_callback *user_callback; // initialize user callback pointer. + for (size_t i = 0; i < promise->catch_callbacks->length; i++) + { // for each catch callback, execute it. + user_callback = promise->catch_callbacks->data[i]; // getting callback. + (user_callback->callback)(promise->error_code, promise->error_message, user_callback->userdata); // executing callback with the promise error. + } +} + +void ksrpromise_reject(ksrpromise *promise, int code, const char *message) +{ + // save error details. + promise->error_code = code; + promise->error_message = message; + + // running catch callbacks. + ksrpromise_run_catch_callbacks(promise); +} + +void* ksrpromise_await(ksrpromise *promise) +{ + // register signal reception callback. + signal(SIGINT, onsigint); + + while (!interrupted) + { // infinite loop, waiting for promise end or interruption. + if (!ksrpromise_is_running(promise)) + // promise is not running anymore, returning result if there is one. + return promise->result; + + usleep(KSRPROMISES_AWAIT_SLEEP_DELAY); // sleeping a bit... + } + + return NULL; // the promise have been interrupted before the result was found. +} + +void ksrpromise_then(ksrpromise *promise, ksrpromise_then_callback_f callback, void *userdata) +{ + // allocate user callback. + promise_then_user_callback *user_callback = malloc(sizeof(promise_then_user_callback)); + user_callback->callback = callback; + user_callback->userdata = userdata; + + // we use a pointer of the given function pointer as a workaround for generic arrays (see https://stackoverflow.com/a/16682718/7496951). + ksrarray_push(promise->then_callbacks, user_callback); +} + +void ksrpromise_catch(ksrpromise *promise, ksrpromise_catch_callback_f callback, void *userdata) +{ + // allocate user callback. + promise_catch_user_callback *user_callback = malloc(sizeof(promise_catch_user_callback)); + user_callback->callback = callback; + user_callback->userdata = userdata; + + // we use a pointer of the given function pointer as a workaround for generic arrays (see https://stackoverflow.com/a/16682718/7496951). + ksrarray_push(promise->catch_callbacks, user_callback); +} diff --git a/src/regex.c b/src/regex.c new file mode 100644 index 0000000..fca4d18 --- /dev/null +++ b/src/regex.c @@ -0,0 +1,58 @@ +#include +#include +#include + +ksregex* ksregex_new(const char *expression, unsigned nmatches, int flags) +{ + ksregex *regex = malloc(sizeof(ksregex)); + regex->nmatches = nmatches; + regex->matches = NULL; + + if (regcomp(®ex->expr, expression, // compile expression in regex. + flags | REG_EXTENDED | (nmatches == 0 ? REG_NOSUB : 0)) != 0) + return NULL; // an error happened, returning NULL. + + return regex; // return created regex. +} + +void ksregex_save_string_matches(ksregex *regex, regmatch_t *raw_matches, const char *source) +{ + // allocate string matches array. + regex->matches = malloc(sizeof(char *) * regex->nmatches); + + // save full string match. + regex->matches[0] = strdup(source); + + for(unsigned i = 1; i < regex->nmatches; i++) + { // for each group match, converting it to an independent string. + regoff_t current_match_length = raw_matches[i].rm_eo - raw_matches[i].rm_so; + regex->matches[i] = malloc(current_match_length + 1); // allocate the current group match string. + // copying the string part from source to the current match string. + strncpy(regex->matches[i], &source[raw_matches[i].rm_so], current_match_length); + regex->matches[i][current_match_length] = 0; // set end of string. + } +} + +bool ksregex_matches(ksregex *regex, const char *tested) +{ + // if there was matches, free them. + _ksregex_free_matches(regex); + + // allocate raw matches array. + regmatch_t *raw_matches = malloc(sizeof(regmatch_t) * regex->nmatches); + + if (regexec(®ex->expr, tested, regex->nmatches, raw_matches, 0) != 0) + { // an error happened, freeing results then returning NULL. + free(raw_matches); // free raw groups matches. + return false; + } + + if (regex->nmatches > 0) + { // there are matches, getting them. + ksregex_save_string_matches(regex, raw_matches, tested); + } + + free(raw_matches); // free raw groups matches. + + return true; // no error, return true. +} diff --git a/src/string.c b/src/string.c new file mode 100644 index 0000000..e74c277 --- /dev/null +++ b/src/string.c @@ -0,0 +1,15 @@ +#include +#include + +char* to_lowercase(const char* src) +{ + // duplicate the string. + char *lowercased = strdup(src); + char *ret = lowercased; // store the first pointer to return it at the end of the conversion. + + for (;*lowercased; lowercased++) + // for each character, convert it to lowercase. + *lowercased = tolower(*lowercased); + + return ret; // returning the first string pointer. +} diff --git a/tests/ksrarrays.c b/tests/ksrarrays.c new file mode 100644 index 0000000..196d609 --- /dev/null +++ b/tests/ksrarrays.c @@ -0,0 +1,58 @@ +#include +#include +#include + +int main(/*int argc, char *argv[]*/) +{ + // try to create arrays. + ksrarray *array_tiny = ksrarray_new_tiny(); + ksrarray *array_small = ksrarray_new_small(); + ksrarray *array_basic = ksrarray_new_basic(); + ksrarray *array_big = ksrarray_new_big(); + ksrarray *array_huge = ksrarray_new_huge(); + + // try to push things in arrays. + ksrarray_push(array_tiny, "hello"); + ksrarray_push(array_tiny, "world"); + + // check that things have been inserted properly. + assert(strcmp(array_tiny->data[0], "hello") == 0); + assert(strcmp(array_tiny->data[1], "world") == 0); + assert(array_tiny->_allocated_size == KSRARRAY_TINY_SIZE); + assert(array_tiny->length == 2); + + // try to remove something. + ksrarray_remove(array_tiny, 0); + // try to remove something that does not exists. + ksrarray_remove(array_tiny, 3); + + // check that the removal produces a good result. + assert(strcmp(array_tiny->data[0], "world") == 0); + assert(array_tiny->_allocated_size == KSRARRAY_TINY_SIZE); + assert(array_tiny->length == 1); + + // insert more things. + ksrarray_push(array_tiny, "hello"); + ksrarray_push(array_tiny, "you"); + ksrarray_push(array_tiny, "!"); + + // check that the allocated size has increased. + assert(array_tiny->length == 4); + assert(array_tiny->_allocated_size == 2*KSRARRAY_TINY_SIZE); + + // check that foreach is working. + char *element; + ksrarray_foreach((*array_tiny), element) + { + assert(element); + } + + // free arrays. + ksrarray_free(array_tiny); + ksrarray_free(array_small); + ksrarray_free(array_basic); + ksrarray_free(array_big); + ksrarray_free(array_huge); + + return 0; +} diff --git a/tests/ksrbuffers.c b/tests/ksrbuffers.c new file mode 100644 index 0000000..6c10171 --- /dev/null +++ b/tests/ksrbuffers.c @@ -0,0 +1,49 @@ +#include +#include + +int main(/*int argc, char *argv[]*/) +{ + // create an empty buffer and check that it is properly instantiated. + ksrbuffer *empty_buffer = ksrbuffer_new_empty(30); + assert(empty_buffer->bytes[0] == 0); + assert(empty_buffer->bytes[10] == 0); + assert(empty_buffer->bytes[20] == 0); + assert(empty_buffer->bytes[29] == 0); + + // create a buffer and set some bytes. + ksrbuffer *buffer = ksrbuffer_new(20); + + buffer->bytes[0] = 12; + buffer->bytes[5] = 3; + buffer->bytes[10] = 44; + buffer->bytes[15] = 54; + buffer->bytes[19] = 8; + + // create a buffer from existing content, taking raw content from the previous buffer. + ksrbuffer *copied_buffer = ksrbuffer_new_from_content(buffer->length, buffer->bytes); + + // checking that content has been properly copied. + assert(buffer->bytes[0] == copied_buffer->bytes[0]); + assert(buffer->bytes[5] == copied_buffer->bytes[5]); + assert(buffer->bytes[10] == copied_buffer->bytes[10]); + assert(buffer->bytes[15] == copied_buffer->bytes[15]); + assert(buffer->bytes[19] == copied_buffer->bytes[19]); + + assert(copied_buffer->bytes[0] == 12); + assert(copied_buffer->bytes[5] == 3); + assert(copied_buffer->bytes[10] == 44); + assert(copied_buffer->bytes[15] == 54); + assert(copied_buffer->bytes[19] == 8); + + // try to create a default buffer. + ksrbuffer *default_buffer = ksrbuffer_new_default(); + assert(default_buffer->length == BUFSIZ); + + // free buffers. + ksrbuffer_free(empty_buffer); + ksrbuffer_free(buffer); + ksrbuffer_free(copied_buffer); + ksrbuffer_free(default_buffer); + + return 0; +} diff --git a/tests/ksregex.c b/tests/ksregex.c new file mode 100644 index 0000000..cf1f4f8 --- /dev/null +++ b/tests/ksregex.c @@ -0,0 +1,45 @@ +#include +#include +#include + +int main(/*int argc, char *argv[]*/) +{ + // try a simple regex and check that it matches properly. + ksregex *reg1 = ksregex_new_nogroup("^[0-9]+$", 0); + assert(ksregex_matches(reg1, "12") == true); + assert(reg1->matches == NULL); + + // try a more complex regex with matches groups. + ksregex *reg2 = ksregex_new("^([0-9])([a-z])$", 3, REG_ICASE); + assert(ksregex_matches(reg2, "1a")); + assert(!ksregex_matches(reg2, "H8")); + assert(!ksregex_matches(reg2, "4MM")); + assert(!ksregex_matches(reg2, "4_")); + assert(ksregex_matches(reg2, "3F")); + + // check that groups have been extracted properly. + assert(str_equals("3F", reg2->matches[0])); + assert(str_equals("3", reg2->matches[1])); + assert(str_equals("F", reg2->matches[2])); + + // check that groups are properly extracted even in more complex cases. + ksregex *reg3 = ksregex_new("^([0-9]+)([a-z]?)$", 3, 0); + assert(ksregex_matches(reg3, "123545")); + assert(str_equals("123545", reg3->matches[0])); + assert(str_equals("123545", reg3->matches[1])); + assert(str_equals("", reg3->matches[2])); + + // check that matches are empty when matching fail. + assert(!ksregex_matches(reg3, "123bA")); + assert(reg3->matches == NULL); + + // try to create an invalid regex. + assert(ksregex_new_nogroup("^(123$", 0) == NULL); + + // try to free instantiated regex. + ksregex_free(reg1); + ksregex_free(reg2); + ksregex_free(reg3); + + return 0; +} diff --git a/tests/ksrfiles.c b/tests/ksrfiles.c new file mode 100644 index 0000000..4751302 --- /dev/null +++ b/tests/ksrfiles.c @@ -0,0 +1,33 @@ +#include +#include + +int main(/*int argc, char *argv[]*/) +{ + // delete test directory if it exists. + system("rm -Rf ksrtesttesttmp"); + + // create a new buffer and write it in a file. + ksrbuffer *buffer = ksrbuffer_new_from_content(12, "Hello World!"); + write_file("ksrtesttesttmp/tmp/test.tmp", buffer); + + // try to read the previously written file, and checking that the buffer has been read properly. + ksrbuffer *read_buffer = read_file_from_path("test.tmp"); + assert(read_buffer->length == 13); + assert(read_buffer->bytes[0] == 'H'); + assert(read_buffer->bytes[3] == 'l'); + assert(read_buffer->bytes[7] == 'o'); + assert(read_buffer->bytes[11] == '!'); + assert(read_buffer->bytes[12] == 0); + + // delete test directory. + system("rm -Rf ksrtesttesttmp"); + + // try to write a file where it is not writable. + assert(write_file("/dev/test/test.tmp", buffer) == false); + // try to read a non-existing file. + assert(read_file_from_path("/dev/test.tmp") == NULL); + // try to read a NULL file. + assert(read_file(NULL) == NULL); + + return 0; +} diff --git a/tests/ksrpromises.c b/tests/ksrpromises.c new file mode 100644 index 0000000..fc69eb1 --- /dev/null +++ b/tests/ksrpromises.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +void promise_execution_ok(ksrpromise *promise, void *data) +{ + // checking that user data has been properly transfered. + assert(data == NULL); + + sleep(1); + // try to resolve the promise. + ksrpromise_resolve(promise, "OK"); +} + +void promise_execution_sigint(/*ksrpromise *promise, void *data*/) +{ + // raise a signal to interrupt await. + raise(SIGINT); +} + +void promise_execution_error(ksrpromise *promise, void *data) +{ + // try to read the error details from user data ; the content will be checked in the catch callback. + const char* error = data; + sleep(1); + // try to reject the promise with an error. + ksrpromise_reject(promise, 124, error); +} + +void on_promise_ok(void *result, void *userdata) +{ + // checking that result and user data have been properly transfered. + assert(str_equals("OK", result)); + assert(userdata == NULL); +} + +void on_promise_failed(int error_code, const char *error_message, void *userdata) +{ + // checking that error details and user data have been properly transfered. + assert(error_code == 124); + assert(str_equals("hello", error_message)); + assert(str_equals("blabla", userdata)); +} + +int main(/*int argc, char *argv[]*/) +{ + // try to create a promise. + ksrpromise *promise = ksrpromise_new(promise_execution_ok, NULL); + // checking that then is properly launched. + ksrpromise_then(promise, on_promise_ok, NULL); + // checking that await is working properly on a successful promise. + assert(str_equals("OK", ksrpromise_await(promise))); + + // try to create a promise which fails. + ksrpromise *promise_fail = ksrpromise_new(promise_execution_error, "hello"); + // checking that catch is properly launched. + ksrpromise_catch(promise_fail, on_promise_failed, "blabla"); + // checking that await is working properly on a promise which fails. + assert(ksrpromise_await(promise_fail) == NULL); + + // try to create a promise that will be interrupted. + ksrpromise *interrupted_promise = ksrpromise_new(promise_execution_sigint, NULL); + // checking that interruption is handled properly in an await. + assert(NULL == ksrpromise_await(interrupted_promise)); + + return 0; +} diff --git a/tests/ksrstring.c b/tests/ksrstring.c new file mode 100644 index 0000000..3f42c8d --- /dev/null +++ b/tests/ksrstring.c @@ -0,0 +1,15 @@ +#include "ksr/string.h" +#include +#include + +int main(/*int argc, char *argv[]*/) +{ + assert(str_equals("hello", to_lowercase("heLLo"))); + assert(str_equals("hello", to_lowercase("HELLO"))); + assert(str_equals("hello", to_lowercase("Hello"))); + assert(str_equals("hello", to_lowercase("hellO"))); + + assert(!str_equals("heLLo", to_lowercase("heLLo"))); + + return 0; +}